mattintosh note

どこかのエンジニアモドキの備忘録

lsyncd+rsync を root で安全に運用する方法を考える

負荷分散等でサーバを複数台使うことが増えて lsyncd を使うことが増えた。lsyncd は rsh セクションで使用する秘密鍵を自由に指定出来るのでセキュリティの観点から一般ユーザでデータのやり取りをするのが良いのだけど同期先に root で接続しなければならないことがある。

というのは複数のユーザがファイルやディレクトリを作成する環境だとファイルやディレクトリの Owner、Group がそれぞれのものになる。これを一般ユーザ(例えば lsyncd)でログイン、コピーしようとすると lsyncd の設定で archive = trueowner = truegroup = true を設定していても Owner はすべて lsyncd となる。同期先の環境ではパーミッションは変更出来ても所有者を変更する権限が無いので所有者を維持したまま同期したければ root を使わざるを得ない。(ローカルの lsyncd 自体は root で動く)

ネット上には lsyncd の設定で一般で同期させるために rsh = "ssh -i /home/lsyncd/.ssh/id_rsa" のような設定をする方法をあまり見かけないのでそもそも一般ユーザで同期させようとする方が特殊なのかもしれない。

ということで lsyncd と rsync を root で同期するけどなるべくセキュリティを高めたいなということで考えてみる。lsyncd 自体に設定をすることは出来ないの SSH の方をなんとかする。

Match ブロックで接続元を制限する

SSH サーバには Match ブロックで条件付をすることが出来るようになっている。この条件には IP アドレスなどを指定することが出来るのでこれを利用して root でログイン可能なホストを絞り込む。詳しくは man sshd_config を見れば書いてあるのだけどだいたいこんな感じで書くことが出来る。複数指定する場合にカンマの間にスペースなどを入れてはならない。サンプルとして PermitRootLogin yes と書いているが lsyncd を使用する場合はパスワード認証が使えないので必然的に prohibit-passwordwithout-password)を使うことになるだろう。

/etc/ssh/sshd_config

PermitRootLogin no

Match Address 10.0.0.1
    PermitRootLogin yes

Match Address 10.0.0.2,10.0.0.3
    PermitRootLogin prohibit-password

Match Address 172.16.0.0/16 LocalPort 10022
    PermitRootLogin prohibit-password

Match ブロックは次の Match ブロックが出現するまでがひとつのブロックになっている。IP などの指定は IP アドレス以外にネットワークアドレスを指定したり複数指定することが出来る。Address 以外にも指定可能なものがいくつかあり、 LocalPort などを使えば「パブリックな 22 番ポートへのアクセスはブロック、ファイヤーウォール側の 10022 番ポートへのアクセスは許可」という設定も出来る。

man sshd_config

The available criteria are User, Group, Host, LocalAddress, LocalPort, and Address.

設定したあとは必ず確認しておいた方がいい。特に IP アドレスで制限を設ける場合。正しく設定したつもりがすべての条件に一致しなくて二度と SSH 接続出来なくなるなんてこともある。(実際 Qiita にそういう人が居た)

確認方法は sshd -T だけど、OpenSSH のバージョンによって動作が異なるっぽい。

CentOS 8

Terminal

sshd -T

Mach Address が使われている場合は addr が必要になる。

'Match Address' in configuration but 'addr' not in connection test specification.

addr の指定方法は下記の通り。

Terminal

sshd -T -C addr=10.0.0.1

Amazon Linux 2

sshd -T で普通にテスト結果が出力される。connection_spec を指定して実行してみるとどうなるか。

Terminal

sshd -T -C addr=10.0.0.1

user, host and addr are all required when testing Match configs

上記の通り CentOS 8 とは挙動が違うらしく、userhostaddr のすべての指定が必要になる。少し長くなって面倒だが下記のように指定する。

Terminal

ssh -T -C user=root,host=localhost,addr=10.0.0.1

これで結果が出力されるのであとは | grep permitroot なんかで見やすいように絞り込む。

