純規の暇人趣味ブログ

首を突っ込んで足を洗う

rsync+sshの自動バックアップをもっと安全にする

      HimaJyun

rsync+ssh(rsync over ssh)をcronで回して自動かつ安全にバックアップする方法は定番です。

とはいえ、これだとコマンドの拒否(rsync専用)やディレクトリのアクセス制限などは難しいです。

今回は「rsync daemon over ssh」を用いて、より細かいアクセス制限が出来る、延いてはより安全にcronで回せるrsyncの設定でもやってみようと思います。

より安全な自動バックアップを求めて

サーバであれば、当然ながらに別のサーバに対してバックアップを転送する設定はされているでしょう。

でないと「rm -rf /」でデータ仏の出来上がりです。(過去に遠隔バックアップをしてなかったが故にデータ消失被害が拡大したレンタルサーバーがありましてね……その名をファ……うわなにをするやめr)

昨今だとVPSを使うかも知れませんが、それもある日突然自分のせいでもないのにサーバを止められてデータ人質にされる事があります。

なので複数(VPSなら異なる企業)のサーバへバックアップを転送するというのは必要不可欠です、いつ何がどう吹き飛んでも構わないように。

で、それを実現するためにrsync+sshで転送するスクリプトをcronで回したりする、というのは一般的かと思われます。

思われます、が……その設定、殆どの人が無駄な事をしています、rsyncのdaemonモードを使った方が圧倒的に便利です。

という訳で、rsyncのdaemonモードを布教しようというのがこの記事の趣旨

rsync、daemonモード

しばしdaemonモードの布教にお付き合い下さいませ……

rsyncにはdaemonモードがあります、rsyncdとかって呼ばれる事の方が多いかな?

このdaemonモード、普通は常駐させることが多いのですが、実はSSHに包んで実行することが出来ます。(私は「rsync daemon over ssh」と呼んでいます)

(ちなみにLinuxの「デーモン」は「daemon」ですよ、「demon」ではないので注意)

通常のrsyncとdaemonの特徴的な違いは設定ファイルで細かい制御が出来る事です。

通常rsyncだとrsync専用にする(他のコマンドを使わせない)ためには-vオプションを付けて……というのもrsyncdでは不要です。

接続元の許可や拒否、書き込み専用/読み取り専用、アクセス範囲も細かく設定できます、最高です。(布教終わり

実際に設定する

ここではpush型のバックアップで設定します、pull型だと少し面倒かも?

SSHの鍵を設定する

専用ユーザを作る必要はありません、公開鍵を1つ追加すればその鍵がrsync専用になります。

要はバックアップに使うユーザのauthorized_keysに以下のように追加すれば良いわけです。

command="rsync --server --daemon --config=/path/to/rsyncd.conf .",no-pty,no-port-forwarding,no-X11-forwarding,no-agent-forwarding ssh-rsa 鍵

commandに「rsync --server --daemon --config=/path/to/rsyncd.conf .」を指定する事で、接続された瞬間にrsync daemonを起動します。(これでこの鍵でコマンドを使用される事はなくなります。)

/path/to/rsyncd.confは後で作成するので読み取れて管理しやすい所でも指定すればよいかと。

「no-pty,no-port-forwarding,no-X11-forwarding,no-agent-forwarding」でシェルやらポートフォワーディングを使わせないようにします。

この辺りは簡単

rsyncの設定ファイルを用意

先ほど指定した所にrsyncd.confを作成します。

設定項目は「rsyncd.conf」なんかでググれば出てきます。

参考までに私の設定を

# SSHで一般ユーザの場合は使えないのでno
use chroot = no
# 書き込む必要があるのでread onlyはno
read only = no
# 逆に読み取る必要はないのでwrite onlyをyesに
write only = yes
# 一旦すべて拒否
hosts deny = *
# 圧縮済みの物は再圧縮しない
dont compress = *.pdf *.jpg *.jpeg *.gif *.png *.mp3 *.mp4 *.ogg *.avi *.7z *.z *.gz *.tgz *.zip *.lzh *.bz2 *.rar *.xz

[foo]
  path = /path/to/backup/foo
  # 接続元のIPアドレスを設定
  hosts allow = 192.168.1.2

[bar]
  path = /path/to/backup/bar
  hosts allow = 192.168.1.3

まあだいたい読めば分かるかな?

こんな感じで、接続元とアクセス範囲を厳しめに設定しています。

ちなみに[foo]や[bar]の部分は「モジュール」と呼ぶそうです。

実際に転送してみる

rsyncの宛先アドレスとして該当のサーバやモジュールを指定します。

構文は「ユーザ@ホスト::モジュール/ディレクトリ」のような感じ

すなわち、以下のような感じ

rsync -e "ssh -i /path/to/key.rsa" /var/www test@rsync.local::foo/2017-05-02

これで/var/wwwのデータがrsync.localのモジュールfoo(/path/to/backup/foo)で設定したディレクトリ内に2017-05-02として(すなわち/path/to/backup/foo/2017-05-02に)送信されます。

同時に読み取りが出来ない事や他のホストから接続できない事も確認しておきましょう。

これで仮に鍵が盗まれても精々HDDが一杯になるまで書き込まれる程度の嫌がらせしか出来なくなります。

先ほどの設定でwrite onlyにして読み取り出来ないようにしているので、データが漏れる事はないでしょう。

もう少し細かい制御をしたい時は?

例えばn日以上経過したデータを削除する、link-destのために前回のバックアップを取得する、など少し細かい制御がしたい場合はちょっとしたシェルを作れば良いです。

authorized_keysで「command="/path/to/backup.sh"」みたいに指定し、自作のシェルを経由して最低限の操作だけは許可するようにします。

シェル側では環境変数「$SSH_ORIGINAL_COMMAND」を見て振り分けてやれば良いと思います、この辺りは書くと長くなりすぎるので各自ググるとして、ざっと以下のような感じ

#!/bin/bash -eu

if [ -z "$SSH_ORIGINAL_COMMAND" ];then
	echo "Missing argument."
fi
set $SSH_ORIGINAL_COMMAND

case "$1" in
	"latest" )
		# ここに以前のバックアップを取得する処理
	;;
	"clean" )
		# ここに古いバックアップを削除する処理
	;;
	"rsync" )
		rsync --server --daemon --config=/path/to/rsyncd.conf .
	;;
	* )
		echo "Invalid argument."
	;;
