rrsync がステキ
必要なディレクトリだけ cpio なりでアーカイブして、 rsync over SSH で吸い出すことにする。 まだ設定してないけど。
というわけで、この鯖 (さくらの VPS) のクリティカルなデータを自宅の NAS にバックアップしましょう、と、ようやく重い腰を上げた次第。で、前々から目をつけてた rrsync がいざ使ってみたらちょいステキだったので軽く紹介してみようかと。
rrsync とは何ぞや
かの有名な rsync に付属のプログラム、というか 200 行ちょっとの Perl スクリプト。名前の由来は Restricted rsync の略 (のはず) 。
いざインターネット越しでサーバからデータを吸い出す処理を自動化しようと思うと、これが単純に見えて意外とめんどくさい。
--delete
とかチョー便利だから、やっぱ scp とかじゃなくて rsync 使いたいよね。- インターネットに出るなら通信を暗号化したいから、やっぱ通信は over SSH 一択だよね。
- その場合お決まりのパスフレーズなし鍵ペアで公開鍵認証、なんだけど、シェル開けるのはなんか怖いよね。
- えっ?ログイン時に実行するコマンドを公開鍵に設定できるの?でも rsync のサーバ側コマンドなんてどう決め打てばいいのさ!?
このモヤモヤをさくっと解決してくれるのが、 rrsync 。
ざっくり使い方と仕組み
まずは rrsync をサーバ上のどっかに置く。 Debian 系のパッケージならたぶん /usr/share/doc/rsync/scripts/rrsync.gz
に gzip 圧縮されたものが、 source tarball なら support/rrsync
に生スクリプトがあるはず。
次に rsync したいアカウントで普通に鍵ペアを作って、公開鍵認証の設定をする。で、ログインできるのを確認したらサーバ側でおもむろに authorized_keys の command=
に rrsync を設定する。アクセスを許可するサブディレクトリと、必要なら -ro
(read-only) オプションも。
これだけで、そのアカウントの権限で SSH ログインする際、許可されたサブディレクトリに対する rsync アクセス ( -ro
オプションが付いていれば pull なアクセス) 以外は慈悲なく蹴られるようになる。
仕組みとしては、公開鍵の設定で起動された rrsync が本来リクエストされた rsync のサーバ側コマンドライン (OpenSSH の仕様上、 $SSH_ORIGINAL_COMMAND
から取れる) と自分の引数を突き合わせて、問題がなければ rsync を exec するというもの。まぁ言ってしまえば単なるラッパなんだけど、これマジメに自分で書こうと思うとかなりめんどいはず。
まとめ
めんどくさいので細かい設定手順とか書かなかったけど、まぁ実際に自分で rrsync のソースを読んでみてくだせぇ。大した分量じゃないし、設定方法も書いてあるし。
rrsync を適切に設定できれば、万一秘密鍵を盗まれても被害を最小限に抑えられます。
# まぁ、そもそも秘密鍵を盗まれるという管理体制が問題アリアリなんじゃ、というツッコミもあるけど、それはまた別のお話。
参考文献
- rrsync のソースコード
- SSHD(8) の AUTHORIZED_KEYS FILE FORMAT セクション
2015-03-07 追記
どうも 2 月の頭くらいからバックアップが止まってた様子。いろいろ調べてみたところ この FIX で、 crontab に rsync のコマンドラインを書くときにリモートパスをより一層ご丁寧にクォートするようになったことが原因らしい。
"user"@remote-host:\""remote-path"\"
つまりこれは FreeNAS のエンバグか?と言えばそう簡単な話でもなくて、普通はサーバ側の rsync もシェル経由で実行されるので、シェルがクォートを外してくれるから何ら問題はない。一方で rrsync は $SSH_ORIGINAL_COMMAND
を自力でバラして解釈するので、結果的にクォートを外さずに rsync にパスを渡してしまい、
rsync: link_stat ""remote-path"" failed: No such file or directory (2)
とか言われるというわけ。そりゃ、ねーわ。
まぁイレギュラーなことしてるのはこっちですよねー、というわけで rrsync にクォート外す処理を入れて回避。
--- ./rrsync 2015-03-07 00:37:07+09 1.1
+++ ./rrsync 2015-03-07 00:43:58+09 1.2
@@ -180,6 +180,8 @@
s{^$}{.};
die "$0: do not use .. in any path!\n" if m{(^|/)\\?\.\\?\.(\\?/|$)};
}
+ # Remove quotation from args
+ s{^["'](.*)["']$}{$1} if m{^".*"$} or m{^'.*'$};
push(@args, bsd_glob($_, GLOB_LIMIT|GLOB_NOCHECK|GLOB_BRACE|GLOB_QUOTE));
}
}
セキュリティ的に本当に大丈夫かなこれ‥‥。