這裡介紹如何在 CentOS Linux 7 中使用 firewalld 的指令設定防火牆規則。

在舊版的 CentOS Linux 中,防火牆都是以傳統的 iptables 來設定,而從 CentOS 7 開始,則改用 firewalld 來管理防火牆。

傳統的 iptables 在每次修改防火牆時都要清除舊規則,重新套用一次新規則,使用上不是很方便,而新的 firewalld 以區域(zone)的方式管理規則,並使用動態的方式執行,修改規則後可立即生效,也不需要重新啟動系統的服務(service 或 daemon),所以後來的 RHEL、CentOS 與 Fedora 都改用 firewalld 了。

iptables 與 firewalld 兩個防火牆管理系統只能選用其中一種,不可以同時開啟,否則會有問題。

以下是我在 CentOS Linux 7 的測試環境中所整理出來的 firewalld 使用教學。

安裝 firewalld

安裝 firewalld 之前,請先確認 iptables 是否有被啟用,若系統上原本就有運行 iptables 的防火牆,一定要先將其關閉後,才能啟用 firewalld,否則會有問題:

# 檢查 iptables 服務是否正在運行
systemctl status iptables

# 停止正在執行的 iptables 服務
systemctl stop iptables

# 將 iptables 服務永久關閉
systemctl mask iptables

firewalld 在 RHEL/CentOS 7 與 Fedora 21 之中應該是預設就會安裝好的,若您的系統上沒有安裝,可用 yum 安裝:

sudo yum install firewalld

當 firewalld 安裝好之後,檢查 firewalld 服務是否有啟動:

# 檢查 firewalld 服務狀態
systemctl status firewalld

若 firewalld 沒有啟動,則手動啟動它:

# 啟動 firewalld 服務
systemctl start firewalld

# 停止 firewalld 服務
systemctl stop firewalld

# 重新啟動 firewalld 服務
service firewalld restart

若要設定讓 firewalld 在開機時自動啟動,可執行:

# 設定開機自動啟動 firewalld 服務
systemctl enable firewalld

區域簡介

firewalld 的區域(zone)可用來設定網路連線、介面等所處的運作環境,對內使用的區域其防火牆規則會較為寬鬆,反之若是對外的區域其規則會較為嚴謹。

一條網路連線或介面只能隸屬於一個區域,我們可以自訂區域的設定,也可以直接從 firewalld 預設的幾個區域中選擇:

區域描述
drop任何往內的封包都會被丟棄,只允許往外傳送的封包。
block任何來自於外部的連線都會被阻擋,只允許自己系統主動建立的連線。
public公開區域,預設不信任其他電腦與網路,只有被允許的連線才能進入。通常大部分的連線設定都會放在這裡。
external公開區域,適用於 NAT 網路環境。
dmz非軍事區域(demilitarized zone,有點像是放在外頭的危險區域),允許外部的連線進入,但其對內的連線則有限制,只有被允許的連線才能連進內部網路。
work公司內部等工作區域,在此區域中不應該會有惡意的攻擊者。只有被允許的連線可以進入。
home家裡頭的網路區域,在此區域中不應該會有惡意的攻擊者。只有被允許的連線可以進入。
internal內部網路區域,在此區域中不應該會有惡意的攻擊者。只有被允許的連線可以進入。
trusted完全信任的區域,接受所有連線。

查詢區域設定

firewalld 都是透過 firewall-cmd 指令來操作的,若要列出 firewalld 中已經定義好的區域,可執行:

# 列出所有的區域
firewall-cmd --get-zones
block dmz drop external home internal public trusted work

列出所有的區域與其詳細的設定內容:

# 列出所有的區域與內容
sudo firewall-cmd --list-all-zones
block
  target: %%REJECT%%
  icmp-block-inversion: no
  interfaces:
  sources:
  services:
  ports:
  protocols:
  masquerade: no
  forward-ports:
  source-ports:
  icmp-blocks:
  rich rules:
[略]

若要查詢特定區域的內部設定,可執行:

# 列出指定的區域與內容
sudo firewall-cmd --zone=public --list-all
public (active)
  target: default
  icmp-block-inversion: no
  interfaces: enp7s0d1 ib0
  sources:
  services: ssh dhcpv6-client rpc-bind https http tftp dhcp
  ports: 944/tcp 944/udp 945/tcp 945/udp 946/udp 8649/udp 8649/tcp 8651/tcp 8652/tcp 15001/tcp 15002/tcp 15003/tcp 15004/tcp 15007/tcp 17001/tcp 8080/tcp
  protocols:
  masquerade: no
  forward-ports:
  source-ports:
  icmp-blocks:
  rich rules:

