CentOS7のfirewalldをまじめに使うはじめの一歩（systemdも少し）


基本
=======

デフォルト設定ファイルとユーザー設定ファイルの関係
-------------------------------------------------

`/usr/lib`下の`systemd`ないし`firewalld`にデフォルト設定ファイルがあるので、設定を変更する場合は該当するファイルを同じディレクトリ構成で`/etc`に置く。

例えば`system/default.target`というファイルの場合、`/usr/lib`での`graphical.target`へのシンボリックリンクが、`/etc`で`multi-user.target`へのシンボリックリンクで上書きされているのがわかる。

```shell-session
[root@localhost ~]# ls -l /usr/lib/systemd/system/default.target
lrwxrwxrwx. 1 root root 16 Oct 21 14:26 /usr/lib/systemd/system/default.target -> graphical.target
[root@localhost ~]# ls -l /etc/systemd/system/default.target
lrwxrwxrwx. 1 root root 37 Jul 22 03:38 /etc/systemd/system/default.target -> /lib/systemd/system/multi-user.target
```

`firewalld`の設定についても同様で、`etc`の下にある`zones/public.xml`が`/usr/lib`下のデフォルト設定を上書きしている。

```shell-session
[root@localhost ~]# ls -l /etc/firewalld/zones/public.xml
-rw-r--r--. 1 root root 340 Oct 21 15:32 /etc/firewalld/zones/public.xml
[root@localhost ~]# ls -l /usr/lib/firewalld/zones/public.xml 
-rw-r-----. 1 root root 315 Jun 10 07:19 /usr/lib/firewalld/zones/public.xml
```

NICの確認
---------------

`ip l`でリンク、`ip a`でアドレス、`ip r`でルーティング。

```
[root@localhost ~]# ip l
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT 
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
2: enp0s3: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP mode DEFAULT qlen 1000
    link/ether 08:00:27:20:5d:4b brd ff:ff:ff:ff:ff:ff
[root@localhost ~]# ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN 
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host 
       valid_lft forever preferred_lft forever
2: enp0s3: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP qlen 1000
    link/ether 08:00:27:20:5d:4b brd ff:ff:ff:ff:ff:ff
    inet 10.0.2.15/24 brd 10.0.2.255 scope global dynamic enp0s3
       valid_lft 82486sec preferred_lft 82486sec
    inet6 fe80::a00:27ff:fe20:5d4b/64 scope link 
       valid_lft forever preferred_lft forever
[root@localhost ~]# ip r
default via 10.0.2.2 dev enp0s3  proto static  metric 1024 
10.0.2.0/24 dev enp0s3  proto kernel  scope link  src 10.0.2.15 
```

### 参考

* http://www.unix-power.net/centos7/firewalld.html
* http://d.hatena.ne.jp/enakai00/20140712/1405139841

ケーススタディ1: http通信を許可する :octopus: 
===================================

6までならiptablesで操作していたもの。nginxは別途インストール済という前提で。

`systemctl status firewalld`で`firewalld`が動いてるか調べて、動いてなかったら`systemctl start firewalld`で起動。

### 現状のゾーン設定を調べる `firewall-cmd --list-all`

`firewalld`ではゾーンという単位で設定が定義され、ゾーンとNICが紐づくことで設定が有効になる。

```shell-session
[root@localhost ~]# firewall-cmd --list-all
public (default, active)
  interfaces: enp0s3
  sources: 
  services: dhcpv6-client ssh
  ports: 
  masquerade: no
  forward-ports: 
  icmp-blocks: 
  rich rules: 
```

上記の`ip`コマンドででてきた、`enp0s3`インタフェースに紐付いていることがわかる。

#### すべてのゾーンの情報をみる `firewall-cmd --list-all-zones`

```shell-session
[root@localhost ~]# firewall-cmd --list-all-zones
block
  interfaces: 
  sources: 
...
dmz
  interfaces: 
...
drop
  interfaces: 
  sources: 
...
work
  interfaces: 
  sources: 
...
```

### ゾーンに設定されているサービスを確認する: `firewall-cmd --list-services --zone=public  --permanent`

`--permanent`については後述。

