ルールの書き方やそのルールで何をしようとしているかを覚えられない私の iptables 管理方法

#概要
iptables を使っているけど、ルールの書き方を覚えられない（覚える気がない）し、個々のルールが何の目的で設定してあるのかすぐに忘れてしまう（たとえば、あるサービスを提供しなくなったのでルールを削除したいが、どれを削除していいのか、また削除して他に影響がないのかすぐには判断できない）、そんなダメ管理者がやっている iptables 管理方法を晒します。

#基本方針

* `/etc/iptables.sh` に、何をどうするのか、という形で iptables の設定を書く。
* 何 (たとえば、443 というのは https のポート番号である、とか) を、どうする (たとえば TCP のサーバを動かすためにパケットを通す、とか) という定義は `/etc/iptables.inc.sh` にまとめて書く。
* 引数なしで起動すれば設定するルールを表示し、引数として `exec` をつければ実際に iptables コマンドを実行する。
* 両スクリプトは `/etc/` に置いて etckeeper の対象にする。

```shell-session
[root@heaven /etc]# ./iptables.sh
```

とやると、ずらずらと実行する予定の iptables コマンドが表示され、

```shell-session
[root@heaven /etc]# ./iptables.sh exec
```

とすると実際に実行されます。

#中身

`iptables.sh` の方はこんな感じ。

```bash:iptables.sh
 #!/bin/bash
exec=${1}
. /etc/iptables.inc.sh
set_policy DROP
#create_new_chain NAMED_IN DROP
#create_new_chain NAMED_OUT DROP
accept_any_packets_on_loopback_if
drop_private_packets eth0
for ip in $bad_ip1; do
  drop_packets_from eth0 $ip
done
for port in $ssh $smtp $submission $http $imap $squid; do
  tcp_server eth0 $port
done
for port in $smtp $http $ftp $https; do
  tcp_client $port
done
for ip in $ns1_dns_ne_jp $ns2_dns_ne_jp; do
  udp_service_to $dns $ip
done
for port in $ntp $dns; do
  udp_client $port
done
# drop_broadcast_packets
accept_established_or_related_packets
# end of file
```

`iptables.inc.sh` の方はこんな感じ。

```bash:iptables.inc.sh
if [ "${exec}" = "exec" ] ; then
  iptables=/sbin/iptables
else
  iptables='echo x'
fi

ssh=___secret___
squid=15120
smtp=25
submission=587
ntp=123
dns=53
http=80
https=443
ftp=21
imap=143
ns1_dns_ne_jp=210.188.224.9
ns2_dns_ne_jp=210.224.172.13


add_in_drop_rule() {
  i=${1}
  privates="10.0.0.0/8 172.16.0.0/12 192.168.0.0/16"
  for net in ${privates}; do
    $iptables -A INPUT -i $i -s $net -j DROP
    $iptables -A INPUT -i $i -d $net -j DROP
  done
}

set_policy() {
  policy=${1}
  # clear
  $iptables -F
  $iptables -X
  # default policy
  $iptables -P INPUT $policy
  $iptables -P FORWARD $policy
  $iptables -P OUTPUT $policy
}

accept_any_packets_on_loopback_if() {
  $iptables -A INPUT -i lo -j ACCEPT
  # $iptables -A INPUT -s 127.0.0.1 -d 127.0.0.1 -j ACCEPT
  # $iptables -A OUTPUT -s 127.0.0.1 -d 127.0.0.1 -j ACCEPT
}

drop_private_packets() {
  i=${1}
  add_in_drop_rule $i
}

tcp_server() {
  i=${1}
  port=${2}
  $iptables -A INPUT -i $i -m state --state NEW -m tcp -p tcp --dport $port -j ACCEPT
}

tcp_client() {
  port=${1}
  $iptables -A OUTPUT -m state --state NEW -m tcp -p tcp --dport $port -j ACCEPT
}

udp_server() {
  port=${1}
  $iptables -A INPUT -p udp --dport $port -j ACCEPT
}

udp_client() {
  port=${1}
  $iptables -A OUTPUT -p udp --dport $port -j ACCEPT
  $iptables -A INPUT -p udp --sport $port -j ACCEPT
}

drop_broadcast_packets() {
  touch /tmp
  # $iptables -A INPUT -i $i -d 255.255.255.255 -j DROP
  # $iptables -A INPUT -i $i -d 224.0.0.1 -j DROP
}

accept_established_or_related_packets() {
  $iptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
  $iptables -A OUTPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
}

drop_packets_from() {
  i=${1}
  ip=${2}
  $iptables -A INPUT -i $i -s $ip -j DROP
}

create_new_chain() {
  name=${1}
  policy=${2}
  $iptables -N $name
  $iptables -P $name $policy
}
# end of file
```

## 補足

* 基本的に、そのとき必要な関数や定数はそのとき追加しているので、汎用的なものにはなっていません。
* sshd はポートをデフォルト (22) と変えてサービスしてます。
* drop_broadcast_packets 関数は実質 nop みたいだけど、なぜそうなっているかは 2 年前の私に聞かないとわかりません。


#議論

* `iptables.inc.sh` にはどのサイトでも使える関数、定数を定義しておいて、サーバごとの個別の設定は別の、たとえば `iptables.config.sh` とかにまとめて書くと配布とか使いまわしとかの点で便利かも。
* って、`iptables.config.sh` を用意するなら　...

```bash:iptables.config.sh
...
tcp_server_ports="$squid $ssh $smtp $submission $http $https $imaps"
tcp_client_ports="$smpt $http $ftp $https"
...
```

とかしといて、`iptables.inc.sh` に、これらの変数を見て iptables コマンドを発行する関数 `accept_site_tcpip_packets` とか用意して、`iptables.sh` には

```bash:iptable.sh
...
accept_site_tcpip_packets
...
```

とだけ書くのがスマートかも。

* ポートの定数は `/etc/services` から自動的にとってくるほうがかっこいいかも。
* IP アドレスの定数を DNS 問合せして動的にとってくる、というのはたぶんやりすぎ。
* 最近、管理しているサーバが攻撃を受けているので、ログ周り含め、いろいろ細かく設定しようとしているので、もっと変わっていくことでしょう。


# オチ
最近、久々に新しいサーバを立てたので、このスクリプトをコピペして使おう、と思ったら iptables じゃなくて firewalld になってて ...