Linux 实战(五):DNS、hosts、CLOSE-WAIT、SSH 权限与改端口 —— 三台真机实测

这一篇讲什么

网络和 SSH 的问题有个共同点:你改的那个文件,往往不是真正生效的那个。 本篇在三台机器上逐个复现,贴原始输出。

机器 网络由谁管 OpenSSH
CentOS 7.9(VMware) network 脚本 + dhclient(这台 NetworkManager 是停着的) 7.4p1
Rocky 9.8(LXD 虚拟机) NetworkManager 9.9p1
Ubuntu 24.04.5(LXD 虚拟机) systemd-networkd + systemd-resolved(netplan 生成配置) 9.6p1

先说一个会让很多老教程直接失效的事实 ------ 这些命令三台默认都没有:

scss 复制代码
ifconfig    (没有)
netstat     (没有)
route       (没有)
arp         (没有)
ip          /usr/sbin/ip
ss          /usr/sbin/ss
dig         (没有)
nslookup    (没有)
host        (没有)
telnet      (没有)
nmap        (没有)
traceroute  (没有)
tcpdump     (没有)

(Rocky 9 的输出;CentOS 7、Ubuntu 24.04 这几行一样。)nc 只有 Ubuntu 自带,wget Rocky 9 没有。ip 和 ss 是唯一能指望的 ;dig 在 RHEL 系要装 bind-utils,Ubuntu 装 dnsutils。


1. 改了 /etc/hosts,不同命令给出不同答案 ✅

往 /etc/hosts 追加一行 1.2.3.4 www.baidu.com,然后用五种方式解析:

CentOS 7 / Rocky 9(两台一样):

ini 复制代码
== getent hosts:
2409:8c54:870:310:0:ff:b0ed:40ac www.a.shifen.com www.baidu.com
2409:8c54:870:187:0:ff:b0d9:bb1c www.a.shifen.com www.baidu.com
== getent ahosts:
1.2.3.4         STREAM www.baidu.com
== dig +short:
111.45.11.5
183.240.99.224
== ping:
PING www.baidu.com (1.2.3.4) 56(84) bytes of data.

Ubuntu 24.04:

ini 复制代码
== getent hosts:
1.2.3.4         www.baidu.com
== getent ahosts:
1.2.3.4         STREAM www.baidu.com
== dig +short:
1.2.3.4
== ping:
PING www.baidu.com (1.2.3.4) 56(84) bytes of data.

两个反直觉的地方:

① 「用 getent hosts 验证 hosts 有没有生效」在 CentOS 7 / Rocky 9 上会骗你。 getent hosts 优先查 IPv6 地址,hosts 里写的是 IPv4,于是它去 DNS 拿了百度的 IPv6 回来。ping 实际用的就是 1.2.3.4。要验证,用 getent ahosts,三台都给出 1.2.3.4。

② 「dig 不读 /etc/hosts」在 Ubuntu 上不成立。 Ubuntu 的 /etc/resolv.conf 指向本机的 systemd-resolved(127.0.0.53),dig 问的是它,而 resolved 自己会读 /etc/hosts。CentOS 7 / Rocky 9 的 dig 直接问上游 DNS,确实不读 hosts。


2. 手改 /etc/resolv.conf,过一会儿就没了 ✅

Rocky 9:文件头写着是谁生成的。

ruby 复制代码
# Generated by NetworkManager
手改后:nameserver 9.9.9.9
nmcli con up 之后:nameserver 10.115.180.1 nameserver fd42:7e9b:f1d3:c525::1 nameserver fe80::216:3eff:feca:e4d9%enp5s0

连接一重新激活(重启网络、重启机器都会),手改的内容就被覆盖。要改 DNS,改连接配置 :nmcli con mod <连接名> ipv4.dns ...。

Ubuntu 24.04 :/etc/resolv.conf 根本不是一个普通文件。