```shell-session
[root@localhost ~]# firewall-cmd --list-services --zone=public  --permanent
dhcpv6-client ssh
```

### 追加できるサービスを確認する: `firewall-cmd --get-services`

```shell-session
[root@localhost ~]# firewall-cmd --get-services
amanda-client bacula bacula-client dhcp dhcpv6 dhcpv6-client dns ftp high-availability http https imaps ipp ipp-client ipsec kerberos kpasswd ldap ldaps libvirt libvirt-tls mdns mountd ms-wbt mysql nf
s ntp openvpn pmcd pmproxy pmwebapi pmwebapis pop3s postgresql proxy-dhcp radius rpc-bind samba samba-client smtp ssh telnet tftp tftp-client transmission-client vnc-server wbem-https
```

各サービスの詳細は設定ファイルをみるのがよいと思う。変更したい場合は上記の通り、`/etc/firewalld`にコピーして行う。

```shell-session
[root@localhost ~]# cat /usr/lib/firewalld/services/http.xml 
<?xml version="1.0" encoding="utf-8"?>
<service>
  <short>WWW (HTTP)</short>
  <description>HTTP is the protocol used to serve Web pages. If you plan to make your Web server publicly available, enable this option. This option is not required for viewing pages locally or developing Web pages.</description>
  <port protocol="tcp" port="80"/>
</service>
```

### ゾーンにサービスを追加する: `firewall-cmd --add-service=http --zone=public --permanent`

`--permanent`をつけないと設定ファイルが更新されないので、サービスをreloadすると元の状態に戻ってしまう。

```shell-session
[root@localhost ~]# firewall-cmd --add-service=http --zone=public --permanent
success
```

確認。

```shell-session
[root@localhost ~]# firewall-cmd --list-services --zone=public  --permanent
dhcpv6-client http ssh
```

追加できました。
実際のファイルをみても、`/usr/lib/firewalld/zones/public.xml`は変更されてなくて、

```shell-session
[root@localhost ~]# cat /usr/lib/firewalld/zones/public.xml
<?xml version="1.0" encoding="utf-8"?>
<zone>
  <short>Public</short>
  <description>For use in public areas. You do not trust the other computers on networks to not harm your computer. Only selected incoming connections are accepted.</description>
  <service name="ssh"/>
  <service name="dhcpv6-client"/>
</zone>
```

`/etc/firewalld/zones/public.xml`が更新されてます。

```shell-session
[root@localhost ~]# cat /etc/firewalld/zones/public.xml
<?xml version="1.0" encoding="utf-8"?>
<zone>
  <short>Public</short>
  <description>For use in public areas. You do not trust the other computers on networks to not harm your computer. Only selected incoming connections are accepted.</description>
  <service name="dhcpv6-client"/>
  <service name="http"/>
  <service name="ssh"/>
</zone>
```

これで、外部から80ポートへアクセスできるようになりました。サービスの再起動も不要でした。

ケーススタディ2: 特定ポートのhttp通信を許可する :bell: 
==================================================

例として5000ポートでの通信を許可する場合。

5000ポートを使う適当なプロセスをでっちあげる。

```shell-session
[root@localhost ~]# python -m SimpleHTTPServer 5000
Serving HTTP on 0.0.0.0 port 5000 ...
```

`/usr/lib/firewalld/services/http.xml`を`/etc/firewalld/services`にコピーして編集。

```shell-session
[root@localhost ~]# cp /usr/lib/firewalld/services/http.xml /etc/firewalld/services/
[root@localhost ~]# vi /etc/firewalld/services/http.xml
```

みればわかるくらい単純だけどこんな感じに。

```xml
<?xml version="1.0" encoding="utf-8"?>
<service>
  <short>WWW (HTTP)</short>
  <description>HTTP is the protocol used to serve Web pages. If you plan to make your Web server publicly available, enable this option. This option is not required for viewing pages locally or developing Web pages.</description>
  <port protocol="tcp" port="80"/>
  <port protocol="tcp" port="5000"/>
</service>
```

httpサービスは追加済なので`reload`する。

```shell-session
[root@localhost ~]# firewall-cmd --reload
```

以上で完了。
設定の書き方は`man firewalld.service`みる。

