systemd には IPAddressDeny と IPAddressAllow という機能があるので、それを用いて特定のサービスやソケットのアクセス制御をする話題です。Linuxでは通常 iptables
, nftables
, firewalld
, ufw
などで行われる処理です。防御する機器はインターネットに直接接続されていて(例えばIPv6アドレスを割り振られている場合や固定IPv4アドレスサービスを契約している場合など)、その機器で防御手段を講じないと攻撃され放題になる状況を想定しています。
制御したサービスの特定
まず lsof -i | fgrep LISTEN | fgrep '*'
で現在全世界に対して開いているポートを探します。以下実行例
root@raspi4b-router:~# lsof -i | fgrep LISTEN | fgrep '*'
systemd 1 root 256u IPv6 15487 0t0 TCP *:9090 (LISTEN)
sshd 579 root 3u IPv4 12942 0t0 TCP *:ssh (LISTEN)
sshd 579 root 4u IPv6 12944 0t0 TCP *:ssh (LISTEN)
dnsmasq 584 dnsmasq 7u IPv4 17425 0t0 TCP *:domain (LISTEN)
dnsmasq 584 dnsmasq 9u IPv6 17427 0t0 TCP *:domain (LISTEN)
pmcd 805 pcp 0u IPv4 13066 0t0 TCP *:44321 (LISTEN)
pmcd 805 pcp 3u IPv6 13067 0t0 TCP *:44321 (LISTEN)
pmproxy 850 pcp 15u IPv4 17695 0t0 TCP *:44322 (LISTEN)
pmproxy 850 pcp 16u IPv6 17696 0t0 TCP *:44322 (LISTEN)
pmproxy 850 pcp 17u IPv4 17697 0t0 TCP *:44323 (LISTEN)
pmproxy 850 pcp 18u IPv6 17698 0t0 TCP *:44323 (LISTEN)
pmlogger 1648 pcp 7u IPv4 21511 0t0 TCP *:4330 (LISTEN)
pmlogger 1648 pcp 8u IPv6 21512 0t0 TCP *:4330 (LISTEN)
systemdによるIPアクセス制御
これで、全世界に向かってポートを開いているサービスは ssh
, dnsmasq
, pmcd
, pmproxy
, pmlogger
であることがわかります。またsystemdがポート9090で待ち受けして何らかのサービス(実はcockpit)を起動しています。ここでsshはそのまま全世界にポートを開き、残りはlocalhost
と 192.168.1.0/24
だけ通信を許すことにします。以下のコマンドをroot
で実行します。
cd /etc/systemd/system
mkdir dnsmasq.service.d pmcd.service.d pmproxy.service.d pmlogger.service.d
cat >dnsmasq.service.d/ipfilter.conf <<EOF
[Service]
IPAddressAllow=localhost
IPAddressAllow=192.168.1.0/24
IPAddressDeny=any
EOF
ln dnsmasq.service.d/ipfilter.conf pmcd.service.d
ln dnsmasq.service.d/ipfilter.conf pmproxy.service.d
ln dnsmasq.service.d/ipfilter.conf pmlogger.service.d
これでポート9090以外の待ち受けはlocalhost
ならびにLANに制限できました。のこりはsystemdが待ち受けしているポート9090の対応ですが、これはcockpit.socket
が待ち受けの設定をしています。そこで以下のコマンドを実行します。
cd /etc/systemd/system
mkdir cockpit.socket.d
cat >cockpit-socket.d/socket-ipfilter.conf <<EOF
[Socket]
IPAddressAllow=localhost
IPAddressAllow=192.168.1.0/24
IPAddressDeny=any
EOF
これで設定は終わったので systemctl reboot
で機器を再起動します。
systemd socketファイルの安全な書き方
自作のプログラムを特定のTCPポートで待ち受けさせるときはsystemdのListenStream
を用いますが、ここで ListenStream=64193
などと書くと全世界から待ち受けしてしまいます。LAN側だけで待ち受けしたくて、LAN側のIPアドレスが192.168.1.2
ならListenStream=192.168.1.2:64193
と書くとWAN側からの接続要求を無視します。しかし、待ち受けがLAN側IPアドレスの設定以前に開始されるとエラーになるため、FreeBIND=yes
もsystemd設定ファイルに書きます。まとめると、例えば以下のようなsystemd socket待ち受けファイルを作るとよいです。
```/etc/systemd/system/testhttp.socket`
[Socket]
FreeBind=yes
ListenStream=192.168.1.2:64193
Accept=yes
[Install]
WantedBy=socket.target