bash 复制代码
lrwxrwxrwx 1 root root 39 Sep 20 07:46 /etc/resolv.conf -> ../run/systemd/resolve/stub-resolv.conf
nameserver 127.0.0.53

手改写进的是 /run 下那份文件,重启 resolved 就回去了:

ruby 复制代码
手改后:nameserver 9.9.9.9
restart systemd-resolved 之后:nameserver 127.0.0.53

真实的上游 DNS 要用 resolvectl status 看:

arduino 复制代码
Current DNS Server: 10.115.180.1

CentOS 7 (这台用 dhclient):文件头是 ; generated by /usr/sbin/dhclient-script,DHCP 续租时同样会被重写。

🔑 动 resolv.conf 之前先 ls -l 看它是不是软链、head -1 看是谁生成的。


3. 用 /dev/tcp 测端口 ------ 但别在 sh 里用 ✅

没有 telnet、nc 的机器,bash 自带的 /dev/tcp 能顶上:

shell 复制代码
# timeout 3 bash -c 'echo > /dev/tcp/127.0.0.1/22' && echo "22 端口 OK" || echo "22 端口 FAIL"
22 端口 OK
9 端口 FAIL
bash: connect: Connection refused

三台都行。但它是 bash 的功能,不是系统的 。脚本开头写 #!/bin/sh 时:

bash 复制代码
lrwxrwxrwx. 1 root root 4 Apr 30  2024 /bin/sh -> bash          (Rocky 9)
sh 下 /dev/tcp 可用

lrwxrwxrwx 1 root root 4 Mar 31  2024 /bin/sh -> dash           (Ubuntu 24.04)
sh: 1: cannot create /dev/tcp/127.0.0.1/22: Directory nonexistent

CentOS 7 的 /bin/sh 也是 bash,能用。同一个脚本,搬到 Ubuntu 就坏,报错还是「目录不存在」,很难联想到原因。

顺带,两种 nc 的输出完全不一样,写脚本判断输出文字的要注意:

vbnet 复制代码
Ncat: 0 bytes sent, 0 bytes received in 0.01 seconds.          (CentOS 7 / Rocky 9 装的 nmap-ncat,端口通)
Ncat: Connection refused.
Connection to 127.0.0.1 22 port [tcp/ssh] succeeded!           (Ubuntu 自带的 openbsd nc,端口通)
nc: connect to 127.0.0.1 port 9 (tcp) failed: Connection refused

4. CLOSE-WAIT 堆积 ------ 复现一次就知道该找谁 ✅

写一个 python 服务端:accept() 之后永远不 close(模拟连接池泄漏)。客户端连 5 次,每次连上就关:

ini 复制代码
== 服务端(9100)一侧:
      5 CLOSE-WAIT
      1 LISTEN
== 客户端一侧:
      5 FIN-WAIT-2

三台一样。CLOSE-WAIT 在哪一侧,就是哪一侧的程序没关连接 ------ 对方已经关了(发了 FIN),自己这边一直没调 close()。调内核参数没用,这是程序的 bug。

css 复制代码
# ss -tan state close-wait | head -3
Recv-Q Send-Q Local Address:Port Peer Address:Port
1      0          127.0.0.1:9100    127.0.0.1:44834
1      0          127.0.0.1:9100    127.0.0.1:44842

Recv-Q 是 1:对方的 FIN 到了,程序没读。在 Rocky 9 / Ubuntu 24.04 上杀掉服务进程后:

objectivec 复制代码
杀掉服务进程后 CLOSE-WAIT 剩:0

5. 连一个没人监听的端口,抓包看到的是 RST ✅

bash 复制代码
IP 127.0.0.1.51416 > 127.0.0.1.9999: Flags [S], seq 1006525363, win 65495, ...
IP 127.0.0.1.9999 > 127.0.0.1.51416: Flags [R.], seq 0, ack 1006525364, win 0, length 0

