雲伺服器安全基線:上線前必做的 9 項加固清單,從 SSH 金鑰登入、安全組最小許可權,到 MFA、補丁節奏與恢復演練,附可直接複製的命令。
一臺雲伺服器被攻破的路徑通常很樸素:掃到開放的 22 埠 → 爆破出弱口令或撞庫成功 → 拿到 root → 植入計劃任務、挖礦或橫向移動。雲伺服器安全基線要做的,就是把這條鏈上的每一環都變貴:讓掃到無意義、讓爆破無入口、讓 root 拿不到、讓操作留得下痕跡。
判斷標準不是「做沒做」,而是「能不能被別人驗證」。 每項加固都要配一條你自己能跑的驗證命令——基線不是一份文件,而是每月能重新跑一遍的檢查。
上線前 9 項加固總覽(可直接當 checklist)
這 9 項拆開看,本質是三件事:減少入口(SSH、防火牆)、減少許可權(非 root、MFA、最小授權)、保證出事能兜住(備份、日誌、憑證輪換)。下表是最低標準,不是目標值——先做到,再談更嚴格。需要託管側能力(WAF、DDoS 高防、堡壘機)時,對照 雲安全產品 補齊。
| # | 加固項 | 上線前最低標準 | 怎麼驗證 |
|---|---|---|---|
| 1 | SSH 登入 | 禁止 root 與密碼登入,僅金鑰 | sshd -T 中兩項均為 no |
| 2 | 防火牆 / 安全組 | 管理埠只對來源 IP 開放 | 安全組無 0.0.0.0/0 的 22/3306/6379 |
| 3 | 賬號與許可權 | 一人一個 IAM 身份,不共享 root 金鑰 | IAM 使用者可逐人對應到人 |
| 4 | MFA | 主賬號與高許可權子賬號強制開啟 | 控制檯顯示已啟用 |
| 5 | 補丁 | 系統每月,業務依賴每季度 | apt list --upgradable 無積壓 |
| 6 | 備份與恢復 | 每日自動快照 + 季度恢復演練 | 從快照拉起新實例成功一次 |
| 7 | 加密 | 磁碟靜態加密 + 全站 TLS 1.2+ | curl -I https:// 返回 200 |
| 8 | 日誌與告警 | 留存 ≥ 90 天,新 SSH key 告警 | lastb -ai 有記錄且可查 |
| 9 | 金鑰管理 | 憑證不入庫,託管並可輪換 | 倉庫掃描無高熵串命中 |
順序有講究:先 SSH 金鑰登入,再動防火牆。 反過來做(先關埠再推 key)會把自己鎖在門外,只能走雲廠商的救援模式,而那通常需要人工工單。
加固 1|SSH:關掉密碼登入,只留金鑰
暴力破解 SSH 至今仍是雲主機被入侵的第一入口。第一項加固就是:讓「密碼」這條路徑根本不存在。
- 給自己或
deploy使用者生成新金鑰:ssh-keygen -t ed25519 -C "you@laptop",再把公鑰推上去:cat ~/.ssh/id_ed25519.pub | ssh deploy@SERVER "umask 077; mkdir -p ~/.ssh && cat >> ~/.ssh/authorized_keys" - 在新開的終端裡實測能登入,確認成功後再動配置。原來的會話先別關。
- 備份配置:
sudo cp /etc/ssh/sshd_config /etc/ssh/sshd_config.bak。注意 sshd 多數配置項是首次出現生效,所以要改現有行,往檔案末尾追加是無效的。 - 禁 root 登入:
sudo sed -i "s/^#*PermitRootLogin.*/PermitRootLogin no/" /etc/ssh/sshd_config - 禁密碼登入:
sudo sed -i "s/^#*PasswordAuthentication.*/PasswordAuthentication no/" /etc/ssh/sshd_config。舊發行版裡這一項叫ChallengeResponseAuthentication,同樣要處理。 - 先驗語法再過載:
sudo sshd -t && sudo systemctl reload ssh(RHEL 系服務名為sshd)。 - 確認生效值:
sudo sshd -T | grep -Ei "permitrootlogin|passwordauthentication",應輸出PermitRootLogin no與PasswordAuthentication no。
再疊一層 fail2ban 限制嘗試頻率:sudo apt install -y fail2ban && sudo systemctl enable --now fail2ban,用 sudo fail2ban-client status sshd 看已封禁的 IP。
關於「把 22 改成 2222」:這不會讓你更安全。公開掃描工具 1–65535 全掃,改埠只是讓預設日誌安靜一點;真正的收益來自來源限制與金鑰登入。
加固 2|防火牆與安全組:預設拒絕,按需開口
網路層的目標是預設拒絕。雲廠商的安全組是第一道牆(流量到達主機前就被丟棄),主機內的 ufw / firewalld 是第二道牆。兩道牆都留著,成本幾乎為零。
- 安全組:只把 80/443 放給
0.0.0.0/0;22 只放給辦公網出口 IP;3306 / 5432 / 6379 / 27017 / 9200 一律不給0.0.0.0/0 - Debian/Ubuntu 主機防火牆:
sudo ufw default deny incoming→sudo ufw allow 22/tcp→sudo ufw allow 80,443/tcp→sudo ufw enable - RHEL 系主機防火牆:
sudo firewall-cmd --permanent --add-service=http --add-service=https,再用--add-rich-rule只對指定來源放行 22,最後sudo firewall-cmd --reload - 資料庫只監聽迴環或內網:MySQL 配
bind-address=127.0.0.1;Redis 配bind 127.0.0.1 -::1並保持protected-mode yes,遠端訪問走 VPC 私網 + 安全組
ufw enable 之前必須先 ufw allow 22/tcp(Ubuntu 上 ufw allow OpenSSH 也可以,它是同名別名規則)。順序反了會立刻斷開當前 SSH 會話。
自查兩處:本機看監聽埠 ss -tulpn | grep LISTEN,從另一臺機器看對外暴露 nmap -Pn your.server.ip。兩處結果都只應有你打算開放的埠。
加固 3|賬號:一個人一個身份,不共享 root
把 root 當成「系統自己的賬號」,人一律不直接用。目標狀態是:每個人的操作都能追溯到一個具體身份,出事能單獨吊銷而不影響同事。
- 雲控制檯側:新建 IAM 子賬號(RAM 子使用者同理),按最小許可權給策略,只授予「只讀監控」或「只能操作這臺實例」這類範圍;刪掉主賬號的長期 AccessKey,CI 與運維改用角色 + 臨時憑證,有效期設 15–60 分鐘
- 主機側:建
deploy使用者做日常運維,用sudo visudo只授權必要命令,而不是整條NOPASSWD: ALL - 一人一把 SSH 金鑰,不要把同一個
id_rsa複製給全組。人員變動時刪一行authorized_keys即可精確吊銷 - 給私鑰加口令並用 ssh-agent 快取:
ssh-keygen -p -f ~/.ssh/id_ed25519+eval "$(ssh-agent -s)",兼顧安全與體驗
精確授權的寫法(追加到 /etc/sudoers.d/deploy):deploy ALL=(ALL) NOPASSWD: /usr/bin/systemctl restart myapp.service。只放開真正要用的那一條命令,而不是整臺機器的 root。
加固 4|MFA:給「刪庫」動作加一道真實校驗
金鑰和密碼都可能被複制、被釣走,MFA 是唯一能推翻「憑證已洩露就直接可用」這個假設的機制。它不防入侵,防的是憑證已經洩露的情況。
- 主賬號必須開 MFA,且不要只依賴簡訊:SIM 補卡、簡訊轟炸都是常見手法,優先 TOTP(Google Authenticator、1Password、Bitwarden 等)
- 按許可權分級強制:能改安全組、能刪資源的子賬號必須開,純只讀賬號可以暫不強制
- 恢復碼離線儲存:放密碼管理器或列印密封,不放在同一臺機器上,也不要發到工作群裡
- 控制檯登入與 SSH 登入是兩件事:前者用控制檯 MFA,後者需要在 PAM 裡接
pam_google_authenticator,或用 sshd 的AuthenticationMethods組合策略
別把 MFA 做成「形同虛設」:團隊裡必須有人能走流程幫同事重置,而這個人自己也要開 MFA。否則一次誤操作就會變成一場線上求助。
雲側可以用策略強制,而不是口頭要求:在許可權策略的 Condition 裡加 "Bool": { "aws:MultiFactorAuthPresent": "true" },未開啟 MFA 的呼叫直接被拒。
加固 5|補丁:把更新變成日程,而不是事故
加固不是一次性動作。原則很簡單:能修的當天修,修不了的(要發版、要重啟)排進固定視窗。
- 系統層:Debian/Ubuntu 用
sudo apt update && sudo apt list --upgradable檢視,裝unattended-upgrades並在/etc/apt/apt.conf.d/20auto-upgrades裡設APT::Periodic::Update-Package-Lists "1";與APT::Periodic::Unattended-Upgrade "1"; - RHEL 系:手動用
dnf update -y,自動化交給dnf-automatic:sudo systemctl enable --now dnf-automatic.timer - 核心與應用依賴:核心升級要重啟才生效(
uname -r確認版本,固定視窗執行sudo reboot);依賴單獨掃:npm audit --production、pip-audit、govulncheck ./... - 容器映象:定期
docker build --pull重建,基礎映象的漏洞不會因為你的業務程式碼沒改就消失
發行版有生命週期上限:主流發行版的標準維護視窗通常約 5 年,之後不再有安全更新(CentOS 7 已於 2024 年 6 月結束,Ubuntu 20.04 LTS 標準維護已於 2025 年 5 月結束)。仍在跑的機器要麼升版本,要麼確認已接入商業延保。
想要一次性全量體檢:sudo apt install -y lynis && sudo lynis audit system,把報告存檔作為合規證據,也方便對比下一次的變化。
加固 6|備份:有快照不算數,恢復成功才算
備份的價值不在「有」,在「能恢復」。沒演練過的備份只是一個假設。
- 自動快照:每日 1–2 次,保留 7–14 天;每次發版或改資料庫結構前手動打一次快照並打標籤註明變更
- 快照不等於應用一致性備份:程序寫到一半被快照,恢復後 InnoDB 會自行回滾,但邏輯上可能丟事務。資料庫額外做匯出:
mysqldump --single-transaction --quick -u root -p dbname > /backup/db-$(date +%F).sql.gz(InnoDB 用--single-transaction代替--lock-tables) - 異地留一份:快照跨區域複製,或同步到物件儲存,並且給備份本身加密——備份檔案常含全量使用者資料,是最容易被忽略的洩露源
- 恢復演練:每季度從快照拉一臺新實例(隔離網路),核對資料條數與最新時間戳,記錄真實 RTO,然後銷燬測試實例
演練結果才寫進 runbook。寫「RTO 2 小時」不如寫「2026 年 8 月實測:從快照到資料可讀 38 分鐘」——後者是數字,前者是承諾。
加固 7|加密:磁碟靜態加密 + 全鏈路 TLS
加密的目標很具體:磁碟被人拿走、備份被下載、流量被中間人讀取時,資料依然不可讀。
- 靜態加密:雲硬碟與快照通常預設啟用廠商側靜態加密(一般不額外計費);備份桶、物件儲存、資料庫快照建議啟用客戶管理金鑰(CMK),這樣人員離職時可以連金鑰一起吊銷。CMK 一般按金鑰數量與呼叫次數計費,以雲廠商官網實時報價為準
- 傳輸加密:全站 HTTPS,Nginx 只保留
ssl_protocols TLSv1.2 TLSv1.3;,80 全量跳 443 並配置 HSTS;證書用sudo certbot --nginx -d example.com自動續期 - 內部鏈路也加密:同 VPC 內多機互訪走私網 IP,資料庫連線強制 TLS 並校驗證書,Redis / MySQL 埠不對外暴露
- SSH 細節:
ForwardAgent no,關閉不必要的埠轉發;團隊用 ssh-agent 統一管理私鑰,而不是散落在多臺機器上
傳輸加密不只影響前臺 API。上傳介面、webhook 回撥、物件儲存的預簽名 URL、第三方 SDK 的證書校驗開關都要確認沒有被關掉——這類問題在測試環境常常是預設開啟的。
證書過期引發的事故,根因通常不是加密機制有問題,而是沒人管續期。把續期做成自動化,而不是靠人記得。
加固 8|日誌與告警:留痕 90 天,新鑰匙要報警
出事時的分水嶺只有一個問題:三天前發生過什麼,現在能不能查到。
- 系統日誌留存:
/etc/logrotate.d/rsyslog裡設rotate 14+daily,並用journalctl --disk-usage確認落盤量在可控範圍內 - 開 auditd 記錄敏感檔案變更:
sudo systemctl enable --now auditd,規則寫進/etc/audit/rules.d/audit.rules:
- 規則行:
-w /etc/passwd -p wa -k identity、-w /etc/sudoers -p wa -k sudo、-w /etc/ssh/sshd_config -p wa -k sshd_config - 載入生效:
sudo augenrules --load(RHEL 系還要sudo systemctl restart auditd) - 事後查詢:
sudo ausearch -k identity -ts recent
- 雲側審計:開啟物件儲存訪問日誌與雲廠商的操作審計服務(CloudTrail / 操作審計一類),日誌桶用獨立賬號寫入並開啟保留策略
- 告警只留三條高訊號:新增 SSH 公鑰、失敗登入突增(陌生 IP 段爆破)、非工作時間的管理操作
留存週期給個下限:線上 30–90 天,匯出到物件儲存再存一年。登入記錄用 last -ai(成功)與 lastb -ai(失敗)快速排查。
fail2ban 的預設閾值容易在網路抖動時誤封整段辦公室出口 IP。配置後先用 sudo fail2ban-client status sshd 觀察一週,再收緊封禁時長。
加固 9|金鑰管理:任何憑證都不進程式碼倉庫
憑證洩露最常見的路徑不是被駭客攻破,而是被提交進 git、被寫進映象層、被貼進工單和聊天記錄。
- 程式碼裡不寫金鑰:
.env進.gitignore;已經提交過就按已洩露處理——git rm --cached .env只能停止後續跟蹤,歷史裡的必須用git filter-repo改寫,並且同時輪換那把憑證 - 提交前自動掃描:
gitleaks detect或trufflehog git .,掛到 pre-commit 或 CI 上 - 執行時注入:由 CI/CD 注入環境變數,或放雲廠商的金鑰管理服務(Secrets Manager / KMS 一類),應用啟動時拉取並快取;不要寫進 Dockerfile 的
ENV,那等於打進映象 - 檔案許可權:
chmod 600 .env,用umask 077建立敏感檔案,應用以獨立系統使用者執行,必要時chown root:appgroup後chmod 640
命令列引數會出現在 ps 輸出裡:mysql -uroot -pPASSWORD 這種寫法,同機器任何使用者 ps aux 就能看到密碼。改用互動提示符,或 mysql --defaults-extra-file=/root/.my.cnf。同理檢查 shell history 裡有沒有 export SECRET=...。
輪換要有節奏:資料庫密碼 ALTER USER 與應用同步更新,第三方 API key 按季度輪換,並給每把憑證登記用途、負責人、建立時間與失效日期——沒有到期日的金鑰,遲早會變成永久金鑰。
金鑰洩露了怎麼辦:15 分鐘應急清單
假設一把 SSH 私鑰或一個雲 AccessKey 洩露了。第一個動作是吊銷,不是改密碼,也不是先觀察兩天。
- 吊銷(0–5 分鐘):在 IAM 控制檯禁用或刪除洩露的 AccessKey;SSH 側從所有伺服器的
authorized_keys移除對應公鑰(先確認你還有別的入口,否則會把自己關在門外) - 留證(5–15 分鐘):立刻給受影響實例打快照並匯出證據——
sudo last -ai、lastb -ai、sudo journalctl -u ssh --since "-24h"、當前ss -tulpn結果、計劃任務與自定義 systemd 單元(/etc/cron*、/etc/systemd/system/) - 排查(15–60 分鐘):新增系統使用者、
authorized_keys差異、crontab -l、異常監聽埠、異常出站連線(ss -tunp)、可疑的公鑰註釋名 - 輪換(同步進行):從這臺機器能觸達的一切都要換——資料庫密碼、物件儲存憑證、第三方 API key、CI/CD 裡的 secrets、映象倉庫 token。洩露一把通常意味著整套憑證都該換
- 重建而不是清理:一旦確認已獲得 root,在原機上清理是不夠的,持久化可能藏在沒查到的地方。從可信映象或乾淨快照重建,只遷移經過檢查的資料
- 覆盤:寫下時間線(何時發現、哪些憑證受影響、是否被使用過),按影響面通知相關方,並把這次發現變成一條告警規則
如果洩露的憑證在被吊銷前被實際使用過,就要按資料洩露事件處理:評估哪些資料被訪問、是否需要對外通報,而不是隻當成一次配置錯誤關掉工單。
怎麼驗證:15 分鐘自檢清單
基線應該是可以在 15 分鐘內驗證的東西。換個終端,按順序跑下面這些:
- SSH 生效配置:
sudo sshd -T | grep -Ei "permitrootlogin|passwordauthentication",期望看到兩項均為no - 對外暴露面(從另一臺機器跑):
nmap -Pn your.server.ip,只應出現 22/80/443 及你有意開放的埠 - 本機監聽:
ss -tulpn | grep LISTEN,資料庫與快取的監聽地址應為127.0.0.1或內網地址 - 防火牆規則:
sudo ufw status verbose(或sudo firewall-cmd --list-all),預設策略應為拒絕或僅白名單 - 待更新包:
sudo apt list --upgradable(Debian 系)/dnf check-update(RHEL 系) - 失敗登入:
lastb -ai | head -20,出現陌生 IP 段就繼續追 - SUID 檔案與計劃任務:
sudo find / -xdev -perm -4000 -type f 2>/dev/null、crontab -l、ls -la /etc/cron.d/ - 倉庫金鑰掃描:
git ls-files | xargs grep -nEi "akia|secret|password",有命中就按已洩露處理
把這 8 條寫進每月一次的例行檢查,或者做成 CI 裡的基線任務。能被自動化的加固才叫基線,只做一次的只是改過一次配置。
下一步:挑一臺已經上線的機器跑完這 8 條,把命中的項分成「5 分鐘能改完」和「需要排期」兩欄——前一欄今天清掉,後一欄進發版視窗,寫清負責人和日期。賬號側的另一半風險(來源不明的賬號、共用支付方式、被牽連封禁)不在這 9 項裡,單獨見 雲賬號被封的 5 類原因與預防清單。
本文給的是跨廠商通用的最小集。具體到你用的雲廠商,安全組預設值說明與最佳實踐文件可能更嚴格(例如強制 IMDSv2、預設拒絕入站),以官方文件實時內容為準。