當它成為「我的伺服器」那一刻,一切都是我的責任
上次,我翻過了 Linux CLI 與權限(Permission)這道牆。chmod、chown,還有絕對不能做的 chmod 777。對單一檔案的存取控制,總算抓到了一點感覺。
但那時的我犯了一個天大的誤會。以為只要把「檔案權限」管好,伺服器就安全了。
「伺服器安全?那不是資安團隊的事嗎?」
如果是大企業,或許是這樣。但我是獨立開發者。伺服器管理員、資安負責人、網路工程師,全部都是「我自己」。所有的資安責任,都壓在我的肩膀上。
而讓我真正意識到這件事的,是某個星期一早上發現的伺服器連線紀錄。

危機:有人一直在敲我伺服器的大門
星期一早上,我像往常一樣用 SSH 連進伺服器查看日誌,結果發現了不對勁的東西。
# 檢查最近 SSH 登入失敗的紀錄
$ grep "Failed password" /var/log/auth.log | tail -20
看到畫面上跑出來的日誌,我當場起雞皮疙瘩。
Feb 10 03:14:22 sshd: Failed password for root from 185.220.xx.xx
Feb 10 03:14:25 sshd: Failed password for root from 185.220.xx.xx
Feb 10 03:14:27 sshd: Failed password for admin from 185.220.xx.xx
Feb 10 03:14:30 sshd: Failed password for ubuntu from 185.220.xx.xx
...
從凌晨三點開始,數百次的登入嘗試。IP 在海外。帳號名稱在 root、admin、ubuntu 之間輪流試。這就是有人跑了「自動化程式(機器人)」,對密碼進行暴力破解(Brute Force)。
「欸?我還沒被駭吧……?」
幸好沒被攻破。但面對一個站在門口一把一把試鑰匙的小偷,能說「還沒進來所以沒事」嗎?我當下立刻開始檢查伺服器的資安狀態。

第一步:鎖好大門 — 強化 SSH 安全
伺服器的大門是 SSH(Secure Shell)。我連到伺服器時用的就是它。問題是,駭客也想從同一扇門進來。首先要做的,就是把這扇大門補強。
變更 SSH 連接埠 (預設 22 號 → 其他編號)
SSH 預設連接埠是 22 號。駭客的自動掃描器,第一個敲的就是 22 號埠。光是改連接埠編號,就能過濾掉超過 90% 的暴力攻擊。
# 打開 SSH 設定檔
$ sudo vi /etc/ssh/sshd_config
# 變更連接埠編號 (預設 22 → 例如 2222)
Port 2222
# 重新啟動 SSH 服務
$ sudo systemctl restart sshd
打個比方,小偷通常會盯上一樓正門(22 號)。如果把入口搬到三樓的頂樓門(2222 號),大多數小偷連門在哪都找不到。
禁止 root 帳號直接登入
看日誌就知道,駭客第一個嘗試的帳號是「root」。因為 root 是 Linux 的最高管理員帳號,一旦被攻破,整台伺服器就被掌控了。
# 在 SSH 設定檔中禁止 root 直接登入
$ sudo vi /etc/ssh/sshd_config
PermitRootLogin no
現在連 root 要 SSH 登入本身都不可能了。用一般帳號登入之後,需要時再用 sudo 提升權限即可。
以 SSH 金鑰驗證取代密碼
密碼再怎麼複雜,只要自動程式嘗試數萬種組合,總有一天會被破解。最保險的方法,就是把密碼登入完全關掉,只用「SSH 金鑰(Key)」連線。
# 在自己電腦(本機)上產生 SSH 金鑰對
$ ssh-keygen -t rsa -b 4096
# 把公鑰複製到伺服器
$ ssh-copy-id -p 2222 user@my-server-ip
# 在伺服器上停用密碼登入
$ sudo vi /etc/ssh/sshd_config
PasswordAuthentication no
SSH 金鑰驗證不是「鑰匙」,而是「指紋辨識」。密碼(鑰匙)可以被複製,但只存在於我電腦上的私鑰(指紋),實際上幾乎無法複製。
第二步:築牆 — 防火牆(Firewall)
強化完大門(SSH)之後,接下來該在建築周圍築牆,控制出入口了。這就是「防火牆(Firewall)」。
伺服器總共有 65,535 個連接埠(出入口)。沒有防火牆的話,等於這六萬多扇門全部敞開。必須只開需要用的門,其餘一律鎖死。
在 Linux 上最容易上手的防火牆工具,就是「UFW(Uncomplicated Firewall)」。
# 啟用 UFW
$ sudo ufw enable
# 預設政策:進來的全部擋,出去的允許
$ sudo ufw default deny incoming
$ sudo ufw default allow outgoing
# 只開需要的連接埠
$ sudo ufw allow 2222/tcp # SSH (變更後的連接埠)
$ sudo ufw allow 80/tcp # HTTP
$ sudo ufw allow 443/tcp # HTTPS
# 查看目前的防火牆狀態
$ sudo ufw status
| 連接埠 | 用途 | 是否開放 |
|---|---|---|
| 2222 | SSH (變更後的連接埠) | ✅ 允許 |
| 80 | HTTP (網頁) | ✅ 允許 |
| 443 | HTTPS (安全網頁) | ✅ 允許 |
| 22 | SSH (預設,不再使用) | ❌ 封鎖 |
| 其餘 | 全部 | ❌ 封鎖 |
六萬多扇門中,只留下三扇開著,其餘全用磚頭封死。小偷要敲的門,本身就不存在了。