sshd_config で接続元を絞り込んでも許可されているホストからは秘密鍵を持っていれば接続してコマンドを実行出来てしまう(万が一のときに役に立つかもしれないが)。そこで、authorized_keys のオプションを使って端末の割り当てやエージェント転送などをさせないようにする。restrict は OpenSSH 7.2 以降に追加された機能で、初期の Amazon Linux では恐らく OpenSSH 6.6 (2013) くらいなので対応しておらず、無効なオプションになり公開鍵も無効になってしまうので注意が必要。OpenSSH のバージョンが古い場合はパッケージを更新した方がいいだろう。なお、この機能は SSH サーバ側の機能である。

/root/.ssh/authorized_keys

no-pty,no-port-forwarding,no-agent-forwarding,no-X11-forwarding ssh-rsa XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX lsyncd

# OpenSSH 7.2 以降 であれば ``restrict'' でまとめることが出来る
restrict ssh-rsa XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX lsyncd

これで root でログインされてもシェルは使えないようになる。

なお、ユーザに端末を割り当てない方法としてはユーザのログインシェルに /usr/sbin/nologin/usr/bin/false を使う方法があるが、これは SSH 接続が確立出来ないので lsyncd では使えない。

authorized_keys で接続元を制限する

/etc/ssh/sshd_config にあまり変更を加えたくないという場合もあるかと思う。また、Amazon Linux の場合は最初から PermitRootLogin yes になっているのでそもそも Match ブロックを使う必要が無い(PermitRootLogin no に変更するなら話は別だが)。

authorized_keys には上記のように端末の割り当てなどを禁止する他に、接続元を制限するオプションもある。

man 8 sshd

AUTHORIZED_KEYS FILE FORMAT

     from="pattern-list"
             Specifies that in addition to public key authentication, either
             the canonical name of the remote host or its IP address must be
             present in the comma-separated list of patterns.  See PATTERNS in
             ssh_config(5) for more information on patterns.

             In addition to the wildcard matching that may be applied to host-
             names or addresses, a from stanza may match IP addresses using
             CIDR address/masklen notation.

             The purpose of this option is to optionally increase security:
             public key authentication by itself does not trust the network or
             name servers or anything (but the key); however, if somebody
             somehow steals the key, the key permits an intruder to log in
             from anywhere in the world.  This additional option makes using a
             stolen key more difficult (name servers and/or routers would have
             to be compromised in addition to just the key).

man 5 ssh_config

PATTERNS
     A pattern consists of zero or more non-whitespace characters, `*' (a
     wildcard that matches zero or more characters), or `?' (a wildcard that
     matches exactly one character).  For example, to specify a set of decla-
     rations for any host in the ".co.uk" set of domains, the following pat-
     tern could be used:

           Host *.co.uk

     The following pattern would match any host in the 192.168.0.[0-9] network
     range:

           Host 192.168.0.?

     A pattern-list is a comma-separated list of patterns.  Patterns within
     pattern-lists may be negated by preceding them with an exclamation mark
     (`!').  For example, to allow a key to be used from anywhere within an
     organization except from the "dialup" pool, the following entry (in
     authorized_keys) could be used:

           from="!*.dialup.example.com,*.example.com"

     Note that a negated match will never produce a positive result by itself.
     For example, attempting to match "host3" against the following pattern-
     list will fail:

           from="!host1,!host2"

     The solution here is to include a term that will yield a positive match,
     such as a wildcard:

           from="!host1,!host2,*"

注意しなければならないのは例の最後にあるが、否定条件のみ指定した場合はすべて拒否することになるため上記の example.com の例のように許可するものを追加しておく必要がある。

アドレスには単一の IP アドレス以外にワイルドカードと CIDR(IPアドレス/マスク長)も使える。[0-5] などのレンジの指定は出来ず、10.0.0.0/255.255.255.0 のような記述は誤り。記述が誤っていてもエラーメッセージは「ブロックされています」などではなく単に「公開鍵エラー」となるだけなので制限が正しく効いているか確認するのは大事。

/root/.ssh/authorized_keys

from="10.0.0.1",restrict ssh-rsa XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX lsyncd
from="10.0.0.*",restrict ssh-rsa XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX lsyncd
from="10.0.?.*",restrict ssh-rsa XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX lsyncd
from="10.0.1?.*",restrict ssh-rsa XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX lsyncd
from="10.0.0.0/24",restrict ssh-rsa XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX lsyncd

なお、コンフィグで PermitRootLogin no になっている場合は authorized_keysfrom 設定してもログインは出来ない。