FreeBSD服务器无法访问时,如何从DNS解析与防火墙端口逐层定位
本文面向排查FreeBSD服务器访问失败的运维人员,介绍如何依次验证DNS记录、IPv4与IPv6入口、TCP端口监听、PF或IPFW规则、反向代理及应用状态,并结合抓包、规则计数和日志建立完整的故障证据链。

“网页打不开”并不等于服务器负载过高。DNS 返回错误、域名指向旧地址、IPv6 记录不可达、端口没有监听、防火墙丢弃连接、反向代理无法连接上游,都可能表现为超时或访问失败。CPU、内存和带宽指标只能描述资源状态,不能直接证明请求已经到达 FreeBSD 服务器。
有效的定位方法是把一次访问拆成 DNS 解析、网络入口、TCP 端口、防火墙、代理、应用响应 六个阶段,并为每个阶段寻找可验证的证据。通常应先在外部客户端确认域名和端口,再到服务器检查监听与数据包,最后检查代理和应用日志,避免一开始就修改防火墙或重启服务。
先把“无法访问”转换成可判断的现象
不同错误对应的故障范围并不相同。排查前应记录客户端看到的原始错误,而不是只描述“访问不了”。
| 客户端现象 | 通常说明什么 | 不能直接证明什么 |
|---|---|---|
NXDOMAIN |
查询结果认为域名不存在 | 不能证明服务器是否正常 |
SERVFAIL |
递归或权威DNS处理失败 | 不一定是服务器网络故障 |
Connection timed out |
TCP连接未在等待时间内建立 | 不能直接判断是防火墙还是路由问题 |
Connection refused |
目标主机返回拒绝,常见于端口未监听 | 不表示DNS一定正确 |
| TLS证书错误 | 已到达某个TLS服务,但证书、SNI或时间可能异常 | 不代表后端应用正常 |
| HTTP 404 | 已有HTTP服务响应,但站点或路由未匹配 | 不一定是目标业务应用返回 |
| HTTP 502 | 代理已响应,但连接上游应用失败或收到异常响应 | 通常不是客户端到代理端口被阻断 |
| HTTP 504 | 代理等待上游超时 | 不能仅凭状态码判定上游负载过高 |
如果客户端安装了 curl,可以将访问耗时拆分为多个阶段:
curl -sk -o /dev/null \
-w 'dns=%{time_namelookup} connect=%{time_connect} tls=%{time_appconnect} first_byte=%{time_starttransfer} code=%{http_code}\n' \
https://example.com/
其中:
time_namelookup表示DNS解析完成的累计时间。time_connect - time_namelookup大致对应TCP连接阶段。time_appconnect - time_connect大致对应TLS握手阶段。time_starttransfer - time_appconnect可用于观察HTTPS服务开始返回内容前的等待时间。
这些值只能帮助确定等待发生在哪一段,不能单独证明根因。例如首字节等待较长,既可能是应用处理慢,也可能是代理排队、上游网络等待或外部依赖阻塞。示例中的 -k 会忽略证书校验,只适合临时诊断,不应作为正式健康检查参数。
DNS要检查“解析结果”,不只是检查“能否解析”
域名能返回地址,不代表地址一定正确。DNS阶段需要确认以下内容:
- A记录是否指向实际IPv4入口。
- AAAA记录是否存在,以及IPv6入口是否真的可用。
- CNAME最终指向哪里,是否仍引用旧域名或旧平台。
- 权威DNS与递归DNS结果是否一致。
- 多个地址中是否只有部分节点可访问。
- DNS变更后,客户端或递归服务器是否仍使用缓存结果。
在装有 drill 的环境中,可以分别检查记录类型:
drill example.com A
drill example.com AAAA
drill example.com CNAME
drill example.com NS
如需直接查询权威DNS,可先从NS记录中取得服务器名称,再执行:
drill @ns1.example-dns.com example.com A
drill @ns1.example-dns.com example.com AAAA
如果系统没有 drill,也可以使用已安装的 dig 或 host。不要为了排障直接更改域名记录,应先保存当前记录值、TTL和变更时间。
特别检查IPv4与IPv6差异
一种常见情况是A记录可用,但AAAA记录指向未配置服务或未放行端口的IPv6地址。部分客户端优先尝试IPv6,于是会出现“有的网络能访问,有的网络超时”。
可以从外部客户端分别测试:
curl -4Iv --connect-timeout 5 https://example.com/
curl -6Iv --connect-timeout 5 https://example.com/
如果IPv4成功而IPv6失败,应继续检查:
- AAAA记录是否仍有必要保留。
- FreeBSD服务器是否配置了对应IPv6地址。
- 服务是否监听IPv6端口。
- PF、IPFW及上游网关是否允许IPv6入站。
- 默认IPv6路由是否正确。
删除AAAA记录不是唯一处理方式。如果业务需要IPv6,应修复监听、防火墙和路由;只有明确不提供IPv6服务时,才考虑撤销无效记录。
还要注意,FreeBSD中的 /etc/resolv.conf 主要影响服务器自身发起的DNS查询,例如应用连接数据库域名或外部接口。它通常不会决定访客如何解析网站域名,因此修改该文件不能解决权威DNS记录指向错误的问题。
核对DNS地址与服务器实际网络入口
登录 FreeBSD 服务器后,先查看接口地址和路由。以下命令以只读检查为主:
ifconfig -a
netstat -rn -f inet
route -n get default
如使用IPv6,还应查看IPv6路由:
netstat -rn -f inet6
判断时不能只比较 ifconfig 输出与DNS记录。如果服务器位于NAT、端口映射或前置网关之后,系统接口上看到的可能是私网地址,而DNS指向的是公网入口。此时还需要核对:
- 公网地址是否仍映射到当前服务器。
- TCP 80、443或业务端口是否完成转发。
- 网关健康检查是否将该节点移出。
- 是否存在旧服务器、旧NAT规则或错误的目标地址。
- 返回流量是否经由正确网关出去。
ping 成功不能证明业务端口开放,ping 失败也不能证明服务器离线,因为ICMP可能被限制。端口访问应使用TCP连接测试。
从服务器外部执行:
nc -vz -w 5 example.com 80
nc -vz -w 5 example.com 443
结果可按以下方式理解:
- 成功:TCP连接已建立,可以继续检查TLS、代理和应用。
refused:请求大概率已经到达目标主机,但端口没有监听,或防火墙主动拒绝。- 超时:可能是上游路由、NAT、防火墙丢弃、地址错误或返回路径异常,需要结合抓包判断。
端口监听状态比服务进程名称更有判断力
服务进程存在,不代表它监听了正确地址和端口。FreeBSD可使用 sockstat 查看监听套接字:
sockstat -4 -l
sockstat -6 -l
重点观察本地地址:
127.0.0.1:8080:只能从本机IPv4访问。192.0.2.10:443:只监听指定IPv4地址。*:80:通常表示监听所有IPv4地址。::1:8080:只允许本机IPv6访问。- IPv6通配监听是否同时接受IPv4,取决于系统和应用配置,不能直接假设。
如果业务本应直接向公网提供443端口,但 sockstat 中没有对应监听,应检查服务状态和启动日志,而不是先开放防火墙。
可先列出已启用的服务,再检查具体服务:
service -e
service nginx status
服务名取决于实际安装的软件,不能假设一定是 Nginx。若使用 Apache、HAProxy或自建应用,应替换为对应的 rc.d 服务名。
还可以分别测试回环地址和业务地址:
nc -vz 127.0.0.1 443
nc -vz 192.0.2.10 443
如果回环地址成功而业务地址失败,常见原因是服务只绑定了本地地址;如果两者都成功,但外部仍然超时,则应重点检查主机防火墙、上游入口和回程路由。
用抓包确认请求是否真正到达FreeBSD服务器
当外部测试超时时,tcpdump 是区分“流量没到服务器”和“服务器收到后未响应”的关键工具。
抓包可能包含客户端地址、域名或业务数据,应限制过滤条件和抓取数量,不要在生产环境长时间无过滤抓包。先通过 ifconfig 确认接口名称,再执行:
sudo tcpdump -ni em0 -c 50 'tcp port 80 or tcp port 443'
如果没有配置 sudo,应通过受控的 root 会话执行。将 em0 替换为实际外网接口。
在外部客户端重新发起一次访问,并观察结果:
- 完全看不到入站SYN:DNS可能指向其他地址,或流量在上游路由、NAT、外部防火墙处被拦截。
- 能看到SYN,但没有SYN-ACK或RST:主机防火墙可能丢弃数据包,也可能存在协议栈或返回路径问题。
- 看到SYN后立即返回RST:通常是端口没有监听,或规则主动拒绝连接。
- TCP三次握手完成:网络入口和TCP端口基本可用,应转向TLS、代理或应用层。
- 服务器发出SYN-ACK,但客户端继续重传SYN:可能存在回程路由错误,或返回包在中间路径被丢弃。
抓包证据比“服务器能上网”更有价值。服务器能够访问外部网站,只能说明部分出站路径可用,不能证明公网入站和回程路径正常。
分清PF、IPFW与上游防火墙
FreeBSD环境可能使用PF、IPFW,也可能没有启用主机防火墙,而由上游网关控制访问。先确认实际使用哪一种,不要同时套用两套规则。
查看PF状态和规则:
sudo pfctl -s info
sudo pfctl -sr
sudo pfctl -sn
sudo pfctl -vvs rules
其中:
pfctl -sr查看过滤规则。pfctl -sn查看NAT规则。pfctl -vvs rules可显示规则及累计计数器。
查看IPFW规则和计数器:
sudo ipfw list
sudo ipfw -a list
规则计数器应与一次受控复测结合使用。测试前后某条规则的计数增加,说明流量命中了它;计数没有变化,不能立即认定规则正确,还可能是流量没有到达、匹配了更前面的规则,或接口、地址族、协议条件不一致。
修改防火墙前先保留控制通道
远程修改防火墙可能中断SSH。操作前应确认有控制台、带外管理或其他恢复方式,并备份配置。以PF为例:
sudo cp -p /etc/pf.conf "/etc/pf.conf.bak.$(date +%Y%m%d%H%M%S)"
sudo pfctl -nf /etc/pf.conf
pfctl -nf 只做语法检查,不加载配置。修改完成后,应再次确认以下规则均存在:
- 当前SSH管理来源和端口允许访问。
- Web端口匹配正确的外网接口。
- IPv4与IPv6规则符合实际DNS记录。
- 返回流量和状态跟踪规则没有被破坏。
- NAT场景中的目标地址与转换方向正确。
一个PF入站规则的概念示例如下:
ext_if="em0"
web_ports="{ 80, 443 }"
pass in on $ext_if inet proto tcp from any to ($ext_if) port $web_ports flags S/SA keep state
该示例仅展示IPv4 Web端口的匹配思路,不能直接替换现有 /etc/pf.conf。接口名称、多IP绑定、NAT、IPv6以及默认拒绝策略都可能要求不同写法。完成备份、语法检查并确认管理规则后,才可按维护流程加载:
sudo pfctl -f /etc/pf.conf
若加载后访问异常,应通过控制台使用备份文件恢复,而不是执行清空全部规则之类的高风险操作。
对于IPFW,也应先阅读现有规则顺序。IPFW按规则编号匹配,新增允许规则如果位于更早的拒绝规则之后,仍然不会生效。修复应写回实际使用的持久化配置或启动脚本,不能只依赖临时命令。
TCP端口正常后再检查代理和应用
如果外部TCP连接成功,但网页仍返回404、502、504或TLS错误,故障范围已经进入代理和应用层。
确认虚拟主机、Host与SNI
同一IP和端口可能承载多个站点。直接访问IP时,代理可能返回默认站点,因此应带上正确的域名测试。
如果安装了 curl,可在服务器本机测试HTTP虚拟主机:
curl -v http://127.0.0.1/ -H 'Host: example.com'
测试HTTPS站点及SNI:
curl -vk --resolve example.com:443:127.0.0.1 https://example.com/
结果判断:
- 本机访问成功、外部失败:重点检查防火墙、NAT和网络入口。
- 返回默认证书或错误站点:检查监听端口、SNI及虚拟主机匹配。
- 返回404:请求已经到达HTTP服务,但域名、路径或路由配置可能不匹配。
- TLS握手失败:检查证书文件、私钥权限、协议配置和系统时间。
检查反向代理与上游应用的关系
以Nginx为例,FreeBSD软件包的配置通常位于 /usr/local/etc/nginx/,但日志路径和包含文件由实际配置决定,应通过配置检查确认,而不是猜测固定路径。
sudo nginx -t
sudo nginx -T 2>&1 | grep -E 'listen|server_name|proxy_pass|access_log|error_log'
完整配置可能包含内部地址或敏感参数,不应直接复制到公开渠道。
如果代理配置为将请求转发到 127.0.0.1:8080,应确认上游确实监听:
sockstat -4 -l | grep ':8080'
nc -vz 127.0.0.1 8080
如应用提供健康检查接口,还可进行本机访问:
curl -v http://127.0.0.1:8080/health
需要结合应用实际路径,不能假设所有程序都有 /health。
代理访问日志和错误日志能够进一步区分问题:
- 访问日志没有请求:请求可能未到代理,或命中了其他虚拟主机及日志文件。
connect() failed:上游端口未监听、地址错误或本机策略阻断。upstream timed out:上游接受连接后未及时返回,需要检查应用日志和外部依赖。- 502伴随应用重启:可能是进程退出、启动失败或套接字短暂不可用。
- 代理返回正常,但页面功能异常:继续检查应用路由、数据库连接和依赖服务,而不是调整公网端口。
按证据链解释结果,避免无依据重启
一次完整复测应同时保留外部现象、服务器监听、抓包、规则计数和应用日志。可以按以下顺序收敛范围:
- 域名没有返回预期地址:修复DNS记录或权威DNS配置,暂不调整服务器防火墙。
- 域名正确,但服务器抓不到SYN:检查上游入口、NAT、外部防火墙和路由。
- 服务器收到SYN并返回RST:检查端口监听、服务状态和绑定地址。
- 服务器收到SYN但不响应:检查PF、IPFW规则及回程路由。
- TCP握手成功但TLS失败:检查证书、SNI、时间和TLS监听配置。
- TLS成功但出现404:检查虚拟主机、Host和应用路由。
- 返回502或504:检查反向代理上游地址、应用端口、应用日志和依赖响应。
- 本机访问正常而外部失败:故障重点仍在公网入口、防火墙、NAT或地址族差异。
修改完成后,不要只在服务器本机执行一次访问。至少应从服务器外部重新验证A与AAAA记录、80与443端口、TLS证书、目标页面和关键接口,并同步观察 tcpdump、防火墙规则计数及代理日志。只有DNS结果正确、TCP握手完整、规则命中符合预期、应用返回正确状态码,这次修复才形成了可复现的证据闭环。