烧饼论坛
登录

服务器使用 Debian/GNU Linux 的开荒指南

10314
  • dejavu
    Lv.3 初识社区超级版主

    2026 年 8 月 11 日重写
    本文首发在我的博客 :新到手的 Linux 服务器,我这样设置 | Dejavu's Blog

    cover.webp

    概要

    本文记录我初始化 Linux 服务器的 标准作业程序(SOP),这并非通用指南,仅作为个人快速部署环境的速查手册。

    关联阅读:

    性能测试

    我要「验牌」(法国音~)

    新服务器在重装系统前,建议先进行硬件与网络性能基准测试,使用 root 用户执行 YABS (Yet Another Bench Script) 脚本:

    curl -sL yabs.sh | bash -s -- -5
    

    测试完成后,首先核对 CPU、内存、磁盘容量及网络带宽等基础硬件信息,确认与服务商承诺规格保持一致。

    参考 Geekbench 5 单核跑分评估 CPU 性能区间:

    • 单核 ~400 分: 入门级性能。适合静态网页托管、轻量定时任务、机器人、轻量代理或个人博客,不适合处理高并发任务。
    • 单核 ~800 分: 主流性能。足以应对常规建站(如中等流量 WordPress)、基础 Docker 容器部署及小型数据库。
    • 单核 ~1200 分: 优秀性能。可承载高负载 Web 应用、中型关系型数据库(MySQL/PostgreSQL)、CI/CD 构建及多容器并行。
    • 单核 ~2000 分: 顶级性能。适合单核敏感型游戏服务端、视频转码、高并发后端及数据处理。

    对于 KVM 虚拟化服务器,理想情况 是多核得分接近 核心数 × 单核得分。若多核比例严重偏低,表明宿主机存在严重的 CPU 超售或资源争抢,实际使用体验会受影响。

    确认上述各项指标符合预期后,即可开始初始化配置系统。

    重装操作系统

    重装有风险,笔者不为任何脚本的安全性背书,请自行衡量并承担后果。如果条件允许,请尽可能使用自定义 ISO 镜像手动安装。

    使用一键 DD 脚本

    对于不支持自定义 ISO 镜像的服务商,使用一键 DD 脚本比较方便。开始前,务必花费几分钟读下作者的说明文档,这远比盲目粘贴命令重要。

    1. bin456789/reinstall 支持重装的系统类型更多
    2. bohanwood/debi 专注于纯净、精简的 Debian 系统环境

    这里以使用第一个脚本重装 Debian 13 为例:

    curl -fLO https://raw.githubusercontent.com/bin456789/reinstall/main/reinstall.sh
    bash reinstall.sh debian 13 \
      --password '替换为强密码'
    

    完成后 reboot 重启,耐心等待系统重装完成。

    使用自定义 ISO 镜像

    首选支持自定义 ISO 镜像的重装方式,以确保系统环境纯净。

    完成系统安装后,使用预设凭据登录并进入后续配置。

    配置非特权用户

    日常运维应使用非特权用户,启用免密 sudo 存在安全风险,仅建议在完全掌控系统安全的前提下配置。

    创建用户

    下面以新建非特权用户 dejavu 为例,使用 root 用户执行:

    adduser dejavu
    

    配置 sudo 免密

    apt update \
      && apt install -y sudo
    
    install -m 0440 /dev/null /etc/sudoers.d/90-dejavu
    
    printf '%s\n' \
      'dejavu ALL=(ALL:ALL) NOPASSWD: ALL' \
      > /etc/sudoers.d/90-dejavu
    

    检查 sudoers 配置:

    visudo -cf /etc/sudoers.d/90-dejavu
    visudo -c
    

    OK 的话,切换到非特权用户:

    sudo -iu dejavu
    

    本文后续命令默认由该普通用户执行,需要管理员权限的命令使用 sudo

    更新系统并安装基础软件

    更新操作系统

    sudo apt update \
      && sudo apt full-upgrade -y
    

    安装基础软件包

    sudo apt install -y --no-install-recommends \
      ca-certificates \
      curl \
      git \
      gnupg \
      unzip \
      vim \
      wget \
      tree
    

    设置默认编辑器

    我个人习惯使用 Vim 作为默认编辑器:

    sudo update-alternatives --set editor /usr/bin/vim.basic
    

    安装常用运维工具(可选)

    以下软件包通常不是必须的,可根据自己需要选择安装:

    sudo apt install -y --no-install-recommends \
      dnsutils \
      ethtool \
      git \
      jq \
      lsof \
      mtr-tiny \
      ncdu \
      rsync \
      tcpdump \
      tmux \
      jq
    

    设置时区自动同步

    统一时区

    对于不同区域的 Linux 服务器,建议统一设置为 UTC 时区:

    sudo timedatectl set-timezone UTC
    

    注意,KVM 的 kvm-clock 只提供虚拟化时钟源,并不能保证外部 UTC 一致性,仍需依靠 NTP 服务同步。

    使用 systemd-timesyncd

    适用于绝大多数使用场景:

    sudo systemctl disable --now chrony 2>/dev/null || true
    sudo apt install -y systemd-timesyncd
    sudo systemctl enable --now \
      systemd-timesyncd.service
    sudo timedatectl status
    sudo timedatectl timesync-status
    

    使用 Chrony

    仅当机器需要极度苛刻的时间同步收敛速度和质量时,才换用 Chrony 服务:

    sudo apt install -y chrony
    sudo systemctl disable --now systemd-timesyncd
    sudo systemctl enable --now chrony
    chronyc tracking
    chronyc sources -v
    

    安装 KVM Guest Agent(可选)

    仅在信任服务商且宿主机已暴露 virtio-serial 通道时,安装 KVM Guest Agent 才有意义。

    检查 QEMU Guest Agent 通道:

    if [ -e /dev/virtio-ports/org.qemu.guest_agent.0 ]; then
        echo "QEMU Guest Agent 通道可用!"
    else
        echo "QEMU Guest Agent 通道不存在!"
    fi
    

    通道可用时:

    sudo apt install -y qemu-guest-agent
    sudo systemctl enable --now qemu-guest-agent
    sudo systemctl status qemu-guest-agent --no-pager
    

    通道不存在时跳过安装。

    配置自动安全更新

    AI 时代各种软件、云服务和基础设置面临的安全风险比以前要大得多,建议启用 Debian 的自动更新安全补丁。

    每日自动更新

    sudo apt install -y unattended-upgrades \
      needrestart
    # 每日更新策略
    cat << 'EOF' | sudo tee /etc/apt/apt.conf.d/20auto-upgrades > /dev/null
    APT::Periodic::Update-Package-Lists "1";
    APT::Periodic::Unattended-Upgrade "1";
    EOF
    

    禁止自动重启

    即使启用自动更新,生产服务器也不应当自动重启,配置:

    cat << 'EOF' | sudo tee /etc/apt/apt.conf.d/52unattended-upgrades-local > /dev/null
    Unattended-Upgrade::Automatic-Reboot "false";
    EOF
    

    启用定时任务

    sudo systemctl enable --now \
      apt-daily.timer \
      apt-daily-upgrade.timer
    

    检查配置

    sudo unattended-upgrade --dry-run
    apt-config dump | grep -iE 'Periodic|Unattended'
    systemctl list-timers 'apt-*' --all
    sudo unattended-upgrade --dry-run --debug
    sudo needrestart -r l
    

    检查自动更新日志

    # unattended-upgrades 日志
    sudo tail -n 100 \
      /var/log/unattended-upgrades/unattended-upgrades.log
    # dpkg 输出
    sudo tail -n 100 \
      /var/log/unattended-upgrades/unattended-upgrades-dpkg.log
    # systemd 执行记录
    sudo journalctl \
      -u apt-daily-upgrade.service \
      --since '7 days ago'
    

    SSH 安全加固

    确认当前登录的是目标非特权用户,例如 dejavu

    写入 SSH 公钥

    直接写入你的 SSH 公钥内容:

    install -d -m 0700 ~/.ssh
    touch ~/.ssh/authorized_keys
    chmod 0600 ~/.ssh/authorized_keys
    # 写入 SSH 公钥
    PUBKEY='ssh-ed25519 ...<实际公钥内容>...'
    grep -qxF "$PUBKEY" ~/.ssh/authorized_keys \
      || printf '%s\n' "$PUBKEY" >> ~/.ssh/authorized_keys
    

    或者也可以在本地计算机进行拷贝:

    ssh-copy-id \
      -i ~/.ssh/id_ed25519.pub \
      -p <端口> \
      <用户名>@服务器IP
    

    只启用 ssh.service 服务

    Debian 13 的 ssh.socket 固定监听 TCP 22。关闭它,SSH 监听端口统一由 ssh.servicesshd_config 管理。

    sudo systemctl disable --now ssh.socket
    sudo systemctl enable --now ssh.service
    

    写入 SSH 加固配置

    关于选择 SSH 端口
    多数教程建议将 SSH 迁移至高位端口,但是这里其实存在一个「安全悖论」。Linux 中 1024 以上的端口允许非特权用户绑定。若 SSH 服务中断,普通用户可能抢占该高位端口并伪造服务。因此,建议选择一个 1024 以下且未被常规服务占用的特权端口。

    cat << 'EOF' | sudo tee /etc/ssh/sshd_config.d/00-custom.conf > /dev/null
    # 注意修改实际的 SSH 端口
    Port 822
    # 未完成认证的连接最多保留 1 分钟
    LoginGraceTime 1m
    # 禁止 root 用户直接登录
    PermitRootLogin no
    # 检查用户目录和密钥文件权限
    StrictModes yes
    # 开启 SSH 公钥认证
    PubkeyAuthentication yes
    # 禁止密码认证
    PasswordAuthentication no
    # 禁止空密码登录
    PermitEmptyPasswords no
    # 禁止键盘交互式认证
    KbdInteractiveAuthentication no
    # 保留 PAM 的账户检查和会话管理功能
    UsePAM yes
    # 禁止 X11 转发
    X11Forwarding no
    # 服务器每 60 秒检测一次客户端连接
    ClientAliveInterval 60
    # 客户端连续 5 次未响应后断开
    ClientAliveCountMax 5
    # 禁止登录前进行反向 DNS 查询
    UseDNS no
    EOF
    

    检查配置并重启 SSH 服务

    sudo sshd -t && sudo systemctl restart ssh.service
    

    不要关闭当前终端会话,新建 SSH 连接测试登录,确认无误后再退出,否则回到原会话中自查配置。

    更改主机名

    myvps 替换为实际的主机名:

    sudo hostnamectl set-hostname <myvps>
    sudo vim /etc/hosts
    

    一般情况,像下面这样编辑 /etc/hosts 即可。

    127.0.0.1       localhost
    127.0.1.1       <myvps>
    ::1             localhost ip6-localhost ip6-loopback
    ff02::1         ip6-allnodes
    ff02::2         ip6-allrouters
    

    对于真实 FQDN 和固定公网 IP 的邮件服务器,则应该这样:

    127.0.0.1       localhost
    # 替换为实际的域名
    <公网 IPv4 地址>  myvps.example.com <myvps>
    <公网 IPv6 地址>  myvps.example.com <myvps>
    ::1             localhost ip6-localhost ip6-loopback
    ff02::1         ip6-allnodes
    ff02::2         ip6-allrouters
    

    IPv6 静态路由

    以 Netcup 服务器为例,它们提供 /64 的 IPv6,建议设置静态 IPv6 路由(不同服务商配置有所区别)。

    sudo vim /etc/network/interfaces
    

    添加:

    iface <网卡名称> inet6 static
        address <静态 IPv6 地址>/64
        gateway fe80::1
    

    重启后生效。

    更换 Linux 内核(可选)

    Debian Cloud 内核精简了物理硬件驱动,面向云平台进行了虚拟化优化,更适合大多数 KVM 虚拟机。

    {{<callout type="warning"title=" 风险提示 ">}}
    更换内核可能导致系统无法启动,请确保具备 VNC 或带外管理手段后再进行操作。
    {{</callout>}}

    安装 Cloud 内核:

    ARCH="$(dpkg --print-architecture)"
    sudo apt install -y "linux-image-cloud-${ARCH}"
    

    重启系统:

    sudo reboot
    

    重新登录后执行检查,预期输出包含 cloud-amd64cloud-arm64

    uname -r
    

    卸载标准内核(仅在成功运行 Cloud 内核后执行):

    ARCH="$(dpkg --print-architecture)"
    case "$ARCH" in
      amd64)
        sudo apt purge -y --ignore-missing linux-image-amd64
        ;;
      arm64)
        sudo apt purge -y --ignore-missing linux-image-arm64
        ;;
    esac
    
    sudo apt autoremove --purge -y
    sudo update-grub
    

    启用 SSD/NVMe TRIM(可选)

    检查虚拟磁盘是否支持 discard:

    lsblk -D
    

    DISC-MAX 非零,服务商支持 TRIM,启用定时任务:

    sudo systemctl enable --now fstrim.timer
    systemctl list-timers fstrim.timer
    

    配置 ZRAM 和 Swap

    配置 ZRAM

    sudo apt install -y systemd-zram-generator
    cat << 'EOF' | sudo tee /etc/systemd/zram-generator.conf > /dev/null
    [zram0]
    # 根据实际配置
    # 物理内存的一半,最大 4GB (4096)
    zram-size = min(ram / 2, 4096)
    swap-priority = 100
    EOF
    

    立即启用 ZRAM:

    sudo systemctl daemon-reload
    sudo systemctl start [email protected]
    

    检查 ZRAM 和 Swap 状态:

    sudo zramctl
    sudo swapon --show
    free -h
    

    创建 Swap 文件

    以 2GB Swapfile 为例:

    sudo fallocate -l 2G /swapfile
    sudo chmod 600 /swapfile
    sudo mkswap /swapfile
    # 自动挂载
    grep -qE '^[[:space:]]*/swapfile[[:space:]]' /etc/fstab \
      || echo '/swapfile none swap defaults 0 0' \
         | sudo tee -a /etc/fstab > /dev/null
    

    启用 Swapfile:

    sudo swapon /swapfile
    

    检查 Swap 状态:

    sudo swapon --show
    

    启用 BBR 拥塞控制算法

    从 Linux 4.20 起,BBR 在 qdisc 未提供时会自动启用 TCP 内部 pacing,因而不再强制依赖 fq。此外,Debian 13 官方内核已将 BBR 编译为模块,且系统默认 qdiscfq_codel

    写入配置:

    cat << 'EOF' | sudo tee /etc/sysctl.d/90-bbr.conf > /dev/null
    net.ipv4.tcp_congestion_control = bbr
    EOF
    

    立即应用:

    sudo sysctl --load=/etc/sysctl.d/90-bbr.conf
    

    检查当前采用算法:

    sudo sysctl net.ipv4.tcp_congestion_control
    

    安装 Docker CE(可选)

    清除旧版软件包

    全新的操作系统可跳过此步:

    sudo apt update
    sudo apt remove -y $(
      dpkg --get-selections \
        docker.io docker-compose docker-doc docker-buildx \
        podman-docker containerd runc \
        2>/dev/null | cut -f1
    )
    

    添加 Docker 软件源

    sudo apt install -y ca-certificates curl
    sudo install -m 0755 -d /etc/apt/keyrings
    sudo curl -fsSL \
      https://download.docker.com/linux/debian/gpg \
      -o /etc/apt/keyrings/docker.asc
    sudo chmod a+r /etc/apt/keyrings/docker.asc
    cat <<EOF | sudo tee /etc/apt/sources.list.d/docker.sources >/dev/null
    Types: deb
    URIs: https://download.docker.com/linux/debian
    Suites: $(. /etc/os-release && echo "$VERSION_CODENAME")
    Components: stable
    Architectures: $(dpkg --print-architecture)
    Signed-By: /etc/apt/keyrings/docker.asc
    EOF
    

    安装 Docker

    sudo apt update \
      && sudo apt install -y \
        docker-ce \
        docker-ce-cli \
        containerd.io \
        docker-buildx-plugin \
        docker-compose-plugin \
        && sudo systemctl enable --now docker
    

    写入 Docker 配置

    cat <<'EOF' | sudo tee /etc/docker/daemon.json >/dev/null
    {
      "log-driver": "local",
      "log-opts": {
        "max-size": "20m",
        "max-file": "5"
      },
      "live-restore": true
    }
    EOF
    sudo systemctl restart docker
    

    验证安装

    sudo docker version
    sudo docker compose version
    

    安装和配置 Nginx

    建议使用 Nginx 官方软件源安装,而非 Debian 默认软件源。

    环境准备

    sudo apt install -y \
      curl \
      gnupg2 \
      ca-certificates \
      lsb-release \
      debian-archive-keyring
    

    导入 Nginx 官方 GPG Key:

    curl -fsSL https://nginx.org/keys/nginx_signing.key \
      | gpg --dearmor \
      | sudo tee /usr/share/keyrings/nginx-archive-keyring.gpg \
      > /dev/null
    # 注意和官方 Key 对比验证
    install -d -m 700 ~/.gnupg
    gpg \
      --dry-run \
      --quiet \
      --no-keyring \
      --import \
      --import-options import-show \
      /usr/share/keyrings/nginx-archive-keyring.gpg
    

    添加 Nginx 软件源

    echo "deb [signed-by=/usr/share/keyrings/nginx-archive-keyring.gpg] \
    https://nginx.org/packages/debian $(lsb_release -cs) nginx" \
      | sudo tee /etc/apt/sources.list.d/nginx.list \
      > /dev/null
    # 设置优先级
    cat << 'EOF' | sudo tee /etc/apt/preferences.d/99nginx > /dev/null
    Package: *
    Pin: origin nginx.org
    Pin: release o=nginx
    Pin-Priority: 900
    EOF
    

    安装 Nginx

    sudo apt update
    apt-cache policy nginx
    sudo apt install -y nginx
    

    删除默认站点

    sudo rm -f /etc/nginx/conf.d/default.conf
    

    使用 Cloudflare CDN(推荐)

    创建默认回退 (Fallback) 主机

    从 Nginx 1.19.4 开始,无需自签 TLS 证书即可阻断 SNI 探测:

    cat << 'EOF' | sudo tee /etc/nginx/conf.d/00-default.conf > /dev/null
    server {
        listen 443 ssl default_server;
        listen [::]:443 ssl default_server;
    
        server_name _;
        ssl_reject_handshake on;
    }
    EOF
    

    透传客户端真实 IP 地址

    cat << 'EOF' | sudo tee /etc/nginx/conf.d/10-cloudflare-realip.conf > /dev/null
    set_real_ip_from 173.245.48.0/20;
    set_real_ip_from 103.21.244.0/22;
    set_real_ip_from 103.22.200.0/22;
    set_real_ip_from 103.31.4.0/22;
    set_real_ip_from 141.101.64.0/18;
    set_real_ip_from 108.162.192.0/18;
    set_real_ip_from 190.93.240.0/20;
    set_real_ip_from 188.114.96.0/20;
    set_real_ip_from 197.234.240.0/22;
    set_real_ip_from 198.41.128.0/17;
    set_real_ip_from 162.158.0.0/15;
    set_real_ip_from 104.16.0.0/13;
    set_real_ip_from 104.24.0.0/14;
    set_real_ip_from 172.64.0.0/13;
    set_real_ip_from 131.0.72.0/22;
    
    set_real_ip_from 2400:cb00::/32;
    set_real_ip_from 2606:4700::/32;
    set_real_ip_from 2803:f800::/32;
    set_real_ip_from 2405:b500::/32;
    set_real_ip_from 2405:8100::/32;
    set_real_ip_from 2a06:98c0::/29;
    set_real_ip_from 2c0f:f248::/32;
    
    real_ip_header CF-Connecting-IP;
    EOF
    

    使用 Cloudflare 源服务器证书

    使用 Cloudflare 源服务器证书,免于日常维护 SSL/TLS 证书。在 Cloudflare 后台创建 Origin CA,然后安装:

    # 准备证书目录
    sudo install \
      -d \
      -o root \
      -g root \
      -m 0700 \
      /etc/nginx/tls
    # 安装证书公钥(替换实际域名和文件)
    sudo install \
      -o root \
      -g root \
      -m 0644 \
      ~/example.com-origin.pem \
      /etc/nginx/tls/example.com-origin.pem
    # 安装证书私钥(替换实际域名和文件)
    sudo install \
      -o root \
      -g root \
      -m 0600 \
      ~/example.com-origin.key \
      /etc/nginx/tls/example.com-origin.key
    

    后续虚拟主机配置直接引用即可:

    ssl_certificate     /etc/nginx/tls/example.com-origin.pem;
    ssl_certificate_key /etc/nginx/tls/example.com-origin.key;
    

    启用 Authenticated Origin Pulls (AOP)

    用上了 Cloudflare Origin CA,笔者还建议启用 Authenticated Origin Pulls (AOP)。一般情况下,使用 Global AOP 证书即可。

    sudo curl -fsSL \
    https://developers.cloudflare.com/ssl/static/authenticated_origin_pull_ca.pem \
      -o /etc/nginx/tls/cloudflare-origin-pull-ca.pem
    sudo chown root:root \
      /etc/nginx/tls/cloudflare-origin-pull-ca.pem
    sudo chmod 0644 \
      /etc/nginx/tls/cloudflare-origin-pull-ca.pem
    

    然后,在 Cloudflare 后台为域名开启 AOP,Nginx 虚拟主机增加一条配置:

    ssl_verify_client on;
    

    建议先使用 optional 验证,再切换为强制验证,详见 Cloudflare Docs

    不使用 Cloudflare CDN 的情况

    创建默认回退 (Fallback) 主机

    cat << 'EOF' | sudo tee /etc/nginx/conf.d/00-default.conf > /dev/null
    server {
        listen 80 default_server;
        listen [::]:80 default_server;
    
        server_name _;
        return 444;
    }
    
    server {
        listen 443 ssl default_server;
        listen [::]:443 ssl default_server;
    
        server_name _;
        ssl_reject_handshake on;
    }
    EOF
    

    启动 Nginx 服务

    sudo nginx -t
    sudo systemctl enable --now nginx
    sudo systemctl reload nginx
    

    配置 nftables 流量防火墙

    现在,我们应该摒弃过时的 iptables 和 UFW,拥抱原生高效的 nftables。针对三种典型的 Web 服务器使用场景:

    1. 使用且仅限 Cloudflare CDN 访问

      开放且仅允许 Cloudflare CDN 的 IP 段回源访问 443/TCP,无需开放 80/TCP、80/UDP 或 443/UDP。

    2. 不使用 CDN 的公网 Nginx

      开放 80/TCP 和 443/TCP。

    3. 不使用 CDN 的公网 Caddy

      开放 80/TCP、443/TCP,Caddy 原生支持 HTTP/3 QUIC 还应当开放 443/UDP。

    本文使用两个 nftables 配置表:nftables.conf 用于通用配置,web_server.nft 用于 Web 服务器场景配置。

    我们约定:

    • 默认表名 default_filter
    • 主机出站默认允许
    • Docker 自行维护转发和 NAT,容器端口绑定 127.0.0.1

    安装 nftables

    从 Debian 10 开始,nftables 是默认安装的,但通常处于未启用状态。

    sudo apt update \
      && sudo apt install -y nftables
      && sudo systemctl status nftables
    

    当前的 inactive 状态是预期行为。

    写入 nftables 主配置

    注意替换 SSH 端口为实际端口:

    cat << 'EOF' | sudo tee /etc/nftables.conf > /dev/null
    #!/usr/sbin/nft -f
    
    # 只重建本 SOP 管理的表。
    destroy table inet default_filter
    
    # 修改为实际 SSH 端口。
    define SSH_PORT = 822
    
    table inet default_filter {
        # Web 访问策略。
        include "/etc/nftables.d/web_server.nft"
    
        chain input {
            type filter hook input priority filter;
            policy drop;
    
            # 本机回环。
            iifname "lo" accept
    
            # 丢弃无效连接,放行已建立连接及其关联流量。
            ct state invalid drop
            ct state { established, related } accept
    
            # 兼容使用 DHCP / DHCPv6 获取地址的 VPS。
            udp sport 67 udp dport 68 accept
            udp sport 547 udp dport 546 accept
    
            # ICMP、PMTU 和 IPv6 邻居发现。
            meta l4proto icmp accept
            meta l4proto ipv6-icmp accept
    
            # 公网 SSH。
            tcp dport $SSH_PORT accept
    
            # 当前选择的 Web 策略。
            jump web_input
        }
    
        chain output {
            type filter hook output priority filter;
            policy accept;
        }
    }
    EOF
    

    配置 Web 访问场景表(三选一)

    仅限 Cloudflare CDN HTTPS 回源

    cat << 'EOF' | sudo tee /etc/nftables.d/web_server.nft > /dev/null
    set cloudflare_v4 {
        type ipv4_addr;
        flags interval;
    
        elements = {
            173.245.48.0/20,
            103.21.244.0/22,
            103.22.200.0/22,
            103.31.4.0/22,
            141.101.64.0/18,
            108.162.192.0/18,
            190.93.240.0/20,
            188.114.96.0/20,
            197.234.240.0/22,
            198.41.128.0/17,
            162.158.0.0/15,
            104.16.0.0/13,
            104.24.0.0/14,
            172.64.0.0/13,
            131.0.72.0/22
        };
    }
    
    set cloudflare_v6 {
        type ipv6_addr;
        flags interval;
    
        elements = {
            2400:cb00::/32,
            2606:4700::/32,
            2803:f800::/32,
            2405:b500::/32,
            2405:8100::/32,
            2a06:98c0::/29,
            2c0f:f248::/32
        };
    }
    
    chain web_input {
        ip saddr @cloudflare_v4 tcp dport 443 accept
        ip6 saddr @cloudflare_v6 tcp dport 443 accept
    }
    EOF
    

    不使用 CDN 的公网 Nginx

    cat << 'EOF' | sudo tee /etc/nftables.d/web_server.nft > /dev/null
    chain web_input {
        tcp dport { 80, 443 } accept
    }
    EOF
    

    不使用 CDN 的公网 Caddy

    cat << 'EOF' | sudo tee /etc/nftables.d/web_server.nft > /dev/null
    chain web_input {
        tcp dport { 80, 443 } accept
        udp dport 443 accept
    }
    EOF
    

    修正 Debian 默认行为

    Debian 13 自带的 nftables.service 在停止时默认执行:

    nft flush ruleset
    

    这会删除 Docker、Fail2ban 等服务动态插入的防火墙表。将其修改为仅清空我们的 default_filter 表:

    sudo install \
      -d \
      -o root \
      -g root \
      -m 0755 \
      /etc/systemd/system/nftables.service.d
    cat << 'EOF' | sudo tee \
      /etc/systemd/system/nftables.service.d/override.conf \
      > /dev/null
    [Service]
    ExecStop=
    ExecStop=/usr/sbin/nft destroy table inet default_filter
    EOF
    

    检查并应用配置

    保持当前 SSH 会话,检查完整配置(含引用的 Web 策略表):

    sudo nft -c -f /etc/nftables.conf
    

    确认无误,重载配置启动服务:

    sudo systemctl daemon-reload
    sudo systemctl enable --now nftables
    

    新建 SSH 会话测试连接,若连接失败,请在原会话中自查端口配置。

    安装配置 Fail2ban

    安装 Fail2ban

    sudo apt update \
      && sudo apt install -y fail2ban
    

    确认当前实际的 SSH 端口:

    sudo sshd -T | awk '$1 == "port" {print $2}'
    

    以下以端口 822 为例,实际执行时,请替换为您的实际 SSH 端口。

    创建 SSH 监狱规则

    sudo install -d -m 0755 /etc/fail2ban/jail.d
    
    cat <<'EOF' | sudo tee /etc/fail2ban/jail.d/sshd.local >/dev/null
    [sshd]
    enabled = true
    # 注意替换实际 SSH 端口
    port = 822
    backend = sys
    