第三步:通訊加密 — HTTPS
如果伺服器要對外提供網站服務,使用者與伺服器之間的通訊也必須保護。這就是「HTTPS」。
HTTP 傳送資料就像「明信片」一樣直接寄出。中途任何人都可以偷看內容。HTTPS 則是把資料放進「密封的信封」再寄出。就算被攔截,也無法讀取內容。
如果使用者在登入表單輸入的帳號/密碼,是以 HTTP 傳送的會怎樣?與你共用咖啡廳 Wi-Fi 的某個人,就能原封不動地看到這些內容。
在實務上,部署 HTTPS 最簡單的方法,就是用「Let’s Encrypt」申請免費的 SSL 憑證。
# 安裝 Certbot (Let's Encrypt 自動化工具)
$ sudo apt install certbot python3-certbot-nginx
# 申請憑證並自動套用到 Nginx
$ sudo certbot --nginx -d mydomain.com
# 測試自動更新 (憑證每 90 天需要更新一次)
$ sudo certbot renew --dry-run
只要這幾行指令,自己服務的網址就會從 http:// 變成 https://,瀏覽器網址列也會出現鎖頭圖示。等於告訴使用者:「這個網站是安全的。」
第四步:自動防禦系統 — Fail2Ban
就算改了 SSH 連接埠、設定了金鑰驗證,這世上還是有鍥而不捨的機器人。一定會有找出新連接埠、不斷嘗試連線的傢伙。
這時候派得上用場的,就是「Fail2Ban」。它是一款能自動封鎖多次登入失敗 IP 的工具。
# 安裝 Fail2Ban
$ sudo apt install fail2ban
# 設定 SSH 保護
$ sudo vi /etc/fail2ban/jail.local
[sshd]
enabled = true
port = 2222
maxretry = 5
bantime = 3600
findtime = 600
| 設定 | 意義 |
|---|---|
| maxretry = 5 | 試錯 5 次 |
| bantime = 3600 | 就把該 IP 封鎖 1 小時 |
| findtime = 600 | 在 10 分鐘內失敗 5 次時觸發 |
# 啟動 Fail2Ban
$ sudo systemctl enable fail2ban
$ sudo systemctl start fail2ban
# 查看目前被封鎖的 IP
$ sudo fail2ban-client status sshd
Fail2Ban 就像立在大門前的「監視器 + 自動鎖」。可疑人物密碼輸錯太多次,系統就會自動禁止他出入。

實務建議:資安檢核清單
把目前為止設定的內容整理成檢核清單。每次架新伺服器時只要照著這份清單跑一遍,就能建立起基本的防線。
| # | 項目 | 指令/設定 |
|---|---|---|
| 1 | 變更 SSH 連接埠 | sshd_config 中設 Port 2222 |
| 2 | 禁止 root 登入 | PermitRootLogin no |
| 3 | 切換為 SSH 金鑰驗證 | PasswordAuthentication no |
| 4 | 啟用防火牆 | ufw enable,只允許必要連接埠 |
| 5 | 部署 HTTPS | Let’s Encrypt + Certbot |
| 6 | 安裝 Fail2Ban | 登入失敗時自動封鎖 IP |
| 7 | 定期更新 | sudo apt update && sudo apt upgrade |
第 7 項意外地重要。資安漏洞每天都在冒出來。就算把防火牆築得再牢,只要作業系統或軟體本身有洞,還是沒用。定期更新,是最基本卻也最有效的資安對策。
結語:資安不是什麼特別的事,而是一種習慣
一開始,「資安」這個字聽起來很了不起。防駭、入侵偵測、加密……感覺都是電影裡才有的東西。
但實際做過才發現,這並不需要什麼了不起的技術。變更 SSH 連接埠、開防火牆、設定金鑰驗證、裝上 Fail2Ban。幾行指令就搞定了。重要的不是「知道」,而是「去做」。
資安事故不是因為多高超的駭客技術,而是發生在連最基本措施都沒做的伺服器上。
那天之後,我每次架新伺服器,在把程式碼丟上去之前,一定先跑過這份資安檢核清單。打開門之前先檢查鎖。這就是守住一台伺服器最基本的態度。
一路走來,我們為了在 Linux 這片荒野中生存,鍛鍊了基本體能(CLI、權限、資安)。但有一個根本問題仍然沒解決:我筆電(Windows)和 Linux 伺服器的環境,根本不一樣。Java 版本不同,函式庫也不同。
有一項技術,能永遠終結「我電腦上可以跑耶?」這種藉口。下次,我們就來聊聊把環境差異徹底消除的「虛擬化與容器」。