セキュアな rsync - 理屈編

サーバ間で rsync を使ったファイルの同期の課題を与えられた後輩が、あれこれ調べてたどり着いたサイトの内容を後ろから覗き込んだ時、思わず「イケてない。自分ならやらない」と言ってしまったことがありました。セキュリティに気を使って「ssh 経由で rsync」までは良いとして、sshd_config で PermitRootLogin を yes にするのは、個人的には「ありえない」と思いました。

以前に、自分で ssh + rsync を実際に動かした時に、どんな風に考えて、どのような事をやったのか、書いてみます。

同期の方向

rsync で同期する際、マスター側からプッシュするのか、バックアップ側からプルするのか、というがあります。基本的にはどっちでも良いと思いますが、仮にバックアップ側がダウンしている場合に、余分なエラーを発生させたくない、と思い、バックアップ側からプルする事にしました。

通信方法

ssh 経由で rsync をすると、通信内容が暗号化される、というのは有名な手法です。インターネット越しでなければ、第3者が通信内容を取得することは案外、難しい*1のですが、暗号化しておくことに越したことはないです。

認証

rsync を手動で動かすのであれば、手でパスワードを入力すれば良いのですが、cron を使ってスケジュールで動かそうとすると、認証情報をどこかに保存する必要が出てきます。

ユーザ名とパスワードをそのままファイルとして保存するのはさすがにまずい、と思う人は多いと思いますが、その回避方法として、ssh の公開鍵認証で秘密鍵パスフレーズを外して使っている人がいました。これも一つの手だと思いますが、ちょっとトリッキーな感じがします。パスワードなどを入力しない方法としては、ホストベース認証を使う方が一般的です。

ホストベース認証でも、パスフレーズ無しの秘密鍵でも、同じ弱点があります。それは、秘密鍵が裸の状態でファイルに保存される点です。このファイルが流出してしまうと、パスワード認証でパスワード文字列が漏れたのと変わりありません*2。なので、秘密鍵が漏洩しない事が大前提になります。

その上で、ホストベース認証の方がベターな点は、「特定のホストと特定のユーザに対してのみ、ホスト鍵を使った認証を許可する」とう事ができる点です。

ssh サーバを用意する場合、通常は、rsync のみの用途ではなく、保守・管理用にログインするためにも使います。sshd で特定のホストを許可する場合、iptables のようなファイアーウォールか、/etc/hosts.allow、/etc/hosts.deny を使う事になりますが、という事は、rsync の同期対象以外に、保守用でログインしてくる可能性のある端末も許可する必要があります。

しかし、ホストベース認証であれば、ホストベース認証で許可されるホストとユーザを shosts.equiv ファイルを使って絞り込む事ができ、保守・管理用の端末に対する許可ポリシーより狭い範囲で許可する事ができます。

つまり、本当に rsync 用にだけ、ホストベース認証を適用できます。

rsync 用のユーザ

特定のユーザのファイルだけを同期する場合には、そのユーザで rsync を動かせば良い、という事になるのですが、不特定のユーザのファイルを対象とするのであれば、何らかの形で root 特権を利用する必要があります。

それを、sshd_config の「PermitRootLogin」を Yes にする事で回避するのはダメです。もし、パスワード認証が有効で、かつ、安易なパスワードが設定されていたら、ブルートフォースアタックで root が取られることになります*3

なので、rsync 用のアカウントを用意し、sudo を使って、「rsync 用のアカウントが rsync を実行する時だけ root 権限を与える」とします。

リモート側の rsync ユーザ

ローカル側は sudo 付きで rsync を呼び出せば、root 権限が使えます。しかし、それではリモート側で対応する rsync は一般ユーザで動きます。リモート側で rsync 用のアカウントが sudo の設定で root 権限を取れるようになっていても、root 権限を取れるのはあくまでも sudo 経由で rsync が呼ばれた時です。

ここで、rsync のマニュアルをじっと読みます。

rsync の Man Page

この中に「--rsync-path」というオプションにあります。

--rsync-path=PROGRAM
Use this to specify what program is to be run on the remote machine to start-up rsync. Often used when rsync is not in the default remote-shell's path (e.g. --rsync-path=/usr/local/bin/rsync).

このオプションで、リモート側のプログラムが指定出来ます。例では、デフォルトで見つかる rsync の代わりにフルパスを指定して /usr/local/bin/rsync を呼び出すようにしていますが、これを使って「sudo rsync」が呼び出されるようにすれば、リモート側の rsync も root 特権が使えます。

まとめ

と、ここまで考えてきたことをまとめます。

  • rsyncssh 経由で使う。
  • ssh の認証はホストベース認証を使う。
  • rsync を実行するためのアカウントを作る。
  • rsync 用のアカウントは sudo を使って、root 権限で rsync を実行出来るようにする。
  • rsync の --rsync-path を使って、リモート側の rsync を sudo 付きで起動するようにする。

理屈はこんなところですが、長くなったので実践編は後日にします。

*1:当事者となるホスト上では簡単です。第3者が傍受するには、それなりに仕掛けが必要になるので、不可能では無いけど、もし可能な状態になっているとしたら、もう、いろんな物が乗っ取られている状態です。

*2:それでも、公開鍵認証の方がベターなのは、ブルートフォースアタックに対する耐性がパスワード認証よりも高い点です。まぁ、すべてのアカウントで乱数で生成した何十字ものパスワードを設定すれば、パスワード認証でも耐性は高くなりますが。

*3:PermitRootLogin が No なら root が取られない訳ではないですが、root 以外のユーザであれば、そこから root を取るために、また別の攻撃を仕掛ける必要があります。感覚的には2ロックの扉みたいな感じです。