3 人打赏,共 7 积分 EastonMan 1Showfom 5Opacity 1

14 条回复

  • dejavu
    Lv.3 初识社区超级版主
    #1

    给自己加个精

  • Showfom
    Lv.2 新手上路管理员
    #2

    看完了,除了命令比较啰嗦以外,其他没啥大毛病,需要注意的是 Cloudflare 的 IP 不一定是一直固定的,最好可以写个脚本每天抓取并更新他最新的 IP 然后重启 nftables 和 nginx 服务

    https://www.cloudflare.com/ips-v4/
    https://www.cloudflare.com/ips-v6/
    
  • guest
    Lv.1 初来乍到
    #3

    给我也加个精

  • dejavu
    Lv.3 初识社区超级版主
    #4

    @Showfom #2 Cloudflare 好像好几年没更新 IP 段了,所以把重写的时候把自动更新 IP 段的脚本删掉了

  • dejavu
    Lv.3 初识社区超级版主
    #5

    @guest #3 您的积分已 - 10+1(狗头

  • Showfom
    Lv.2 新手上路管理员
    #6

    @dejavu #4

    https://www.cloudflare.com/ips/

    但是遇上他们更新的时候就不至于手忙脚乱挨个更新服务器了

    image.webp

  • 猫咪
    Lv.2 新手上路
    #7

    zici,常看楼主的博客
    说起来配置非特权用户这步,是不是考虑更进一步,限制普通用户能以 root 身份执行的命令

  • ping9timeout
    Lv.2 新手上路
    #9

    有请 @grok 来:1. 打个分 2. 锐评一下 3. 指出错误或值得改进的地方

  • grok
    Lv.1 初来乍到AI 机器人
    #10

    @ping9timeout #8 1. 打分:8.6/10

    作为「个人 SOP / 速查手册」已经很扎实,覆盖面、安全意识和现代工具选择都明显强于大多数中文 VPS 初始化教程。扣分主要来自啰嗦的命令、个别有争议的默认选择,以及硬编码维护点。

    2. 锐评

    这不是通用指南,楼主自己也说了。它更像一份「我自己下次还能抄、少踩坑」的作业程序,优点很明确:

    • 先测再装、重装风险提示、非特权用户、SSH 公钥 + 禁密码、nftables 默认 drop、不自动重启、Cloudflare Origin CA + AOP、ssl_reject_handshake、ZRAM、Cloud 内核、BBR,这些选择都站得住。
    • 比还在教 UFW、改 22 端口、密码登录、iptables 的教程高一个档次。
    • Debian 13 的 ssh.socket 处理、nftables.service 停止时别 flush ruleset 以免干掉 Docker 表,这些细节说明作者真的踩过坑。

    槽点也直白:命令又长又碎,heredoc + tee 满天飞,复制粘贴容易手滑;日常用户直接 NOPASSWD ALL,等于给被攻破的账号开了 root 通道(楼主自己也写了风险,但还是这么配了);Cloudflare IP 写死,作者说「好几年没更新所以删了脚本」,Showfom 的担心仍然成立——真更新的那天你会手忙脚乱。整体像一份完成度很高的私人笔记,而不是给别人无脑照抄的「标准答案」。

    3. 错误或值得改进的地方

    • 免密 sudo:建议至少改成 visudo 限制命令(或单独 admin 用户 + 密码 sudo)。猫咪说得对,这一步可以更严。
    • Cloudflare IP:即使很少变,也建议在配置里加注释指向官方列表(https://www.cloudflare.com/ips-v4/ 和 v6),或留一个简单拉取 + 校验脚本。硬编码 + Nginx realip 两处都要改,漏一处就尴尬。nftables 和 Nginx 两边同步是维护成本。
    • Fail2ban 段被截断,且默认 jail 往往还是 iptables 动作。既然已经全面 nftables,应明确用 nftables action(或 `fi
  • ping9timeout
    Lv.2 新手上路
    #11

    @grok #9 你的输出超出字数限制而被截断,请继续说完。

  • grok
    Lv.1 初来乍到AI 机器人
    #12

    @ping9timeout #10 #10 接着 #9 被截断的第 3 点说完。

    Fail2ban:主贴本身就截在 backend = sys,完整 jail 没贴出来。Debian 13 默认 jail 仍可能走 iptables/nft 混用,既然全文已经拥抱 nftables,应写死例如:

    backend = systemd
    banaction = nftables-multiport
    chain = input
    

    并确认它打在独立表(常见是 inet f2b-*),不要写进 default_filter。否则你每次 nft -f /etc/nftables.conf 或重启 nftables,封禁会丢。改完防火墙后应 sudo fail2ban-client reload。主贴改 nftables.serviceExecStop 是对的,但 Fail2ban 与 nft 的配合要写清楚,否则「默认 drop + 动态封禁」会互相踩。

    Nginx AOP 配置不完整。只写 ssl_verify_client on; 不够,必须同时指定:

    ssl_client_certificate /etc/nginx/tls/cloudflare-origin-pull-ca.pem;
    ssl_verify_client optional;   # 先 optional,确认无误再 on
    

    ssl_client_certificateon 会直接让握手失败。Origin CA 和 AOP 是两件事:前者证明源站身份给 Cloudflare,后者证明来访者是 Cloudflare。

    Cloudflare IP 硬编码有两处(nftables set + set_real_ip_from),漏改一处会出现「防火墙已放行但 real_ip 仍是 CF 边缘」或反过来。即使很少改,配置里应注释官方源:https://www.cloudflare.com/ips-v4/https://www.cloudflare.com/ips-v6/。Showfom 说的脚本不必天天跑,做「拉取 → 校验非空 / CIDR → 原子替换 → nft/nginx 重载」即可,失败则保留旧文件。

  • unhitched1233
    Lv.1 初来乍到
    #13

    好贴

  • gego
    Lv.1 初来乍到
    #14

    好贴,收藏了

  • deck
    Lv.1 初来乍到
    #15

    nftables 虽然新但配置有点麻烦

发表回复