rsync（over SSH）をセキュアに設定する

# はじめに
rsyncでフォルダの同期・バックアップを自動化している方はそれなりにいらっしゃると思います。私もサーバインフラ系の仕事をしていた頃にはバリバリに使っていました。
ただ最近、それをセキュアに設定する方法について相談を受けて、「ここを見て設定すればいいよ」と答えられそうなサイトを探してみると、まとまっている情報は意外とないことに気付きました。そこで（たまには）自分でまとめてみることにしました。
Qiita初執筆なので至らない点も多いと思いますが、気付いたところがあればコメントいただけると嬉しいです。

# 概要
rsyncで使われるデフォルトシェルは、[2004年からssh](https://download.samba.org/pub/rsync/NEWS#2.6.0)になっています。若い人は「生まれる前から」という人もいるかも知れませんね。
SSHのポートを開けるということは、何も考えずに設定してしまうとサーバ側に対して**リモートシェルが可能**になってしまい、セキュリティ的には侵入のリスクをかなり上げてしまうことになります（というのはrshの頃から変わりませんが）。
そういうこと（リモートシェル経由での侵入）ができないように、できるだけセキュアに設定する方法を自分なりにまとめてみました。

## 環境
以降の設定の検証には、AlmaLinux 9.4（openssh-8.7p1 + rsync-3.2.3）を使いました。
設定ファイル名やパスは他の環境だと異なるかも知れませんが、設定内容はあまり変わらないと思います。その他、気付いた点があればコメントください。

構成としては、業務システム（almasys1/172.16.101.132）でローカルにダンプ/スナップショットしておいたディレクトリ（/path/to/dump）に対し、バックアップサーバ（almabk1/172.16.101.131）から業務システムに接続して、PULLでこのシステムのバックアップ用ディレクトリ（/backup/almasys1）にコピーする、という想定です。
|ホスト種別|ホスト名|IPアドレス|ユーザ名|パス|
|:-:|:-:|:-:|:-:|:-:|
|バックアップサーバ|almabk1|172.16.101.131|bksys1|/backup/almasys1|
|業務システム|almasys1|172.16.101.132|backup|/path/to/dump|
```mermaid
stateDiagram-v2
 almabk1 --> almasys1: 接続（rsync over SSH）
 almasys1 --> almabk1: データコピー
```

# 設定方法
以下のすべて、または一部（なるべく多く）の設定の組み合わせを行うことをお勧めします。

## 接続元IPアドレス制限
SSH/rsyncサーバが接続されているネットワーク機器やファイアウォール（FW）が管理下にある場合は、SSH（TCP/22）ポートに対する接続元IPアドレスを限定しておきましょう。
ネットワーク機器での設定ができない場合や、障壁を多重にしておきたい場合は、以下のようにOSのFWやアプリでも接続元を絞ります。

### firewalld
SSHサービスを無効化してから、バックアップサーバのIPアドレスからだけSSH接続を許可します。
```bash
[user@almasys1 ~]$ sudo firewall-cmd --list-all
...
  services: cockpit dhcpv6-client ssh
...
[user@almasys1 ~]$ sudo firewall-cmd --remove-service=ssh --permanent
[user@almasys1 ~]$ sudo firewall-cmd --zone=public --add-rich-rule="rule family=ipv4 source address=172.16.101.131/32 port protocol=tcp port=ssh accept" --permanent
[user@almasys1 ~]$ sudo firewall-cmd --reload
[user@almasys1 ~]$ sudo firewall-cmd --list-all
...
  services: cockpit dhcpv6-client
...
  rich rules: 
	rule family="ipv4" source address="172.16.101.131/32" port port="ssh" protocol="tcp" accept
```

#### （メモ）TCP Wrapperは使えない
昔ながらのTCP Wrapper（hosts.alllow/deny）は、最近（RHEL系では8以降）は使えなくなっていると思います。
まだ使える環境の人はそっちで設定してもよいですが、そうじゃない＆まだ慣れていない（おっさん世代の）人は頑張ってfirewalldに慣れておきましょう。

### openssh server （sshd_config）
アプリレベルでも接続元を（＋ついでにユーザも）絞ります。
ちょっと迷うかも知れませんが、SSH接続を受ける際にSSHサーバ（almasys1）から見えるユーザはbackupとなります。
```ssh:almasys1:/etc/ssh/sshd_config.d/10-allowusers.conf
AllowUsers backup@172.16.101.131
```

## SSHパスワード認証無効化
パスワード認証は弱いので無効化（≒鍵認証強制）しておきましょう。
```ssh:almasys1:/etc/ssh/sshd_config
PasswordAuthentication no
```
何らかの制約でパスワード認証も併用する必要がある場合は、[Match](https://man.openbsd.org/sshd_config#Match)等を使ってパスワード認証の有効化/無効化をユーザ単位/ホスト単位で細かく設定してください。
今回の話のキモではなく、またパスワード認証を残しておくこと自体あまりオススメできないので、詳細は割愛します。

## 専用ユーザアカウント作成
業務システム側でrsync/ssh接続を受けるユーザ（backup）は、バックアップ専用に作成します。
シェルも無効化（-s /sbin/nologin）しておきます。
```bash
[user@almasys1 ~]$ sudo useradd backup -s /sbin/nologin
```
（メモ）ダンプ先ディレクトリの権限を厳しくしたり、chrootも設定した方がよいのですが、その部分は後日追記予定です。やりたい人は、とりあえずchrootとかでググっておいてください。

## 専用SSH鍵
SSH鍵もバックアップ専用に作成します。
```bash
[bksys1@almabk1 ~]$ ssh-keygen -t rsa -b 4096 -C "bksys1@almabk1"
```
* 暗号化方式はとりあえずRSAのまま（この辺りにこだわる人はそういう記事を読んでください）で、鍵長は気持ち長めにしておきます。
* 鍵の素性がわかるように、コメントも一応残しておきます。
* 鍵作成作業はSSHサーバ/クライアントどちらででもできますが、秘密鍵をネットワーク越しに移したり、複数箇所に存在するのは気持ち悪いので、SSHクライアント（バックアップサーバ）側で作成して、公開鍵をSSHサーバ（業務サーバ）にコピーします。
* （メモ）パスフレーズは、とりあえずなしにしています。パスフレーズありの鍵で自動化も必要な場合は、expectでパスフレーズ入力、といった情報を探してください。ただ、個人的にはあまりセキュリティ向上にならない気がしています（秘密鍵にアクセスできる権限が取られると、expectで使うパスフレーズも見られてしまうと思いますので）。

## 実行可能なコマンドを限定
作成したユーザ/SSH鍵でできること（コマンド）をrsyncだけに限定します。
まずは下準備です。
```bash
[bksys1@almabk1 ~]$ rsync -vv -az --delete --dry-run backup@almasys1:/path/to/dump/ /backup/almasys1/
opening connection using: ssh -l backup almasys1 rsync --server --sender -vvnlogDtprze.iLsfxCIvu . /path/to/dump/  (10 args)
...
```
以下の設定では、この _rsync --server --sender -vvnlogDtprze.iLsfxCIvu . /path/to/dump/_ の部分を使います。
設定は、ユーザ権限で（authorized_keysに）書くか、管理者権限で（sshd_configに）書くか、どちらでも可能です。

### ユーザ権限で設定
SSHの鍵設定（autnorized_keys）の行頭に *command="..."* で、その鍵でのリモート実行コマンドを限定します。
```ssh:backup@almasys1:~/.ssh/authorized_keys
command="rsync --server --sender -vvnlogDtprze.iLsfxCIvu . /path/to/dump/" ssh-rsa AAAAB3Nz...Xo1Nr4Bw== bksys1@almabk1
```
### 管理者権限で設定
同様の制限は、SSHサーバ自体の設定で行うことも可能です。
[Match](https://man.openbsd.org/sshd_config#Match)で対象ユーザを指定して、[ForceCommand](https://man.openbsd.org/sshd_config#ForceCommand)でコマンドを限定します。
```ssh:almasys1:/etc/ssh/sshd_config.d/99-rsync-only.conf
Match User backup
    ForceCommand rsync --server --sender -vvnlogDtprze.iLsfxCIvu . /path/to/dump/
```

### ちょこっと考察
セキュリティ面では、管理者権限での設定の方が若干よいと思います。
万が一ですが、opensshの脆弱性などで侵入できるようになって、上記のリモートコマンド制限の設定も上書きされる、というようなことがあった場合でも、管理者権限の設定であれば、さらに管理者権限も奪う（権限昇格）というハードルもあるので。

ただ、運用面では、バックアップ対象のシステム/サーバが増えると、設定のメンテもたいへんになると思われますので、ユーザ権限の設定で行う方がよいかも知れません。
実行可能なコマンドを（ユーザ単位でなく）鍵単位で限定できるのもいいと思います。

各々の環境に合わせて、適切な方（両方ならなおベター）を使ってください。