Community程式設計與開發github.com

igor-batrakov/new-vps-setup

逐步安全指南(代理技能 + 檢查清單),適用於新手設定 Ubuntu VPS 至生產環境:強化設定、自動更新、UFW、fail2ban、Docker、nginx + HTTPS、restic 自動備份、監控。

new-vps-setup 是什麼?

此技能提供一個結構化的逐步指南,用於將全新的 Ubuntu VPS 強化為安全且可自主維護的生產伺服器,特別針對缺乏系統管理經驗的新手。其主要目標是通過安全的預設配置和自動化流程來最大化安全性與自主性,減少人為干預。技能涵蓋系統更新、建立專用 sudo 使用者、設定 SSH 金鑰與強化、設定自動安全性更新、安裝 fail2ban 以防暴力破解、設定 UFW 防火牆、選擇性安裝 Docker 並設定日誌輪替,以及透過 nginx 與 Let's Encrypt 設定 HTTPS。一個關鍵部分專門用於使用 restic 進行自動化異地備份,強調備份驗證和安全的密碼管理。最後包含監控設定,以確保伺服器健康並能在問題發生時發出警報,最終打造一個僅需對警報進行回應的系統。

相容平台~Claude Code~Codex CLI~Cursor
npx skills add igor-batrakov/new-vps-setup

Installed? Explore more 程式設計與開發 skills: steipete/bluebubbles, steipete/eightctl, steipete/blucli · View all 6 →

在你喜歡的 AI 中提問

開啟一個已預先載入此 Agent Skill 的新對話。

說明文件

將新的 VPS 設定為生產環境(適用於新手)

概觀

此技能逐步引導您將乾淨的 Ubuntu 伺服器準備好投入生產環境,使其能夠以最大自主性進行維護。目標受眾是沒有系統管理經驗的使用者。因此:

  • 安全的預設值,而非「專家設定」。不採用 NOPASSWD-sudo,也不為了方便而停用檢查。對專家來說簡化生活的事,對新手可能是個陷阱。
  • 透過自動化實現自主性。安全性更新、備份和監控應自動運行。使用者只需對警報做出反應。
  • 每個步驟都需驗證。變更後必須檢查結果,然後才能進行下一步。

目標作業系統: Ubuntu 22.04 / 24.04 LTS。其他發行版的命令會有所不同。

黃金法則(在任何變更之前)

  1. 備份設定檔: sudo cp /path/config /path/config.bak.$(date +%F-%H%M)
  2. 檢查狀態: sudo systemctl status <service> / sudo docker ps
  3. 變更後檢查結果(日誌、狀態、測試連線)。
  4. 在確認新 SSH 工作階段中的存取權限和 sudo 能正常運作之前,切勿關閉當前的 SSH 工作階段。失去對遠端伺服器的 SSH 連線等於必須使用供應商提供的控制台(VNC)進行連線。

生產環境安全性原則(在所有步驟中遵守):

  • 機密資訊(API 金鑰、令牌、密碼)只能放在 .env 檔案或環境變數中,絕對不能寫在程式碼裡。
  • .env 檔案必須加入 .gitignore(否則機密資訊會在首次提交時洩漏到 Git)。
  • 資料庫不應暴露在網際網路上 — 僅限於 localhost 或透過私有網路/VPN。
  • 伺服器上不應啟用除錯模式:DEBUG=False、NODE_ENV=production。
  • 應用程式不應以 root 身分執行 — 應使用專用使用者或容器。
  • 不應有不必要的開放埠口(管理頁面、儀表板、監控指標應關閉或僅限信任的 IP 存取)。
  • 不要為了方便而停用安全檢查(verify=False、allowAll — 僅限於本地端使用)。
  • SSH 僅允許金鑰登入,不允許密碼登入。

0. 診斷 — 現有配置

請優先執行此步驟。 主機商提供的伺服器通常已有部分配置(如 cloud-init、ufw 等)。

# 系統
lsb_release -a && uname -r
free -h && df -h && nproc

# 我是誰以及權限
whoami && id

# SSH 設定(檢查實際生效的值,而不僅僅是設定檔!)
sudo sshd -T | grep -iE 'permitrootlogin|passwordauthentication|pubkeyauthentication|^port '

# 防火牆
sudo ufw status verbose

# 監聽中的埠口
sudo ss -tlnp

# 自動更新(已啟用?)
systemctl status unattended-upgrades 2>/dev/null | head -5
cat /etc/apt/apt.conf.d/20auto-upgrades 2>/dev/null