上面這個查詢方式所得到的結果是目前系統上執行的設定(runtime),若要查詢寫在硬碟中的永久設定,可以使用:

# 查詢永久設定值
sudo firewall-cmd --zone=public --list-all --permanent

預設區域

當網路介面或是連線沒有指定區域時,就會直接納入預設區域中。若要查詢目前預設的區域,可執行:

# 列出預設區域
firewall-cmd --get-default-zone
public

若要更改 firewalld 預設的區域,可以執行:

# 設定預設區域
firewall-cmd --set-default-zone=home

更改後,可再確認一次預設區域:

# 列出預設區域
firewall-cmd --get-default-zone
home

介面所屬區域

列出目前所有運作中的區域,以及各個網路介面所屬的區域:

# 查詢運作中的區域
firewall-cmd --get-active-zones
public
  interfaces: enp7s0d1 ib0
trusted
  interfaces: eno2

若要查詢指定網路介面所屬區域,可執行:

# 查詢網路介面所屬區域
firewall-cmd --get-zone-of-interface=enp7s0d1
public

若要更改指定網路介面的所屬區域,可以使用:

# 將 enp7s0d1 網路介面設定至 home 區域
sudo firewall-cmd --zone=home --change-interface=enp7s0d1

上面這種修改網路介面所屬區域的方式只是暫時的,若要讓系統重新開機後還可以維持這樣的設定,就要直接更改 /etc/sysconfig/network-scripts/ 底下的設定檔,以 enp7s0d1 這個網路介面來說,就修改 ifcfg-enp7s0d1 這個檔案中的ZONE 設定值:

ZONE=home

接下來要介紹如何在防火牆的區域中開啟特定的連接埠,讓對外的服務可以正常運作。

區域的服務

若要在防火牆上開啟一些連接埠,讓對外的服務使用,可以再區域設定中新增一些服務設定。firewalld 中有預先定義一些常用的服務名稱,這用這個指令查詢:

# 列出預先定義的服務名稱
firewall-cmd --get-services
RH-Satellite-6 amanda-client amanda-k5-client bacula bacula-client bitcoin bitcoin-rpc bitcoin-testnet bitcoin-testnet-rpc ceph ceph-mon cfengine condor-collector ctdb dhcp dhcpv6 dhcpv6-client dns docker-registry dropbox-lansync elasticsearch freeipa-ldap freeipa-ldaps freeipa-replication freeipa-trust ftp ganglia-client ganglia-master high-availability http https imap imaps ipp ipp-client ipsec iscsi-target kadmin kerberos kibana klogin kpasswd kshell ldap ldaps libvirt libvirt-tls managesieve mdns mosh mountd ms-wbt mssql mysql nfs nrpe ntp openvpn ovirt-imageio ovirt-storageconsole ovirt-vmconsole pmcd pmproxy pmwebapi pmwebapis pop3 pop3s postgresql privoxy proxy-dhcp ptp pulseaudio puppetmaster quassel radius rpc-bind rsh rsyncd samba samba-client sane sip sips smtp smtp-submission smtps snmp snmptrap spideroak-lansync squid ssh synergy syslog syslog-tls telnet tftp tftp-client tinc tor-socks transmission-client vdsm vnc-server wbem-https xmpp-bosh xmpp-client xmpp-local xmpp-server

大部分常見的網路服務都有在這個列表中,若要將指定的服務新增至指定的區域中,可執行:

# 將 http 服務新增至 public 區域中
sudo firewall-cmd --zone=public --add-service=http

上面這行指令只是暫時將 http 服務新增至 public 區域中,重新開機後這個設定就不見了,通常在比較重要的伺服器上,我們都會先使用暫時的設定先進行測試,若測試沒問題,再將設定寫入設定檔,永久保存:

# 永久將 http 服務新增至 public 區域中
sudo firewall-cmd --zone=public --permanent --add-service=http

執行永久的設定之後,可再重新查詢一次永久設定值,確認設定值是否正確:

# 列出 public 區域永久的服務設定值
sudo firewall-cmd --zone=public --permanent --list-services

這樣設定之後,在 public 這個區域就會開啟網頁的 80 連接埠,讓外面可以連進來我們的網站,而現在的網站都會提供安全加密的網頁,所以最好連 HTTPS 的 443 連接埠也一並開啟,開啟的方式大同小異:

