防火墙 / 端口 / 网络
一个服务的访问条件可以写成:客户端从什么地址出发,使用什么协议,连接哪个地址和端口,最终由哪个进程处理。每一项都会影响排查。浏览器连接的 443 可能属于负载均衡器,Linux 上的 18081 可能映射到容器内的 18080,应用实际接收到的源地址又可能来自代理。
把这些地址对应起来,才能决定查哪个监听、哪张路由表和哪组防火墙规则。单独问“端口开了吗”,缺少了协议、方向和观察位置。
地址、端口和协议怎样组成一次通信
从 URL 到连接端点
以 https://api.example.com:8443/orders 为例:
https://api.example.com:8443/orders
├─ https 应用通信方式:HTTP 使用 TLS 保护
├─ api.example.com 目标主机名:通过名称解析获得候选 IP
├─ 8443 显式目标端口;HTTPS 未写端口时默认 443
└─ /orders HTTP 资源路径;路由表和普通端口规则不识别它
一次经 TCP 建立的连接
├─ 源地址 + 源端口 客户端 IP + 内核分配的临时端口
├─ 目标地址 + 目标端口 某个解析结果 + 8443
└─ 传输协议 TCP源 IP、源端口、目标 IP、目标端口、协议合称五元组。普通客户端通常不必固定源端口,操作系统会在可用范围内分配;服务器则需要在约定端口接收请求。服务端回包时,两端地址和端口对调。无状态网络 ACL 若只允许请求方向、遗漏返回方向的客户端临时端口,连接仍会失败。
HTTP/1.1 和常见的 HTTP/2 HTTPS 连接使用 TCP;HTTP/3 使用 QUIC,运行在 UDP 上。因此,允许 443/tcp 与允许 443/udp 是两项规则。HTTP 方法、请求体、状态码和浏览器行为见从请求到响应;HTTP/3 的传输规定见 RFC 9114。
IP、前缀和下一跳
IP 地址标识网络层端点。网卡可以配置多个地址,一台机器也可以有物理接口、回环接口、VPN 接口和容器网桥。10.20.30.40/24 中,/24 表示前 24 位属于网络前缀,对应 IPv4 掩码 255.255.255.0。它所在的前缀为 10.20.30.0/24。
内核按路由规则和路由表决定走哪条路径:在常见以太网中,直连目标通过邻居解析获得目标 MAC;跨网段目标先交给下一跳网关,解析的是网关 MAC。数据包的目标 IP 仍然是远端地址,除非途中发生地址转换。
| 地址或写法 | 常见用途 | 使用时要分清什么 |
|---|---|---|
127.0.0.1、::1 | IPv4、IPv6 回环 | 都指向当前网络命名空间自身 |
0.0.0.0、:: | 绑定时的通配地址 | 表示接受相应地址族的本地地址;不要当客户端目标地址 |
10.0.0.0/8、172.16.0.0/12、192.168.0.0/16 | IPv4 私有地址空间 | 跨组织可能重叠,经 VPN 访问时容易选错路由 |
/32、/128 | 单个 IPv4、IPv6 地址的前缀 | 来源白名单可精确到一个地址 |
fe80::/10 | IPv6 链路本地地址 | 通常还要指定接口作用域,不能当跨路由地址使用 |
IPv6 地址用冒号分隔,放进带端口的 URL 时需要方括号,例如 http://[::1]:18080/ready.txt。IPv6 有独立的地址、路由和过滤条件;机器有 A、AAAA 两种解析结果时,只测 IPv4 会遗漏另一条访问路径。IPv4 绑定行为、IPv6 地址作用域及私有地址定义分别见 ip(7)、ipv6(7) 和 RFC 1918。
TCP、UDP 与 ICMP 各自能测什么
TCP 建连的普通过程是客户端发 SYN、服务端发 SYN-ACK、客户端确认 ACK,之后双方交换字节流。nc -vz 能检查 TCP 建连,后面的 HTTP、TLS、认证和业务处理还没有经过检查。
UDP 以数据报传递消息,没有这套握手。UDP socket 在 ss 中常显示为 UNCONN,这是正常状态。nc -u 没收到错误,只能说明当前没有获得可识别的失败反馈;验证 DNS 服务应发送 DNS 查询,验证其他 UDP 服务也应使用对应协议的请求。
ICMP 传递错误和控制信息,ping 使用其中的回显请求与应答。允许 ICMP 回显的主机,完全可能拒绝 TCP 连接;禁止回显的主机,也可能正常提供 HTTPS。IPv6 还依赖 ICMPv6 完成邻居发现等工作,路径 MTU 发现也需要相应控制消息。安全策略应按用途处理 ICMP 类型,不能简单理解为“业务不用 ping,全部丢掉即可”。协议细节见 TCP、UDP 与 IPv6 节点要求。
先让一个真实服务在回环地址上响应
准备 Ubuntu 工具与测试文件
以下主机命令使用 Ubuntu 24.04、Bash。安装软件需要 sudo;启动服务和发送请求使用普通用户。包安装会修改测试机,请在有管理权限的开发机或测试 VM 执行。已有工具可跳过安装。
sudo apt-get update
sudo apt-get install --no-install-recommends \
python3 curl iproute2 iputils-ping iputils-tracepath \
dnsutils netcat-openbsd tcpdump ca-certificates
python3 --version
curl --version
ip -Version安装失败时先区分仓库解析失败、出口代理、证书错误或签名校验失败。企业内网使用批准的软件仓库与代理配置;离线机器使用按同一发行版准备的包仓库和依赖。不要用关闭签名、关闭 TLS 校验或来源不明的一键脚本代替排查。
在终端 A 创建单独的临时目录,仅放一个测试文件,再启动前台服务:
LAB_ROOT=$(mktemp -d /tmp/http-netlab.XXXXXX)
printf 'network-ready\n' > "$LAB_ROOT/ready.txt"
printf '实验目录:%s\n' "$LAB_ROOT"
python3 -m http.server 18080 --bind 127.0.0.1 --directory "$LAB_ROOT"终端会显示监听 127.0.0.1 和端口 18080,随后保持运行。若出现 Address already in use,在终端 B 用下面的 ss 确认占用者;不要直接结束未知进程。Python 简易服务器只用于这个小文件实验,不承担生产服务。命令与目录参数见 Python http.server 文档。
读懂监听与 HTTP 结果
在同一台机器的终端 B 执行:
ss -H -lnt 'sport = :18080'
curl -q --noproxy '*' --fail-with-body --silent --show-error \
--connect-timeout 2 --max-time 5 \
--write-out '\nhttp=%{http_code} peer=%{remote_ip}:%{remote_port}\n' \
http://127.0.0.1:18080/ready.txt预期响应包含:
network-ready
http=200 peer=127.0.0.1:18080ss 输出中的本地地址应为 127.0.0.1:18080。-l 筛选监听 socket,-n 保留数值,-t 只看 TCP,-H 去掉表头。若要确认所属进程,用 sudo ss -H -lntp 'sport = :18080';普通用户可能看不到其他用户的进程详情。完整筛选表达式见 ss(8)。
curl 的 -q 放在第一个参数,跳过默认配置文件;--noproxy '*' 为这次本地请求禁用代理。--fail-with-body 让 HTTP 错误产生非零退出码,同时保留错误正文。这里还检查文件内容和状态码,防止把另一个占用端口的 HTTP 服务误认为测试服务。这些选项及退出码见 curl 官方手册。
另开一个终端只是在同一命名空间内重复请求。它没有经过另一台机器的路由、云入口或入站策略。需要跨机验证时,先确定本机允许提供服务的接口地址,再决定是否扩展监听。
绑定地址决定进程接收哪些本地目的地址
127.0.0.1:18080 只接受回环目标;绑定具体网卡地址则接受发往该地址的连接;0.0.0.0:18080 接受当前命名空间内所有本地 IPv4 地址上的该端口。跨机开放还需要路由和过滤规则配合,绑定本身不负责认证。
如果确实要在隔离测试网络中允许其他机器访问,在终端 A 按 Ctrl+C 停止服务,再执行:
python3 -m http.server 18080 --bind 0.0.0.0 --directory "$LAB_ROOT"随后在服务器上检查 ip -br address 与监听结果,选取测试网络中的真实地址;从另一台获准客户端请求该地址的 /ready.txt。不要把文件服务器开放到公网,也不要将家目录设为服务目录。完成后按 Ctrl+C,在终端 A 精确删除实验文件并移除空目录:
rm -- "$LAB_ROOT/ready.txt"
rmdir -- "$LAB_ROOT":: 的监听能否同时接收 IPv4,取决于 socket 的 IPV6_V6ONLY 设置与系统配置。看到 *:18080 时应分别用 ss -4、ss -6 和实际请求确认,不能仅凭缩写推断。
容器通常有自己的网络命名空间,其中的接口、路由、监听和防火墙与宿主机分开。容器访问 127.0.0.1 会找自己;宿主机的 ss 也不会自动列出容器内的 socket。共享网络命名空间的进程是例外,例如同一个 Kubernetes Pod 内的常规容器。隔离对象见 network_namespaces(7)。
数据包如何选路,又怎样返回
地址和路由配置由谁提供
主机可以通过 DHCP 获取地址、路由和 DNS,也可以使用静态配置;IPv6 还常通过路由通告与 SLAAC 形成地址和默认路由。接口显示 UP 表示接口状态,仍要检查它是否取得了所需地址、默认路由是否可用。以太网接口、VLAN 子接口、网桥和 VPN 隧道连接的是不同链路,不应把所有接口上的地址混用。
Ubuntu Server 常由 Netplan 描述网络配置,再交给 systemd-networkd 或 NetworkManager 执行;云镜像中的配置还可能由 cloud-init 生成。ip address 和 ip route 显示内核运行状态,临时用 ip 修改后,重启或配置管理重载可能恢复原值。排查“刚改好又失效”时,应查到真正负责配置的文件或管理系统。配置字段见 Netplan YAML。
远程改地址、默认路由或接口归属会影响当前 SSH 连接。保留原配置,在具备控制台恢复能力的窗口操作;Netplan 提供需要确认的 netplan try,但虚拟设备等场景有回退限制,超时后仍需检查运行状态和磁盘配置,不能只依赖自动回退承诺。限制见 netplan try。
检查实际选中的接口和源地址
在发起请求的 Linux 客户端执行下面的只读检查。输入自己有权测试的服务 IPv4 地址,不要填域名;解析问题稍后单独检查。
read -r -p '目标服务 IPv4:' TARGET_IP
ip -br address
ip -4 route get "$TARGET_IP"
ip -4 route show
ip -4 neigh showroute get 可能得到形如以下的结果;地址和接口会随机器变化:
10.20.40.15 via 10.20.30.1 dev ens3 src 10.20.30.40读法是:目的地址为 10.20.40.15,下一跳为 10.20.30.1,从 ens3 发出,默认选择源地址 10.20.30.40。没有 via 的直连路由通常表示直接送往目标所在链路。route get 做路由查询,不会替你完成远端连通测试。
常规路由表优先匹配更长的前缀,同等适用条件下再考虑优先级等属性。VPN 安装了一条更具体的私网路由后,原本到数据中心的流量可能改走隧道。多网卡主机还可能按源地址、标记等选择不同路由表;这时继续查看:
ip -4 rule show
ip -4 route show table all需要复现指定源地址时,可用 ip -4 route get "$TARGET_IP" from "$SOURCE_IP",但必须先把 SOURCE_IP 设为本机实际地址。应用显式绑定接口、容器命名空间和策略标记会影响路径,默认查询未必完整复现该应用。路由查询与策略细节见 ip-route(8)。
邻居解析与回程是两个不同问题
以太网中,IPv4 通过 ARP、IPv6 通过 Neighbor Discovery 找到同一链路下一跳的 MAC。ip neigh 中 REACHABLE 表示近期确认可达,STALE 表示缓存需要后续确认,单独出现通常正常;持续 INCOMPLETE 或 FAILED 才值得结合接口、VLAN、下一跳地址和链路检查。不同隧道或点到点接口不一定使用同样的邻居机制。状态说明见 ip-neighbour(8)。
服务端收到请求后,还要查路由把响应送回客户端。NAT、VPN、多出口和负载均衡会改变这里的条件。排查时在服务端查询它实际观察到的源 IP,不要直接使用客户端最初配置的地址。若源地址已经被 SNAT 改成网关地址,回包先返回网关,再由连接跟踪恢复对应关系。
IPv4 严格反向路径过滤也可能丢弃不符合反向路由要求的报文。先核对接口、策略路由和 rp_filter 的实际值;不要把全局关闭过滤当作通用修复。相关模式和接口/全局取值关系见 Linux IP Sysctl 文档。
IPv6 与 MTU 需要单独验证
双栈服务应分别检查 ip -6 address、ip -6 route 和 curl -6;只有 IPv4 正常、IPv6 失败时,继续定位 AAAA 记录、IPv6 监听、默认路由及 IPv6 策略。禁用 IPv6 会隐藏这条故障路径,不能替代修正错误配置。
MTU 是接口能承载的网络层报文大小。VPN 和 Overlay 封装会占用额外空间;路径中某一段 MTU 更小时,短请求可能正常,大响应或 TLS 握手却反复重传。对获准测试的 IPv4 目标,可执行:
tracepath -n "$TARGET_IP"
ping -4 -c 3 -W 2 -M do -s 1472 "$TARGET_IP"普通 IPv4、无 IP 选项时,1472 字节数据加 8 字节 ICMP 和 20 字节 IP 头对应 1500 字节报文。-M do 禁止本机分片,错误可能提示报文过大。若目标不响应 ICMP,超时无法据此确定 MTU;结合 tracepath、ICMP 错误与重传报文判断。不要照搬这个数值到 IPv6,也不要在没有路径证据时批量调整 MTU 或 TCP MSS。命令参数见 iputils ping 与 tracepath。
防火墙怎样处理本地流量与转发流量
路由决定进入 INPUT 还是 FORWARD
Linux 的 Netfilter 在网络栈的多个 hook 上处理报文。对常见的入站 IPv4/IPv6 流量,可以用下面的简化路径理解过滤位置;图中省略了连接跟踪等内部步骤,也没有涵盖所有 bridge、XDP 或 CNI 实现。
网卡收到报文
↓
PREROUTING(可发生 DNAT:改变目标地址/端口)
↓
路由判断
├─ 目标属于本命名空间 → INPUT → 本地 socket / 进程
└─ 目标需要转发 → FORWARD → POSTROUTING(可发生 SNAT)→ 出接口
本地进程发出的报文 → OUTPUT → POSTROUTING → 出接口防火墙允许某个端口,只会允许匹配流量继续处理,不会替应用创建监听。反过来,ss 能看到监听,也只说明内核已有接收端点;此前的路由或过滤仍可能阻止客户端到达它。
DNAT 改写目的地址,常用于把宿主机端口转给容器;SNAT 改写源地址,常用于共享出口,MASQUERADE 是常见的动态源地址转换方式。NAT 依靠连接跟踪维护正反方向映射。它解决地址转换,业务身份认证仍由应用负责。
有状态过滤常允许 ct state established,related:前者匹配已跟踪连接,后者包含被识别为相关的流量,例如部分 ICMP 错误。跟踪状态描述网络交互,无法说明用户已经登录。已有连接还可能继续命中这条允许规则,所以修改来源策略后必须建立新连接验证。
规则由谁管理
| 工具 | 主要职责 | 排查入口 |
|---|---|---|
nftables / nft | 配置规则集、表、链、集合和计数器 | sudo nft list ruleset |
| iptables | 传统规则接口;可能使用 nft 或 legacy 后端 | iptables --version,再按实际后端查看规则 |
| UFW | Ubuntu 常用的主机防火墙管理界面 | sudo ufw status verbose、sudo ufw show raw |
| firewalld | 按 zone、service、policy 管理过滤 | 活跃 zone、接口/来源归属、运行态与永久配置 |
| Docker、Kubernetes 网络组件 | 为容器连接和服务转发生成规则或其他数据面配置 | 对应产品的网络、端口和策略对象 |
不要同时启用多套主机防火墙管理器,再用手工规则修补它们生成的链。iptables --version 中的 nf_tables 表示兼容接口使用 nft 后端;legacy 则是另一套实现。一个工具的列表为空,不代表整台机器没有过滤,尤其还存在其他网络命名空间或 eBPF 数据面时。
nftables 的 base chain 关联 hook 和优先级,同一 hook 上可能有多条链。accept 允许当前处理继续,后续链或后续 hook 仍可能丢弃;drop 会终止该报文的后续处理。普通链需要被跳转或调用才会运行。表、链、判决与 handle 语义见 nftables 官方手册。
DROP 与 REJECT 的可观察差异
DROP 静默丢弃。客户端可能重传并最终超时,耗时受应用和 TCP 设置影响。REJECT 主动返回错误,例如对 TCP 返回 RST;客户端通常更快收到连接失败。中间设备也能发拒绝报文,RST 并不总由目标应用产生。
这两种动作都可以合理使用。受控内网中明确拒绝能缩短错误反馈,静默丢弃则减少对外反馈。选择时要考虑安全策略和客户端行为,不能按“更慢就更安全”判断。
在隔离容器中观察放行、拒绝和丢弃
创建两端,不改宿主机规则
实验需要已安装的 Docker Engine,以及当前用户操作 Docker 的权限。使用 Linux 容器,服务镜像基于 Ubuntu 24.04;Docker Desktop 可以观察容器内行为,但它的网络还经过 Linux VM,不能据此声称验证了物理 Linux 主机的完整路径。Docker 安装与容器基础见 Docker。
应用和客户端均以 UID/GID 10001:10001 运行。只有服务容器额外获得 NET_ADMIN,以便通过 docker exec --user 0 在它自己的网络命名空间配置规则。不要增加 --privileged、--network host 或宿主目录挂载,也不要把下面的 nft 命令去掉 docker exec 后在宿主执行。Docker 管理权限本身很高,应使用专用测试环境。
在新的空目录保存 Dockerfile:
FROM ubuntu:24.04
RUN apt-get update && DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
python3 curl iproute2 nftables tcpdump ca-certificates \
&& rm -rf /var/lib/apt/lists/*
RUN useradd --uid 10001 --user-group --no-create-home labuser \
&& mkdir -p /opt/netlab \
&& printf 'network-ready\n' > /opt/netlab/ready.txt
USER 10001:10001
CMD ["python3", "-u", "-m", "http.server", "18080", "--bind", "0.0.0.0", "--directory", "/opt/netlab"]构建阶段以镜像默认 root 安装软件;最后的 USER 控制默认运行身份。没有把动态端口、真实地址或凭据写进镜像。需要离线运行时,在有网环境构建、通过批准的镜像仓库或 docker image save/load 搬运,保留镜像来源与身份记录;内网不能拉取基础镜像时,不会自动存在这个实验镜像。
在保存文件的目录、同一个 Bash 终端执行以下命令。后续实验继续使用这些变量:
docker build -t local/firewall-netlab:1 .
LAB_ID="netlab-$$-$RANDOM"
LAB_NET="$LAB_ID-net"
SERVER="$LAB_ID-server"
CLIENT="$LAB_ID-client"
docker network create --internal "$LAB_NET"
docker run -d --name "$SERVER" --network "$LAB_NET" --network-alias app \
--cap-add NET_ADMIN --read-only --tmpfs /tmp:rw,noexec,nosuid,size=16m \
local/firewall-netlab:1
docker run -d --name "$CLIENT" --network "$LAB_NET" --read-only \
local/firewall-netlab:1 sleep infinity
docker exec "$SERVER" id
docker exec "$SERVER" ss -H -lnt 'sport = :18080'预期身份为 uid=10001(labuser),监听为 0.0.0.0:18080。两个容器未发布宿主端口;--internal 阻止这张桥接网络经普通外部路由出站,但不等于完全隔绝 Docker 宿主。容器启动失败时用 docker logs --tail 30 "$SERVER" 查看原因,修复后再继续。若安全策略禁止 NET_ADMIN,停止此实验,不扩大容器权限绕过限制。
先由客户端发送 IPv4 请求:
docker exec "$CLIENT" curl -q --noproxy '*' -4 \
--fail-with-body --silent --show-error --connect-timeout 2 --max-time 4 \
--write-out '\nhttp=%{http_code}\n' http://app:18080/ready.txt预期 network-ready、http=200。app 是这张用户自定义网络内的服务别名。应用监听和请求成功后,再改变过滤条件,否则无法区分服务未启动与防火墙阻断。
加入 DROP,观察超时和计数器
创建实验专用的 inet article_netlab 表与 input base chain。以下链的默认策略是 accept,只有明确匹配的实验端口被丢弃:
docker exec --user 0 "$SERVER" nft add table inet article_netlab
docker exec --user 0 "$SERVER" nft \
'add chain inet article_netlab input { type filter hook input priority 0; policy accept; }'
docker exec --user 0 "$SERVER" nft \
'add rule inet article_netlab input tcp dport 18080 counter drop'
if docker exec "$CLIENT" curl -q --noproxy '*' -4 \
--fail-with-body --silent --show-error --connect-timeout 2 --max-time 4 \
http://app:18080/ready.txt; then
printf '异常:DROP 条件下请求成功\n'
else
rc=$?
printf 'curl exit=%s;预期 28(超时)\n' "$rc"
fi
docker exec --user 0 "$SERVER" nft -a list chain inet article_netlab input
docker exec "$SERVER" ss -H -lnt 'sport = :18080'预期 curl 退出码 28,DROP 规则的 counter packets 大于零,监听仍然存在。报文到达了此规则并被丢弃;应用进程没有因此退出。计数包含 TCP 重传,具体数值无需与别人一致。
若请求意外成功,检查实际容器、规则和目标地址;若退出码为 6,先处理别名解析;若是 7,检查是否收到拒绝或服务已经停止。这些结果不能当成预期 DROP 实验通过。
改为 REJECT,再限定允许的来源
读取刚才规则末尾的 handle,再精确替换它。下面仅匹配自己刚建立的、唯一一条目标端口 DROP 规则;在已有业务规则集里不要直接复用这个筛选器。
DROP_HANDLE=$(docker exec --user 0 "$SERVER" nft -a list chain inet article_netlab input |
awk '/tcp dport 18080 counter.*drop/ {print $NF}')
if [[ "$DROP_HANDLE" =~ ^[0-9]+$ ]]; then
docker exec --user 0 "$SERVER" nft \
"replace rule inet article_netlab input handle $DROP_HANDLE tcp dport 18080 counter reject with tcp reset"
else
printf '没有得到唯一的数字句柄,停止修改\n' >&2
fi
if docker exec "$CLIENT" curl -q --noproxy '*' -4 \
--fail-with-body --silent --show-error --connect-timeout 2 --max-time 4 \
http://app:18080/ready.txt; then
printf '异常:REJECT 条件下请求成功\n'
else
rc=$?
printf 'curl exit=%s;预期 7(连接失败)\n' "$rc"
fi预期快速失败、退出码 7。这次内核主动发送 TCP reset;应用仍在监听。相同的客户端“连接被拒绝”提示,既可能来自未监听端口,也可能来自明确 REJECT,需要结合服务端规则和报文判断。
接着只允许实验客户端的 IPv4 地址。inspect 从当前容器读取地址,不把另一台机器的具体地址写死。清空操作只作用于自己新建的实验链,不影响其他表或链:
CLIENT_IP=$(docker inspect --format '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' "$CLIENT")
printf '允许的实验来源:%s\n' "$CLIENT_IP"
docker exec --user 0 "$SERVER" nft flush chain inet article_netlab input
docker exec --user 0 "$SERVER" nft \
"add rule inet article_netlab input ip saddr $CLIENT_IP tcp dport 18080 counter accept"
docker exec --user 0 "$SERVER" nft \
'add rule inet article_netlab input tcp dport 18080 counter drop'
docker exec "$CLIENT" curl -q --noproxy '*' -4 \
--fail-with-body --silent --show-error --connect-timeout 2 --max-time 4 \
http://app:18080/ready.txt此客户端应恢复 network-ready。allow 规则在前,其他到 18080/tcp 的来源走后面的 DROP。因为实验客户端只接入一个网络,地址抽取才是单值;生产容器连多张网络时,应明确索引指定网络。
再创建一个自动清理的临时客户端验证拒绝面:
if docker run --rm --network "$LAB_NET" --read-only local/firewall-netlab:1 \
curl -q --noproxy '*' -4 --fail-with-body --silent --show-error \
--connect-timeout 2 --max-time 4 http://app:18080/ready.txt; then
printf '异常:非白名单来源请求成功\n'
else
rc=$?
printf 'curl exit=%s;预期 28\n' "$rc"
fi
docker exec --user 0 "$SERVER" nft list chain inet article_netlab input第二个客户端使用不同的容器 IPv4 地址,预期超时。此处只演示 IPv4 来源限制,不能把它直接当作双栈生产策略。
撤销实验规则并确认恢复
docker exec --user 0 "$SERVER" nft delete table inet article_netlab
docker exec "$CLIENT" curl -q --noproxy '*' -4 \
--fail-with-body --silent --show-error --connect-timeout 2 --max-time 4 \
http://app:18080/ready.txt预期恢复 network-ready。这一步验证过滤撤销后应用仍可用。先保留两个容器与 Bash 变量,后面的抓包会使用它们;全部实验结束后的清理命令在抓包章节末尾。
在真实主机上安全调整放行范围
先确定对象与恢复入口
生产规则至少写清来源、目标地址、协议、目标端口、方向,以及请求实际经过的设备。SSH 管理入口、业务入口、数据库和监控端口可以有完全不同的来源范围。不要用“内网”代替准确 CIDR,也不要把数据库端口对所有来源开放。
远程修改前保留现有 SSH 会话,确认控制台或其他经过验证的恢复入口可用,并记录要新增、撤销的准确规则。现有会话可能受 established 规则保护,它仍然可用无法证明下一次登录成功。收紧 SSH 或默认策略时,按服务器初始化中的管理入口流程操作,不能边猜端口边启用防火墙。
如果配置由 Ansible、云代理或其他管理平台维护,应修改其声明配置;现场手工规则可能被下一次同步覆盖。防火墙服务停止也未必代表所有内核规则已经消失,其他管理器还可能继续工作。
Ubuntu:用 UFW 增加一条来源限定规则
以下用于 UFW 已启用、默认入站拒绝、SSH 已正确放行 的 Ubuntu 主机,目标是本机进程,不是 Docker 转发端口。需要 sudo。首先检查:
sudo ufw status verbose
sudo ufw status numbered
sudo ufw show raw确认没有对该业务端口更宽的既有 allow,也没有会优先命中的 deny。新增窄 allow 无法自动收紧原来的宽 allow;规则顺序、before/after 规则及实际匹配都需要一起检查。如果状态是 inactive,先完成初始化流程,不在此处直接启用。
输入经过确认的 IPv4 来源 CIDR、本机业务地址和真实监听端口。以下操作不应使用 Docker 实验容器的临时 IP 作为生产白名单。
read -r -p '获准来源 IPv4 CIDR:' ALLOWED_CIDR
read -r -p '本机业务 IPv4:' SERVICE_IP
read -r -p '业务 TCP 端口:' SERVICE_PORT
sudo ufw --dry-run allow proto tcp from "$ALLOWED_CIDR" \
to "$SERVICE_IP" port "$SERVICE_PORT"确认预览中的地址、协议和端口正确后才执行:
sudo ufw allow proto tcp from "$ALLOWED_CIDR" \
to "$SERVICE_IP" port "$SERVICE_PORT"
sudo ufw status numberedUFW 会管理运行规则及持久化配置。随后从允许的客户端建立新连接,检查应用协议与内容;再从不在白名单内的获准测试客户端验证拒绝。失败时先核对实际来源是否经 NAT 改变、目标地址是否正确,再检查匹配顺序。
撤销这次新增项时,使用完整规则表达式,避免把会随其他变更移动的编号当作固定标识:
sudo ufw delete allow proto tcp from "$ALLOWED_CIDR" \
to "$SERVICE_IP" port "$SERVICE_PORT"
sudo ufw status numbered只删除自己新增的规则;如果添加时提示规则已存在,应先确认归属。双栈服务还要按 IPv6 地址单独设计与测试。语法、预览和来源限制见 Ubuntu 防火墙文档。
firewalld:先找正确 zone,再区分临时与永久
已经由 firewalld 管理的主机继续使用它,不为复用上一节命令再启用 UFW。管理员先执行:
sudo firewall-cmd --state
sudo firewall-cmd --get-active-zones活跃 zone 可以关联接口或来源。根据报文的入接口和源地址确定适用 zone,不能直接假定是 public。选定后在同一 Bash 终端输入并查看运行态与永久配置:
read -r -p '确认适用的 zone:' ZONE
sudo firewall-cmd --zone="$ZONE" --list-all
sudo firewall-cmd --permanent --zone="$ZONE" --list-all假设要对本机服务临时放行一个 IPv4 来源,继续输入实际值并构造 rich rule:
read -r -p '获准来源 IPv4 CIDR:' ALLOWED_CIDR
read -r -p '本机业务 IPv4:' SERVICE_IP
read -r -p '业务 TCP 端口:' SERVICE_PORT
RULE="rule family=\"ipv4\" source address=\"$ALLOWED_CIDR\" destination address=\"$SERVICE_IP\" port port=\"$SERVICE_PORT\" protocol=\"tcp\" accept"
sudo firewall-cmd --zone="$ZONE" --add-rich-rule="$RULE" --timeout=5m
sudo firewall-cmd --zone="$ZONE" --query-rich-rule="$RULE"新增成功会提示 success,查询应为 yes、退出码 0。临时项会在五分钟后移除。在此窗口内用新连接完成允许与非允许来源验证;zone 中已有 service、port 或其他 rich rule 可能给出更宽权限,不能只查新项。
确认需要长期保留时,将同一条规则加入永久配置,再把带超时的运行规则换成不带超时的运行规则。这两条运行命令之间有短暂撤销窗口,应在允许短暂中断的测试或变更窗口执行:
sudo firewall-cmd --permanent --zone="$ZONE" --add-rich-rule="$RULE"
sudo firewall-cmd --zone="$ZONE" --remove-rich-rule="$RULE"
sudo firewall-cmd --zone="$ZONE" --add-rich-rule="$RULE"
sudo firewall-cmd --permanent --zone="$ZONE" --query-rich-rule="$RULE"
sudo firewall-cmd --zone="$ZONE" --query-rich-rule="$RULE"若临时规则已经到期,移除会提示不存在,先核对状态再继续添加。永久配置供后续加载使用,写入它不会自动改变当前运行规则;这里显式更新两处,不需要全量 reload。撤销自己新增的这条规则时执行:
sudo firewall-cmd --zone="$ZONE" --remove-rich-rule="$RULE"
sudo firewall-cmd --permanent --zone="$ZONE" --remove-rich-rule="$RULE"再分别查询两处,预期为 no、退出码 1,并从原客户端建立新连接检查恢复后的访问范围。其他非零退出码应按具体错误处理。
--runtime-to-permanent 会保存整个运行配置,--reload 又可能丢掉其他尚未持久化的运行项,不适合拿来收尾一条不相关的端口变更。完整选项与匹配语法见 firewall-cmd 和 rich language。
容器、代理和云入口会怎样改变路径
Docker:发布端口通常走转发路径
默认桥接网络下,-p 127.0.0.1:18081:18080 包含三个不同对象:宿主绑定地址 127.0.0.1、宿主端口 18081、容器端口 18080/tcp。镜像的 EXPOSE 只描述端口,不完成发布。
在之前保留变量的 Bash 终端,用同一镜像启动一个普通桥接网络中的发布实例。以下限定 Docker Engine 28 及以上、默认 NAT 桥接模式,且不启用额外直接路由配置;旧版本回环发布存在同二层网络访问问题,应先升级。具体限制见 Docker 端口发布文档。
PUBLISHED="$LAB_ID-published"
docker run -d --name "$PUBLISHED" --read-only \
-p 127.0.0.1:18081:18080 local/firewall-netlab:1
docker port "$PUBLISHED" 18080/tcp
curl -q --noproxy '*' --fail-with-body --silent --show-error \
--connect-timeout 2 --max-time 4 http://127.0.0.1:18081/ready.txt预期映射为 127.0.0.1:18081,响应 network-ready。端口占用时 Docker 会启动失败;检查占用者或在命令和请求中一同更换测试端口,不停掉未知业务。Windows 的 PowerShell 应使用 curl.exe 并按 PowerShell 语法执行,不能直接粘贴 Bash 的变量与续行写法。
容器应用需要接收发往其容器接口地址的连接。若改成只绑定容器内 127.0.0.1,即使宿主发布正确,转入容器接口的流量也找不到对应监听。这与“宿主发布到回环”是两件事:前者控制容器内接收地址,后者控制宿主入口。
Linux 上常见的外部访问路径为:宿主收到报文,DNAT 改成容器地址和端口,再经过 FORWARD 到容器。因此,宿主 INPUT 放行规则不是容器入口的完整保护;宿主 ss 未出现对应监听,也可能有正常工作的内核端口映射。Docker 与 UFW 的处理位置存在差异,不能凭 ufw status 推断发布端口已被限制。官方说明见 Docker 包过滤与防火墙。
需要额外限制转发来源时,先确认 Docker 后端。iptables 后端提供 DOCKER-USER;到该链时 DNAT 通常已完成,匹配原始目标需要连接跟踪原始元组。nftables 后端没有同名用户链,需要自建适当 hook/priority 的表;官方仍将该后端标为实验性,不能照抄 iptables 规则迁移。生产规则以实际后端和网络模式为准,参见 Docker with iptables 和 Docker with nftables。不要清空 Docker 生成的规则,也不要为排查关闭 daemon 的规则管理。
反向代理建立第二段连接
普通 HTTP 反向代理中,客户端与 Nginx 建立一段连接,Nginx 再连接上游。两段可以使用不同地址、端口乃至不同传输保护方式:
客户端 → Nginx:443 HTTPS,客户端验证入口证书
Nginx → app:18080 HTTP,上游看到代理的网络源地址HTTP 头中的 X-Forwarded-For 不能代替防火墙看到的源 IP;应用也应只信任受控代理写入的转发头。在 Nginx 容器中配置 127.0.0.1:18080,目标是 Nginx 所在命名空间;独立应用容器应使用可达服务名或地址。
收到 502 或 504,先定位哪一层代理返回了状态,再看它的上游错误。上游连接被拒绝,应核对代理侧解析、目标端口和应用监听;等待上游响应时超时,则继续查应用处理、依赖、连接池及网络重传。上游也可以自己返回这些状态,不能只凭浏览器状态码确认 Nginx 到应用断网。
对已有 Nginx 部署,具备配置读取权限的管理员可运行 sudo nginx -t 检查配置语法,并在配置指定的 error log 中查对应请求的 upstream。nginx -T 会输出完整配置,可能包含凭据或敏感地址,不要直接贴到公开问题单。网络请求需从代理所在容器或主机复测,而不是只从运维笔记本发起。
proxy_connect_timeout 约束建立上游连接,proxy_read_timeout 约束两次相邻读取之间的等待,不是整份响应的总时长。增大它只会延长部分等待,无法修复拒绝连接或错误目标。参数详解见 Nginx proxy 模块;完整部署入口见 Nginx。
云安全组与负载均衡按实际拓扑检查
直接访问虚拟机公网入口、通过四层负载均衡访问、通过七层网关访问,是不同路径。公网 IP 可能由云网络映射到虚拟机私网接口,不一定出现在虚拟机的 ip address 中。七层负载均衡通常重新建立到后端的连接;四层产品是否保留源地址、是否 SNAT,则取决于实现和配置。
检查时对照入口监听、目标后端、健康检查端口、实际来源和回程路由。健康检查可能使用不同来源地址和路径:健康检查通过而业务失败,需要继续核对业务端口、Host、路径和授权条件;健康检查失败也未必意味着全部业务连接都失败。
安全组、网络 ACL、WAF 和主机防火墙分别作用于不同对象,不能画成每次请求都必经的固定链。安全组的有状态规则、规则优先级和跨资源引用方式以所用云产品为准,相关入口见云服务器与安全组。修改前先确认实际关联资源,避免在错误实例或错误负载均衡器上不断扩大来源范围。
Kubernetes:配置对象与真正转发的节点分开看
常规有 selector 的 Service 用标签选择后端 Pod;EndpointSlice 保存后端地址、端口及条件,由数据面组件使用。EndpointSlice 是配置元数据,不是报文会穿过的一台转发服务器。 Service 的虚拟地址可能由 kube-proxy 或其他实现处理,并不要求存在一个监听 ClusterIP 的用户进程。
Service port 是服务入口端口,targetPort 指向后端端口或命名端口;NodePort 则增加节点入口。Headless、无 selector 和 ExternalName Service 有不同语义,不能套用所有普通 ClusterIP 假设。定义见 Kubernetes Service。
对自己有读取权限的现有集群,在管理终端确认 context,再输入 namespace 和 Service 名:
kubectl config current-context
read -r -p 'Namespace:' NS
read -r -p 'Service 名:' SVC
kubectl -n "$NS" get service "$SVC" -o yaml
kubectl -n "$NS" get endpointslices \
-l "kubernetes.io/service-name=$SVC" -o yaml
kubectl -n "$NS" get pods -o wide
kubectl -n "$NS" get networkpolicy检查 Service selector 是否匹配目标 Pod、端口映射是否指向真实监听、EndpointSlice 地址和 readiness 条件是否合理。没有后端地址时先查选择器和工作负载;有地址但未就绪时查探针与应用,不急于修改集群网络。publishNotReadyAddresses 等配置会影响就绪地址的发布语义。
下一步从同一业务网络位置的、已获准使用的诊断容器分别请求 Pod IP、Service 地址和外部入口。Pod 可达而 Service 不通,继续检查 Service 映射与数据面;二者可达而入口失败,转向 Ingress/Gateway 的路由匹配、控制器、证书和后端配置。kubectl port-forward 经 API 通道访问选定 Pod,可以辅助验证应用,但会绕开正常 Service/入口路径。详细操作见 Debug Services。
NetworkPolicy 只有在网络插件支持并执行时才产生预期效果;源 Pod 的 egress 与目标 Pod 的 ingress 都要允许相关流量。开启出站隔离后还应核对 DNS UDP/TCP 查询是否被允许。规则会受标签、namespace 和地址转换位置影响,参见 Network Policies。不要为测试随意部署高权限诊断 Pod,也不要假设每个集群都安装同一种 Ingress 控制器。
把名称解析、TLS 与 HTTP 分开检查
名称解析结果来自哪里
应用拿到域名后,可能经系统名称服务、自己的 DNS 库或代理解析。Linux 的 /etc/nsswitch.conf 决定系统名称查询的来源顺序,/etc/hosts 是其中一种本地来源;dig 直接发 DNS 查询,通常不按 hosts 文件查找。容器还有各自的解析配置,浏览器可能启用加密 DNS,JVM 也可能缓存结果。
在发生问题的客户端输入业务域名,执行只读查询:
read -r -p '业务域名:' HOST_NAME
getent ahosts "$HOST_NAME"
dig "$HOST_NAME" A
dig "$HOST_NAME" AAAA
dig +tcp "$HOST_NAME" Agetent ahosts 更接近使用系统 getaddrinfo 的应用视角,具体输出还受本机地址族配置影响。dig 中应同时看状态、ANSWER、TTL 和使用的 DNS 服务器:NXDOMAIN 表示查询名称不存在,SERVFAIL 表示服务器未能完成查询;NOERROR 也可能没有所求记录。UDP 查询失败而 TCP 查询成功时,继续检查 DNS UDP/TCP 53 的路径、响应尺寸和过滤。DNS 通常使用 UDP,但 TCP 支持同样必要。
修改 hosts 后,若应用仍使用旧地址,应检查应用缓存、进程是否在另一个容器以及请求是否经过代理。不要先删系统解析配置再试。查询工具与名称服务说明见 BIND dig 和 getent(1)。
用 curl 做受控的节点对照
需要区分“正常解析路径”和“指定入口地址”时,保持域名、端口、路径、地址族与 TLS 校验一致。下面仅用于允许直连的测试环境;企业要求必须经代理出网时,应按其网络要求测试,不能擅自绕过访问控制。
先只检查常见代理变量是否存在,不打印可能带账号密码的值:
for name in http_proxy https_proxy all_proxy no_proxy HTTP_PROXY HTTPS_PROXY ALL_PROXY NO_PROXY; do
if [[ -v "$name" ]]; then printf '%s 已设置\n' "$name"; fi
done输入同一业务域名与获准验证的入口 IPv4。示例请求根路径,应选用该业务明确返回成功的路径;需要认证时按组织要求供应凭据,不把令牌写入共享命令记录。
read -r -p 'HTTPS 业务域名:' HOST_NAME
read -r -p '获准直连的入口 IPv4:' TARGET_IP
URL="https://$HOST_NAME/"
curl -q --noproxy '*' -4 --fail-with-body --silent --show-error \
--connect-timeout 3 --max-time 10 \
--output /dev/null \
--write-out 'http=%{http_code} peer=%{remote_ip}:%{remote_port}\n' "$URL"
curl -q --noproxy '*' -4 --fail-with-body --silent --show-error \
--connect-timeout 3 --max-time 10 --resolve "$HOST_NAME:443:$TARGET_IP" \
--output /dev/null \
--write-out 'http=%{http_code} peer=%{remote_ip}:%{remote_port}\n' "$URL"--resolve 为这次 curl 的主机名与端口提供地址映射,同时保留 URL 主机名供 HTTP Host、TLS SNI 和证书名称校验使用。这里没有跟随重定向;如果得到 3xx,应先检查目标,再决定是否另测。curl 的成功退出也可能包含 3xx,仍需确认预期状态。
指定入口成功、普通请求失败,说明两条选址路径存在差异,接下来比对普通请求选择的 IP、解析结果和入口节点配置。原因可能是错误记录、不同后端、地址族或节点路径,不能只据此认定 DNS 服务故障。通过代理执行 --resolve 时,实际连接可能仍到代理,因此不具备这组直连对照的条件。
直接把 HTTPS 域名改成 IP 会改变主机名校验和虚拟主机选择;加 -k 又会跳过证书验证。排查证书时保留正确主机名,核对链、信任库和有效性。私有 CA 使用经过核验的 CA 文件或系统信任配置,详细部署见 SSL 证书。
超时发生在哪个阶段
对单个 HTTPS URL、一次新连接、不跟随重定向的测试,可以输出累计计时:
curl -q --noproxy '*' -4 --fail-with-body --silent --show-error \
--connect-timeout 3 --max-time 10 --output /dev/null \
--write-out 'dns=%{time_namelookup} tcp=%{time_connect} tls=%{time_appconnect} first=%{time_starttransfer} total=%{time_total} http=%{http_code}\n' \
"$URL"计时值以秒为单位,从开始累计。tcp-dns 近似反映解析后到建连完成的阶段,tls-tcp 是随后的 TLS 建立阶段,first-tls 包含请求发送、网络传播、服务器处理和返回首字节;不能全部算作服务器执行耗时。失败阶段、代理、连接复用和重定向会改变解释条件,未完成阶段的零值也不代表耗时为零。
--connect-timeout 包括名称解析以及连接建立所需的 TCP、TLS 等阶段,--max-time 限制整个传输。退出码 28 只说明到达某个时间限制,要结合错误文本和计时值定位;6 常见于名称解析失败,7 为连接失败,60 常见于证书验证失败,22 是启用 fail 选项后的 HTTP 错误。
浏览器中按 F12 打开 Network,保留请求记录后重新发起访问,先核对 Request URL、方法、状态、Remote Address、重定向与 Timing。代理缓存、Service Worker、连接复用和浏览器安全策略都可能让它与独立 curl 有不同结果。CORS 错误属于浏览器对响应使用的限制,即使网络请求已到达服务,也可能阻止页面脚本读取结果。Network 面板的字段解释见 Chrome DevTools Network。导出的 HAR 和 Copy as cURL 可能带 Cookie、Authorization 与业务数据,分享前必须脱敏。
用连接状态与抓包缩小故障位置
连接状态先说明本机在等什么
在应用实际所在的 Linux 网络命名空间中,按真实服务端口查看连接。实验服务器可直接执行:
docker exec "$SERVER" ss -H -nt state all '( sport = :18080 or dport = :18080 )'
docker exec "$SERVER" ss -s| 状态或现象 | 含义与下一步 |
|---|---|
| LISTEN | 进程已建立接收端点;继续检查绑定地址、来源与转发路径 |
| SYN-SENT | 本机发起连接、等待建连响应;两端抓包检查请求与回包 |
| SYN-RECV | 服务端处于握手中间状态;检查客户端 ACK、回程与队列变化 |
| ESTAB | TCP 已建立;继续查协议交换、读取等待和应用处理 |
| CLOSE-WAIT 持续累积 | 对端已关闭,本地应用尚未完成关闭;结合进程和调用栈查资源释放 |
| TIME-WAIT | 主动关闭后的一段 TCP 状态;短连接较多时常见,先看规模与资源压力 |
对已建立 TCP 连接,Recv-Q 常表示尚未被应用读取的数据,Send-Q 常表示尚未被对端确认的数据;监听 socket 的队列字段有不同含义。持续增长需要结合应用消费速度、重传和内核统计看趋势,不能把一个快照直接当瓶颈原因。
实际主机还可读取 nstat -az 中的 TCP 重传、监听溢出等累计计数,比较问题窗口前后的变化。计数属于所在网络命名空间,并非天然归属于某个接口。只因 TIME-WAIT 多就缩短超时、只因失败就调大 backlog,会掩盖连接复用、处理速度或流量模式问题。
在实验服务器抓握手报文
继续使用之前已撤销过滤规则的两个实验容器。在保存 SERVER、CLIENT 变量的终端执行以下有限抓包:
docker exec --user 0 "$SERVER" timeout 10 \
tcpdump -i eth0 -nn -t -c 8 -s 96 'tcp port 18080' &
CAPTURE_JOB=$!看到 listening on eth0 后,在同一终端发送请求,再等待抓包结束:
docker exec "$CLIENT" curl -q --noproxy '*' -4 \
--fail-with-body --silent --show-error --connect-timeout 2 --max-time 4 \
http://app:18080/ready.txt
if wait "$CAPTURE_JOB"; then
printf '已达到抓包条数上限\n'
else
rc=$?
printf '抓包退出码=%s;124 表示到达十秒上限,其他非零需检查错误\n' "$rc"
fi-nn 保留数字地址与端口,-t 不打印时间戳,-c 8 限制条数,-s 96 限制每条捕获长度。终端只显示协议摘要,不使用 -A 或 -X 输出载荷。抓包需要容器内 root 及相应网络能力,实际业务环境必须先获得流量访问授权;即使只保留前 96 字节,也可能含少量业务数据,不应把捕获文件当作天然脱敏材料。
摘要中客户端端口通常是临时高位端口,服务端端口为 18080。关注 Flags [S]、[S.]、[.] 的握手方向,之后才是数据和关闭。每次分段、条数、确认合并方式可能不同,识别状态关系即可。选项及捕获权限说明见 tcpdump 官方手册源码。
若希望观察 DROP 与 REJECT,可在实验表中重新设置对应规则再抓包,但完成后仍应删除该实验表。宿主 -i any 的观察范围不等于所有容器命名空间;某些虚拟接口还可能重复呈现同一报文。
从报文位置推导下一步
客户端是否发出 SYN?
├─ 没有:查解析、应用目标、客户端 OUTPUT、路由和本地错误
└─ 已发出:服务端正确接口是否看到 SYN?
├─ 没有:沿实际路径检查 VPN、入口、转发与中间过滤
└─ 看到了:是否看到 SYN-ACK 或 RST?
├─ RST:核对实际监听、目标转换及 REJECT 来源
├─ 没有响应:查本地过滤计数、回程、资源与监听队列
└─ SYN-ACK:到客户端确认是否收到,并检查最终 ACK“没有抓到”需要先排除接口、命名空间、过滤表达式和捕获丢包。服务端捕获到 SYN-ACK,只说明它在该观察点出现,还要在客户端确认是否收到。No route to host 也可能由收到的 ICMP 拒绝引起,不只是本地没有路由。
握手完成后,TLS 失败就看握手和证书;HTTP 已响应就看准确状态、响应来源与内容;传输中停顿则比较重传、窗口和应用读取。网卡校验和卸载可能让本机出站抓包显示未完成的 checksum,GRO/GSO 也会影响看到的报文大小,不能只凭这些现象认定线上报文损坏。
全部实验结束后,在原 Bash 终端只清理本轮名称对应的容器和网络。先查看名称,确认无误后执行;如果没有执行发布实例实验,从命令中去掉 "$PUBLISHED"。不存在的实例应核对此前是否成功创建,不扩大删除范围。
printf '%s\n' "$SERVER" "$CLIENT" "$PUBLISHED" "$LAB_NET"
docker rm -f "$SERVER" "$CLIENT" "$PUBLISHED"
docker network rm "$LAB_NET"这会丢弃测试容器及其中的临时规则,保留 local/firewall-netlab:1 镜像供重复实验。真实系统完成修复后,则应从原客户端和实际协议重新验证新连接、授权内容及拒绝范围,并核对配置下次加载后的结果。
权威资料与规范地址
以下资料可查询协议规定、命令参数、规则匹配和各部署形态的实现差异。
地址、协议与 Linux 网络
- HTTP/3:https://www.rfc-editor.org/rfc/rfc9114.html
- IPv4 私有地址:https://www.rfc-editor.org/rfc/rfc1918.html
- IPv6 节点要求:https://www.rfc-editor.org/rfc/rfc8504.html
- Linux IPv4:https://man7.org/linux/man-pages/man7/ip.7.html
- Linux IPv6:https://man7.org/linux/man-pages/man7/ipv6.7.html
- TCP:https://man7.org/linux/man-pages/man7/tcp.7.html
- UDP:https://man7.org/linux/man-pages/man7/udp.7.html
- 网络命名空间:https://man7.org/linux/man-pages/man7/network_namespaces.7.html
- 路由:https://man7.org/linux/man-pages/man8/ip-route.8.html
- 邻居表:https://man7.org/linux/man-pages/man8/ip-neighbour.8.html
- Socket 统计:https://man7.org/linux/man-pages/man8/ss.8.html
- 内核网络参数:https://docs.kernel.org/networking/ip-sysctl.html
- ping:https://man7.org/linux/man-pages/man8/ping.8.html
- tracepath:https://man7.org/linux/man-pages/man8/tracepath.8.html
- Netplan 配置:https://netplan.readthedocs.io/en/stable/netplan-yaml/
- Netplan 试运行及回退限制:https://netplan.readthedocs.io/en/stable/netplan-try/
测试、名称解析与浏览器
- Python HTTP 测试服务:https://docs.python.org/3.12/library/http.server.html
- curl:https://curl.se/docs/manpage.html
- dig:https://bind9.readthedocs.io/en/stable/manpages.html#dig-dns-lookup-utility
- getent:https://man7.org/linux/man-pages/man1/getent.1.html
- tcpdump 手册源码:https://github.com/the-tcpdump-group/tcpdump/blob/master/tcpdump.1.in
- Chrome Network:https://developer.chrome.com/docs/devtools/network/reference/
防火墙与部署网络
- nftables:https://netfilter.org/projects/nftables/manpage.html
- Ubuntu 防火墙:https://ubuntu.com/server/docs/how-to/security/firewalls/
- firewall-cmd:https://firewalld.org/documentation/man-pages/firewall-cmd.html
- firewalld rich language:https://firewalld.org/documentation/man-pages/firewalld.richlanguage.html
- Docker 端口发布:https://docs.docker.com/engine/network/port-publishing/
- Docker 包过滤:https://docs.docker.com/engine/network/packet-filtering-firewalls/
- Docker iptables 后端:https://docs.docker.com/engine/network/firewall-iptables/
- Docker nftables 后端:https://docs.docker.com/engine/network/firewall-nftables/
- Nginx proxy 模块:https://nginx.org/en/docs/http/ngx_http_proxy_module.html
- Kubernetes Service:https://kubernetes.io/docs/concepts/services-networking/service/
- Kubernetes Service 调试:https://kubernetes.io/docs/tasks/debug/debug-application/debug-service/
- Kubernetes NetworkPolicy:https://kubernetes.io/docs/concepts/services-networking/network-policies/
