概要
iptables を使っているけど、ルールの書き方を覚えられない(覚える気がない)し、個々のルールが何の目的で設定してあるのかすぐに忘れてしまう(たとえば、あるサービスを提供しなくなったのでルールを削除したいが、どれを削除していいのか、また削除して他に影響がないのかすぐには判断できない)、そんなダメ管理者がやっている iptables 管理方法を晒します。
基本方針
-
/etc/iptables.sh
に、何をどうするのか、という形で iptables の設定を書く。 - 何 (たとえば、443 というのは https のポート番号である、とか) を、どうする (たとえば TCP のサーバを動かすためにパケットを通す、とか) という定義は
/etc/iptables.inc.sh
にまとめて書く。 - 引数なしで起動すれば設定するルールを表示し、引数として
exec
をつければ実際に iptables コマンドを実行する。 - 両スクリプトは
/etc/
に置いて etckeeper の対象にする。
[root@heaven /etc]# ./iptables.sh
とやると、ずらずらと実行する予定の iptables コマンドが表示され、
[root@heaven /etc]# ./iptables.sh exec
とすると実際に実行されます。
中身
iptables.sh
の方はこんな感じ。
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
の方はこんな感じ。
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
を用意するなら ...
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
には
iptable.sh
...
accept_site_tcpip_packets
...
とだけ書くのがスマートかも。
- ポートの定数は
/etc/services
から自動的にとってくるほうがかっこいいかも。 - IP アドレスの定数を DNS 問合せして動的にとってくる、というのはたぶんやりすぎ。
- 最近、管理しているサーバが攻撃を受けているので、ログ周り含め、いろいろ細かく設定しようとしているので、もっと変わっていくことでしょう。
オチ
最近、久々に新しいサーバを立てたので、このスクリプトをコピペして使おう、と思ったら iptables じゃなくて firewalld になってて ...