動手學 VPS 架站:使用 CentOS 7 + Nginx + PHP-FPM + MariaDB (6) - 防火牆

LNMP

我們可以使用防火牆及 Fail2ban 來阻止惡意連線,保護系統的安全。

3.6 防火牆

防火牆可以由你設定條件,允許什麼連線可以連入、什麼連線禁止通過,限制條件包括服務、ip 及 port 等。

早期 CentOS 是使用 iptables 來設定防火牆,但現在有一個新的 firewalld 服務,可以讓操作更容易,它的底層仍是使用 iptables。

# 安裝
sudo yum install firewalld
# 啟動
systemctl start firewalld
# 開機時自動啟動
systemctl enable firewalld
# 目前狀態
systemctl status firewalld

防火牆的狀態可以查看目前是否正在運作及開機時是否自動啟動: firewalld status

啟動防火牆後,就可以來設定限制條件了:

# 允許 SSH (22)
sudo firewall-cmd --permanent --add-service=ssh
# 允許 HTTP (80)
sudo firewall-cmd --permanent --add-service=http
# 允許 HTTPS (443)
sudo firewall-cmd --permanent --add-service=https
# 允許 MySQL (3306)
sudo firewall-cmd --permanent --add-service=mysql

# 移除服務
sudo firewall-cmd --permanent --remove-service=mysql

--permanent 表示條件永久有效,--add-service 表示允許通過的服務;要移除服務則使用 --remove-service

如果要針對 port 來設定:

# 允許的 port
sudo firewall-cmd --permanent --add-port=1234/tcp

--add-port 表示允許的 port 號,/tcp 表示使用 TCP 協定。

都設定好之後可以查看結果清單:

sudo firewall-cmd --permanent --list-all

結果可能如下:

public (active)
  target: default
  icmp-block-inversion: no
  interfaces: eth0 eth1
  sources:
  services: ssh dhcpv6-client http https mysql
  ports: 1234/tcp
  protocols:
  masquerade: no
  forward-ports:
  source-ports:
  icmp-blocks:
  rich rules:

當你都設定好之後,記得重新啟動防火牆讓設定生效:

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

# 或重新啟動防火牆
systemctl restart firewalld

目前為止,我們針對服務及 port 來設定,允許它們可以通過防火牆。但是對於 ip 就不能這麼做,除非你的系統只允許某些人來使用,否則一個一個 ip 來設定允許連線是不太可能的事,因此我們要反過,允許所有的 ip 連線,但是禁止那些想做壞事的 ip。

# 拒絕 123.123.123.123 這個 ip 的連線
sudo firewall-cmd --permanent --zone=public --add-rich-rule="rule family='ipv4' source address='123.123.123.123/24' reject"

請對照前面 --list-all 的項目,我們對 public 這個區域,增加 rich rules拒絕 reject 它的連線,另外也可使用丟棄 drop

拒絕和丟棄兩者的差別是 reject 會回傳 「ICMP destination-unreachable (目的地無法訪問的 ICMP 訊息)」給來源機器;drop (又稱黑洞) 則是直接把封包丟掉,對方不會收到任何訊息。至於用哪個則沒有定論,被指定為 reject 的來源主機會立即收到無法訪問的訊息,連線會立即中斷,它有可能誤以為伺服器不存在,因為訊息明確,屬於符合規範的動作;drop 則因為是個黑洞,來源主機不知道連線發生了什麼事,因此會保持連線一陣子,直到 timeout 才終止,來源端可能會發現自己被防火牆擋掉了。



3.7 Fail2ban

一開始我為了阻止想嘗試登入我的伺服器的 ip,寫了一支簡單的程式,搭配排程器使用,讓它定期去讀取 SSH 登入的 log 檔 /var/log/secure,找出其中登入失敗超過一定次數的 ip ,將其加入防火牆中。如此一來,被加入防火牆黑名單的 ip,在連線抵達伺服器時就直接被擋在牆外,連嘗試登入的機會都沒有。

後來我找到 fail2ban 這個工具來幫我處理這件事,它可以在開機時就啟動並常駐在背景服務,依照我的設定決定封鎖什麼條件的非法連線,還可以指定永久封鎖或只封鎖一段時間。

3.7.1 安裝

# 安裝
sudo yum -y install fail2ban

# 啟動
systemctl start fail2ban
# 開機時啟動
systemctl enable fail2ban