ケーススタディ3: 独自のサービスを追加して管理する :rocket: 
======================================================

上記では既存の`http`サービスに追加したけど、たぶん、独自のサービスを追加したほうが管理もしやすいので正道なはず。

編集したファイルは不要なので削除。リロードもしておく。

```shell-session
[root@localhost ~]# rm /etc/firewalld/services/http.xml
[root@localhost ~]# firewall-cmd --reload
```

既存のhttpファイルをもとに`py-app`という独自サービスの設定ファイルを作成。
`firewall-cmd --new-service=[service] --permanent`の使い方がいまいちわからないので今回はファイルを直接作成する。

```shell-session
[root@localhost ~]# cp /usr/lib/firewalld/services/http.xml /etc/firewalld/services/py-app.xml
```

中身は以下。

```xml
<?xml version="1.0" encoding="utf-8"?>
<service>
  <short>myapp by python</short>
  <description>for file sharing</description>
  <port protocol="tcp" port="5000"/>
</service>
```

作成できたらサービスとして追加できるが、`--get-services`には表示されないので、`firewall-cmd --reload`したほうがいいとは思う。いずれにせよ、ファイルが作成できた時点で以下のコマンドは実行可能。

```shell-session
[root@localhost ~]# firewall-cmd --add-service=py-app --zone=public --permanent                                                                                                                         
success
[root@localhost ~]# firewall-cmd --list-services --zone=public  --permanent                                                                                                                      
dhcpv6-client http py-app ssh
```

今回は`reload`しないと反映できなかった。さっきのケースが勘違いかも。

```shell-session
[root@localhost ~]# firewall-cmd --reload
```

おそらくこんな感じで`Postgresql`やら`rabbitmq`やら`fluentd`やらのサービスを作成していくことになるんだろう。

ケーススタディ4: firewalldの設定を一覧でみる :neckbeard: 
==========================================================

firewalldはD-BUS経由での操作もできる単なる管理インタフェースみたいなので、実際のフィルタリングはiptablesが動いている。なので、設定を一覧でみたい、実際の反映結果をみたい、というときは今までどおり、`iptables-save`を使えばいい。

```shell-session
[root@localhost ~]# iptables-save 
# Generated by iptables-save v1.4.21 on Tue Oct 21 18:04:25 2014
*nat
:PREROUTING ACCEPT [4:176]
:INPUT ACCEPT [4:176]
:OUTPUT ACCEPT [0:0]
:POSTROUTING ACCEPT [0:0]
...
-A INPUT_ZONES -i enp0s3 -g IN_public
-A INPUT_ZONES -g IN_public
-A IN_public -j IN_public_log
-A IN_public -j IN_public_deny
-A IN_public -j IN_public_allow
-A IN_public_allow -p tcp -m tcp --dport 80 -m conntrack --ctstate NEW -j ACCEPT
-A IN_public_allow -p tcp -m tcp --dport 5000 -m conntrack --ctstate NEW -j ACCEPT
-A IN_public_allow -p tcp -m tcp --dport 22 -m conntrack --ctstate NEW -j ACCEPT
COMMIT
# Completed on Tue Oct 21 18:04:25 2014
```

というわけで簡単なことを筋道立てて設定・管理するなら`firewalld`が便利なんだろう。あとD-BUSから操作する場合。
けど、複雑なことをエイヤッと解決したい場合は今までどおり`iptables`を直接触ったほうがいいと思う。

追記: firewalldとiptablesの設定の共有はできないっぽい
-----------------------------------------------

* [CentOS 7からデフォルトのFirewalldでハマった(iptablesは今後使わない？) | urashita.com](http://urashita.com/archives/963)
* [CentOS7, iptables設定でハマった - Goldstine研究所](http://mosuke5.hateblo.jp/entry/2014/09/20/180326)

などを読むと、`firewalld`は`/etc/sysconfig/iptables`の設定を読んでくれないっぽい。なのでどちらを使うか選ぶしかないっぽい。
`firewalld`の代わりに`iptables`を有効にする際のざっくり手順は以下。

```
yum install iptables-services
systemctl stop firewalld
systemctl disable firewalld
systemctl enable iptables
systemctl start iptables
```