Firewalld編~サーバーへ不要な通信はさせない~ - Linuxセキュリティ入門(2)

こんにちは、川井です。

普段はIT系の学校で講師をしているため、学期の始めは仮想環境(VMware)にLinuxをインストールしてもらいます。それに対してPCを買ってくると、Windowsがインストールされていたり、ネットワーク設定もWiFiから勝手に拾うため、PCの箱を開けたらすぐに使えるのに慣れているためか、DVDイメージからLinuxをインストールする作業に生徒さん達は四苦八苦です。興味を持って学べばとても面白い世界とはいえ、最近は蛇口をひなるだけで水が出てくる水道の様につなぐだけでネットワークが使えるため、学生さん達はネットワークにまったく興味が湧かないみたいですね(T_T)……。

ネットワークの「あるべき姿」

さて、連載の2回目はさくらのサービスとは切っても切り離せないネットワークのセキュリティについて再確認と再設定をしていきます。さくらのサービスなど、インターネットにサーバーを置くということは、一日24時間、年間365日、ひとときの休みもなくインターネットの脅威にさらされている訳で、一瞬足りとも油断することはできません。冷静に考えると、ネットの悪い人達がクリスマスだったり、お正月だからと言って攻撃の手を休めてくれる訳でも無いため、常にあなたのスキを突いてきます。サーバーの電源を切ってしまうのはやり過ぎでしたが、ネットワークの脅威から逃れる次の様な魔法の一手があります。

  サーバーをネットワークにつながない・・・②

確かにネットワークにつながなければクラウドの向こう側からは何も見えない訳で、完璧なセキュリティ対策と言えるでしょう。でも待ってください! 本来はWebアプリを公開したいのが目的だったのに、誰もWebアプリが使えなくなってしまいますよね……。とはいえ、全く間違えているとも言い難く、限度を超えない処置であればセキュリティ対策となるのではないでしょうか。前回と同じ様に、中道を求めると今回のテーマへ導ける様な気がします。

  サーバーへ不要な通信はさせない→サーバーへ必要な通信だけさせる+その他の通信を除外する・・・❷

この機能はパケットをフィルタするファイアウォール機能としてCentOS(Redhat Enterprise Linux)に実装されています。世の中にあるファイアウォールをここで取り急ぎ解説すると、ファイアウォールは大雑把に3つの種類に分類可能です。

1つ目はパケットフィルタと呼ばれるタイプで、インターネットでやり取りされるIPアドレスやポート番号を基準にして、通信の許可・拒否を指定します。2つ目はサーキットレベルゲートウェイでやり取りされるパケットのヘッダ(情報)のIPアドレスとポート番号を少し見るSOCKSなどで、ユーザーが使う機能ごとに止めてしまう方式です。そして、3つ目はアプリケーションゲートウェイで、データの中身を見ることもできる、プロキシサーバーと呼ばれるものです。今回の記事で扱うのは1つ目のパケットフィルタです。

方式 機能
パケットフィルタ IPアドレスとポート番号で(基本レイヤ3レベルで)パケットを制御
サーキットレベルゲートウェイ TCP/IPやUDPで(レイヤ4レベルで)制御
アプリケーションゲートウェイ データの中身をチェックして制御

表1:ファイアウォールの種類

CentOS 6まではiptablesサービスでファイアウォール機能を実現していました。CentOS 7でもiptablesサービスは利用可能ですが、firewalldサービスがデフォルトとなっています。iptablesサービスもfirewalldサービスもカーネル(Linuxの核となる機能)に組み込まれているNetfilter機能のフロントエンドなので、だいたい同じ様なことができ、設定の記述形式が違うだけです。今回はデフォルトとなっているfirewalldサービスを利用しましょう。

Firewalldの動作を確認してみよう

firewalldサービスではまず、設定ファイルとfirewall-cmdコマンドが用意されており、設定ファイルは /usr/lib/firewalld にあります。ここではまず、サービスの大元であるfirewalldが動作しているかどうかを systemctlコマンドにstatusを指定して確認してみましょう(できれば皆さんもお試しください)。