# 查看狀態
systemctl status fail2ban

# 停止
systemctl stop fail2ban
# 重新啟動
systemctl restart fail2ban

fail2ban 服務的設定檔在 /etc/fail2ban/fail2ban.conf,但是不要去修改這個檔案。

/etc/fail2ban 目錄中,所有的 .conf 都可以被 .local 覆寫;fail2ban 服務會先讀取 .conf 檔,然後再讀取 .local 檔,後面讀取的設定覆寫前面的;千萬不要去修改 .conf 檔,避免升級時發生錯誤。

3.7.2 設定 jail

要學會如何使用 fail2ban,就要先瞭解 jail (監獄)檔如何設定。

/etc/fail2ban/jail.conf 是一個設定樣板檔,它已經針對許多服務做了基本的設定,例如 SSH 登入服務的 sshd、SSH DDOS 攻擊的 sshd-ddos 及 Apache、Nginx 等的樣板設定,記得不要去更動這個檔案的內容。

jail.conf 中的設定並不會被啟用,我們必須自行建立 jail.local 然後選擇需要的來啟用,並視需要䨱蓋樣板中的設定值。

jail 檔除了可以放在 /etc/fail2ban/ 目錄下,還可以放在 /etc/fail2ban/jail.d 目錄下,可以讓每個 jail 獨立成一個檔案,方便管理。

現在,動手建立我們的 jail.local

cd /etc/fail2ban
vim jail.local

設定範例如下:

# 預設值,所有的 jail 通用
[DEFAULT]
# 封鎖 30 天 (單位:秒)
bantime = 2592000
# 或是永久封鎖
# bantime = -1

# 以下為兩個連動的參數,表示在限定時間內重試的次數
# 限定時間內(一天內,單位:秒)
findtime = 86400

# 重試的次數
maxretry = 3
# 意思是:只要在限定時間內重試超過限定的次數,就封鎖

# 針對 sshd 服務的 jail
[sshd]
# 啟用這個 jail
enabled = true
# 指定過濾器
filter  = sshd

# 針對 ssh ddos 攻擊的 jail
[ssh-ddos]
enabled = true
# 指定 port
port    = ssh,sftp
filter  = sshd-ddos
# 指定 log 檔的位置
logpath  = /var/log/messages
# 覆寫 [DEFAULT] 中的重試次數的設定
maxretry = 2

注意!設定項目與值間的等號 = 兩邊必須留空格。

每組 jail 以方括號 [] 表示, [DEFAULT] 區塊指定的內容會套用到所有 jail 上。要知道有哪些 jail 可用,可以查看 jail.conf 中的 JAILS 這個區塊。

jail 選項的說明:

  • filter:用來過濾要偵測的條件,一個 jail 只能有一個。
  • logpath:提供給 filter 用的 log 檔路徑,預設值為 /var/log/messages
  • maxretry:重試的最大次數,預設值 3 次。可以想像成提款機的密碼輸入錯誤最多 3 次,然後就鎖卡。
  • findtime:單位為秒,搭配 maxretry,指定在限定時間內允許的重試次數,例如 1 天內最多只能錯誤 3 次,超過就封鎖。預設值是 600 秒。
  • bantime:就是讓非法份子坐牢的時間,單位為秒,例如封鎖某個重試次數達最大值的 ip 1 天。預設值是 600 秒;把值指定為負值表示為 permanent (即無期徒刑,永久封鎖)。

fail2ban 提供 fail2ban-client 這個指令讓你使用:

# 啟動
sudo fail2ban-client start 

# 停止
sudo fail2ban-client stop

# 重新載入設定(有修改 jail 內容時需執行)
sudo fail2ban-client reload

# 查 jail 狀態
sudo fail2ban-client status
# 顯示結果如下:
Status
|- Number of jail: 2 # <- 表示目前啟用的 jail 數量
`- Jail list: sshd, sshd-ddos  # <- 啟用的 jail 清單

# 查指定 jail 的狀態,例如 sshd
sudo fail2ban-client status sshd
# 顯示結果如下:
Status for the jail: sshd
|- Filter
|  |- Currently failed: 0 # <- 目前失敗的數量
|  |- Total failed: 0 # <- 全部失敗的總數
|  `- Journal matches: _SYSTEMD_UNIT=sshd.service + _COMM=sshd
`- Actions
   |- Currently banned: 0 # <- 目前封鎖的數量
   |- Total banned: 0 # <- 全部封鎖的總數
   `- Banned IP list:     # <- 已封鎖的 ip 清單