esac

これはあくまで例なので、実用する前にはもっと厳密な引数チェックを入れた方が良いです。

これでrsyncは今まで通り使いつつ、ssh経由で最低限のゴミ掃除くらいは出来ます。

例えば「ssh test@rsync.local "clean"」みたいにして接続すると、上のスクリプトのcleanの部分が発動してゴミが削除される、という具合。

出来上がり

こんな感じで、いくつかの手法を組み合わせれば「簡単で安全で高速」が現実的な手間で実現出来ます。

とにもかくにもバックアップは転送しないと意味がないので、これで完璧に少しでも近付けば……

データ仏だけは絶対に避けたい、積み上げたものが消え去るなんてのはごめんだぜ。

おまけ

おまけのrsync知見をいくつか……

フィンガープリント

たまにあり得るミスとして、バックアップ先サーバのフィンガープリントがknown_hostsに記録されていなくてcronがコケる、というのもあり得ます。

「ssh-keyscan -H rsync.local >> ~/.ssh/known_hosts」みたいにして、フィンガープリントを記録する事を忘れずに

圧縮の有無

rsyncは-zオプションで圧縮転送が出来るのですが、当然ながら圧縮にはCPUによる計算が必要であり、計算量が多いと却って低速化してしまいます。

手元で(かなり適当に)計った限りだと、50Mbpsを前後くらいから圧縮がオーバーヘッドになって低速化します。(圧縮率1で測定、当然ながらマシン性能に左右されます)

そのため、LAN内のサーバであれば基本的に圧縮せずに転送した方が良いでしょう。

WAN向けであれば「上り帯域を消費/圧迫しないために多少遅くなっても圧縮する」という選択はありだと思います。

圧縮率(--compress-levelで指定)は1(低圧縮)がコスパ高いと思います、1で80%ほどですが、9(最高圧縮)に上げても70%程にしかならないのに計算量だけは増えるので(デフォルトでは6)

ハードリンクで高速化

rsyncでハードリンクを使う方法は「rsyncの--link-destでの差分バックアップ」を見ていただければ良いのですが、これはリモートに転送する時でも使えます。

もちろんdaemonモードでも使えます。

ネットワーク経由で転送する時は特に恩恵が大きいのでぜひ使うと良いでしょう。

統計の出力

rsyncでは-vオプションによって転送したファイルを、--progressオプションで転送中の情報を表示できます。

ただ、これはcronで実行する(後でログとして見直す)には冗長過ぎて邪魔なので、cronで回すには--stats(--info=STATS2と同じ)オプション辺りが便利だと思います。

え?cronのログを/dev/nullに捨てている?なんだってー?!

cronのログは保管しておきましょう、/dev/nullを適当なファイルに変えるだけですし。

 - サーバ運営