# Docker / fail2ban
sudo docker ps 2>/dev/null
sudo fail2ban-client status 2>/dev/null

診斷完成後,向使用者報告發現的內容,並在獲得確認後才開始進行設定。


1. 初始設定與安全強化

請參考第 0 節 — 部分設定可能已完成。不要重複執行。

1.1 更新系統

sudo apt update && sudo apt upgrade -y
sudo apt autoremove -y
# 如果核心已更新 — 需要重新啟動(請參閱輸出結果)。請有意識地重新啟動:
# sudo reboot   # 然後在約 30-60 秒後重新連線

1.2 建立專用的 sudo 使用者(不要用 root 身分操作)

sudo adduser <USERNAME>            # 會要求設定密碼 — 選擇一個強密碼並保存在密碼管理員中
sudo usermod -aG sudo <USERNAME>

對於新手,我們保留 sudo 需要輸入密碼的設定。 這是刻意的:NOPASSWD-sudo 會在發生金鑰或工作階段洩漏時移除最後一道防線。sudo 密碼也作為備用方案,以防 SSH 金鑰遺失(請參閱 1.3)。

1.3 SSH 金鑰(使用金鑰進行無密碼登入)

金鑰類型 — ed25519(非 RSA)。檔案名稱 — <server>_<device>,並設定密碼短語。

在本機端:

ssh-keygen -t ed25519 -f ~/.ssh/<server>_<device> -C "<server>_<device>"
ssh-copy-id -i ~/.ssh/<server>_<device>.pub <USERNAME>@<SERVER_IP>

請在另一個 SSH 工作階段中測試金鑰登入(不要關閉舊的工作階段!):

ssh -i ~/.ssh/<server>_<device> -o IdentitiesOnly=yes <USERNAME>@<SERVER_IP> 'whoami; sudo whoami'

在本機端的 ~/.ssh/config 中設定便利的別名:

Host <alias>
    HostName <SERVER_IP>
    User <USERNAME>
    IdentityFile ~/.ssh/<server>_<device>
    IdentitiesOnly yes

1.4 SSH 強化設定

建立 /etc/ssh/sshd_config.d/99-hardening.conf:

PermitRootLogin no
PasswordAuthentication no
PubkeyAuthentication yes
MaxAuthTries 3
AllowTcpForwarding local
ClientAliveInterval 300
ClientAliveCountMax 2
AllowUsers <USERNAME>

AllowTcpForwarding local 允許本地埠轉發(ssh -L — 在設定網域前,可用來存取 127.0.0.1 上的服務),並禁止遠端轉發(-R)。如果完全不需要埠轉發,則設為 no。

套用並檢查:

sudo sshd -t                                              # 語法正確?
sudo systemctl restart ssh
sudo sshd -T | grep -iE 'permitroot|passwordauth'         # 實際生效的值應為 no

陷阱 1 — cloud-init 會覆寫強化設定。 檔案 /etc/ssh/sshd_config.d/50-cloud-init.conf 通常包含 PasswordAuthentication yes,而在 sshd_config.d/ 目錄中採用「先比對先贏」的規則。請透過 sudo sshd -T(檢查實際生效的值)來驗證,而不僅僅檢查您自己的設定檔。如果設定被覆寫,請修改/刪除 50-cloud-init.conf 中的對應行。

陷阱 2 — Ubuntu 24.04 使用 ssh.socket。 sshd_config 中的 Port 設定會被忽略。若要變更 SSH 埠號:sudo systemctl edit ssh.socket → 加入 ListenStream=<PORT>(並移除預設埠)。請透過供應商提供的控制台進行驗證 — 此處的錯誤會鎖住您的存取權限。

1.5 自動安全性更新 ⭐(實現自主性的核心)

這是實現「伺服器自我維護」最重要的一步。沒有它,套件中的漏洞會不斷累積。

sudo apt install unattended-upgrades apt-listchanges -y
sudo dpkg-reconfigure -plow unattended-upgrades   # 選擇 "Yes" — 會建立 20auto-upgrades

檢查 /etc/apt/apt.conf.d/20auto-upgrades:

APT::Periodic::Update-Package-Lists "1";
APT::Periodic::Unattended-Upgrade "1";
APT::Periodic::AutocleanInterval "7";

在 /etc/apt/apt.conf.d/50unattended-upgrades 中(取消註解/設定關鍵行):