(Rocky 9,三台一样。)SYN 过去,RST 回来,客户端报 Connection refused。报 Connection refused = 包到了对方机器、但那个端口没人监听 ;如果是防火墙丢包,看到的是超时,或者防火墙主动回拒绝(下面第 12 节的 No route to host 就是 firewalld 的拒绝)。


6. MTU:1472 能过,1473 不行 ✅

ini 复制代码
# ping -c1 -M do -s 1472 <网关>
1480 bytes from 10.115.180.1: icmp_seq=1 ttl=64 time=0.140 ms
# ping -c1 -M do -s 1473 <网关>
ping: local error: message too long, mtu=1500

1472 + 8(ICMP 头)+ 20(IP 头)= 1500。-M do 禁止分片,用来探测路径 MTU。CentOS 7 的报错是大写开头的 Message too long,Rocky 9 / Ubuntu 24.04 是小写 ------ 用 grep 判断时加 -i。


7. sudo 之后代理没了 ✅

复制代码
当前 shell:1
sudo 之后:0
sudo -E 之后:1

(Rocky 9 / Ubuntu 24.04 一样;数字是 env 里 http_proxy 的行数。)sudoers 默认 Defaults env_reset,sudo 会清掉环境变量,于是 sudo yum install、sudo curl 走不了你 export 的代理。用 sudo -E,或者在 sudoers 里 env_keep += "http_proxy https_proxy"。


8. Rocky 9:nmcli 改完不生效,还要 up 一次 ✅

rust 复制代码
== mod 之后、up 之前:
lab0             UNKNOWN        10.99.0.1/24 fe80::74e0:5aff:febb:7296/64
10.99.0.2/24
== up 之后:
lab0             UNKNOWN        10.99.0.2/24 fe80::74e0:5aff:febb:7296/64

nmcli con mod 只改配置(nmcli -g 已经显示 .2),网卡上还是 .1,nmcli con up 之后才生效。新建的连接存成 keyfile 格式:

复制代码
lab0.nmconnection

(在 /etc/NetworkManager/system-connections/ 下。)

「RHEL 9 不再读 ifcfg 文件」这个说法过头了。 Rocky 9 cloud 镜像自己的网卡配置就是 ifcfg-enp5s0;手写一个 ifcfg-lab1 再 nmcli con reload,NetworkManager 照样认:

javascript 复制代码
lab1::/etc/sysconfig/network-scripts/ifcfg-lab1

准确的说法是:新建的连接默认存成 keyfile,旧的 ifcfg 仍然能读。


9. Ubuntu:netplan 的三个坑 ✅

① 权限太宽只给警告(退出码仍是 0):

kotlin 复制代码
** (generate:1421): WARNING **: 17:37:10.219: Permissions for /etc/netplan/90-lab.yaml are too open. Netplan configuration should NOT be accessible by others.

② 缩进用了 Tab 直接失败:

bash 复制代码
有 Tab 时 generate 退出码=1
/etc/netplan/90-lab.yaml:5:1: Invalid YAML: tabs are not allowed for indent:

③ 网上教程里的 gateway4 已经废弃(还能用,给警告):

go 复制代码
** (generate:1583): WARNING **: 17:37:13.058: `gateway4` has been deprecated, use default routes instead.

netplan try 回滚的只是运行状态,不回滚文件。 把地址改成 10.99.0.9 后 netplan try --timeout 8,不按回车:

php 复制代码
Press ENTER before the timeout to accept the new configuration
Reverting.
== try 结束后:
lab0             UNKNOWN        fe80::ccc0:aeff:fedb:490d/64
      addresses: [10.99.0.9/24]

网卡上已经没有 10.99.0.9,但 yaml 文件里还是它。下一次 netplan apply 或重启,这个你以为已经回滚了的配置就会生效。 远程改网络时这一点最要命:try 救了你一次,重启机器又断了。


10. SSH 默认配置,三台不一样 ✅

yaml 复制代码
OpenSSH_7.4p1, OpenSSL 1.0.2k-fips  26 Jan 2017                 (CentOS 7)
permitrootlogin yes
passwordauthentication yes
usedns yes