# 如果要查看 fail2ban 的 log
sudo tail -f /var/log/fail2ban.log
# 使用 tail 來看最後(最新)的 log 狀態
# -f 會持續更新,ctrl + c 離開

以上是針對 SSH 的設定範例,接下來是針對 Nginx 的一些設定範例。

3.7.3 Nginx 保護

3.7.3.1 針對 Forbidden 保護

當有人連到你的網站,但輸入錯誤的 URI 時,就會出現 forbidden (被禁止) 的訊息,大部份情況是單純輸入錯誤,但是有心人可能會亂猜你的 URI 來找漏洞,這時候可以使用 fail2ban 讓這些連線暫時關禁閉。

首先,建立 forbidden 過濾器 /etc/fail2ban/filter.d/nginx-forbidden.local

[Definition]
failregex = ^.* \[error\] \d+#\d+: .* forbidden.*,.* client: <HOST>.*, .*$

ignoreregex =

這個過濾器會去找出 Nginx 的 error.log 檔中出現 forbidden 的連線客戶端。

接著建立 /etc/fail2ban/jail.d/nginx-forbidden.local

[nginx-forbidden]
enabled = true
# 使用我們前面建立的過濾器
filter = nginx-forbidden
port = http,https 
logpath = /var/log/nginx/error.log
# 一天內
findtime = 86400
# Forbidden 5 次
maxretry = 5
# 封鎖一天
bantime = 86400

3.7.3.2 針對登入錯誤的保護

建立 /etc/fail2ban/jail.d/nginx-http-auth.local

# 啟用 fail2ban 提供的 jail
[nginx-http-auth]
enabled  = true
# 使用 fail2ban 提供的過濾器
filter   = nginx-http-auth
port     = http,https
logpath  = /var/log/nginx/error.log
action = iptables-multiport[name=Forbidden, port="http,https"]
findtime = 86400
maxretry = 3
bantime = 86400

3.7.3.3 針對機器人的保護

建立 /etc/fail2ban/jail.d/nginx-badbots.local

[nginx-badbots]
enabled  = true
port     = http,https
filter   = nginx-botsearch
logpath  = /var/log/nginx/error.log
findtime = 86400
maxretry = 3
# 永久封鎖
bantime = -1

每當你完成一個 jail 設定檔時,最好先檢查是否有錯誤:

sudo fail2ban-client -d

如果沒出現 ERROR 的話就表示成功了,最後記得重新讀取設定檔:

sudo fail2ban-client reload

3.7.4 白名單

如果你想將某個 ip 區段加入白名單 (jail.local):

[DEFAULT]
# ...略
# 白名單,忽略的 ip 段,
# 以下為遮罩,一個 ip 共分為 4 段,第一段以 /8 表示,
# 意思是固定第一段的數值,後面則全部接受
# /8  表示 127.x.x.x 這段
# /16 表示 127.0.x.x 這段
# /24 表示 127.0.0.x 這段
ignoreip = 127.0.0.1/8 10.0.0.0/8 # <-- 以空白隔開不同段的 ip

3.7.5 解除封鎖

如果要取消被封鎖的 ip 讓它出獄:

sudo fail2ban-client set sshd unbanip 123.123.123.123

其他更詳細的設定,可以參考 fail2ban 操作手冊


繼續閱讀:動手學 VPS 架站:使用 CentOS 7 + Nginx + PHP-FPM + MariaDB (7) - 網頁伺服器

本文網址:https://blog.tonycube.com/2018/08/vps-centos-7-nginx-php-fpm-mariadb-6-firewall.html
Tony Blog 撰寫,請勿全文複製,轉載時請註明出處及連結,謝謝 😀

我要留言

留言小提醒:
1.回覆時間通常在晚上,如果太忙可能要等幾天。
2.請先瀏覽一下其他人的留言,也許有人問過同樣的問題。
3.程式碼請先將它編碼後再貼上。(線上編碼:http://bit.ly/1DL6yog)
4.文字請加上標點符號及斷行,難以閱讀者恕難回覆。
5.感謝您的留言,您的問題也可能幫助到其他有相同問題的人。