# systemctl status firewalld ↩︎
* firewalld.service - firewalld - dynamic firewall daemon
   Loaded: loaded (/usr/lib/systemd/system/firewalld.service; disabled; vendor preset: enabled)
   Active: active (running) since Wed 2019-07-10 07:18:11 JST; 5 days ago
     Docs: man:firewalld(1)
 Main PID: 770 (firewalld)
   CGroup: /system.slice/firewalld.service
           `-770 /usr/bin/python -Es /usr/sbin/firewalld --nofork --nopid

Warning: Journal has been rotated since unit was started. Log output is incomplete or unavailable.

結果の3行目くらいに running と表示されていれば、動作しています。デフォルトで動作しているはずなので、止めてないはずなのに動作していない時は、systemctlコマンドにstartを指定してすぐにでも起動しましょう。

また、firewalldの確認・設定をするためのfirewall-cmdコマンドが用意されており、firewalldが動いているか否かはfirewall-cmdコマンドに--stateオプションをつけても確認できます。結果に「running」が出てくれば動作しています。動作していない場合は「not running」と表示されます。

# firewall-cmd --state ↩︎
running

動作しているのであれば、次は、動作の内容を確認する必要があるでしょう。firewalldはサービスについて許可・拒否などのルールを決め、ルールをゾーンという単位にまとめ、そのゾーンをどのネットワーク・インターフェースに適用するかを決定できます。さくらのサービスでも複数台借りて、複数台の仮想マシンを連携して使うのであれば、インターフェースごとの設定が必要です。ただ、この連載では単体サーバーの動作を考えているのでインターフェースごとでの設定はまたの機会に譲りたいと思います。まずはゾーンについて見てみましょう。

※さくらのVPSでは、デフォルト(初期状態)で仮想マシンの外側にFirewalldと同等の働きをするパケットフィルタ機能が用意・適用されています(サーバーのコントロールパネルの「パケットフィルタ」タブで確認・設定が可能)。

Firewalldの現在設定を確認してみよう

firewalldの現状設定で適用されているゾーンを確認するには、firewall-cmdコマンドに--get-active-zoneオプションを指定します。

# firewall-cmd --get-active-zone ↩︎
public
  interfaces: eth0

結果としてpublicと表示されたのは、eth0インターフェースにpublicゾーンが設定してある状態です。ゾーンの設定ファイルはxml形式で/usr/lib/firewalld/zonesディレクトリに用意してあります。

# ls /usr/lib/firewalld/zones ↩︎
block.xml  dmz.xml  drop.xml  external.xml  home.xml  internal.xml  public.xml  trusted.xml  work.xml
# 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>

xml形式のファイルはホームページを記述するときに使うhtmlの親戚みたいなファイルです。また、ゾーンはpublicの他に次表のゾーンが用意されていますが、複数台の仮想マシンを使うなどの凝った使い方をしないのであれば、外部ネットワーク向けでデフォルト設定されているpublicゾーンに許可を足したり引いたりして使うので十分でしょう。

ゾーン名 対応
drop 外部からのすべてのパケットを破壊
block 内部から送信して外部から返信されたパケットのみ通過、その他のパケットを受信拒否
dmz DMZ(DeMilitarized Zone:非武装地帯)向け設定、デフォルトでsshのみを許可
external IPマスカレードを有効にしたゲートウェイ向け設定、デフォルトでsshのみを許可
public コンピュータを信頼できないパブリックエリア向け設定、sshとdhcpv6-clientのみ許可
work 職場向け設定、デフォルトでsshとdhcpv6-clientのみ許可
home 自宅向け設定、デフォルトでsshとmdnsとsamba-clientとdhcpv6-clientのみ許可
internal ほぼ信頼できる内部ネットワーク向け、sshとmdnsとsamba-clientとdhcpv6-clientのみ許可
trusted すべてのパケットを受信許可

表2:ゾーンの種類

現在のゾーンを詳しく見るには、firewall-cmdコマンドに--list-allオプションを指定してください。

# firewall-cmd --list-all ↩︎
public (active)
  target: default
  icmp-block-inversion: no
  interfaces: eth0
  sources: 
  services: ssh dhcpv6-client
  ports: 
  protocols: 
  masquerade: no
  forward-ports: 
  source-ports: 
  icmp-blocks: 
  rich rules:

ここでは、eth0インターフェースへの設定で、サービスとしてはsshとdhcpv6-clientを許可してそれ以外のサービスを拒否。ICMP(通信確認に使う取り決め:pingコマンドなどで利用)をブロックする設定はno(無し)です。他に、masqueradeは、自宅LANなどの複数のIPアドレス(インターネットでは使えない特殊なアドレス)から1つのグローバルIPアドレス(インターネット用アドレス)を経由でインターネットをアクセスするIPマスカレード機能がno(オフ)となります。

Firewalldにサービスの許可を追加してみよう

firewall-cmdコマンドに--list-allオプションを指定した結果としては2つのサービスが使える様になっていました。これだけではWebアプリさえ公開できず、更に利用したいサービスは増えていくはずです。なので、利用したいサービスが増えたらpublicゾーンに追加し、逆に利用しなくなったサービスがあったらpublicゾーンから取り除きます。また、ICMPに関しては使わない機能を追加していきます。サービスは/usr/lib/firewalld/servicesに、ICMPは/usr/lib/firewalld/icmptypesにそれぞれxml形式のファイルで用意されています。どの様なサービスが用意されているのかみてみましょう。

# firewall-cmd --get-services ↩︎
RH-Satellite-6 amanda-client amanda-k5-client bacula bacula-client bgp bitcoin bitcoin-rpc bitcoin-testnet bitcoin-testnet-rpc ceph ceph-mon cfengine condor-collector ctdb dhcp dhcpv6 dhcpv6-client dns docker-registry docker-swarm dropbox-lansync elasticsearch freeipa-ldap freeipa-ldaps freeipa-replication freeipa-trust ftp ganglia-client ganglia-master git gre high-availability http https imap imaps ipp ipp-client ipsec irc ircs iscsi-target jenkins kadmin kerberos kibana klogin kpasswd kprop kshell ldap ldaps libvirt libvirt-tls managesieve mdns minidlna mongodb mosh mountd ms-wbt mssql murmur mysql nfs nfs3 nmea-0183 nrpe ntp openvpn ovirt-imageio ovirt-storageconsole ovirt-vmconsole pmcd pmproxy pmwebapi pmwebapis pop3 pop3s postgresql privoxy proxy-dhcp ptp pulseaudio puppetmaster quassel radius redis rpc-bind rsh rsyncd samba samba-client sane sip sips smtp smtp-submission smtps snmp snmptrap spideroak-lansync squid ssh syncthing syncthing-gui synergy syslog syslog-tls telnet tftp tftp-client tinc tor-socks transmission-client upnp-client vdsm vnc-server wbem-https xmpp-bosh xmpp-client xmpp-local xmpp-server zabbix-agent zabbix-server

# firewall-cmd --get-icmptypes ↩︎
address-unreachable bad-header communication-prohibited destination-unreachable echo-reply echo-request fragmentation-needed host-precedence-violation host-prohibited host-redirect host-unknown host-unreachable ip-header-bad neighbour-advertisement neighbour-solicitation network-prohibited network-redirect network-unknown network-unreachable no-route packet-too-big parameter-problem port-unreachable precedence-cutoff protocol-unreachable redirect required-option-missing router-advertisement router-solicitation source-quench source-route-failed time-exceeded timestamp-reply timestamp-request tos-host-redirect tos-host-unreachable tos-network-redirect tos-network-unreachable ttl-zero-during-reassembly ttl-zero-during-transit unknown-header-type unknown-option

どの様なサービスが用意されているかはfirewall-cmdコマンドに--get-servicesオプションを付けて確認できます。ちなみに、icmpの定義を見るにはfirewall-cmdコマンドに--get-icmptypesオプションを指定してください。ついでに、各設定ファイルを一覧表示すると次の様になります。

# ls /usr/lib/firewalld/services ↩︎
RH-Satellite-6.xml       ctdb.xml                 ganglia-client.xml     iscsi-target.xml  mdns.xml       openvpn.xml               pulseaudio.xml       smtp.xml               tftp.xml
amanda-client.xml        dhcp.xml                 ganglia-master.xml     jenkins.xml       minidlna.xml   
:
:
# ls /usr/lib/firewalld/icmptypes ↩︎
address-unreachable.xml       failed-policy.xml              ip-header-bad.xml            no-route.xml              reject-route.xml             timestamp-reply.xml             ttl-zero-during-transit.xml
bad-header.xml                fragmentation-needed.xml       neighbour-advertisement.xml
:
:

さくらのサービスでCentOS 7を使い始めたばかりの場合、sshとdhcpv6-clientだけが許可されています。実際に皆さんは、ホームページやWebアプリを公開したいでしょうから、httpsとhttpのサービスを追加してみましょう。これらの様な一般的なサービスはCentOS 7のパッケージに用意されているため、次の様に既存の設定を使いますと指定するだけで済みます。

# firewall-cmd --permanent --add-service=https ↩︎
success
# firewall-cmd --permanent --add-service=http ↩︎
success
# firewall-cmd --reload ↩︎
success
# firewall-cmd --zone=public --list-services ↩︎
ssh dhcpv6-client https http

firewall-cmdコマンドでサービスを追加するには「--add-service=サービス名」オプションと、追加したサービスを永続化させる--permanentオプションを使います。--permanentオプションを付けないと一時的に追加するだけで、再起動したときに設定が消えてしまいます。--permanentオプションを付けると、フィルタの再読み込み、またはfirewalldサービスの再起動から有効となります。firewall-cmdコマンドに--reloadオプションを付けるとフィルタを読み込み直します(--permanentオプションと--reloadオプションはセットで覚えましょう)。最後のfirewall-cmdコマンドに「--zone=ゾーン名」オプションはpublicゾーンを指定し、--list-servicesオプションで許可されているサービス一覧を表示します。

逆に、使わなくなったサービスを削除することもあるでしょう。dhcpv6-clientサービスを削除してみます。

# firewall-cmd --permanent --remove-service=dhcpv6-client ↩︎
success
# firewall-cmd --reload ↩︎
success
# firewall-cmd --zone=public --list-services ↩︎
ssh https http

firewall-cmdコマンドでサービスを取り除くのには「--remove-service=サービス名」オプションと削除したサービスを永続化させる--permanentオプション を使います。--permanentオプションを指定したら、必ず--reloadオプションを付けてフィルタを読み込み直す癖をつけましょう。

Firewalldに用意されていないサービスを追加してみよう

今までにないサービスを提供するときはどうするかというと、/etc/firewalld/servicesにxmlファイルを作成すれば良いとはいえ、だいたいのサービスは提供されています。パッと見たところ、前回出てきたtomcatサービス(Javaのサーバー)がないですが、tomcatサービスが使う8080番ポートを利用しているxmlファイルがないかとgrepコマンド探してみると、jenkins.xmlに定義があるのでjenkinsを追加してもよし、コピーして表題(shortタグ)と説明(descriptionタグ)を変更するのでも良しです。もし、新たにtomcat.xmlファイルを作るのであれば、次の様にすると簡単です。

# cat <<EOL> tomcat.xml ↩︎## 今いるところにtomcat.xmlファイルを作成
緑文字部分をコピー&ペースト
<?xml version="1.0" encoding="utf-8"?>                      
<service>
 <short>Apache Tomcat</short>
 <description>Apache Tomcat implements several Java EE specifications including Java Servlet, JavaServer Pages (JSP), Java EL, and WebSocket, and provides a "pure Java" HTTP web server environment in which Java code can run.</description>
 <port protocol="tcp" port="8080"/>
</service>
EOL
#

注)catコマンドを入力すると > が表示されますが、構わずに「<?xml〜EOL」をコピー&ペーストしましょう。

実際に作成したtomcat.xmlファイルを使ってtomcatサービスをfirewall-cmdコマンドで追加してみます。

# firewall-cmd --permanent --new-service-from-file=tomcat.xml --name=tomcat ↩︎
success
# firewall-cmd --permanent --zone=public --add-service=tomcat ↩︎
success
# firewall-cmd --reload ↩︎
success
# firewall-cmd --zone=public --list-services ↩︎
ssh https http tomcat

間違えて新しいサービスを登録した人は、登録したサービスを削除するのに「--delete-service=tomcat」オプションを使います。他のオプションの様にremoveでは無くてdeleteなので注意が必要です。

FirewalldでIPアドレス許可を追加してみよう

これまで設定してきたfirewall-cmdコマンドではサービスのポート番号だけで許可しました。次は、会社限定や自宅限定でtomcatサービスを利用したいみたいな応用をしたいので、ポート番号に加えてIPアドレスでの許可・不許可の指定が是非ともしたいです。先ほど作成したtomcat.xmlファイルにアドレスを追加するものかと思いきや、おっとどっこいそんな機能はありません……。複雑な指定は、リッチルールを書く必要があります。

# firewall-cmd --permanent --zone=public --remove-service=tomcat ↩︎
success
# firewall-cmd --permanent --zone=public --add-rich-rule="rule family="ipv4" source address="XXX.X.XXX.XX" port protocol="tcp" port="8080" accept" ↩︎
success
# firewall-cmd --reload ↩︎
success
# firewall-cmd --list-all ↩︎
public (active)
  target: default
  icmp-block-inversion: no
  interfaces: eth0
  sources: 
  services: ssh dhcpv6-client
  ports: 
  protocols: 
  masquerade: no
  forward-ports: 
  source-ports: 
  icmp-blocks: 
  rich rules: 
	rule family="ipv4" source address="XXX.X.XXX.XX" port port="8080" protocol="tcp" accept

まずは、先ほど登録したtomcatサービスを「--remove-service=サービス名」オプションでpublicゾーンから削除し、「--add-rich-rule=〜」オプションでリッチルールを追加します。ここでは、XXX.X.XXX.XXというIPアドレスから8080ポート(tomcatが利用)への要求にaccept(受け入れ)としました。tomcatサービスを削除したのであれば、XXX.X.XXX.XX以外のIPアドレスからはアクセスできなくなります。firewall-cmdコマンドに--list-allオプションを指定した結果の最後の「rich rules:」の項目が追加されたルールです。

リッチルールの活用例としては、22番ポートを使ったssh接続を自宅からの限定にしたいなど、セキュアなメンテナンスにも使えるのではないでしょうか。学校や自宅や会社などは多くのIPアドレスを持っていないので、全てのアドレスを指定する必要はなく、Proxyサーバなどゲートウェイとなるアドレスを許可してあげれば良いはずです(ご自宅のゲートウェイアドレスを知るには「ゲートウェイを調べるサービス」などでgoogle検索)。また、連続したアドレスの指定にはXX.XX.XX.0〜XX.XX.XX.255であればネットマスクが24ビットとなり、「XX.XX.XX.0/24」の様にまとめて記述できます。

今回もかなり早口で説明した感じとなり、全てのオプションは書けなかったですが、ポイントとしては公開するサービスのポートだけを開き、限定公開するサービスは更にIPアドレスでアクセスを絞る様にしてください。そして使わなくなったサービスは早々に取り除きましょう。

コマンド+オプション 動作内容
firewall-cmd --state 起動確認
firewall-cmd --get-active-zone 現在利用されているゾーン名確認
firewall-cmd --list-all 利用されているゾーンの詳細確認
firewall-cmd --list-services 現在のゾーンで許可されているサービスの一覧表示
firewall-cmd --get-services 定義されているサービスの一覧表示
firewall-cmd --get-icmptypes 定義されているicmpの一覧表示
firewall-cmd --permanent --add-service=サービス名 サービスを現在のゾーンに追加
firewall-cmd --permanent --remove-service=サービス名 現在のゾーンからサービスを取り除く
firewall-cmd --reload 定期の再読み込み
firewall-cmd --permanent --new-service-from-file=サービス名.xml --name=サービス名 サービス名.xmlファイルでサービスを登録
firewall-cmd --permanent --delete-service=サービス名 サービス定義を削除
firewall-cmd --permanent --zone=ゾーン名 --add-service=サービス名 現在のゾーンに指定したサービスを追加
firewall-cmd --permanent --zone=ゾーン名 --remove-service=サービス名 現在のゾーンから指定したサービスを取り除く
firewall-cmd --permanent --zone=ゾーン名 --add-rich-rule="rule family="ipv4" source address="IPアドレス" port protocol="プロトコル名" port="ポート番号" 許可・不許可" 現在のゾーンにリッチルールを追加(指定IPアドレス(群)からポート番号へのアクセスを許可・不許可)
firewall-cmd --permanent --zone=ゾーン名 --remove-rich-rule="ルール" 現在のゾーンからリッチルールを取り除く

表3:firewall-cmdコマンドのまとめ