OpenSSH_9.9p1, OpenSSL 3.5.8 25 Aug 2026                        (Rocky 9)
permitrootlogin without-password
passwordauthentication yes
usedns no

(sshd -T 输出节选。Ubuntu 24.04 的 OpenSSH_9.6p1 三项与 Rocky 9 相同。)

  • root 能不能用密码登录 :CentOS 7 能;Rocky 9 / Ubuntu 24.04 默认 without-password,只能用密钥。
  • usedns yes:CentOS 7 默认开着,这是「SSH 登录要卡好几秒」的常见原因(服务端反查客户端 IP 的主机名)。
  • 配置文件 :Rocky 9 / Ubuntu 24.04 的 sshd_config 里有 Include /etc/ssh/sshd_config.d/*.conf,而且这行在文件开头附近 ------ sshd 对同一选项取第一次出现的值 ,所以 sshd_config.d/ 里的设置会压过主文件后面写的。CentOS 7 没有这个目录。
  • 日志 :CentOS 7 / Rocky 9 在 /var/log/secure,Ubuntu 在 /var/log/auth.log。

改完配置先检查语法,别直接重启:

arduino 复制代码
# echo 'PermitRootLogin maybe' > /etc/ssh/sshd_config.d/99-lab.conf; sshd -t
/etc/ssh/sshd_config.d/99-lab.conf line 1: unsupported option "maybe".
sshd -t 退出码=255

11. 密钥登录 Permission denied ------ 权限到底要多严 ✅

同一把密钥,只改权限,在 Rocky 9 和 Ubuntu 24.04 上逐项试:

改动 Rocky 9 Ubuntu 24.04
家目录 777 ❌ 拒绝 ❌ 拒绝
家目录 775(组可写) ❌ 拒绝 ✅ 能登录
家目录 755 + .ssh 755 ✅ 能登录 ✅ 能登录
.ssh 777 ❌ 拒绝 ❌ 拒绝
authorized_keys 644 ✅ 能登录 ✅ 能登录
authorized_keys 666 ❌ 拒绝 ❌ 拒绝
authorized_keys 属主改成 root ❌ ❌ 拒绝

结论:规则是「不能让别人写」,不是「必须 700 / 600」。 .ssh 755、authorized_keys 644 都能登录;真正致命的是组或其他人可写。Ubuntu 对「家目录组可写」放行(推测是 Debian 系对「每个用户一个私有组」做的补丁,未查证),Rocky 9 不放行。

客户端只会告诉你:

sql 复制代码
labu@127.0.0.1: Permission denied (publickey,gssapi-keyex,gssapi-with-mic,password).

原因只在服务端日志里:

bash 复制代码
== /var/log/secure:
      2 Authentication refused: bad ownership or modes for directory /home/labu
      1 Authentication refused: bad ownership or modes for directory /home/labu/.ssh
      1 Authentication refused: bad ownership or modes for file /home/labu/.ssh/authorized_keys

直接告诉你是哪一层目录的问题。排查密钥登录,第一步就是去服务端看这个日志,不是在客户端反复试。

🆕 Rocky 9 上「属主改成 root」那一次,客户端看到的不是 Permission denied,而是:

csharp 复制代码
[authorized_keys 属主改成 root] kex_exchange_identification: read: Connection reset by peer

服务端日志:

arduino 复制代码
drop connection #0 from [127.0.0.1]:54934 on [127.0.0.1]:22 penalty: failed authentication
persourcepenalties crash:90 authfail:5 noauth:1 grace-exceeded:10 refuseconnection:10 max:600 min:15 max-sources4:65536 max-sources6:65536 overflow:permissive overflow6:permissive

这是 OpenSSH 9.8 起新增的 PerSourcePenalties :同一个来源 IP 认证失败累积到一定程度,sshd 会直接断开 它一段时间。测试时连续失败了好几次,于是被「罚」了。排查时反复重试,可能把自己的 IP 罚进去 ,看到 Connection reset 别以为是网络问题。Ubuntu 24.04 的 9.6 没有这个机制。


12. Rocky 9 改 SSH 端口:重启成功,端口却没开 ✅

在 sshd_config.d/ 里写 Port 22 + Port 2222:

ini 复制代码
sshd -t 通过
restart 退出码=0 is-active=active
0.0.0.0:22
[::]:22

语法检查通过、重启成功、服务 active ------ 但只监听了 22。 日志:

vbnet 复制代码
error: Bind to port 2222 on 0.0.0.0 failed: Permission denied.
error: Bind to port 2222 on :: failed: Permission denied.

是 SELinux:sshd 只被允许绑定标记为 ssh_port_t 的端口。

markdown 复制代码
# semanage port -a -t ssh_port_t -p tcp 2222; semanage port -l | grep '^ssh_port_t'
ssh_port_t                     tcp      2222, 22
0.0.0.0:2222
0.0.0.0:22
[::]:2222
[::]:22

⚠️ 如果只写 Port 2222、把 22 去掉,结果是这样的(换成 2224 测试,没加 SELinux 标记):

vbnet 复制代码
sshd -t 通过
只留 Port 2224 时 restart 退出码=1 is-active=activating
0
Job for sshd.service failed because the control process exited with error code.

一个端口都没监听。 已连着的会话没断,所以你不会马上发现 ------ 退出后就再也连不上了。改端口一定先加新端口、保留 22,确认新端口能登录再删 22。 semanage 在 Rocky 9 cloud 镜像里默认没有,要装 policycoreutils-python-utils。

端口开了,外面还是连不上,那是 firewalld:

yaml 复制代码
nc: connect to 10.115.180.175 port 2222 (tcp) failed: No route to host

firewall-cmd --add-port=2222/tcp 之后:

css 复制代码
Connection to 10.115.180.175 2222 port [tcp/*] succeeded!

注意报错是 No route to host ,不是 Connection refused,很容易被误导去查路由。


13. Ubuntu 24.04 改 SSH 端口:restart ssh 不管用 ✅

同样写 Port 22 + Port 2222:

ini 复制代码
sshd -t 通过
restart ssh 退出码=0
0.0.0.0:22 users:(("sshd",pid=1112,fd=3),("systemd",pid=1,fd=69))
[::]:22 users:(("sshd",pid=1112,fd=4),("systemd",pid=1,fd=70))

注意监听者里有 systemd:Ubuntu 24.04 的 SSH 是 socket 激活的 ,端口由 ssh.socket 占着,sshd 配置里的 Port 不直接决定监听哪个端口。

复制代码
sshd         active
ssh          active
ssh.socket   active

要这样才生效:

ini 复制代码
# systemctl daemon-reload; systemctl restart ssh.socket
0.0.0.0:2222 users:(("sshd",pid=1177,fd=3),("systemd",pid=1,fd=60))
0.0.0.0:22 users:(("sshd",pid=1177,fd=5),("systemd",pid=1,fd=62))
[::]:2222 users:(("sshd",pid=1177,fd=4),("systemd",pid=1,fd=61))
[::]:22 users:(("sshd",pid=1177,fd=6),("systemd",pid=1,fd=63))

daemon-reload 这一步是关键:有个生成器会读 sshd_config 里的 Port,生成 socket 的监听配置:

ini 复制代码
# /run/systemd/generator/ssh.socket.d/addresses.conf
ListenStream=
ListenStream=0.0.0.0:2222

网上大量「改 /etc/ssh/sshd_config 的 Port,然后 systemctl restart sshd」的教程,在 Ubuntu 24.04 上执行不报错、端口不变。


14. 服务器重装后连不上:REMOTE HOST IDENTIFICATION HAS CHANGED ✅

在 Rocky 9 上重新生成主机密钥(相当于重装系统),从 Ubuntu 再连:

python 复制代码
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@    WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED!     @
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
IT IS POSSIBLE THAT SOMEONE IS DOING SOMETHING NASTY!
Offending ED25519 key in /root/.ssh/known_hosts:1
退出码=255

(节选。)确认是自己重装的,删掉旧记录:

bash 复制代码
# ssh-keygen -R 10.115.180.175
# Host 10.115.180.175 found: line 1
/root/.ssh/known_hosts updated.
Original contents retained as /root/.ssh/known_hosts.old

两个细节:

  • 别想着用 grep IP known_hosts 找那一行 ------ Ubuntu 默认把主机名哈希了,文件里是 |1|77ChuBKUWhZ2GkMu3fKEWd4gNbg=|... 这样的内容,grep 不到。用 ssh-keygen -R。
  • 非 22 端口的记录名字是 [IP]:端口,要单独删:ssh-keygen -R '[10.115.180.175]:2222'。

15. 只监听 127.0.0.1 的服务,用 SSH 隧道访问 ✅

Rocky 9 上一个 HTTP 服务只监听本机:

makefile 复制代码
127.0.0.1:8080

从外面连:

yaml 复制代码
nc: connect to 10.115.180.175 port 8080 (tcp) failed: No route to host

本地转发:

arduino 复制代码
# ssh -fNL 18080:127.0.0.1:8080 labu@10.115.180.175
ssh -fNL 退出码=0
经隧道访问 127.0.0.1:18080 → 200

-f 放后台,-N 不执行远程命令。用完记得关,它不会自己退出:

ini 复制代码
1089 ssh -fNL 18080:127.0.0.1:8080 -i /root/.ssh/lab_ed -o BatchMode=yes labu@10.115.180.175

小结

现象 真正的原因
hosts 改了,getent hosts 不认 它优先查 IPv6;用 getent ahosts
Ubuntu 上 dig 认 hosts dig 问的是 resolved,resolved 读 hosts
resolv.conf 手改被冲掉 NetworkManager / resolved / dhclient 在管它
#!/bin/sh 脚本里 /dev/tcp 报目录不存在 Ubuntu 的 sh 是 dash
CLOSE-WAIT 堆积 这一侧的程序没 close
netplan try 回滚了,重启又断 文件没回滚
密钥登录 Permission denied 看服务端日志 Authentication refused
反复试后变成 Connection reset OpenSSH 9.8+ 的 PerSourcePenalties
Rocky 改端口没生效 SELinux,semanage port -a -t ssh_port_t
Ubuntu 24.04 改端口没生效 daemon-reload + restart ssh.socket

下一篇:日志与性能 ------ journal 重启就没了、OOM 杀进程、logrotate 的两种模式。

相关推荐
Linux-lucky1 小时前
43-Linux学习之旅之Shell 脚本(二)
linux·运维·学习·ubuntu·dash
yv798an1 小时前
从 OpenSBI 引导到 C++17 异步运行时:自制 RISC-V 嵌入式专有 NAS 踩坑全记录
linux
不悔哥2 小时前
Linux系统编程多线程程序如何安全退出
linux
Fcy6482 小时前
Linux下 数据链路层详解(从以太网到ARP协议)
linux·mac·以太网·数据链路层·arp
‎ദ്ദിᵔ.˛.ᵔ₎4 小时前
linux 进程信号(下)
linux·运维·服务器
skywalk81634 小时前
在FreeBSD系统的linux仿真环境下安装和使用kiro 这个亚马逊的AI agent
linux·人工智能·freebsd·kiro
Mikko74 小时前
Linux 磁盘满了但 du 找不到?LVM 扩容 df 不变?fstab 写错开不了机?三台真机实测磁盘排查
linux·运维·centos
Madison-No74 小时前
多语言聊天大模型--测试报告
linux·git·python·selenium·jmeter·自动化·postman
Liuqy-054 小时前
特殊进程——孤儿、僵尸、守护进程
linux·运维·服务器