ローカルのファイルを暗号化しながらリモートに保存する

Posted on 2019/01/13 in tech

そもそも論

シェルを書くなら標準入出力ベースで考えるのがスマートだと思うのです。

極論いうと

$ cat hoge.txt

みたいなのは

$ cat < hoge.txt

と書こう、みたいな。

流石に cat でこんなことはしないけれど、

  • 読み込みは標準入力
  • 出力は標準出力

をベースとして考えるのが、シェラー1の道第一歩だと思うのです。

ローカルのファイルをリモートサーバに保存したい。

たまたまそれなりなサイズの嫁2動画ファイルが手元にあって、いいアーカイブ場所を探してたときに、いい感じに容量が有り余ってるレンタルサーバがあった。

......これはもう ここに置くしかない でしょう。

ただ、レンタルサーバというのは基本管理者(サービス提供者)が自由にアクセス・閲覧できるサーバである。 そこにプライベートなものを置くのは多少なり抵抗があるので、 暗号化したい

対象ファイル

ファイルというかディレクトリ。ビデオカメラで撮影したデータを SD カード経由で取り込み、 そのままのディレクトリ階層で保存したい

ここでは単純に hoge,foo,bar の 3 つのディレクトリを保存することを考える。

$ ls
hoge foo bar

保存先は ssh 接続可能なリモートサーバ。
保存先のディレクトリは backup としましょう。~/.ssh/config 設定済なら以下のような感じ。

$ ssh remote
# ここからリモートサーバ
$ ls
backup # ここに保存する

scp でいうとこの remote:backup で表されるパスに保存する感じ。

保存方法

ざっくりこれらを使ってリモートに保存する。

  • ssh
    • リモートサーバに置く
  • openssl
    • 暗号化する
  • tar
    • アーカイブする

なお、動画ファイルは圧縮済3なので、圧縮処理は行わない。

手間の多い保存方法

1 個 1 個頑張る。

$ tar cf hoge.tar hoge
$ openssl enc -e -aes-256-cbc -in hoge.tar -out hoge.tar.enc
$ scp hoge.tar.enc remote:backup
$ tar cf foo.tar foo
$ openssl enc -e -aes-256-cbc -in foo.tar -out foo.tar.enc
$ scp foo.tar.enc remote:backup
$ tar cf bar.tar bar
$ openssl enc -e -aes-256-cbc -in bar.tar -out bar.tar.enc
$ scp bar.tar.enc remote:backup

疲れた。

for を使う

先程の保存方法は

  1. tar でアーカイブ化
  2. openssl で暗号化
  3. scp でリモートサーバへ保存

の繰り返しなので、for を使って記述できる。

$ for d in *; do \
tar -cf $d.tar $d; \
openssl enc -e -aes-256-cbc -in $d.tar -out $d.tar.enc -pass pass:p@ssw0rd; \
scp $d.tar.enc remote:backup \
done

上記のように、openssl-pass オプションをつけることで、プロンプトからパスワードをいちいち入力する手間を省くことができる。

ある程度これで楽にはなったけど、ゴミファイルが残る。

$ ls
hoge
hoge.tar
hoge.tar.enc
foo
foo.tar
foo.tar.enc
bar
bar.tar
bar.tar.enc

rm *.tar* すればいい話だが、このディレクトリ 1 つあたり 10 GB とかある。
単純にローカルに 3 倍のバッファ容量が必要になるってエコロジーではない。スマートにいきたい。

シェルを使うなら標準入出力を駆使せよ

で、そもそも論に戻ります。

そもそも openssl になんで -in-out ってオプションがあると思います?(煽り)

$ openssl enc -e -aes-256-cbc -in hogefile -out hogefile.enc

オプションがないとファイルを読み込めない? 書き出せない?

違う。これらは飽くまで オプション です。

デフォルトは標準入出力

通常、伝統的なコマンドはデフォルトとして 標準入力から読み込み4、標準出力に出力する ように実装されているものがほとんど(grep, awk, sed...)。

openssl enc の場合は 標準入力を暗号化して標準出力に出力する(「標準出力に出力」って別のよい言い方ないだろうか)

だから、-in-out がなくても、こう書ける。

$ openssl enc -e -aes-256-cbc < hogefile > hogefile.enc

何がいいのか

基本的にコマンドは 標準入出力を扱う ことを知っていれば、上のコマンドは 初見で何しているのかが直感的にわかる んですよ。

そして、シェルを扱う上で必須の パイプ を駆使すれば、コマンドの組み合わせで柔軟に処理を記述できる。

リモートに一発で置く

つまり、 ディレクトリは暗号化しながらリモートに一発でおける。

$ for d in *; do \
tar -c $d | \ # アーカイブして
pv -s $(du -sb $d | awk '{print $1}') | \ # 進捗表示しながら(なくてもよい)
openssl enc -e -aes-256-cbc -pass pass:p@ssw0rd | \ # 暗号化して
ssh remote "cat > backup/$d.tar.enc" ; \ # リモートに保存
done

tar -c $d はあえて tar -cf - $d とは書かなかった5。実は tartar -c file だけでアーカイブデータを標準出力に吐いてくれる。6 7

pv コマンドは cat にデータの処理状況を表示する機能がついたものだと思えばいい。Pipe Viewer の略で pv らしい。
-s オプションで進捗データ全体のサイズをバイトで指定できるので、du -sb $d | awk '{print $1}'8ディレクトリのバイトサイズを取得している。

まとめ

標準入出力つかおう。


参考


  1. いま思いついた造語。シェルを巧みに書くひと。 

  2. 3次元でリアルです。ダンスの舞台とか健全な動画です。 

  3. MPEG や MOV などは、動画に適した圧縮方法で圧縮済なので、これを gzip 化などするとかえって容量が増えたりする 

  4. 引数がある場合は、標準入力ではなく引数に指定されたファイルを読み込む場合が多い。 

  5. 多くのコマンドで - は標準入力もしくは標準出力を表す。 

  6. BSD 系だとうまくいかないこともあったので、-f オプションを使ったやり方も大事。 

  7. 逆に、tar -x は標準入力からアーカイブデータを読み込んで展開してくれる(tar -xf - としなくてもよい)。 

  8. BSD 系は du-b (バイト表示)オプションが使えなかった。Mac なら coreutils をインストール(brew install coreutils)して、gdu -sb とできる。