Loading...

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 一下就回來了。

今天我們要學會四件事:

  1. 基本網路設定——hostname、DHCP、靜態 IP
  2. 防火牆管理——networking.firewall 的各種設定
  3. Nginx 服務架設——用 NixOS 的方式跑一個 web server
  4. 網路除錯——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";
  };
};

💡 小提醒NetworkManagersystemd-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預設閘道
nameserversDNS server 清單

⚠️ 注意:設定靜態 IP 前,請先用 ip linkip addr 確認你的網路介面名稱。不同硬體的介面名稱不同,可能是 enp0s3ens18eth0 等。

使用 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(基於 iptablesnftables),這是一個非常好的安全預設值。但你得知道怎麼開放特定 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 ];
  };
};

💡 小提醒nftablesiptables 的後繼者,語法更簡潔、效能更好。如果你是新建的系統,建議直接使用 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 ];

這段設定會自動幫你:

  1. 向 Let’s Encrypt 申請 SSL certificate
  2. 自動設定 certificate 的定期更新
  3. 將 HTTP 流量自動導向 HTTPS
  4. 套用推薦的 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 所需的 UpgradeConnection header。如果你的 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

💡 小提醒dignslookup 預設可能沒有安裝。把 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

常見的介面名稱:

  • enp0s3enp0s25 — 有線網路(PCI 裝置)
  • ens18ens192 — 虛擬機環境
  • wlp2s0wlp3s0 — 無線網路
  • 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 網路管理的世界,學會了以下重要技能:

學到了什麼對應設定
設定 hostnamenetworking.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 語言與開發環境的深水區囉。準備好了嗎?

我們明天見! 🚀