概要
RHEL7.Xからサービスの起動管理はsystemdで行われるようになり、これまでxinetdで実装されていたアクセス契機でデーモンを起動して処理を受け付けるような方式は、socket(Unitの一種)で実装されるようになった(RHEL7.Xでもxinetdを利用することは可能であるが、後方互換的な位置づけと思われる)。xinetdは設定が煩雑だったところもあり、新しくRHEL7.6で構築したサーバではsocketを利用してみた。ところが、hosts.allowのアクセス制御を実施するのにあたり問題が2点あり、それが分かるまでに結構嵌ったためその話を書きたいと思う。
結論
socketで受け付けるアクセスに関し、hosts.allow(hosts.deny)で制御するならば、下記2点を対応すべし。
・ socketに対応するserviceファイルのExecStart=
の内容の頭に、@-/usr/sbin/tcpd
を付与し、元々頭に-
があった際は除去する
...
[Service]
(変更前)ExecStart=/usr/sbin/in.tftpd -vvv -c -s /tftpboot
↓
(変更後)ExecStart=@-/usr/sbin/tcpd /usr/sbin/in.tftpd -vvv -c -s /tftpboot
...
・ socketファイルの[socket]
項のListenStream or ListenDatagram
のポート番号の前に、0.0.0.0:
を追記。また、[socket]
項に、FreeBind=true
の1行を追加(TCP通信の場合は不要かも)。
...
(変更前)
[Socket]
ListenDatagram=69
↓
(変更後)
[Socket]
ListenDatagram=0.0.0.0:69
FreeBind=true
...
問題1.TCP Wrapper がサポートされていない
この問題に関しては、以前に書いたRHEL7.Xの変更点全般の記事にも記載したのだが、socketを使う際の注意点として再掲する。このQiitaの記事を見てもらうのが早いのだが、RedHat公式ページによると、"systemdはTCPWrapperをサポートしないので、TCPWrapperを使うならばデーモンをtcpdから起動してね"といったことが記載されている。具体的な対処方法としては下記の通り、socketと連携するservice設定ファイルのExecStart項の頭に、@-/usr/sbin/tcpd
を付与する。
...
[Service]
(変更前)ExecStart=-/usr/sbin/in.telnetd
↓
(変更後)ExecStart=@-/usr/sbin/tcpd /usr/sbin/in.telnetd
...
なお、頭の@
と-
に関してだが、ネットを調べても出ないのでマニュアルを見たところ、下記の通り記載があった。
...(略)...
For each of the specified commands, the first argument must be an
absolute path to an executable. Optionally, if this file name is
prefixed with "@", the second token will be passed as "argv[0]" to
the executed process, followed by the further arguments specified.
If the absolute filename is prefixed with "-", an exit code of the
command normally considered a failure (i.e. non-zero exit status or
abnormal exit due to signal) is ignored and considered success. If
both "-" and "@" are used, they can appear in either order.
...(略)...
私の拙い英語力でなんとか読解して下記のように解釈したが、間違っていたら誰か教えて頂きたい...。
・ @
2つ目の引数(今回の例の場合、"/usr/sbin/in.telnetd")を1つ目のコマンドライン引数として渡す(=ps コマンドで見えるプロセス名を tcpd→telnetdにする)
・ -
コマンド実行結果の戻り値が0でなくとも無視して起動成功とみなす
2点目は付けていてよいのかちょっと気になるが、検証では特に支障なく動いていたし従っておこうかな。
問題2.デフォルト設定だと、IPv6アクセスとして受け付けられる
RHEL7.Xのsocketを利用してアクセスを受け付けた場合、デフォルトではなぜかIPv6アクセスとして受け付けられてしまう。これがどう影響するかというと、hosts.allowにIPv4表記で許可セグメントを記載してもIPv6アドレスからのアクセスとして扱われてしまうため、アクセス拒否されてしまう。例えば下記のように/etc/hosts.allow
、/etc/hosts.deny
に記載して、10.0.2.2
から10.0.2.15
へアクセスしても拒否される。
...(略)...
in.tftpd : 10.0.2.0/255.255.255.0
...(略)...
ALL:ALL
/var/log/messages
を見てみると、下記のようなメッセージ出力があった。
Jan 9 11:48:42 localhost in.tftpd[17840]: connection refused from ::ffff:10.0.2.15
最初は気付けなかったのだが、アドレスの頭の::ffff:
がIPv6アドレスとして認識されていることを示している。対処としては、下記のようにsocket設定ファイルのポート指定の前に0.0.0.0:
を付与すると、IPv4アドレスとして処理してくれるようだ。FreeBind=true
を付与すると、アクセスを受けるサーバ自身が持つNICのアドレス以外でもアクセス受付出来るようになるそうなので、念のため設定しておく。
...
(変更前)
[Socket]
ListenDatagram=69
↓
(変更後)
[Socket]
ListenDatagram=0.0.0.0:69
FreeBind=true
...
設定変更を実施したところ、アクセスが通ることが/var/log/messages
にて確認出来た。
※tftpのデータ送信ポートは動的に決まるため、virtual box のNATで外から通信を通す方法が分からず、やむなくログで refused
が出力されていないことをもって確認とした。
Jan 9 13:16:56 localhost in.tftpd[19045]: RRQ from ::ffff:10.0.2.2 filename test
Jan 9 13:16:56 localhost in.tftpd[19045]: Client ::ffff:10.0.2.2 finished test
おわりに
socket(systemd)とhosts.allowの組み合わせは、デフォルトでサポートされていないだけあって割と嵌った。RHEL7.Xの流儀に合わせるならば、アクセス制御はfirewalldでやるべきなのかもしれない。hosts.allowでやりたいという方がいたならば、本記事が参考になれば幸いである。