// 套用安全性更新(預設為啟用):
Unattended-Upgrade::Allowed-Origins {
    "${distro_id}:${distro_codename}-security";
    "${distro_id}ESMApps:${distro_codename}-apps-security";
    "${distro_id}ESM:${distro_codename}-infra-security";
};
Unattended-Upgrade::Remove-Unused-Dependencies "true";
// 如果更新需要重新啟動(例如核心更新),則自動重新啟動。選擇負載最低的時間視窗:
Unattended-Upgrade::Automatic-Reboot "true";
Unattended-Upgrade::Automatic-Reboot-Time "04:00";

「自動重新啟動」的決策: true = 伺服器在核心更新時會於夜間自動重新啟動(更安全,但可能會有短暫停機)。false = 核心會更新,但重新啟動需要您手動執行(需要留意)。對於單一生產伺服器的新手來說,通常 true 搭配夜間時間視窗是較好的選擇。

檢查 — 不實際安裝的模擬執行:

sudo unattended-upgrade --dry-run --debug 2>&1 | tail -20
systemctl list-timers 'apt-daily*'   # 計時器是否正在執行?

1.6 fail2ban(保護 SSH 免受暴力破解攻擊)

基本設定就已足夠 — 不要在初期就使用 recidive/nftables 來增加複雜度。

sudo apt install fail2ban -y

/etc/fail2ban/jail.d/sshd.local:

[sshd]
enabled = true
bantime = 1h
findtime = 30m
maxretry = 4
# 不要封鎖自己:加入您的靜態 IP(查詢:curl ifconfig.me)
ignoreip = 127.0.0.1/8 ::1 <YOUR_HOME_IP>
sudo systemctl enable --now fail2ban
sudo fail2ban-client status sshd

1.7 時區與(針對小型 VPS)Swap 空間

sudo timedatectl set-timezone <Region/City>     # 例如 Asia/Taipei 或 UTC

# 對於記憶體較小(< 2 GB)的 VPS,需要 Swap 空間以防止 OOM 終止服務:
free -h
sudo fallocate -l 2G /swapfile && sudo chmod 600 /swapfile
sudo mkswap /swapfile && sudo swapon /swapfile
grep -q '/swapfile' /etc/fstab || echo '/swapfile none swap sw 0 0' | sudo tee -a /etc/fstab
echo 'vm.swappiness=10' | sudo tee /etc/sysctl.d/99-swap.conf && sudo sysctl --system

2. UFW 防火牆

原則:預設關閉所有連線,僅開放必要連線。 首先必須允許 SSH,否則您會將自己鎖在外面。

sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw allow 22/tcp comment 'SSH'      # 重要:在 enable 之前執行!
sudo ufw enable
sudo ufw status verbose

對於 Web 伺服器,請加入 HTTP/HTTPS:

sudo ufw allow 80/tcp comment 'HTTP'
sudo ufw allow 443/tcp comment 'HTTPS'

管理頁面/儀表板/監控指標 — 僅限信任的 IP 存取,而非對「所有人」開放:

sudo ufw allow from <TRUSTED_IP> to any port <ADMIN_PORT> proto tcp comment 'admin panel'

絕對不要使用 sudo ufw reset — 它會重置所有規則,包括 SSH。


3. Docker(選擇性 — 如果應用程式以容器方式執行)

安裝

curl -fsSL https://get.docker.com | sudo sh
sudo usermod -aG docker <USERNAME>      # 重新登入以使群組權限生效

成為 docker 群組的成員實際上等同於擁有 root 權限(透過 docker run -v /:/host)。這對於單一管理者來說是可接受的,但不要將此群組授予您不信任其擁有完整伺服器存取權限的使用者。

日誌輪替(必要 — 否則日誌會耗盡磁碟空間)

/etc/docker/daemon.json:

{
  "log-driver": "json-file",
  "log-opts": { "max-size": "10m", "max-file": "3" }
}
sudo systemctl restart docker

⚠️ Docker 會繞過 UFW

使用 -p 8080:8080 映射的連接埠,即使 UFW 未允許,也可從網際網路存取。 安全選項:

  • 僅綁定到 localhost:-p 127.0.0.1:8080:8080(僅能透過 nginx/SSH 隧道存取)。
  • 完全不要將資料庫的連接埠映射到外部 — 在 Docker 網路內部使用服務名稱進行通訊。

4. nginx + HTTPS(選擇性 — 如果有 Web 應用程式和網域)

