Day 6:網路與防火牆 —— 讓你的 NixOS 上線
學會在 configuration.nix 中設定網路介面、靜態 IP、DNS 與防火牆規則,讓你的 NixOS 伺服器安全地連上網路。
Day 6:網路與防火牆 —— 讓你的 NixOS 上線
🗓 系列:NixOS 30 天學習之旅
📦 階段:第一階段 — 基礎與生存守則 (Day 1 – Day 7)
🎯 階段核心目標:理解 Declarative 配置與不可變性
前言
一台不能上網的 server,就像一間沒有門的房子——蓋得再漂亮也沒用。
在前幾天裡,我們學會了 NixOS 的基本配置、套件安裝和 SSH 服務。但如果你想把 NixOS 當作一台真正的 server 來運作,網路設定與防火牆絕對是你必須掌握的核心技能。
傳統 Linux distribution 的網路管理往往散落在 /etc/network/interfaces、/etc/sysconfig/network-scripts/、iptables 規則檔等各處,設定完還得記得 systemctl restart networking,稍有不慎就可能鎖死自己。NixOS 的做法截然不同:所有網路配置都寫在 configuration.nix 裡,一次 rebuild 全部生效。忘記設了什麼?打開檔案就能看到全貌。設錯了?rollback 一下就回來了。
今天我們要學會四件事:
- 基本網路設定——hostname、DHCP、靜態 IP
- 防火牆管理——
networking.firewall的各種設定 - Nginx 服務架設——用 NixOS 的方式跑一個 web server
- 網路除錯——DNS 與常見問題排解
基本網路設定
Hostname 設定
最基本的起手式,給你的機器一個名字:
networking.hostName = "nix-server";
這等同於傳統 Linux 的 hostnamectl set-hostname,但差別在於——這個設定是 declarative 的,寫在配置裡就永遠不會跑掉。
DHCP(動態 IP)
NixOS 預設使用 DHCP 來取得 IP 位址。如果你的環境用 DHCP 就夠了(例如家用路由器後面的開發機),通常不需要額外設定:
networking = {
hostName = "nix-server";
# 啟用 NetworkManager,適合桌面環境
networkmanager.enable = true;
};
如果你是純 server 環境,不需要 NetworkManager,可以改用 systemd-networkd:
networking = {
hostName = "nix-server";
useNetworkd = true;
};
systemd.network = {
enable = true;
networks."10-wan" = {
matchConfig.Name = "enp0s3";
networkConfig.DHCP = "ipv4";
};
};
💡 小提醒:
NetworkManager和systemd-networkd是兩種不同的網路管理方式,請不要同時啟用,以免互相衝突。桌面環境建議用NetworkManager,server 環境建議用systemd-networkd。
靜態 IP 設定
在 server 環境中,你通常會需要一個固定的 IP 位址。NixOS 提供了非常直觀的設定方式:
networking = {
hostName = "nix-server";
# 關閉 DHCP(全域)
useDHCP = false;
interfaces.enp0s3 = {
useDHCP = false;
ipv4.addresses = [{
address = "192.168.1.100";
prefixLength = 24;
}];
};
defaultGateway = "192.168.1.1";
nameservers = [ "1.1.1.1" "8.8.8.8" ];
};
各欄位說明
| 欄位 | 說明 |
|---|---|
useDHCP = false | 關閉全域 DHCP,改為手動設定 |
interfaces.enp0s3 | 指定網路介面名稱(用 ip link 查詢你的介面名稱) |
ipv4.addresses | 設定 IP 位址與 subnet mask(prefixLength = 24 等同於 255.255.255.0) |
defaultGateway | 預設閘道 |
nameservers | DNS server 清單 |
⚠️ 注意:設定靜態 IP 前,請先用
ip link或ip addr確認你的網路介面名稱。不同硬體的介面名稱不同,可能是enp0s3、ens18、eth0等。
使用 systemd-networkd 設定靜態 IP
如果你偏好 systemd-networkd(在較新的 NixOS 版本中推薦),可以這樣寫:
networking = {
hostName = "nix-server";
useNetworkd = true;
useDHCP = false;
nameservers = [ "1.1.1.1" "8.8.8.8" ];
defaultGateway = "192.168.1.1";
};
systemd.network = {
enable = true;
networks."10-lan" = {
matchConfig.Name = "enp0s3";
address = [ "192.168.1.100/24" ];
routes = [{ Gateway = "192.168.1.1"; }];
};
};
兩種方式都可以,選你順眼的就好。
防火牆設定(networking.firewall)
NixOS 預設就會啟用 firewall(基於 iptables 或 nftables),這是一個非常好的安全預設值。但你得知道怎麼開放特定 port,否則外面的流量根本進不來。
基本防火牆配置
networking.firewall = {
enable = true;
allowedTCPPorts = [ 22 80 443 ];
allowedUDPPorts = [ 53 ];
};
就這樣,三行搞定。比起手動寫 iptables 規則,這簡直是天堂。
各設定項目
| 設定 | 說明 |
|---|---|
enable | 是否啟用防火牆,預設為 true |
allowedTCPPorts | 開放的 TCP port 清單 |
allowedUDPPorts | 開放的 UDP port 清單 |
allowedTCPPortRanges | 開放的 TCP port 範圍 |
allowedUDPPortRanges | 開放的 UDP port 範圍 |
allowPing | 是否允許 ICMP ping,預設為 true |
開放 Port 範圍
如果你需要一次開放一段 port(例如 P2P 應用或是媒體串流),可以用 allowedTCPPortRanges:
networking.firewall = {
enable = true;
allowedTCPPorts = [ 22 80 443 ];
allowedTCPPortRanges = [
{ from = 8000; to = 8010; }
];
};
特定介面的規則
有時候你只想對某個網路介面開放特定 port(例如只讓內網存取管理介面):
networking.firewall = {
enable = true;
allowedTCPPorts = [ 22 ];
# 只對 enp0s8(內網介面)開放 8080
interfaces.enp0s8 = {
allowedTCPPorts = [ 8080 ];
};
};
禁止 Ping
雖然不是什麼大不了的安全措施,但有些人就是不想被 ping 到:
networking.firewall.allowPing = false;
進階:使用 nftables
NixOS 也支援更現代的 nftables 作為 firewall backend:
networking = {
nftables.enable = true;
firewall = {
enable = true;
allowedTCPPorts = [ 22 80 443 ];
};
};
💡 小提醒:
nftables是iptables的後繼者,語法更簡潔、效能更好。如果你是新建的系統,建議直接使用nftables。
暫時關閉防火牆(除錯用)
在排查網路問題時,你可能會想先暫時關閉防火牆來確認是否是 firewall 擋住了流量:
networking.firewall.enable = false;
⚠️ 安全提醒:除錯完畢後,請務必重新啟用防火牆!在正式環境中關閉防火牆是非常危險的事情。
Nginx 服務架設實戰
學完網路和防火牆,是時候來做點實際的事了。我們來用 NixOS 架一台 Nginx web server。
最小可運作配置
services.nginx = {
enable = true;
};
networking.firewall.allowedTCPPorts = [ 80 ];
兩行設定,你就有一台跑著 Nginx 的 web server 了。執行 sudo nixos-rebuild switch 之後,用瀏覽器打開 http://<your-ip> 就能看到 Nginx 的預設歡迎頁面。
架設靜態網站
來做點有意義的事——架設一個自己的靜態網站:
services.nginx = {
enable = true;
virtualHosts."my-site.local" = {
root = "/var/www/my-site";
locations."/" = {
index = "index.html";
};
};
};
networking.firewall.allowedTCPPorts = [ 80 ];
然後建立網站目錄與首頁:
sudo mkdir -p /var/www/my-site
echo '<h1>Hello from NixOS!</h1>' | sudo tee /var/www/my-site/index.html
sudo nixos-rebuild switch
啟用 HTTPS(搭配 ACME / Let’s Encrypt)
正式環境當然要上 HTTPS。NixOS 內建了 ACME(Let’s Encrypt)的支援,設定起來異常簡單:
security.acme = {
acceptTerms = true;
defaults.email = "your-email@example.com";
};
services.nginx = {
enable = true;
recommendedTlsSettings = true;
recommendedOptimisation = true;
recommendedGzipSettings = true;
recommendedProxySettings = true;
virtualHosts."example.com" = {
enableACME = true;
forceSSL = true;
root = "/var/www/example.com";
};
};
networking.firewall.allowedTCPPorts = [ 80 443 ];
這段設定會自動幫你:
- 向 Let’s Encrypt 申請 SSL certificate
- 自動設定 certificate 的定期更新
- 將 HTTP 流量自動導向 HTTPS
- 套用推薦的 TLS、壓縮、proxy 等最佳設定
在傳統 Linux 上,光是搞 Let’s Encrypt + Nginx 就得折騰半天(安裝 certbot、設定 cron job、調整 Nginx config⋯⋯)。NixOS 一段配置就全部搞定。
Nginx 作為 Reverse Proxy
如果你的 backend application 跑在 port 3000,想用 Nginx 做 reverse proxy:
services.nginx = {
enable = true;
recommendedProxySettings = true;
virtualHosts."app.example.com" = {
enableACME = true;
forceSSL = true;
locations."/" = {
proxyPass = "http://127.0.0.1:3000";
proxyWebsockets = true;
};
};
};
networking.firewall.allowedTCPPorts = [ 80 443 ];
💡 小提醒:
proxyWebsockets = true會自動加上 WebSocket 所需的Upgrade和Connectionheader。如果你的 application 有用到 WebSocket(例如即時通訊、Hot Module Replacement),記得開啟這個選項。
DNS 與網路除錯
網路設定好之後,難免會遇到一些問題。以下是常用的除錯工具與技巧。
確認網路介面狀態
# 查看所有網路介面
ip addr
# 查看路由表
ip route
# 確認 DNS 設定
cat /etc/resolv.conf
測試連線
# 測試基本連線
ping 8.8.8.8
# 測試 DNS 解析
nslookup google.com
# 或
dig google.com
# 測試特定 port
curl -v http://localhost:80
💡 小提醒:
dig和nslookup預設可能沒有安裝。把dnsutils加到你的environment.systemPackages裡就能使用了。
檢查服務狀態
# 查看 Nginx 服務狀態
systemctl status nginx
# 查看 Nginx 的 log
journalctl -u nginx -f
# 查看防火牆目前的規則
sudo iptables -L -n -v
# 如果用 nftables
sudo nft list ruleset
確認 Port 是否有在監聽
# 查看所有監聽中的 port
ss -tlnp
# 只看特定 port
ss -tlnp | grep :80
自訂 /etc/hosts
有時候你需要在本機加入一些自訂的 DNS 記錄,例如開發測試用:
networking.extraHosts = ''
192.168.1.100 my-site.local
192.168.1.101 api.local
'';
這就等同於手動編輯 /etc/hosts,但是是 declarative 的方式。
常見網路問題排解
問題一:nixos-rebuild switch 之後斷網了
最可能的原因:網路介面名稱寫錯了。
# 確認介面名稱
ip link show
常見的介面名稱:
enp0s3、enp0s25— 有線網路(PCI 裝置)ens18、ens192— 虛擬機環境wlp2s0、wlp3s0— 無線網路eth0— 較舊的命名方式
解法:修改 configuration.nix 中的介面名稱,或者直接重開機,在 GRUB 選單選擇上一個 generation 開機。
問題二:Port 有開放但外部連不上
依序排查:
# 1. 確認服務有在跑
systemctl status nginx
# 2. 確認 port 有在監聽
ss -tlnp | grep :80
# 3. 確認防火牆有開放
sudo iptables -L -n | grep 80
# 4. 從本機測試
curl http://localhost:80
# 5. 從外部測試
curl http://<server-ip>:80
如果步驟 4 成功但步驟 5 失敗,那通常就是防火牆沒開或者是上游路由器/雲端 security group 沒設定。
問題三:DNS 解析失敗
# 確認 DNS server 設定
cat /etc/resolv.conf
# 嘗試直接指定 DNS server 查詢
dig @1.1.1.1 google.com
如果指定 DNS server 可以查詢成功,表示是你的 nameservers 設定有問題。回去檢查 configuration.nix 裡的 networking.nameservers。
問題四:NetworkManager 和 systemd-networkd 衝突
如果你同時啟用了兩個網路管理工具,可能會出現奇怪的問題。請確保只啟用其中一個:
# 方案 A:使用 NetworkManager(桌面環境推薦)
networking.networkmanager.enable = true;
# 方案 B:使用 systemd-networkd(server 推薦)
networking.useNetworkd = true;
# ❌ 不要同時啟用兩者!
完整配置範例
把今天學到的內容整合成一個 server 配置範例:
# /etc/nixos/configuration.nix
{ config, pkgs, ... }:
{
imports = [ ./hardware-configuration.nix ];
boot.loader.systemd-boot.enable = true;
boot.loader.efi.canTouchEfiVariables = true;
# ========================
# 網路設定
# ========================
networking = {
hostName = "nix-server";
useDHCP = false;
interfaces.enp0s3 = {
useDHCP = false;
ipv4.addresses = [{
address = "192.168.1.100";
prefixLength = 24;
}];
};
defaultGateway = "192.168.1.1";
nameservers = [ "1.1.1.1" "8.8.8.8" ];
extraHosts = ''
192.168.1.100 nix-server.local
'';
# 防火牆
firewall = {
enable = true;
allowedTCPPorts = [ 22 80 443 ];
};
};
# ========================
# 時區
# ========================
time.timeZone = "Asia/Taipei";
# ========================
# 系統套件
# ========================
environment.systemPackages = with pkgs; [
vim
wget
curl
git
htop
dnsutils
tcpdump
];
# ========================
# SSH
# ========================
services.openssh = {
enable = true;
settings = {
PermitRootLogin = "no";
PasswordAuthentication = false;
};
};
# ========================
# Nginx
# ========================
services.nginx = {
enable = true;
recommendedTlsSettings = true;
recommendedOptimisation = true;
recommendedGzipSettings = true;
recommendedProxySettings = true;
virtualHosts."nix-server.local" = {
root = "/var/www/nix-server";
locations."/" = {
index = "index.html";
};
};
};
# ========================
# 使用者
# ========================
users.users.james = {
isNormalUser = true;
description = "James Hsueh";
extraGroups = [ "wheel" ];
openssh.authorizedKeys.keys = [
"ssh-ed25519 AAAAC3Nza... james@my-laptop"
];
};
system.stateVersion = "24.05";
}
小結
今天我們踏入了 NixOS 網路管理的世界,學會了以下重要技能:
| 學到了什麼 | 對應設定 |
|---|---|
| 設定 hostname | networking.hostName |
| 靜態 IP 配置 | networking.interfaces.<name>.ipv4.addresses |
| 防火牆管理 | networking.firewall.allowedTCPPorts |
| Nginx 服務架設 | services.nginx |
| HTTPS 自動憑證 | security.acme + enableACME |
| 自訂 DNS 記錄 | networking.extraHosts |
回頭看看,在傳統 Linux 上做這些事需要:編輯 netplan YAML、寫 iptables 規則、安裝 Nginx、設定 certbot cron job⋯⋯每一步都是分散的操作,出了問題很難追溯。
而在 NixOS,所有的網路狀態都宣告在同一份檔案裡。你可以一眼看出這台 server 開了哪些 port、跑了哪些服務、用了什麼 IP。更棒的是,這份配置可以版本控制、可以複製到其他機器、可以隨時回滾。
這就是 declarative networking 的美妙之處。
明日預告
Day 7:第一週回顧與實戰演練
第一階段即將收尾!明天我們會回顧 Day 1 到 Day 6 所學的所有內容,並透過一個完整的實戰演練來鞏固觀念——從零開始,用一份 configuration.nix 打造一台功能完整的 NixOS server。
第一週的基礎打穩了,第二週開始就要進入 Nix 語言與開發環境的深水區囉。準備好了嗎?
我們明天見! 🚀