# 將 https 服務新增至 public 區域中
sudo firewall-cmd --zone=public --add-service=https

# 永久將 https 服務新增至 public 區域中
sudo firewall-cmd --zone=public --permanent --add-service=https

自訂開啟連接埠

基本上 firewalld 有內定的服務都可以使用上面這種方式開啟,如果我們想要開啟的連接埠不在 firewalld 內建的服務名單中,也可以直接指定通訊協定(tcpudp)與埠號:

# 開啟 tcp 的 8080 連接埠
sudo firewall-cmd --zone=public --add-port=8080/tcp

# 永久開啟 tcp 的 8080 連接埠
sudo firewall-cmd --zone=public --permanent --add-port=8080/tcp

埠號的部分也可以用範圍的方式指定,一次開通多個連接埠:

# 開啟 udp 的 4990 至 4999 連接埠
sudo firewall-cmd --zone=public --add-port=4990-4999/udp

# 永久開啟 udp 的 4990 至 4999 連接埠
sudo firewall-cmd --zone=public --permanent --add-port=4990-4999/udp

移除服務

若要將指定的服務從某個區域中移除,可以執行:

# 將 http 服務從 public 區域中移除
sudo firewall-cmd --zone=public --remove-service=http

# 永久將 http 服務從 public 區域中移除
sudo firewall-cmd --zone=public --permanent --remove-service=http

如果是要移除自訂的通訊協定與埠號,則執行:

# 關閉 tcp 的 8080 連接埠
sudo firewall-cmd --zone=public --remove-port=8080/tcp

# 永久關閉 tcp 的 8080 連接埠
sudo firewall-cmd --zone=public --permanent --remove-port=8080/tcp

新增服務名稱

除了直接指定通訊協定與埠號之外,我們也可以自訂新的服務名稱,加入 firewalld 的服務名稱清單中,這樣就可以使用服務名稱的方式來設定開啟的服務,這樣做的好處是可以讓防火牆的設定看起來更容易理解,不會搞不清楚某些奇怪埠號的用途。

若要新增服務名稱,可以在 /etc/firewalld/services 新增服務的 XML 設定檔,XML 設定檔的語法可參考 /usr/lib/firewalld/services/ 中的範例,例如 http.xml 就是定義 http 服務的設定檔:

<?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>

參考這些範例後,撰寫自己的服務設定檔(若需要開啟多個連接埠,可以自己新增,或是參考其他的範例檔),然後另外儲存成一個新的檔案,放在 /etc/firewalld/services 目錄下,檔名要設定為服務的名稱加上 XML 的附檔名,例如 my_service.xml,接著讓 firewalld 重新載入設定:

# 重新載入設定
sudo firewall-cmd --reload

重新查詢一次支援的服務名稱,應該就可以看到新加入的 my_service 服務了:

# 列出預先定義的服務名稱
firewall-cmd --get-services

常用範例

這裡蒐集一些常用的 firewalld 防火牆設定指令。

只允許特定來源 IP 位址使用服務

如果我們的服務只想讓某些特定來源 IP 位址的電腦使用,可以這樣設定:

# 允許 192.168.0.0/24 使用 http 服務
firewall-cmd --zone=public \
  --add-rich-rule 'rule family="ipv4" source address="192.168.0.0/24" service name="http" accept'

# 永久允許 192.168.0.0/24 使用 http 服務
firewall-cmd --zone=public \
  --add-rich-rule 'rule family="ipv4" source address="192.168.0.0/24" service name="http" accept' \
  --permanent

白名單 IP 位址

若要將特定的 IP 位址設定為白名單,讓它可以連接任何的連接埠,可以這樣做:

# 將 192.168.0.123 列為 public 區域的白名單
firewall-cmd --zone=public \
  --add-rich-rule='rule family="ipv4" source address="192.169.0.123" accept'

# 永久將 192.168.0.123 列為 public 區域的白名單
firewall-cmd --zone=public \
  --add-rich-rule='rule family="ipv4" source address="192.169.0.123" accept' \
  --permanent

黑名單 IP 位址

若要將特定的 IP 位址設定為黑名單,阻擋該 IP 來源的所有連線,可以這樣設定:

# 禁止 181.215.176.3 的所有連線
firewall-cmd --zone=public \
  --add-rich-rule 'rule family="ipv4" source address="181.215.176.3" reject'

# 永久禁止 181.215.176.3 的所有連線
firewall-cmd --zone=public \
  --add-rich-rule 'rule family="ipv4" source address="181.215.176.3" reject'
  --permanent

參考資料