使用 nginx 作為應用程式的反向代理(應用程式監聽 127.0.0.1,對外僅開放 nginx)。

sudo apt install nginx -y

/etc/nginx/sites-available/<app>:

server {
    listen 80;
    server_name <DOMAIN>;
    location / {
        proxy_pass http://127.0.0.1:<APP_PORT>;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}
sudo ln -s /etc/nginx/sites-available/<app> /etc/nginx/sites-enabled/
sudo nginx -t && sudo systemctl reload nginx

透過 Let's Encrypt 免費取得 HTTPS(certbot 會自動簽發並設定自動續約):

sudo apt install certbot python3-certbot-nginx -y
sudo certbot --nginx -d <DOMAIN>
# 自動續約已由計時器設定完成。驗證:
sudo certbot renew --dry-run

安全強化:在 /etc/nginx/nginx.conf 中加入 server_tokens off;(隱藏 nginx 版本)以及安全性標頭(Strict-Transport-Security、X-Content-Type-Options nosniff)。


5. 自動備份(restic + 異地備份)

完整說明(安裝、儲存庫、腳本、計時器、還原): 請參閱 references/backups.md — 內有逐步指引。

絕對不能違反的關鍵原則:

  1. 備份必須是異地備份。 存放在同一台伺服器(或磁碟)上的副本無法在伺服器/磁碟損毀時保護資料。最低要求 — 外部儲存空間(相容於 S3 的服務:Backblaze B2、rsync.net 等)。
  2. 儲存庫的加密密碼必須存放在伺服器外部(密碼管理員)。遺失密碼 → 所有備份將永久無法讀取。這是導致備份失效最常見的原因。
  3. 未經還原驗證的備份就不是備份。 至少進行一次測試還原(請參閱 backups.md 的「練習還原」章節)。
  4. 自動化: 透過 systemd 計時器每日執行 + 自動輪替舊的快照。

備份內容

請以類別而非個別路徑進行思考 — 這樣才不會遺漏任何項目:

類別路徑範例原因
系統設定檔整個 /etc 目錄nginx、ssh、ufw、fail2ban、systemd 單元 — 用於將伺服器恢復原狀
應用程式資料/home/<USERNAME>/app/data、/var/lib/<service>無法重新安裝的內容:使用者產生的內容、上傳檔案
資料庫匯出/var/backups/*.sql.gz在執行 restic 備份之前,透過 pg_dump/.backup 進行資料庫匯出(存活的資料庫檔案可能已損毀)
機密資訊.env、/root/.config/*以加密形式備份(restic 會加密儲存庫)。加密密碼 — 存放在伺服器外部

不需要備份的內容(可以重新安裝,只會使備份變大): node_modules、.venv/venv、Docker 映像檔(docker pull)、系統套件(apt)、快取、/var/log、/tmp。這些路徑已包含在腳本的 --exclude 參數中(請參閱 backups.md)。

保留策略 — 選擇您的政策

restic 儲存去重複化的快照,因此「保留更多副本」在儲存空間上成本很低。 命令:restic forget --prune --keep-daily N --keep-weekly N --keep-monthly N

策略命令適合對象說明
基本 (預設)--keep-daily 7 --keep-weekly 4 --keep-monthly 6大多數生產伺服器可還原至過去 7 天中的任何一天 + 長達半年的歷史紀錄。既能捕捉最近的錯誤,也能追溯到「問題是何時發生的」。backups.md 中已採用此設定。
輕量--keep-daily 7 --keep-weekly 4沒有重要累積資料的網站/機器人大約 1 個月的歷史紀錄。佔用最少空間,較為簡單。適用於遺失舊資料影響不大的情況。
長期--keep-daily 14 --keep-weekly 8 --keep-monthly 12包含重要資料的資料庫、財務、法律要求一年歷史紀錄 + 14 天的每日備份。儲存成本較高,但可防止未立即發現的「靜默」資料損毀。

給新手的建議: 從基本策略開始 — 在儲存成本極低的情況下,它涵蓋了兩種場景(快速還原與長期歷史紀錄)。您可以隨時切換到長期策略,只需更改 forget 中的數字即可 — 舊的快照不會因此遺失。

關於資料庫的重要提醒: 如果您備份的是「存活的」資料庫檔案,保留策略是沒有意義的 — 該檔案可能已損毀。請務必先執行資料庫匯出(pg_dump / SQLite .backup),然後讓 restic 備份匯出的檔案。


6. 監控

…

[此處截斷以利翻譯]

相關技能