网络配置与远程管理

本文合并自:03-网络配置与远程管理.md、1-7-Linux网络配置与故障排查.md、03-AB压测与NetworkManager.md

适用环境:RHEL 5 / RHEL 6 / CentOS 6 与 RHEL 7 / CentOS 7 及同系发行版。文中凡 CentOS 6 与 CentOS 7 命令、配置、网卡命名不同之处,均并列给出并标注适用版本,旧写法一律保留(生产环境仍有大量 CentOS 6 存量机器)。

版本适用范围提示:CentOS 7 已于 2024-06-30 结束生命周期(EOL),不再获得官方更新。本文中的 systemctl restart network 与 /etc/sysconfig/network-scripts/ 配置方式在 RHEL 8+ / RHEL 9 中已被移除或弃用,新系统应改用 NetworkManager(nmcli / nmtui)管理网络,具体用法见「六、NetworkManager 与 nmcli」;mii-tool(net-tools 包)同样已弃用,建议改用 ethtool <网卡> 查看链路状态。

内容覆盖:TCP/IP 基础回顾、一致性网络设备命名与传统 eth0 命名、ip 命令族与 ifconfig/route 传统写法、临时与永久 IP 配置、ifcfg-* 全字段详解、NetworkManager 与 nmcli/nmtui、主机名与名称解析、多 IP 别名/bonding/team/bridge/VLAN/静态路由/MTU 等进阶配置、SSH 远程管理与安全加固、密钥认证与端口转发、scp/sftp/rsync/nc 文件传输、分层网络故障排查与抓包分析、ab 压力测试、练习与速查表。


一、TCP/IP 基础与通信前提

本节为网络基础理论的必要回顾,属于旁支内容,原文保留。 详见 阶段07 07-01-网络基础与网络配置.md(IP 地址、子网划分、路由与网关的完整讲解)

1.1 IP 地址分类(IPv4)

IPv4 地址是一个 32 位的二进制数字,由 网络地址 和 主机地址 两部分组成。

类别 网络位 / 主机位 首字节范围 完整地址范围 默认子网掩码 用途
A 类 前 8 位 / 后 24 位 1 - 126 1.0.0.0 ~ 126.255.255.255 255.0.0.0 大型网络
B 类 前 16 位 / 后 16 位 128 - 191 128.0.0.0 ~ 191.255.255.255 255.255.0.0 中型网络
C 类 前 24 位 / 后 8 位 192 - 223 192.0.0.0 ~ 223.255.255.255 255.255.255.0 小型网络
D 类 无网络/主机之分 224 - 239 224.0.0.0 ~ 239.255.255.255 - 组播(Multicast)
E 类 无网络/主机之分 240 - 255 240.0.0.0 ~ 255.255.255.255 - 实验/军用预留

127.0.0.0/8 虽处于 A 类地址空间内,但被保留为本地环回,不能作为主机地址分配,因此 A 类的可用首字节范围是 1 - 126。

1.2 特殊 IP 地址

地址 含义
0.0.0.0 本机或所有地址(监听时表示所有接口)
255.255.255.255 全网广播地址
127.0.0.0/8 本地环回地址,用于测试本机 TCP/IP 协议栈
10.0.0.0/8 A 类私网地址
172.16.0.0/12 B 类私网地址
192.168.0.0/16 C 类私网地址

注意:某些集群软件(如 LVS 的 DR 模式)也会使用环回地址。

1.3 网络通信类型

类型 说明 典型应用
单播(点对点) 一对一通信 HTTP、邮件、DNS 查询
组播(点对多点) 向一组节点发送数据 视频会议、集群心跳
广播(点对多点) 向同一广播域所有节点发送 DHCP、ARP

1.4 子网掩码与网络号

子网掩码用于将 IP 地址划分为 网络部分 和 主机部分。两者进行按位与运算,可得到网络号。

示例: 192.168.0.183/24

IP:   11000000.10101000.00000000.10110111  (192.168.0.183)
掩码: 11111111.11111111.11111111.00000000  (255.255.255.0)
与:   11000000.10101000.00000000.00000000  (192.168.0.0)
  • 网络号:主机位全为 0,如 10.1.1.0/24。
  • 广播地址:主机位全为 1,如 10.1.1.255/24。
  • 可用 IP 数量:主机位中除网络号和广播地址外,其余均为可用地址。

网络号相同的节点在默认配置下可直接通信。

1.5 两个节点通信的必要条件

  1. 物理连通:线缆、交换机、网卡等硬件正常。
  2. 路由正确:本机知道到达目的地址的路径。
  3. 通信权限:防火墙、SELinux 等未阻止流量。

1.6 上互联网三要素

要素 配置文件 / 命令
IP 地址 /etc/sysconfig/network-scripts/ifcfg-<网卡>
网关(Gateway) 路由表 / ip r / route -n
DNS /etc/resolv.conf(或 ifcfg 中的 DNS1/DNS2)

二、网卡与接口命名

2.1 一致性网络设备命名(CentOS 7 / RHEL 7 及以上)

在 RedHat 7 之前,网络接口名称一般为 eth0、eth1 等。这种命名由内核探测顺序决定,随着设备增加和删除,名称可能发生漂移,导致不对称信息(本次开机的 eth0 下次可能变成 eth1)。RedHat 7 起启用 一致性网络设备命名(Consistent Network Device Naming),根据设备类型与固件/拓扑信息分配固定名称,例如 ens33:

en          s                  33
类型        适配器类型          接口编号

接口类型前缀:

前缀 类型
en 以太网接口(Ethernet)
wl WLAN 接口(无线局域网,原文写作 w1)
ww WWAN 接口(无线广域网,如 4G 模块)

适配器类型(第二个字母):

字符 类型
o 板载设备(onboard),编号取自固件提供的索引
s 可热拔插插槽设备(hotplug slot)
p PCI 接口,按总线/插槽位置命名

常见命名实例对照:

名称 含义
eno1 板载第 1 块以太网卡(物理服务器最常见)
ens33 位于热插拔插槽 33 的以太网卡(VMware 虚拟机最常见)
enp0s3 PCI 总线 0、插槽 3 上的以太网卡(VirtualBox/KVM 常见)
enp0s25 PCI 总线 0、插槽 25 上的以太网卡
ens192 / ens160 VMware 使用 VMXNET3 网卡时的常见名称
eth0 传统内核顺序命名(CentOS 6 默认,CentOS 7 关闭新命名规则后也会出现)
lo 本地环回接口
virbr0 libvirt 创建的虚拟桥接口

命名规则的优先级顺序(依次尝试,成功即采用):

  1. 固件/BIOS 提供的板载设备索引 → eno1
  2. 固件/BIOS 提供的 PCI Express 热插拔槽索引 → ens33
  3. 硬件接口的物理位置 → enp0s3
  4. 接口的 MAC 地址 → enx001c2900bcde(默认不启用)
  5. 以上都不可用时回退传统内核命名 → eth0

2.2 改回传统 eth0 命名(CentOS 7)

大量老脚本、监控模板、教学环境依赖 eth0,可通过内核参数关闭一致性命名。

方法一:修改 GRUB2 默认内核参数(推荐,永久生效)

sudo vim /etc/default/grub

在 GRUB_CMDLINE_LINUX 一行中追加两个参数:

GRUB_CMDLINE_LINUX="rd.lvm.lv=centos/root rd.lvm.lv=centos/swap rhgb quiet net.ifnames=0 biosdevname=0"

重新生成 GRUB 配置并重启:

# BIOS 引导(传统分区)
sudo grub2-mkconfig -o /boot/grub2/grub.cfg

# UEFI 引导
sudo grub2-mkconfig -o /boot/efi/EFI/centos/grub.cfg

sudo reboot

方法二:安装系统时在启动菜单按 Tab(或 e)临时追加 net.ifnames=0 biosdevname=0,装完即为 eth0。

方法三:通过 udev 规则手工固定名称(按 MAC 绑定,适合多网卡顺序混乱的场景)

sudo vim /etc/udev/rules.d/70-persistent-net.rules
SUBSYSTEM=="net", ACTION=="add", DRIVERS=="?*", ATTR{address}=="00:0c:29:75:88:e4", NAME="eth0"
SUBSYSTEM=="net", ACTION=="add", DRIVERS=="?*", ATTR{address}=="00:0c:29:75:88:ee", NAME="eth1"
  • CentOS 6:/etc/udev/rules.d/70-persistent-net.rules 由系统自动生成。克隆虚拟机后该文件里保留着源机器的 MAC,会出现「网卡变成 eth1、eth0 找不到」的经典问题。处理方法是删除该文件(或删掉旧条目并把新条目的 NAME 改为 eth0),同时同步 ifcfg-eth0 中的 HWADDR,然后重启。
  • CentOS 7:该文件默认不存在,改名后必须同步修改 /etc/sysconfig/network-scripts/ifcfg-* 中的 DEVICE/NAME 以及文件名本身,否则网卡起不来。
  • 参数 net.ifnames=0 关闭 systemd/udev 的一致性命名,biosdevname=0 关闭 Dell 的 biosdevname 命名(会产生 em1、p1p1 这类名字)。两者要一起写。

2.3 查看有哪些网卡(多种手段)

# 查看所有接口及 IP(CentOS 7 首选,CentOS 6 也可用)
ip addr
ip a
ip a s                       # show 的缩写
ip -br addr                  # 单行简表输出(CentOS 7.4+),一屏看完所有接口状态
ip link                      # 只看链路层信息(含 MAC、MTU、state)
ip -s link                   # 附带收发包统计

# 传统命令(CentOS 6 首选;CentOS 7 需 yum install net-tools)
ifconfig                     # 只显示已启用(UP)的网卡
ifconfig -a                  # 显示全部网卡,含 DOWN 状态的
ifconfig eth0                # 查看指定网卡

# NetworkManager 视角(CentOS 7)
nmcli device
nmcli device status
nmcli device show ens33      # 查看某设备的详细信息(速率、MAC、当前 IP、DNS)
nmcli -f DEVICE,TYPE,STATE device status

# 硬件层面确认网卡是否被识别、驱动是否加载
lspci | grep -i ethernet
lspci | grep -i net
lspci -nnk | grep -A 3 -i ethernet     # 顺带看驱动(Kernel driver in use)
lshw -C network                        # 需安装 lshw,信息最全
ethtool -i eth0                        # 查看某网卡使用的驱动与固件版本
dmesg | grep -i eth                    # 内核启动时的网卡识别日志

# 只列出接口名
ls /sys/class/net/
cat /proc/net/dev

排查思路:lspci 能看到网卡但 ip a 没有对应接口 → 驱动未加载(缺少内核模块);ip a 有接口但没有 IP → 配置问题;两者都没有 → 硬件或虚拟机未添加网卡。

2.4 网卡的启用与停用(up / down)

# CentOS 7 推荐:ip 命令
sudo ip link set enp0s25 up
sudo ip link set enp0s25 down
sudo ip l set dev enp0s25 up          # 等价写法
sudo ip l set dev enp0s25 down

# 传统写法(CentOS 6 / CentOS 7 装了 net-tools 均可)
ifconfig eth1 up                      # 启用网卡
ifconfig eth1 down                    # 禁用网卡

# 使用 initscripts 脚本(会读取 ifcfg 文件,连带套用 IP 配置)
ifup eth0
ifdown eth0

# NetworkManager 方式(CentOS 7)
nmcli device connect ens33
nmcli device disconnect ens33
nmcli connection up ens33
nmcli connection down ens33

三者的区别必须分清:

  • ip link set up 只是把链路层拉起来,不会套用任何 IP 配置。
  • ifup/ifdown 会读取 ifcfg-* 文件,套用其中的 IP、网关、DNS。
  • nmcli con up 由 NetworkManager 激活某个「连接配置」,同样会套用完整配置。

2.5 链路状态检查:mii-tool

mii-tool eth0                 # CentOS 6 写法
sudo mii-tool enp0s25         # CentOS 7 写法(需 net-tools)

常见输出:

输出 含义
link ok 网卡已连接,链路正常
no link 网卡已启用但网线未连接
Invalid argument 物理连接存在但网卡未启用(down)
其他报错 可能网卡未驱动或硬件问题

配合启用/禁用网卡验证:

ifconfig eth1 up     # 启用网卡
ifconfig eth1 down   # 禁用网卡
  • link ok 表示网卡物理连接正常,但不代表可以正常访问外网。
  • mii-tool 属于已弃用的 net-tools 包,且对新型网卡(万兆、VMXNET3)常报 SIOCGMIIPHY on 'ens33' failed: Operation not supported,此时改用 ethtool。

2.6 速率与双工:ethtool

ethtool eth0         # 查看网卡工作模式

输出关键字段:

  • Speed: 100Mb/s:速率
  • Duplex: Full:全双工
  • Auto-negotiation: on:自动协商
  • Link detected: yes:链路已连接(等价于 mii-tool 的 link ok)

临时修改速率/双工:

# 改为 10M、半双工、关闭自动协商
ethtool -s eth0 speed 10 duplex half autoneg off

# 改回 100M、全双工、开启自动协商
ethtool -s eth0 speed 100 duplex full autoneg on

其他常用子命令:

ethtool -i eth0            # 驱动与固件版本
ethtool -S eth0            # 网卡级别的收发/错误统计(丢包排查关键)
ethtool -k eth0            # 查看网卡卸载特性(TSO/GSO/GRO/LRO)
ethtool -K eth0 tso off    # 关闭 TCP 分片卸载(排查诡异丢包时用)
ethtool -g eth0            # 查看/设置环形缓冲区大小
ethtool -p eth0 10         # 让网口指示灯闪 10 秒,用于机房定位物理网口
  • 可通过 scp 传输大文件对比不同速率下的实际传输速度,效果明显。
  • ethtool -s 的修改重启后失效;需要永久生效可写入 ifcfg-eth0 的 ETHTOOL_OPTS="speed 100 duplex full autoneg on"。

三、查看与临时配置网络

3.1 ip 命令族(CentOS 7 首选,CentOS 6 亦可用)

ip 命令来自 iproute2 包,是 ifconfig/route/arp/netstat 的统一替代者,语法为 ip [选项] 对象 命令 参数。

常用对象:

对象 缩写 作用 替代的旧命令
address a、addr 管理接口地址 ifconfig
link l 管理网络设备(链路层) ifconfig、mii-tool
route r 管理路由表 route
neighbour n、neigh 管理 ARP/邻居表 arp
maddress m 组播地址 ipmaddr
rule — 策略路由规则 —
netns — 网络命名空间 —
tunnel — 隧道 iptunnel

ip addr:地址管理

# 查看
ip addr                                   # 全部接口
ip addr show ens33                        # 指定接口
ip a s ens33
ip -4 addr                                # 只看 IPv4
ip -6 addr                                # 只看 IPv6
ip -br -c addr                            # 简表 + 彩色输出

# 添加临时 IP(立即生效,重启失效)
sudo ip addr add 192.168.0.100/24 dev enp0s25
sudo ip a add 192.168.0.100/24 dev enp0s25

# 添加时同时指定广播地址与标签(标签即传统的别名 eth0:0)
sudo ip addr add 192.168.0.101/24 broadcast 192.168.0.255 dev eth0 label eth0:0

# 删除 IP
sudo ip addr del 192.168.0.100/24 dev enp0s25
sudo ip a del 192.168.0.100/24 dev enp0s25

# 清空某接口上的所有地址
sudo ip addr flush dev enp0s25

# 动态获取 IP(DHCP 客户端,临时)
sudo dhclient enp0s25
sudo dhclient -r enp0s25                  # 释放租约
sudo dhclient -v enp0s25                  # 显示交互过程,排查 DHCP 问题

ip addr add 是「增加」而非「替换」:一个接口可以同时拥有多个 IP,第二个地址会以 secondary 标记显示。若想替换,先 del 或 flush 旧地址。

ip link:链路层管理

ip link                                   # 查看所有设备
ip link show ens33
ip -s link show ens33                     # 附收发包与错误统计

sudo ip link set ens33 up                 # 启用
sudo ip link set ens33 down               # 停用
sudo ip link set ens33 mtu 9000           # 修改 MTU(临时)
sudo ip link set ens33 address 00:0c:29:aa:bb:cc   # 修改 MAC(需先 down)
sudo ip link set ens33 name eth0          # 重命名接口(需先 down)
sudo ip link set ens33 promisc on         # 开启混杂模式(抓包用)
sudo ip link set ens33 multicast on

ip route:路由表管理

# 查看路由表
ip route
ip r
ip route show
ip route list
ip route get 8.8.8.8                      # 查询到某目标实际会走哪条路由、源 IP 是什么(排障利器)

# 默认路由(网关)
sudo ip route add default via 192.168.0.1
sudo ip r add default via 192.168.0.1 dev eth0
sudo ip route del default
sudo ip route replace default via 192.168.0.254

# 主机路由与网络路由
sudo ip route add 10.1.1.0/24 via 192.168.0.254
sudo ip route add 10.1.1.5/32 via 192.168.0.254 dev eth0
sudo ip route del 10.1.1.0/24

# 指定 metric(多条同目标路由时数值小的优先)
sudo ip route add 10.2.0.0/16 via 192.168.0.254 metric 100

# 查看指定路由表(策略路由场景)
ip route show table main
ip route show table all

ip neigh:ARP / 邻居表

ip neighbour                              # 查看 ARP 表(联系过的 IP↔MAC 映射)
ip neigh
ip n
ip neigh show dev eth0

sudo ip neigh add 192.168.0.50 lladdr 00:0c:29:11:22:33 dev eth0    # 静态绑定
sudo ip neigh del 192.168.0.50 dev eth0
sudo ip neigh flush all                   # 清空 ARP 缓存(IP 冲突/换设备后必做)
sudo ip neigh flush dev eth0

# 传统写法
arp -n                                    # 查看 ARP 表(数字形式)
arp -a
sudo arp -d 192.168.0.50                  # 删除某条 ARP 记录
sudo arp -s 192.168.0.50 00:0c:29:11:22:33  # 静态绑定

ARP 表中的状态含义:

状态 含义
REACHABLE 最近确认可达
STALE 记录尚在但需重新验证
DELAY / PROBE 正在探测
FAILED 探测失败,对方不存在或不响应
INCOMPLETE 请求已发出但未收到应答(常见于 IP 不存在)

3.2 ifconfig 与 route 的传统写法(CentOS 6 首选)

ifconfig 查看

ifconfig          # 查看所有启用网卡
ifconfig -a       # 包含未启用的网卡
ifconfig eth0     # 查看指定网卡

CentOS 6 输出字段说明:

eth0      Link encap:Ethernet  HWaddr 10:C3:7B:69:48:B1
          inet addr:10.0.0.200  Bcast:10.0.0.255  Mask:255.255.255.0
          inet6 addr: fe80::12c3:7bff:fe69:48b1/64 Scope:Link
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:... errors:0 dropped:0 ...
          TX packets:... errors:0 dropped:0 ...
字段 含义
HWaddr MAC 物理地址
inet addr IPv4 地址
Bcast 广播地址
Mask 子网掩码
UP 网卡已启用
RUNNING 网线已连接
MTU:1500 最大传输单元
RX/TX packets 收/发数据包统计(含 errors、dropped)

CentOS 7 的 ifconfig 输出格式有变化(字段名改为 inet/netmask/broadcast/ether):

ens37: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 192.168.2.73  netmask 255.255.255.0  broadcast 192.168.2.255
        inet6 fe80::7ea2:3cc1:a899:773c  prefixlen 64  scopeid 0x20<link>
        ether 00:0c:29:75:88:e4  txqueuelen 1000  (Ethernet)
        RX packets 502  bytes 54467 (53.1 KiB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 29  bytes 4664 (4.5 KiB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0
字段 含义
RUNNING 接口状态,表示可用(网线已连接)
ether 00:0c:29:75:88:e4 MAC 地址
inet 192.168.2.73 IPv4 地址
inet6 fe80::... IPv6 地址
broadcast 192.168.2.255 广播地址
mtu 1500 最大传输单元
txqueuelen 1000 发送队列长度

CentOS 7 最小化安装默认不带 ifconfig,需要 yum -y install net-tools(同时提供 netstat、route、arp、mii-tool)。

ifconfig 临时修改 IP

ifconfig eth0 10.0.0.200 netmask 255.255.255.0
# 等价的 CIDR 写法
ifconfig eth0 10.0.0.200/24

# 同时指定广播地址并启用
ifconfig eth0 10.0.0.200 netmask 255.255.255.0 broadcast 10.0.0.255 up

# 修改 MTU
ifconfig eth0 mtu 9000

使用 ifconfig 修改立即生效,但 重启网络或系统后失效。

临时修改 MAC 地址

ifconfig eth0 down
ifconfig eth0 hw ether 00:01:22:AB:EF:CD
ifconfig eth0 up

需先 down 网卡再修改,再 up。等价的 ip 写法:

sudo ip link set dev eth0 down
sudo ip link set dev eth0 address 00:01:22:AB:EF:CD
sudo ip link set dev eth0 up

route 命令管理路由

route                     # 查看路由表(会反查主机名,较慢)
route -n                  # 以数字形式显示,不反查 DNS(推荐)
netstat -rn               # 等价查看方式

# 添加默认网关
route add default gw 192.168.0.1
route add default gw 192.168.0.1 dev eth0

# 删除默认网关
route del default gw 192.168.0.1

# 添加/删除网络路由
route add -net 10.1.1.0 netmask 255.255.255.0 gw 192.168.0.254
route add -net 10.1.1.0/24 gw 192.168.0.254
route del -net 10.1.1.0/24

# 添加主机路由
route add -host 10.1.1.5 gw 192.168.0.254

# 屏蔽某网段(走黑洞)
route add -net 10.9.9.0/24 reject

route -n 输出各列含义:

Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
0.0.0.0         192.168.0.1     0.0.0.0         UG    0      0        0 eth0
192.168.0.0     0.0.0.0         255.255.255.0   U     0      0        0 eth0
列 含义
Destination 目标网络,0.0.0.0 表示默认路由
Gateway 下一跳网关,0.0.0.0 表示目标在本网段可直达
Genmask 目标网络掩码,255.255.255.255 为主机路由
Flags U 路由可用、G 需经网关、H 目标为主机、! 拒绝
Metric 路由开销,多条同目标路由时数值小的优先
Iface 出接口

排障时重点关注带有 UG 标识的默认路由行;缺少默认路由则只能访问本网段。

3.3 临时配置为何重启失效

ip/ifconfig/route/arp 这类命令都是 直接修改内核运行时状态:

  • 修改立即生效,无需重启服务,适合应急抢修与临时测试。
  • 内核状态不会写入任何磁盘文件,因此 重启网络服务或重启系统后全部丢失,恢复为配置文件中的内容。
  • 反之,只修改配置文件而不重启网络服务/不重载连接,内核状态不变,配置也不会生效。

因此完整的配置流程是:改配置文件(持久化)→ 重启网络服务或 nmcli con reload && nmcli con up(让内核生效)。抢修时可以先用 ip 命令让业务恢复,再补上配置文件。

3.4 查看系统与内核信息

uname 常用选项:

选项 作用
-a 显示全部信息
-r 内核版本
-m 硬件架构(如 x86_64)
-n 主机名
-s 操作系统名称
-v 内核编译版本
-p 处理器类型
uname -a
# Linux qianyun.cluster.com 2.6.32-279.el6.x86_64 ... x86_64 GNU/Linux

发行版版本:

cat /etc/redhat-release
# CentOS release 6.5 (Final)
# CentOS Linux release 7.9.2009 (Core)

cat /etc/os-release          # CentOS 7 起提供的标准化文件

四、永久配置:网卡配置文件

四之前的前置检查

修改前建议先关闭防火墙与 SELinux,避免配置生效后仍无法通信:

CentOS 7 默认防火墙为 firewalld:iptables -F 仅清空 iptables 规则,不会影响 firewalld 管理规则。正确做法是使用 systemctl stop firewalld 停止 firewalld 服务,或通过 firewall-cmd 放行所需端口(如 firewall-cmd --add-port=2049/tcp --permanent && firewall-cmd --reload)。

详见 阶段07 防火墙与 SELinux 相关文档(iptables / firewalld / SELinux 完整讲解)

# 临时停止 firewalld(CentOS 7 默认防火墙)
sudo systemctl stop firewalld

# CentOS 6 关闭 iptables 服务
service iptables stop
chkconfig iptables off

# 临时清空 iptables 规则(仅对原生 iptables 生效)
sudo iptables -F

# 临时关闭 SELinux
sudo setenforce 0

# 查看 SELinux 状态
getenforce

永久关闭 SELinux:

sudo vim /etc/selinux/config

修改配置项:

SELINUX=disabled

4.1 配置文件位置与命名

/etc/sysconfig/network-scripts/ifcfg-<设备名>
  • CentOS 6:/etc/sysconfig/network-scripts/ifcfg-eth0
  • CentOS 7:/etc/sysconfig/network-scripts/ifcfg-ens33(或 ifcfg-enp0s25、ifcfg-eno1)
# CentOS 6
sudo vim /etc/sysconfig/network-scripts/ifcfg-eth0

# CentOS 7
sudo vim /etc/sysconfig/network-scripts/ifcfg-ens33
sudo vim /etc/sysconfig/network-scripts/ifcfg-enp0s25

文件名中的设备名必须与文件内 DEVICE= 的值一致,否则网卡起不来。修改前先备份:cp ifcfg-eth0{,.bak}。

4.2 ifcfg-* 字段全解

参数 说明 适用版本
DEVICE 设备名,必须与文件名 ifcfg-xxx 的后缀及真实网卡名一致 6 / 7
NAME 连接名称(NetworkManager 中显示的 NAME 列),CentOS 7 常与 DEVICE 同名 7
TYPE 接口类型:Ethernet、Bridge、Bond、Team、Vlan、Wireless 6 / 7
BOOTPROTO 地址获取方式:static(静态)、none(静态,CentOS 7 惯用)、dhcp(动态)、bootp 6 / 7
ONBOOT 开机(或重启网络时)是否启用该网卡,yes/no。新装系统最常见的坑就是默认 no 导致没网 6 / 7
IPADDR 静态 IP 地址;多个地址写 IPADDR1、IPADDR2… 6 / 7
NETMASK 子网掩码,如 255.255.255.0 6 / 7
PREFIX 掩码位数写法,如 24。与 NETMASK 二选一,同时写时 PREFIX 优先 6 / 7
GATEWAY 默认网关;也可写在 /etc/sysconfig/network 中对所有网卡生效 6 / 7
DNS1 / DNS2 / DNS3 DNS 服务器,会被写入 /etc/resolv.conf 6 / 7
DOMAIN 搜索域,对应 resolv.conf 的 search 6 / 7
DEFROUTE 是否将此连接的网关作为系统默认路由(多网卡时只让一块为 yes) 7
NM_CONTROLLED 是否受 NetworkManager 控制。no 表示由 network.service 管理,服务器传统上建议 no 6 / 7
UUID 连接的唯一标识,由 NetworkManager 生成;克隆虚拟机时应删除或用 uuidgen 重新生成 7
HWADDR 绑定的网卡 MAC 地址。与实际 MAC 不符时网卡无法启动(克隆虚拟机的典型故障),可直接删掉此行 6 / 7
MACADDR 强制设置 MAC 地址(与 HWADDR 含义相反,不可同时使用) 6 / 7
USERCTL 是否允许普通用户启停该网卡,一般 no 6 / 7
PEERDNS DHCP 时是否用服务器下发的 DNS 覆盖 /etc/resolv.conf,设 no 可保留手工 DNS 6 / 7
PEERROUTES DHCP 时是否接受服务器下发的路由 7
IPV4_FAILURE_FATAL IPv4 配置失败时是否认为整个连接失败 7
IPV6INIT 是否启用 IPv6,不需要时设 no 6 / 7
IPV6_AUTOCONF IPv6 是否自动配置 7
BROADCAST 广播地址,一般可省略由系统计算 6
NETWORK 网络号,一般可省略 6
MTU 最大传输单元,如 1500、9000(巨帧) 6 / 7
ETHTOOL_OPTS 启动网卡时传给 ethtool 的参数,如 "speed 1000 duplex full autoneg on" 6 / 7
ZONE firewalld 区域,如 public、trusted 7
PROXY_METHOD / BROWSER_ONLY 代理相关,CentOS 7 默认生成,保持默认即可 7
SLAVE / MASTER bonding 从属关系(见进阶配置) 6 / 7
TEAM_MASTER / TEAM_CONFIG team 组队相关 7
BRIDGE / STP / DELAY 桥接相关 6 / 7
VLAN / PHYSDEV / VLAN_ID VLAN 子接口相关 6 / 7

4.3 静态 IP 完整配置实操

CentOS 6 写法(/etc/sysconfig/network-scripts/ifcfg-eth0):

DEVICE=eth0
TYPE=Ethernet
BOOTPROTO=static          # 或 none
ONBOOT=yes
NM_CONTROLLED="no"
IPADDR=10.0.0.200
NETMASK=255.255.255.0     # 或写 PREFIX=24
GATEWAY=10.0.0.1
DNS1=10.0.0.1
DNS2=8.8.8.8

CentOS 7 写法(/etc/sysconfig/network-scripts/ifcfg-enp0s25):

DEVICE="enp0s25"
NAME="enp0s25"
TYPE=Ethernet
BOOTPROTO=static
NM_CONTROLLED=no
ONBOOT=yes
DEFROUTE=yes
IPADDR=192.168.0.200
NETMASK=255.255.255.0
GATEWAY=192.168.0.1
DNS1=223.5.5.5
DNS2=192.168.0.1
IPV6INIT=no

CentOS 7 使用 PREFIX 与 UUID 的完整示例:

TYPE=Ethernet
BOOTPROTO=none
NAME=ens33
DEVICE=ens33
UUID=17883cd1-86c8-4ec3-b254-5ce6d462e929
ONBOOT=yes
DEFROUTE=yes
IPADDR=192.168.224.101
PREFIX=24
GATEWAY=192.168.224.2
DNS1=223.5.5.5
DNS2=119.29.29.29
IPV4_FAILURE_FATAL=no
IPV6INIT=no

配置完成后重启网络服务(见 4.6),然后验证:

ip a s ens33                   # 确认 IP 已生效
ip r                           # 确认默认路由存在
cat /etc/resolv.conf           # 确认 DNS 已写入
ping -c 2 192.168.0.1          # 网关连通性
ping -c 2 223.5.5.5            # 外网连通性
ping -c 2 www.baidu.com        # DNS 解析

4.4 DHCP 自动获取配置

CentOS 6:

DEVICE=eth0
BOOTPROTO=dhcp
ONBOOT=yes
NM_CONTROLLED="no"

CentOS 7:

TYPE=Ethernet
BOOTPROTO=dhcp
NAME=ens33
DEVICE=ens33
ONBOOT=yes
DEFROUTE=yes
PEERDNS=yes
PEERROUTES=yes

命令行临时获取:

sudo dhclient ens33            # 获取地址
sudo dhclient -r ens33         # 释放地址
sudo dhclient -v ens33         # 显示 DISCOVER/OFFER/REQUEST/ACK 交互,排查 DHCP 故障

# 查看获取到的租约信息
cat /var/lib/dhclient/dhclient-*.lease
cat /var/lib/dhclient/dhclient.leases

DHCP 方式下,若不希望服务器下发的 DNS 覆盖手工配置的 /etc/resolv.conf,设置 PEERDNS=no。

DHCP 服务端搭建详见 阶段09 09-01-DHCP服务.md

4.5 全局网络配置文件 /etc/sysconfig/network

sudo vim /etc/sysconfig/network

CentOS 6 内容示例:

NETWORKING=yes
HOSTNAME=qianyun.cluster.com
GATEWAY=10.0.0.1
NETWORKING_IPV6=no

CentOS 7 内容示例(主机名已迁移到 /etc/hostname):

NETWORKING=yes
GATEWAY=192.168.0.1
参数 说明
NETWORKING 是否启用网络功能,必须为 yes
HOSTNAME 主机名(仅 CentOS 6;CentOS 7 用 /etc/hostname)
GATEWAY 全局默认网关,对所有网卡生效;ifcfg 文件中的 GATEWAY 优先级更高
NETWORKING_IPV6 是否启用 IPv6
NOZEROCONF 设为 yes 可去掉 169.254.0.0/16 的自动路由

若 NETWORKING=no,网络服务可能不可用,且不能通过简单重启恢复。

4.6 重启网络服务(各版本命令并列)

# ---------- CentOS 6 / RHEL 6 ----------
service network restart
service network stop
service network start
service network status
/etc/init.d/network restart          # 等价写法
chkconfig network on                 # 设置开机自启

# ---------- CentOS 7 / RHEL 7(两套并存,按管理方式选择)----------
# 传统 network.service(配合 NM_CONTROLLED=no 使用)
sudo systemctl restart network
sudo systemctl status network
sudo systemctl enable network

# NetworkManager 方式(配合 nmcli 使用)
sudo systemctl restart NetworkManager
sudo nmcli connection reload         # 重新读取磁盘上的 ifcfg 文件
sudo nmcli connection up ens33       # 重新激活连接

# ---------- 只重启单块网卡(6 / 7 通用,影响面更小)----------
ifdown eth0 && ifup eth0
sudo ifdown ens33 && sudo ifup ens33

# ---------- RHEL 8+ / RHEL 9 ----------
# network.service 已被移除,只有 NetworkManager
sudo nmcli connection reload
sudo nmcli connection up ens33
sudo systemctl restart NetworkManager

远程操作警告:ifdown eth0 && ifup eth0 用 && 连接是必须的写法,但通过 SSH 执行时 ifdown 一执行会话就断了,后半句不会被执行,导致机器彻底失联。远程改网络的安全做法:

  1. 使用 nmcli con up(原子切换,不先断后连);
  2. 或 nohup bash -c 'sleep 2; systemctl restart network' & 让命令在会话断开后继续;
  3. 或事先确认有 VNC / IPMI / 云控制台等带外通道。

五、NetworkManager 与 nmcli(CentOS 7 及以上)

第四节使用的 /etc/sysconfig/network-scripts/ifcfg-* 加 systemctl restart network 是 CentOS 6/7 的传统做法,在 RHEL 8+ / RHEL 9 中 network.service(initscripts 包)已被移除,网络配置一律由 NetworkManager 负责。本节补齐这套替代方案,避免只知道旧命令、上了新系统无从下手。

NetworkManager 是监控和管理网络的服务。命令行工具(nmcli)和图形化工具(nmtui、nm-connection-editor)将网络配置信息保存在 /etc/sysconfig/network-scripts 目录中(RHEL 7/8)或 /etc/NetworkManager/system-connections/(RHEL 9 默认 keyfile 格式)。Linux 网络配置可使用命令行工具、图形化界面工具、配置文件三种方式,RHEL 7 起官方推荐使用命令行工具 nmcli 配置网络 IP 地址信息。

5.1 本节目标

  • 使用 nmcli 和 NetworkManager 管理网络设备。
  • 通过编辑配置文件修改网络设置。
  • 配置和测试系统主机名和名称解析。

5.2 连接与设备:两个必须分清的概念

概念 英文 含义 查看命令
设备 device 真实存在的网卡(如 enp0s25、ens33),由内核识别 nmcli dev status
连接 connection 一套配置方案(IP、网关、DNS、是否自启),可以有多个,绑定到某个设备 nmcli con show

RedHat 7 中有网卡设备和网卡连接两种东西。网卡设备是真实的网卡,网卡连接是网卡的一个配置文件,需要将网卡关联到网卡配置文件上才能正常使用。

理解要点:

  • 一个设备上可以定义多个连接(如「公司静态 IP」和「家里 DHCP」),但同一时刻只有一个处于激活状态。
  • 激活连接靠 nmcli con up <连接名>,不是靠 ip link set up;只 ip 命令启用设备并不会套用任何 IP 配置。
  • NetworkManager 把配置写在 /etc/NetworkManager/system-connections/(RHEL 9 默认 keyfile 格式)或仍兼容读写 ifcfg-* 文件(RHEL 7/8)。

手工改了 ifcfg 文件之后必须执行 nmcli con reload:NetworkManager 只在启动时和收到 reload 指令时读取磁盘上的配置文件,它内存中保存的是旧值。若跳过 reload 直接 nmcli con up,激活的仍是旧配置,会出现「文件里明明写对了、生效的却是老 IP」这类费时的假故障。

# 手工编辑 ifcfg 后的正确顺序
sudo vim /etc/sysconfig/network-scripts/ifcfg-enp0s25
sudo nmcli con reload                 # 重新读取磁盘配置
sudo nmcli con up enp0s25             # 重新激活该连接

5.3 nmcli 命令摘要

nmcli 的对象(可缩写):general(g)、networking(n)、radio(r)、connection(c/con)、device(d/dev)、agent、monitor。

# ---------- general:NetworkManager 整体状态 ----------
nmcli general                  # 总体状态
nmcli general status
nmcli general hostname         # 查看主机名
nmcli general hostname newname # 修改主机名(等价 hostnamectl set-hostname)
nmcli general permissions      # 当前用户可执行的操作权限
nmcli general logging          # 日志级别

# ---------- networking:网络总开关 ----------
nmcli networking on            # 打开全部网络
nmcli networking off           # 关闭全部网络(等价于飞行模式)
nmcli networking connectivity  # 连通性状态:none/portal/limited/full

# ---------- device:物理设备 ----------
nmcli dev status               # 列出物理设备的状态
nmcli dev show                 # 所有设备详细信息
nmcli dev show ens33           # 单个设备详细信息(速率、MAC、IP、DNS)
nmcli dev dis <dev>            # 关闭物理连接(disconnect)
nmcli dev conn <dev>           # 打开物理连接(connect)
nmcli dev wifi list            # 扫描无线网络

# ---------- connection:连接配置 ----------
nmcli con show                 # 列出所有连接(含未激活的)
nmcli con show --active        # 只列出已激活连接
nmcli con show "name"          # 查看某连接的全部参数
nmcli con up "name"            # 激活连接
nmcli con down "name"          # 关闭连接
nmcli con add                  # 添加连接
nmcli con mod                  # 修改连接
nmcli con del                  # 删除连接
nmcli con reload               # 重新读取磁盘配置文件
nmcli con edit "name"          # 进入交互式编辑器

5.4 查看状态与网络信息

# 查看所有设备及其状态(connected / disconnected / unmanaged)
nmcli dev status

# 查看所有连接(含未激活的)
nmcli con show

# 查看某个连接的全部参数(属性非常多,可配合 grep)
nmcli con show static-eth0
nmcli con show static-eth0 | grep -E 'ipv4.(method|addresses|gateway|dns)'

# 查看设备的详细信息(速率、MAC、当前 IP)
nmcli dev show enp0s25

查看全部连接的实际输出:

[root@server ~]# nmcli conn show
NAME                UUID                                  TYPE            DEVICE
Wired connection 1  4057b67c-031d-309c-ac43-5e0afaa3c1b8  802-3-ethernet  ens37
ens33               17883cd1-86c8-4ec3-b254-5ce6d462e929  802-3-ethernet  ens33
virbr0              083789d4-f752-4ba6-aa8a-5d156bef98e8  bridge          virbr0

查看单个网卡(连接)信息:

[root@server ~]# nmcli conn show "ens33"
connection.id:                          ens33
connection.uuid:                        17883cd1-86c8-4ec3-b254-5ce6d462e929
connection.stable-id:                   --
connection.interface-name:              ens33
connection.type:                        802-3-ethernet
connection.autoconnect:                 yes
connection.autoconnect-priority:        0
connection.timestamp:                   1545900407
connection.read-only:                   no
connection.permissions:
connection.zone:                        --
connection.master:                      --
connection.slave-type:                  --

查看网卡物理设备信息:

[root@server ~]# nmcli device status
DEVICE      TYPE      STATE      CONNECTION
virbr0      bridge    connected  virbr0
ens33       ethernet  connected  ens33
ens37       ethernet  connected  Wired connection 1
lo          loopback  unmanaged  --
virbr0-nic  tun       unmanaged  --

设备 STATE 常见取值:

状态 含义
connected 已激活某个连接,配置已生效
disconnected 设备存在但未激活任何连接
unmanaged 不受 NetworkManager 管理(通常因 NM_CONTROLLED=no)
unavailable 设备不可用(如网线未插、未 up)
connecting 正在激活中

nmcli dev status 中出现 unmanaged 表示该设备被排除在 NetworkManager 管理之外,通常就是 ifcfg 文件里写了 NM_CONTROLLED=no(见 5.8)。

5.5 新建连接(con add)

新建静态 IP 连接:

sudo nmcli con add type ethernet con-name static-eth0 ifname enp0s25 \
  ipv4.method manual \
  ipv4.addresses 192.168.0.100/24 \
  ipv4.gateway 192.168.0.1 \
  ipv4.dns 223.5.5.5

参数含义:

参数 说明
type ethernet 连接类型(还可为 bond、bridge、team、vlan、wifi 等)
con-name 连接名称,自定义,后续 up/mod 都用它
ifname 绑定的物理设备名
ipv4.method manual 静态配置;auto 相当于 DHCP
ipv4.addresses 地址加掩码位数,多个地址用逗号分隔
ipv4.gateway 默认网关
ipv4.dns DNS 服务器,多个用逗号分隔,如 223.5.5.5,119.29.29.29

新建 DHCP 连接:

sudo nmcli con add type ethernet con-name dhcp-eth0 ifname enp0s25 ipv4.method auto

新建的连接默认 connection.autoconnect yes(等价于旧的 ONBOOT=yes),如需关闭:

sudo nmcli con mod static-eth0 connection.autoconnect no

nmcli con add 执行后会立即在 /etc/sysconfig/network-scripts/ 下生成对应的 ifcfg-<con-name> 文件,可以打开对照学习字段映射关系。

5.6 修改、激活与重载(con mod / up / down / reload / delete)

# 修改已有连接的属性(改完需要重新激活才生效)
sudo nmcli con mod static-eth0 ipv4.addresses 192.168.0.200/24
sudo nmcli con mod static-eth0 ipv4.dns "223.5.5.5 119.29.29.29"
sudo nmcli con mod static-eth0 ipv4.method auto      # 改回 DHCP

# 追加/移除单个值(属性名前加 + 或 -)
sudo nmcli con mod static-eth0 +ipv4.addresses 192.168.0.201/24
sudo nmcli con mod static-eth0 -ipv4.dns 119.29.29.29

# 激活 / 停用连接
sudo nmcli con up static-eth0
sudo nmcli con down static-eth0

# 重新读取磁盘上的配置文件(手工编辑 ifcfg / keyfile 后必做)
sudo nmcli con reload

# 删除连接
sudo nmcli con delete static-eth0

更多实用修改示例:

# 修改连接名称:把 "Wired connection 1" 改为 abc
nmcli con modify "Wired connection 1" con-name abc
[root@server ~]# nmcli con show
NAME    UUID                                  TYPE            DEVICE
abc     4057b67c-031d-309c-ac43-5e0afaa3c1b8  802-3-ethernet  ens37
ens33   17883cd1-86c8-4ec3-b254-5ce6d462e929  802-3-ethernet  ens33
virbr0  083789d4-f752-4ba6-aa8a-5d156bef98e8  bridge          virbr0
# 关闭自动连接(等价 ONBOOT=no)
nmcli con modify "System eth0" connection.autoconnect no

# 修改 DNS(覆盖原值)
nmcli con modify "System eth0" ipv4.dns 172.25.254.254

# 追加 DNS(保留原值)
nmcli con modify "System eth0" +ipv4.dns 172.25.254.250

# 修改 IP 地址(第二个值为网关的旧式写法)
nmcli con modify "System eth0" ipv4.addresses "172.25.0.10/16 172.25.0.254"

# 同时修改 IP 地址和网关(推荐写法)
nmcli conn modify abc ipv4.addresses 192.168.224.101/24 ipv4.gateway 192.168.224.2

# 只修改网关
nmcli conn modify abc ipv4.gateway 192.168.224.2

# 静态配置时必须把方法改为 manual,否则仍会去 DHCP
nmcli conn modify abc ipv4.method manual

# 重新加载配置(激活连接使修改生效)
nmcli con up "System eth0"

如果 nmcli con up 无效,可以重启 network 服务(CentOS 7 两套机制并存时)或 systemctl restart NetworkManager。

常用属性名速查:

属性 对应 ifcfg 字段 说明
ipv4.method manual / auto BOOTPROTO=static / dhcp 地址获取方式
ipv4.addresses IPADDR + PREFIX 地址,可多个
ipv4.gateway GATEWAY 默认网关
ipv4.dns DNS1/DNS2 DNS 服务器
ipv4.dns-search DOMAIN 搜索域
ipv4.never-default DEFROUTE=no 不作为默认路由
ipv4.routes route-<dev> 文件 静态路由
ipv4.ignore-auto-dns PEERDNS=no 忽略 DHCP 下发的 DNS
connection.autoconnect ONBOOT 开机自动连接
connection.id NAME 连接名
connection.interface-name DEVICE 绑定设备
802-3-ethernet.mtu MTU 最大传输单元
802-3-ethernet.cloned-mac-address MACADDR 伪装 MAC

远程操作警告:通过 SSH 修改正在使用的连接时,nmcli con down 会立即断开你自己的会话。安全做法是使用 nmcli con up(它会原子地切换配置,而不是先断后连),或先在带外通道(VNC / 控制台 / IPMI)留一条退路。

5.7 nmtui:字符界面前端

不想背参数时可以用 nmtui,它是 NetworkManager 的 curses 图形前端,无需 X 环境:

sudo nmtui

三个菜单项:Edit a connection(增删改连接)、Activate a connection(激活/停用)、Set system hostname(改主机名,等价于 hostnamectl set-hostname)。

也可直接进入某个界面:

sudo nmtui-edit
sudo nmtui-connect
sudo nmtui-hostname

图形环境下还有 nm-connection-editor(GUI)与 nmcli con edit <名称>(交互式命令行编辑器,支持 print、set、save、quit 等指令)。

nmtui 保存后同样需要在 Activate a connection 中重新激活连接才生效,不会自动应用。

5.8 NM_CONTROLLED=no 与两套机制的冲突

第 4.2 / 4.3 节的 ifcfg 示例中有一行 NM_CONTROLLED=no,含义是:让 NetworkManager 完全不要接管这块网卡,该设备在 nmcli dev status 中显示为 unmanaged,其 IP 由 network.service(initscripts 脚本)在开机时读取 ifcfg 文件配置。

CentOS 7 的现实是两套机制并存,容易互相打架:

场景 后果
NM_CONTROLLED=no 但只启用了 NetworkManager、禁用了 network.service 开机后该网卡无人配置,永远没有 IP
NM_CONTROLLED=yes(或未写)且两个服务同时启用 两者可能先后写入不同配置,出现 IP 反复变化、DNS 被覆盖
NetworkManager 托管的网卡,手工改 /etc/resolv.conf 被 NetworkManager 重新生成覆盖

实践建议:

  • CentOS 7 上二选一并保持一致:要么全交给 NetworkManager(不写 NM_CONTROLLED,用 nmcli 管理,可 systemctl disable network),要么全部 NM_CONTROLLED=no 并 systemctl disable NetworkManager。教学环境中为了配合 systemctl restart network 才使用后者。
  • RHEL 8+ / RHEL 9 上没有选择:network.service 已不存在,NM_CONTROLLED=no 会导致网卡彻底不通,必须使用 nmcli / nmtui。
  • 想让 NetworkManager 不要动 DNS,可在 /etc/NetworkManager/NetworkManager.conf 的 [main] 段加 dns=none,再自行维护 /etc/resolv.conf。

彻底关闭 NetworkManager 的命令(按版本):

# ---------- CentOS 6 ----------
/etc/init.d/NetworkManager stop
chkconfig NetworkManager off
chkconfig network on

# ---------- CentOS 7 ----------
sudo systemctl stop NetworkManager
sudo systemctl disable NetworkManager
sudo systemctl enable network
sudo systemctl start network

是否禁用 NetworkManager 的取舍:

  • 禁用的理由:服务器 IP 固定,不需要自动切换网络;避免两套机制冲突;老运维脚本依赖 service network restart。
  • 保留的理由:bonding/team/bridge/VLAN 的现代配置方式、nmcli 的原子切换、RHEL 8+ 的唯一选择、笔记本/无线场景必需。
  • 结论:CentOS 7 教学与遗留环境可禁用;新建系统与 RHEL 8+ 一律保留并使用 nmcli。

5.9 传统命令与 nmcli 对照表

传统做法 nmcli 等价命令
ifup enp0s25 nmcli con up <连接名>(或 nmcli dev connect enp0s25)
ifdown enp0s25 nmcli con down <连接名>(或 nmcli dev disconnect enp0s25)
systemctl restart network nmcli con reload + nmcli con up <连接名>(或 systemctl restart NetworkManager)
编辑 ifcfg-* 改 IP nmcli con mod <连接名> ipv4.addresses 192.168.0.100/24
编辑 ifcfg-* 改网关 nmcli con mod <连接名> ipv4.gateway 192.168.0.1
编辑 ifcfg-* 改 DNS / 改 /etc/resolv.conf nmcli con mod <连接名> ipv4.dns 223.5.5.5
BOOTPROTO=static ipv4.method manual
BOOTPROTO=dhcp ipv4.method auto
ONBOOT=yes connection.autoconnect yes
dhclient enp0s25 nmcli con mod <连接名> ipv4.method auto; nmcli con up <连接名>
ip a s 看 IP(仍可用) nmcli dev show enp0s25
hostname / 改 /etc/sysconfig/network nmcli general hostname <名> / hostnamectl set-hostname <名>
手工编辑配置文件后重启服务 手工编辑后 nmcli con reload(必做)

5.10 实战一:为虚拟机增加一块桥接网卡并配置 IP

目标:为 Client2 增加一块物理桥接类型的网卡,将 IP 设置为 192.168.198.131。

  1. 登录虚拟机。

  2. 运行 nmcli conn show,结果显示只有 ens33 一块网卡:

[jim@client1 ~]$ nmcli con show
NAME    UUID                                  TYPE            DEVICE
ens33   17883cd1-86c8-4ec3-b254-5ce6d462e929  802-3-ethernet  ens33
virbr0  ee3ee66a-b0a2-44e8-8fac-ffefe039a0b1  bridge          virbr0
  1. 开机状态下为虚拟机 Client2 增加一块「仅主机」类型的网卡。Linux 自动识别到新网卡 ens37,连接名称为 "Wired connection 1":
[root@client4 ~]# nmcli con show
NAME                UUID                                  TYPE            DEVICE
Wired connection 1  f7ef9df8-e21a-393c-8b9d-5ac5c300b54b  802-3-ethernet  ens37
ens33               17883cd1-86c8-4ec3-b254-5ce6d462e929  802-3-ethernet  ens33
virbr0              69cdf8dd-53a3-4db5-9685-6162a828f381  bridge          virbr0
  1. 再次运行 nmcli conn show,系统自动识别到新网卡 ens37。因为网络默认是 DHCP 类型,机器已自动获得 IP,网段为 192.168.198.X:
[jim@client1 ~]$ ifconfig
ens33: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 192.168.224.11  netmask 255.255.255.0  broadcast 192.168.224.255
        inet6 fe80::aed7:e5a6:39f2:bc3c  prefixlen 64  scopeid 0x20<link>
        ether 00:0c:29:4e:51:b0  txqueuelen 1000  (Ethernet)
        RX packets 19387  bytes 28578038 (27.2 MiB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 8951  bytes 545551 (532.7 KiB)
        TX errors 0  dropped 0  overruns 0  carrier 0  collisions 0

ens37: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 192.168.198.128  netmask 255.255.255.0  broadcast 192.168.198.255
        inet6 fe80::18a7:f3a8:5db7:9e2e  prefixlen 64  scopeid 0x20<link>
        ether 00:0c:29:4e:51:ba  txqueuelen 1000  (Ethernet)
        RX packets 135  bytes 17298 (16.8 KiB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 52  bytes 8999 (8.7 KiB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0
...
  1. 修改 IP 地址为 192.168.198.131:
nmcli con modify "Wired connection 1" ipv4.addresses "192.168.198.131/24"
  1. 激活配置:
nmcli conn up "Wired connection 1"
  1. 运行 ifconfig 查询 IP,结果已生效:
ens37: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 192.168.198.131  netmask 255.255.255.0  broadcast 192.168.198.255
        inet6 fe80::a660:7d23:dfee:475c  prefixlen 64  scopeid 0x20<link>
        ether 00:0c:29:86:28:31  txqueuelen 1000  (Ethernet)
        RX packets 5  bytes 1148 (1.1 KiB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 89  bytes 14164 (13.8 KiB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0
  1. 删除连接配置,将静态 IP 改回自动获取:
nmcli conn delete "Wired connection 1"

5.11 实战二:为一块网卡配置多份连接配置

为网卡 ens37 配置多份连接配置,以便在不同环境下切换 IP。例如配置一 conn1 的 IP 为 192.168.198.120,配置二 conn2 的 IP 为 192.168.198.121。

创建 conn1,IP 为 120:

# nmcli con add con-name conn1 type ethernet ifname ens37 ipv4.addresses "192.168.198.120/24" ipv4.gateway 192.168.192.1

创建 conn2,IP 为 121:

# nmcli con add con-name conn2 type ethernet ifname ens37 ipv4.addresses "192.168.198.121/24" ipv4.gateway 192.168.192.1

激活 conn2:

# nmcli conn up conn2

激活 conn1:

# nmcli conn up conn1

同一设备上的多个连接同一时刻只有一个生效,nmcli con up 会自动把前一个顶下去,这就是「一台机器在办公室/机房两套 IP 之间秒切」的标准做法。


六、主机名与名称解析

6.1 查看与临时修改主机名

hostname                       # 查看当前主机名
hostname qianyun.com           # 临时修改(重启失效)
echo "qianyun.com" > /proc/sys/kernel/hostname   # 等价写法
sysctl kernel.hostname=qianyun.com               # 另一种等价写法

hostname 命令实质上读取 /etc/hostname 文件(CentOS 7),但它修改的主机名不会保存到该文件,只是修改内核中的临时变量,重启后恢复。

[root@desktop0 ~]# hostname desktop0.example.com
[root@desktop0 ~]# hostname XXX.example.com

临时修改后当前 Shell 的提示符不会立刻变化,需要重新登录或执行 exec bash 才能看到新主机名。

6.2 永久修改主机名

CentOS 6 / RHEL 6:编辑 /etc/sysconfig/network

HOSTNAME=qianyun.com

配合临时命令使其立即生效,无需重启:

hostname qianyun.com

CentOS 7 / RHEL 7 及以上:使用 hostnamectl(立即生效且永久保存)

hostnamectl                                  # 查看主机名相关全部信息
hostnamectl status
hostnamectl set-hostname XXXXXX              # 永久修改
hostnamectl set-hostname node1.example.com

# 分类型设置
hostnamectl set-hostname "Web Server 01" --pretty     # 描述性名称
hostnamectl set-hostname web01 --static               # 静态主机名(写入 /etc/hostname)
hostnamectl set-hostname web01 --transient            # 瞬态主机名(内核中,DHCP/mDNS 可改)

# nmcli 等价写法
nmcli general hostname web01

CentOS 7 的主机名配置文件:

cat /etc/hostname
# node1.example.com

# 也可直接编辑(需重启或执行 hostname 命令使其生效)
sudo vim /etc/hostname

三类主机名的区别:

类型 存储位置 说明
static /etc/hostname 传统主机名,开机读取,是「真正的」主机名
transient 内核 运行时主机名,可被 DHCP/mDNS 动态修改
pretty /etc/machine-info 自由格式的描述,可含空格与大写
  • 主机名建议写成 FQDN(Full Qualified Domain Name,全限定域名),即 主机名.域名 格式,如 qianyun.cluster.com。部分软件(如 Apache)要求 FQDN,否则启动时报 Could not reliably determine the server's fully qualified domain name。
  • 如果系统中没有 /etc/hostname 文件,系统会使用 IP 进行 DNS 反向解析,从而通过 DNS 获取主机名称。
  • 修改主机名后,务必同步更新 /etc/hosts,将回环地址与主机名对应起来,否则可能导致图形界面启动缓慢、sudo 报 unable to resolve host、SSH 登录变慢。

相关概念:

  • server:主机名称 hostname。
  • server.example.com:FQDN(Fully Qualified Domain Name,全限定域名)。

6.3 /etc/hosts 本地名称解析

/etc/hosts 是本地静态名称解析文件(本地静态 DNS 缓存),相当于手工 DNS。默认情况下访问主机名或域名需要通过 DNS 服务器将名称解析成 IP;某些情况下需要配置 DNS 服务器中不存在的名称,就可以在本地设置。

格式:

IP地址  主机名/域名  [别名1] [别名2] ...

配置文件路径:

/etc/hosts

示例内容:

127.0.0.1   localhost localhost.localdomain localhost4
::1         localhost localhost.localdomain localhost6
10.0.0.61   server61.com
10.0.0.2    qianyun.com

主机名改为 newname 后,环回地址与实际 IP 都建议对应上:

127.0.0.1     newname
192.168.0.200 newname

集群环境的典型 /etc/hosts(各节点内容保持完全一致):

192.168.0.11   node1  node1.example.com
192.168.0.12   node2  node2.example.com
192.168.0.13   node3  node3.example.com

实际验证:

[root@desktop0 ~]# cat /etc/hosts
…
172.25.254.254       classroom.example.com
172.25.254.254       content.example.com
172.25.0.10          desktop0.example.com

[root@desktop0 ~]# host classroom.example.com
classroom.example.com has address 172.25.254.254

如有定义 /etc/hosts,先按该文件解析;没有相应条目再使用 DNS 解析。

/etc/hosts 的修改立即生效,无需重启任何服务;但只对本机有效,且不支持通配符与轮询。

6.4 名称解析顺序:/etc/nsswitch.conf

系统在解析主机名时,先查 /etc/hosts,再查 DNS,这一顺序由 /etc/nsswitch.conf 中的 hosts: 行控制:

grep '^hosts' /etc/nsswitch.conf
hosts:      files dns myhostname
关键字 含义
files 查询本地文件 /etc/hosts
dns 查询 /etc/resolv.conf 中配置的 DNS 服务器
myhostname systemd 提供的模块,解析本机主机名与 localhost
nis / ldap 查询 NIS / LDAP 目录服务
mdns4_minimal 组播 DNS(.local 域,桌面环境常见)

调换顺序即可改变优先级,例如写成 hosts: dns files 就会先查 DNS。生产环境一般保持 files dns(本地覆盖优先,便于临时改指向)。

6.5 DNS 客户端配置:/etc/resolv.conf

sudo vim /etc/resolv.conf

添加内容(顶行优先):

# 首选 DNS 与备用 DNS,最多前 3 个 nameserver 生效
nameserver 223.5.5.5
nameserver 8.8.8.8
nameserver 114.114.114.114

# 默认搜索域:输入 web01 时自动尝试 web01.example.com
search example.com cluster.com

# 本机所在域(与 search 只写一个即可,search 优先)
domain example.com

# 解析行为微调
options timeout:2 attempts:2 rotate single-request-reopen
指令 说明
nameserver DNS 服务器地址,按顺序尝试,通常只有前 3 个生效
search 搜索域列表,短主机名会依次拼接这些域名再查询
domain 本机域名,作用类似单个 search,与 search 互斥
sortlist 对返回的多个地址按指定网段排序
options timeout:n 单个 DNS 查询超时秒数(默认 5,建议 1~2)
options attempts:n 每个服务器重试次数(默认 2)
options rotate 轮询使用各 nameserver,实现简单负载均衡
options ndots:n 域名中点数少于 n 时先拼 search 域再查(容器环境常调整)
options single-request-reopen 规避某些网关下 A/AAAA 并发查询丢包导致的解析慢

常用公共 DNS:

地址 提供方
223.5.5.5 / 223.6.6.6 阿里云 DNS(国内首选)
114.114.114.114 / 114.114.115.115 114DNS
180.76.76.76 百度 DNS
119.29.29.29 腾讯 DNSPod
8.8.8.8 / 8.8.4.4 Google DNS
1.1.1.1 Cloudflare

/etc/resolv.conf 被覆盖的问题:DHCP 客户端与 NetworkManager 都会重写此文件。彻底解决办法有三:

  1. 在 ifcfg 中写 DNS1/DNS2(推荐,由系统统一生成);
  2. ifcfg 中加 PEERDNS=no 阻止 DHCP 覆盖;
  3. 在 /etc/NetworkManager/NetworkManager.conf 的 [main] 段加 dns=none,再手工维护。

DNS 服务端(BIND)搭建详见 阶段09 09-03-DNS域名解析服务.md

6.6 验证名称解析

# 按 nsswitch.conf 的完整顺序解析(会读取 /etc/hosts,最贴近应用程序的行为)
getent hosts www.baidu.com
getent hosts node1
getent ahosts node1

# 只查 DNS,不看 /etc/hosts —— 用于区分「是 hosts 生效还是 DNS 生效」
nslookup www.baidu.com
nslookup www.baidu.com 8.8.8.8        # 指定 DNS 服务器查询

host www.baidu.com                     # 简洁输出
host -t A www.baidu.com                # 指定记录类型
host -t MX baidu.com
host 223.5.5.5                         # 反向解析

dig www.baidu.com                       # 最详细,运维排障首选
dig +short www.baidu.com                # 只输出结果
dig @8.8.8.8 www.baidu.com              # 指定 DNS 服务器
dig +trace www.baidu.com                # 从根域开始追踪整个解析链
dig -x 223.5.5.5                        # 反向解析

关键区别:ping/getent/应用程序都会读 /etc/hosts,而 nslookup/dig/host 直接问 DNS 服务器,不读 /etc/hosts。出现「ping 域名解析到旧 IP,但 dig 结果是新 IP」时,第一嫌疑就是 /etc/hosts 里有残留条目。

6.7 为什么要互相绑定主机名

使用 SSH 连接另一台机器时,如果没有主机名解析,可能会卡几秒才出现密码提示(服务端做反向 DNS 解析等待超时)。多台服务器互相在 /etc/hosts 中绑定,可显著加快连接。配合服务端的 UseDNS no 效果最佳。

集群软件(Hadoop、Kubernetes、MySQL 主从、Puppet/SaltStack)几乎都要求节点间能用主机名互相解析,且解析结果必须一致,否则会出现「注册上来的节点地址是 127.0.0.1」这类问题。

6.8 大规模服务器的主机名管理方案

当服务器数量很多时,逐台维护 /etc/hosts 不现实,可选方案:

  1. 脚本批量分发 /etc/hosts(for 循环 + scp,见下例)
  2. 自动化运维工具(如 Puppet、Ansible、SaltStack)统一管理
  3. 搭建内部 DNS 服务器(节点数超过几十台时的正解)
  4. 在批量装机(Kickstart/Cobbler)时自动写入
# 方案 1 的最简实现:把标准 hosts 分发到所有节点
for host in node1 node2 node3; do
    scp /etc/hosts root@$host:/etc/hosts
done

七、进阶网络配置

7.1 多网卡与多 IP(IP 别名)

临时添加

在一块物理网卡上绑定多个 IP:

# 传统别名写法(CentOS 6)
ifconfig eth0:0 10.0.0.36 netmask 255.255.255.0
ifconfig eth0:1 10.0.0.37 netmask 255.255.255.0
ifconfig eth0:2 10.0.0.38 netmask 255.255.255.0

# ip 命令写法(CentOS 7 推荐,一个接口挂多个地址,不产生虚拟接口)
sudo ip addr add 10.0.0.36/24 dev eth0
sudo ip addr add 10.0.0.37/24 dev eth0 label eth0:1

临时配置在重启网络后消失。

ifconfig 只能看到带 label 的别名地址,ip addr 才能看到全部地址。CentOS 7 上用 ip a 检查。

永久生效:单个别名(CentOS 6 风格)

新建 /etc/sysconfig/network-scripts/ifcfg-eth0:0:

DEVICE=eth0:0
BOOTPROTO=static
IPADDR=10.0.0.36
NETMASK=255.255.255.0
ONBOOT=yes
NM_CONTROLLED="no"

批量添加:range 文件

新建 /etc/sysconfig/network-scripts/ifcfg-eth0-range0:

IPADDR_START=10.0.0.37     # 起始 IP
IPADDR_END=10.0.0.50       # 结束 IP
NETMASK=255.255.255.0
CLONENUM_START=10          # 别名编号从 eth0:10 开始

CentOS 7 的多 IP 写法

方式一:在同一个 ifcfg 文件中写多组编号

DEVICE=ens33
BOOTPROTO=none
ONBOOT=yes
IPADDR0=192.168.0.200
PREFIX0=24
IPADDR1=192.168.0.201
PREFIX1=24
IPADDR2=192.168.0.202
PREFIX2=24
GATEWAY=192.168.0.1

方式二:用 nmcli 一次配置多个地址

nmcli con mod ens33 ipv4.addresses "192.168.0.200/24,192.168.0.201/24,192.168.0.202/24"
nmcli con mod ens33 +ipv4.addresses 192.168.0.203/24     # 追加
nmcli con up ens33

多 IP 常见用途:一台机器承载多个 HTTPS 站点(SNI 之前的做法)、Keepalived 的 VIP、迁移期间新旧 IP 并存。

7.2 网卡绑定 bonding

bonding 把多块物理网卡聚合成一个逻辑网卡,实现链路冗余或带宽叠加。

常用模式:

模式 名称 说明 是否需要交换机配合
mode=0 balance-rr 轮询发包,带宽可叠加,但可能乱序 需要(端口聚合)
mode=1 active-backup 主备,只有一块工作,故障自动切换(生产最常用) 不需要
mode=2 balance-xor 按源/目的 MAC 哈希分流 需要
mode=3 broadcast 所有从网卡都发一份,容错极高、效率极低 不需要
mode=4 802.3ad (LACP) 标准链路聚合,带宽叠加 + 冗余(机房推荐) 需要(配置 LACP)
mode=5 balance-tlb 按负载分配发送流量,接收不分流 不需要
mode=6 balance-alb 收发都做负载均衡(靠 ARP 协商) 不需要

传统 ifcfg 配置方式(CentOS 6 / 7 通用)

主接口 /etc/sysconfig/network-scripts/ifcfg-bond0:

DEVICE=bond0
NAME=bond0
TYPE=Bond
BONDING_MASTER=yes
BOOTPROTO=none
ONBOOT=yes
IPADDR=192.168.0.200
PREFIX=24
GATEWAY=192.168.0.1
DNS1=223.5.5.5
NM_CONTROLLED=no
BONDING_OPTS="mode=1 miimon=100 primary=eth0 fail_over_mac=1"

从属接口 ifcfg-eth0:

DEVICE=eth0
TYPE=Ethernet
BOOTPROTO=none
ONBOOT=yes
MASTER=bond0
SLAVE=yes
NM_CONTROLLED=no

从属接口 ifcfg-eth1:

DEVICE=eth1
TYPE=Ethernet
BOOTPROTO=none
ONBOOT=yes
MASTER=bond0
SLAVE=yes
NM_CONTROLLED=no

CentOS 6 还需要加载模块(CentOS 7 自动加载):

sudo vim /etc/modprobe.d/bonding.conf
alias bond0 bonding
options bond0 miimon=100 mode=1

重启网络并验证:

# CentOS 6
service network restart
# CentOS 7
sudo systemctl restart network

# 查看绑定状态(当前活动网卡、各从属网卡链路状态)
cat /proc/net/bonding/bond0
ip a s bond0

# 拔掉主网卡网线(或 ip link set eth0 down)验证是否自动切换
sudo ip link set eth0 down
cat /proc/net/bonding/bond0 | grep -E 'Active Slave|MII Status'

BONDING_OPTS 常用参数:

参数 说明
mode 绑定模式,见上表
miimon=100 每 100 毫秒检测一次链路状态(必配)
updelay / downdelay 链路恢复/断开后延迟多少毫秒才切换,防抖动
primary=eth0 指定主用网卡(仅 active-backup 有效)
fail_over_mac 切换时如何处理 MAC,虚拟机环境常设 1
lacp_rate=fast LACP 报文发送频率(mode=4)
xmit_hash_policy=layer3+4 哈希策略(mode=2/4),按 IP+端口分流更均匀

nmcli 配置方式(CentOS 7 推荐)

# 创建 bond 主接口
sudo nmcli con add type bond con-name bond0 ifname bond0 mode active-backup \
  ipv4.method manual ipv4.addresses 192.168.0.200/24 ipv4.gateway 192.168.0.1 ipv4.dns 223.5.5.5

# 加入从属网卡
sudo nmcli con add type bond-slave con-name bond0-p1 ifname ens33 master bond0
sudo nmcli con add type bond-slave con-name bond0-p2 ifname ens37 master bond0

# 激活
sudo nmcli con up bond0
sudo nmcli con up bond0-p1
sudo nmcli con up bond0-p2

# 查看
cat /proc/net/bonding/bond0

7.3 team 组队(CentOS 7 新增)

team 是 RHEL 7 引入的替代 bonding 的方案,由用户空间守护进程 teamd 实现,功能相近但更易扩展。

runner(相当于 bonding 的 mode):broadcast、roundrobin、activebackup、loadbalance、lacp。

# 创建 team 主接口(activebackup 模式)
sudo nmcli con add type team con-name team0 ifname team0 \
  config '{"runner": {"name": "activebackup"}}' \
  ipv4.method manual ipv4.addresses 192.168.0.200/24 ipv4.gateway 192.168.0.1

# 加入成员网卡
sudo nmcli con add type team-slave con-name team0-p1 ifname ens33 master team0
sudo nmcli con add type team-slave con-name team0-p2 ifname ens37 master team0

# 激活
sudo nmcli con up team0

# 查看状态
sudo teamdctl team0 state
sudo teamnl team0 ports
ip a s team0

ifcfg 写法 /etc/sysconfig/network-scripts/ifcfg-team0:

DEVICE=team0
DEVICETYPE=Team
NAME=team0
TEAM_CONFIG='{"runner": {"name": "activebackup"}}'
BOOTPROTO=none
ONBOOT=yes
IPADDR=192.168.0.200
PREFIX=24
GATEWAY=192.168.0.1

成员 ifcfg-ens33:

DEVICE=ens33
DEVICETYPE=TeamPort
TEAM_MASTER=team0
ONBOOT=yes

bonding 与 team 不能同时管理同一块网卡。新系统若无特殊要求,仍可继续使用 bonding(内核态、成熟、文档多);RHEL 9 起 team 已被标记弃用,官方重新推荐 bonding。

7.4 桥接 bridge

桥接把物理网卡变成虚拟交换机的一个端口,KVM 虚拟化和容器网络的基础。

nmcli 方式:

# 创建桥
sudo nmcli con add type bridge con-name br0 ifname br0 \
  ipv4.method manual ipv4.addresses 192.168.0.200/24 ipv4.gateway 192.168.0.1 ipv4.dns 223.5.5.5

# 关闭 STP(单机环境可关,减少 up 后的等待时间)
sudo nmcli con mod br0 bridge.stp no

# 把物理网卡加入桥
sudo nmcli con add type bridge-slave con-name br0-port1 ifname ens33 master br0

sudo nmcli con up br0

ifcfg 方式,桥接口 ifcfg-br0:

DEVICE=br0
NAME=br0
TYPE=Bridge
BOOTPROTO=none
ONBOOT=yes
IPADDR=192.168.0.200
PREFIX=24
GATEWAY=192.168.0.1
DNS1=223.5.5.5
STP=no
DELAY=0

物理网卡 ifcfg-ens33(注意:物理网卡不再配 IP):

DEVICE=ens33
NAME=ens33
TYPE=Ethernet
BOOTPROTO=none
ONBOOT=yes
BRIDGE=br0

查看与管理:

brctl show                     # 需 bridge-utils 包
brctl showmacs br0
ip link show type bridge
bridge link show               # iproute2 提供的新命令
nmcli con show br0 | grep bridge

配置桥接会导致物理网卡上的 IP 转移到 br0,操作过程中网络会短暂中断。远程操作必须使用 nmcli con up 并准备带外通道。

7.5 VLAN 子接口

在一块物理网卡上承载多个 VLAN(需交换机端口配置为 trunk)。

nmcli 方式:

sudo nmcli con add type vlan con-name vlan10 ifname ens33.10 dev ens33 id 10 \
  ipv4.method manual ipv4.addresses 192.168.10.200/24

sudo nmcli con up vlan10

ifcfg 方式 /etc/sysconfig/network-scripts/ifcfg-ens33.10:

DEVICE=ens33.10
BOOTPROTO=none
ONBOOT=yes
VLAN=yes
VLAN_ID=10
PHYSDEV=ens33
IPADDR=192.168.10.200
PREFIX=24

临时创建(ip 命令):

sudo modprobe 8021q                                     # 加载 VLAN 模块
sudo ip link add link ens33 name ens33.10 type vlan id 10
sudo ip addr add 192.168.10.200/24 dev ens33.10
sudo ip link set ens33.10 up

# 查看
ip -d link show ens33.10
cat /proc/net/vlan/config

# 删除
sudo ip link del ens33.10

传统 vconfig 写法(CentOS 6):

vconfig add eth0 10
vconfig rem eth0.10

7.6 静态路由永久化

临时添加的路由(ip route add)重启即失效,需要写入配置文件。

方式一:route-<设备名> 文件(CentOS 6 / 7 通用)

sudo vim /etc/sysconfig/network-scripts/route-eth0

写法 A(ip 命令风格,推荐):

10.1.1.0/24 via 192.168.0.254 dev eth0
172.16.0.0/16 via 192.168.0.254
default via 192.168.0.1

写法 B(键值对风格,多条路由用编号区分):

ADDRESS0=10.1.1.0
NETMASK0=255.255.255.0
GATEWAY0=192.168.0.254
ADDRESS1=172.16.0.0
NETMASK1=255.255.0.0
GATEWAY1=192.168.0.254

生效:

# CentOS 6
service network restart
# CentOS 7
sudo systemctl restart network
# 或只重启该网卡
sudo ifdown eth0 && sudo ifup eth0

方式二:nmcli 写入连接属性(CentOS 7 推荐)

sudo nmcli con mod ens33 +ipv4.routes "10.1.1.0/24 192.168.0.254"
sudo nmcli con mod ens33 +ipv4.routes "172.16.0.0/16 192.168.0.254 100"   # 末位为 metric
sudo nmcli con up ens33
nmcli con show ens33 | grep ipv4.routes

方式三:写进 rc.local(不推荐,但常见于老环境)

sudo vim /etc/rc.d/rc.local
/sbin/ip route add 10.1.1.0/24 via 192.168.0.254
sudo chmod +x /etc/rc.d/rc.local        # CentOS 7 必须加执行权限才会被执行

开启路由转发(让 Linux 充当路由器/网关)

# 临时开启
sudo sysctl -w net.ipv4.ip_forward=1
echo 1 > /proc/sys/net/ipv4/ip_forward

# 永久开启
sudo vim /etc/sysctl.conf          # CentOS 6
# 或 /etc/sysctl.d/99-forward.conf # CentOS 7 推荐
net.ipv4.ip_forward = 1
sudo sysctl -p
sysctl net.ipv4.ip_forward         # 验证

7.7 MTU 调整

MTU(Maximum Transmission Unit)默认 1500 字节。存储网络、集群内网可调至 9000(巨帧)提升吞吐;PPPoE、VPN、隧道环境需调小以避免分片丢包。

# 临时修改
sudo ip link set dev ens33 mtu 9000
ifconfig eth0 mtu 9000              # 传统写法

# 查看当前 MTU
ip a s ens33 | grep mtu
cat /sys/class/net/ens33/mtu

# 永久修改:ifcfg 文件
# MTU=9000

# 永久修改:nmcli
sudo nmcli con mod ens33 802-3-ethernet.mtu 9000
sudo nmcli con up ens33

探测链路实际可用 MTU(发送不允许分片的大包,逐步降低直到成功):

# -M do 禁止分片,-s 指定数据部分大小(实际包大小 = -s 值 + 28 字节头)
ping -c 2 -M do -s 1472 192.168.0.1      # 成功说明 MTU >= 1500
ping -c 2 -M do -s 1473 192.168.0.1      # 失败提示 Frag needed and DF set (mtu = 1500)
ping -c 2 -M do -s 8972 192.168.0.1      # 测试巨帧 9000

# tracepath 自动探测路径 MTU
tracepath 8.8.8.8

MTU 不一致的典型症状:ping 通、SSH 能登录、但 scp 大文件卡死、网页打开一半、数据库大结果集查询超时——即「小包通、大包丢」。全链路(包括交换机端口、对端服务器)必须统一 MTU,只改一端会更糟。


八、SSH 远程登录

8.1 为什么用 SSH 而不是 telnet

远程管理 Linux 的协议有两代:早期的 telnet(23/tcp)与现在通用的 SSH(Secure Shell,22/tcp)。

对比项 telnet SSH
默认端口 23/tcp 22/tcp
传输方式 全程明文 全程加密
用户名/密码 明文出现在网络包中 加密协商,抓包不可读
身份认证 仅口令 口令 + 公钥 + 主机密钥双向校验
文件传输 无 自带 scp / sftp
端口转发 / X11 转发 无 支持
现状 已淘汰,仅用于调试端口连通性 事实标准

telnet 被淘汰的核心原因就一条:它把用户名和密码以明文形式发送。只要在链路上任意一点抓包(交换机镜像口、同网段 ARP 欺骗、公网中间设备),就能直接读出登录凭据。

# telnet 现在的正确用法:只当「TCP 端口探测工具」,不用来登录
telnet 192.168.0.183 22        # 能看到 SSH-2.0-OpenSSH_7.4 说明 22 端口通且 sshd 在

SSH 协议本身有两个版本:

版本 说明
SSH v1 使用 CRC-32 做完整性校验,存在可插入攻击、密钥可被破解等设计缺陷,已废弃,OpenSSH 7.4 起默认不再编译支持
SSH v2 重新设计了密钥交换与完整性校验(HMAC),支持 DH 密钥交换,当前唯一应使用的版本

老配置文件里可能残留 Protocol 2,1,应改为只允许 v2:

# /etc/ssh/sshd_config
Protocol 2

新版 OpenSSH 已移除 Protocol 指令,写了反而会导致 sshd 启动失败,仅在 CentOS 6 这类老系统上需要显式声明。

8.2 基本登录

两种等价写法:

ssh 10.0.0.2                    # 以「当前本地用户名」登录远程主机
ssh user1@10.0.0.2              # 以指定用户登录(最常用)
ssh -l user1 10.0.0.2           # -l 指定登录用户,与上一行完全等价

指定端口(服务端改过端口时必须加):

ssh -p 2222 user1@10.0.0.2      # 注意 ssh 是小写 -p,scp/sftp 是大写 -P

其他常用形式:

ssh user1@server1.com                       # 用主机名登录(依赖 /etc/hosts 或 DNS)
ssh -i ~/.ssh/id_rsa_prod deploy@10.0.0.2   # 指定私钥文件
ssh -o ConnectTimeout=5 user1@10.0.0.2      # 限制连接超时,批量脚本必备

首次登录的指纹确认

第一次连接某台主机时,会出现主机密钥(host key)确认提示:

The authenticity of host '10.0.0.2 (10.0.0.2)' can't be established.
RSA key fingerprint is b1:70:27:1f:8c:2a:...:9e.
Are you sure you want to continue connecting (yes/no)? yes
Warning: Permanently added '10.0.0.2' (RSA) to the list of known hosts.

这里的含义需要讲清楚:

  • 每台安装了 sshd 的主机都会在 /etc/ssh/ 下生成一对主机密钥(如 ssh_host_rsa_key / ssh_host_rsa_key.pub)。
  • 客户端连接时,服务端出示自己的公钥;客户端本地没有记录,无法判断「这台机器是不是我要连的那台」,于是把指纹(公钥的哈希摘要)显示给人工确认。
  • 输入 yes 的真实含义是:我确认这个指纹属于我要连的主机,请把它记下来。此后该公钥被追加进 ~/.ssh/known_hosts,再次连接时客户端自动比对,一致则不再提示。
  • 严格来说,首次连接应通过带外渠道(控制台、装机记录)核对指纹;实际运维中内网环境多数直接 yes。

查看服务端自己的指纹用于核对:

ssh-keygen -lf /etc/ssh/ssh_host_rsa_key.pub          # 显示本机主机密钥指纹
ssh-keygen -lf /etc/ssh/ssh_host_ecdsa_key.pub

known_hosts 的作用:

cat ~/.ssh/known_hosts                # 每行一条:主机名/IP + 密钥类型 + 公钥
ssh-keygen -F 10.0.0.2                # 查询某主机是否已有记录

它是防中间人攻击的关键。如果没有它,攻击者冒充服务端时客户端毫无察觉,密码会直接送到攻击者手里。

免密登录(ssh-keygen 生成密钥对、ssh-copy-id 分发公钥、~/.ssh/authorized_keys)是 SSH 的另一大主题,本文只做要点提及:生成密钥 → 公钥追加到服务端 authorized_keys → 私钥 600、~/.ssh 700。

详见 04-02-计划任务与SSH等效性.md

8.3 报错:REMOTE HOST IDENTIFICATION HAS CHANGED

报错原文:

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@    WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED!     @
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
IT IS POSSIBLE THAT SOMEONE IS DOING SOMETHING NASTY!
Someone could be eavesdropping on you right now (man-in-the-middle attack)!
It is also possible that a host key has just been changed.
The fingerprint for the RSA key sent by the remote host is
b1:70:27:1f:8c:2a:...:9e.
Please contact your system administrator.
Add correct host key in /root/.ssh/known_hosts to get rid of this message.
Offending key in /root/.ssh/known_hosts:6
RSA host key for 10.0.0.2 has changed and you have requested strict checking.
Host key verification failed.

成因:本地 known_hosts 里记录的主机公钥,与服务端这次出示的公钥不一致。常见原因:

  1. 远程主机重装了系统(主机密钥重新生成)——最常见。
  2. 该 IP 被另一台机器复用(DHCP 重新分配、虚拟机重建、云主机释放后重新申请)。
  3. 服务端手工删除或重建了 /etc/ssh/ssh_host_* 文件。
  4. 真的遭遇了中间人攻击(少见但不能排除)。

正确做法:只清理这一条记录

ssh-keygen -R 10.0.0.2            # 按 IP 删除
ssh-keygen -R server1.com         # 按主机名删除(如果是用主机名连的,两条都要删)
ssh-keygen -R "[10.0.0.2]:2222"   # 非默认端口的记录格式是 [主机]:端口

执行后会生成备份文件 known_hosts.old,然后重新连接,重新确认一次指纹即可。

粗暴做法及其代价

rm -f ~/.ssh/known_hosts          # 不推荐

代价是:所有主机的信任记录一次性全部丢失。之后连接每一台机器都会重新提示确认指纹,而在「习惯性一路 yes」的状态下,真正的中间人攻击也会被一并放过——等于把整个 known_hosts 机制的保护作用清零。只删单行同样可行:

sed -i '6d' ~/.ssh/known_hosts    # 删除报错提示的第 6 行

StrictHostKeyChecking 与批量运维的取舍

# 单次跳过检查(不写入 known_hosts)
ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null user@10.0.0.2

# 批量脚本中常见写法
ssh -o StrictHostKeyChecking=no -o ConnectTimeout=5 user@$ip 'uptime'

取值含义:

取值 行为
yes 主机密钥不在 known_hosts 中直接拒绝连接,最严格
ask(默认) 未知主机弹出指纹确认;已知主机密钥变更则拒绝
no / accept-new no 自动接受任何密钥(含变更后的);accept-new 只自动接受新主机,密钥变更仍拒绝

安全含义必须点明:StrictHostKeyChecking=no 等于彻底放弃对服务端身份的校验,中间人可以无声无息地劫持会话并拿到你输入的密码或命令。它只适合「一次性初始化大批新装机器、且处于可信内网」的场景。若确实需要免交互,优先选 accept-new(OpenSSH 7.6+),或提前用 ssh-keyscan 批量收集指纹写入 known_hosts:

ssh-keyscan -t rsa,ecdsa 10.0.0.2 >> ~/.ssh/known_hosts       # 单台
ssh-keyscan -f iplist.txt >> ~/.ssh/known_hosts               # 按 IP 列表批量

8.4 远程执行命令

不进入交互式 Shell,执行完命令直接返回结果:

ssh 10.0.0.2 date                        # 执行 date 并把输出打回本地
ssh user1@10.0.0.2 'df -h /'
ssh user1@10.0.0.2 uptime

引号与转义的注意点

命令会先经过本地 Shell 解析一次,再由远程 Shell 解析一次,因此变量、通配符、重定向的解析位置取决于引号:

ssh 10.0.0.2 echo $HOSTNAME        # 错误:$HOSTNAME 被本地展开,打印的是本地主机名
ssh 10.0.0.2 'echo $HOSTNAME'      # 正确:单引号阻止本地展开,在远程展开
ssh 10.0.0.2 "echo $HOSTNAME"      # 双引号仍会被本地展开,注意区分

ssh 10.0.0.2 'ls /var/log/*.log'   # 通配符在远程展开
ssh 10.0.0.2 'df -h > /tmp/df.txt' # 重定向发生在远程
ssh 10.0.0.2 'df -h' > /tmp/df.txt # 重定向发生在本地

多条命令的写法

ssh 10.0.0.2 'hostname; uptime; df -h /'                    # 分号:不管前一条成败都执行
ssh 10.0.0.2 'cd /tmp && tar zcf log.tar.gz *.log'          # && :前一条成功才继续
ssh 10.0.0.2 'systemctl is-active nginx || systemctl start nginx'

# 多行写法,可读性更好
ssh 10.0.0.2 '
  yum -y install httpd
  systemctl enable --now httpd
  systemctl is-active httpd
'

-t 强制分配伪终端

ssh 10.0.0.2 'sudo systemctl restart nginx'        # 可能报 sudo: no tty present
ssh -t 10.0.0.2 'sudo systemctl restart nginx'     # 加 -t,可以正常交互输入密码
ssh -t 10.0.0.2 'top'                              # 全屏交互程序必须有 tty
ssh -t 10.0.0.2 'vim /etc/hosts'
ssh -tt 10.0.0.2 'command'                         # 即使本地无 tty(脚本中)也强制分配

需要 -t 的典型场景:sudo 需要输入密码、top / vim / less 等全屏交互程序、需要看到进度条的程序。

-T 则是禁用伪终端分配:

ssh -T git@github.com              # 测试 GitHub 密钥,不需要 tty
ssh -T 10.0.0.2 'cat >> /tmp/a'    # 纯管道传输时禁用 tty,避免回显干扰数据

执行本地脚本(不需要先上传)

ssh 10.0.0.2 'bash -s' < /root/init.sh                 # 把本地脚本喂给远程 bash
ssh 10.0.0.2 'bash -s' -- --env=prod < deploy.sh       # 还能传参数给脚本
cat init.sh | ssh 10.0.0.2 'bash -s'                   # 等价写法
ssh 10.0.0.2 'sudo bash -s' < patch.sh                 # 以 root 执行

这是批量初始化机器的常用技巧:脚本只维护一份在本地,无需 scp 到每台机器再删除。

配合远程写文件:

echo "hello" | ssh user@10.0.0.2 'cat > /tmp/file'     # 覆盖写
ssh user@10.0.0.2 'cat >> /tmp/file' < local.txt       # 追加写
tar zcf - /etc/nginx | ssh 10.0.0.2 'tar zxf - -C /'   # 打包直传,不落地临时文件

8.5 远程调用图形程序(X11 转发)

在远程服务器上运行图形程序,把窗口显示到本地:

ssh -X user1@10.0.0.2 firefox      # 单次转发:登录并直接启动远程 firefox
ssh -X user1@10.0.0.2              # 登录后再在提示符下运行图形程序
ssh -Y user1@10.0.0.2              # 信任模式转发

-X 与 -Y 的区别

选项 名称 行为 安全性
-X 不信任转发 受 X11 SECURITY 扩展限制,远程程序不能截取本地其他窗口的键盘输入、不能抓屏 较安全,但部分程序会因限制而报错或异常退出
-Y 信任转发 不加任何限制,远程程序与本地 X 客户端权限相同 不安全:远程主机上的恶意程序可以键盘记录、截屏你的本地桌面

原则:优先用 -X,只有程序确实跑不起来时才临时改用 -Y,且仅对完全可信的服务器使用。

对应的客户端配置项:

# ~/.ssh/config
ForwardX11 yes            # 相当于 -X
ForwardX11Trusted yes     # 相当于 -Y

服务端与依赖条件

# /etc/ssh/sshd_config
X11Forwarding yes         # 必须开启,否则客户端 -X 无效
X11UseLocalhost yes       # 默认 yes
systemctl restart sshd            # CentOS 7+
service sshd restart              # CentOS 6

# 服务端必须安装 xauth,否则 -X 静默失效
rpm -q xorg-x11-xauth || yum -y install xorg-x11-xauth

# 服务端还需要安装图形程序本身及其依赖库
yum -y install xorg-x11-apps      # 提供 xclock / xeyes 等测试小程序

验证是否转发成功

# 登录远程主机后执行
echo $DISPLAY
# 有输出如 localhost:10.0 → 转发已生效
# 无任何输出         → 转发未生效(服务端未开 X11Forwarding、未装 xauth,或客户端没加 -X)

xclock                            # 弹出时钟窗口即完全正常
xeyes

常见失败原因排查顺序:客户端是否加了 -X/-Y → 服务端 X11Forwarding yes 是否生效(sshd -T | grep -i x11)→ 服务端是否装了 xauth → 本地是否有可用的 X Server(Windows 上需 Xming / VcXsrv / MobaXterm,macOS 需 XQuartz)。

8.6 故障排查:Connection refused

报错原文:

ssh 10.0.0.2
ssh: connect to host 10.0.0.2 port 22: Connection refused

Connection refused 的准确含义是:网络是通的,数据包到达了对端,但对端 22 端口没有任何程序在监听(内核直接回了 RST)。所以问题一定在服务端,而不在链路。按下面顺序排查。

第 1 步:远端 sshd 是否在运行

# CentOS 7 及以上
systemctl status sshd
systemctl is-active sshd

# CentOS 6
service sshd status
/etc/init.d/sshd status

第 2 步:是否监听 22 端口

ss -tnlp | grep 22                # CentOS 7+ 推荐
ss -tnlp | grep sshd
netstat -ntlup | grep sshd        # CentOS 6 / 老系统
lsof -i :22

正常输出:

tcp  0  0 0.0.0.0:22  0.0.0.0:*  LISTEN  6840/sshd
tcp  0  0 :::22       :::*       LISTEN  6840/sshd

0.0.0.0:22 表示监听所有网卡。若只看到 127.0.0.1:22,说明 sshd_config 里 ListenAddress 被限制成了回环地址,外部当然连不上。

第 3 步:启动服务并设置开机自启

# CentOS 7 及以上(systemd)
systemctl start sshd
systemctl enable sshd
systemctl enable --now sshd       # 启动 + 开机自启,一条搞定

# CentOS 6(SysV init)
service sshd start
/etc/init.d/sshd start
chkconfig sshd on
chkconfig --list sshd             # 确认 2、3、4、5 为 on

若启动失败,看日志定位原因:

journalctl -u sshd -n 50 --no-pager      # CentOS 7+
tail -50 /var/log/secure                 # 通用
sshd -t                                  # 先做配置语法检查

第 4 步:防火墙是否放行

# CentOS 7+ firewalld
firewall-cmd --list-all
firewall-cmd --permanent --add-service=ssh
firewall-cmd --reload

# CentOS 6 iptables
iptables -nL --line-numbers
/etc/init.d/iptables status

注意:防火墙 DROP 掉数据包时报错通常是 timeout(连接卡住直到超时),而 REJECT 才会立刻返回 refused。所以「立刻 refused」更多指向服务没起,「卡半天才失败」更多指向防火墙。

详见 07-03-防火墙与SELinux.md

第 5 步:端口是否被改过

grep -i '^Port' /etc/ssh/sshd_config      # 服务端确认实际端口
sshd -T | grep -i '^port'                 # 看生效值(含默认值)

ssh -p 2222 user1@10.0.0.2                # 客户端按实际端口连接
nmap -p 1-65535 10.0.0.2 | grep ssh       # 不知道端口时扫描(需授权)
telnet 10.0.0.2 2222                      # 快速验证某端口是否开放

第 6 步:分级调试输出

ssh -v   user1@10.0.0.2       # 一级:看握手阶段、认证方式协商
ssh -vv  user1@10.0.0.2       # 二级:更多协议细节
ssh -vvv user1@10.0.0.2       # 三级:最详细,含密钥交换全过程

调试输出的读法(重点看这几行):

输出行 说明
Connecting to 10.0.0.2 [10.0.0.2] port 22. 目标 IP 和端口是否符合预期
Connection established. TCP 已连通,说明网络和端口没问题,问题在认证阶段
Server host key: ssh-rsa SHA256:... 服务端主机密钥指纹
Authentications that can continue: publickey,password 服务端允许的认证方式(若没有 password,说明密码认证被关了)
Offering public key: /root/.ssh/id_rsa 客户端尝试的私钥
Authentications that can continue: password 反复出现 密钥被服务端拒绝,检查 authorized_keys 与权限
debug1: Next authentication method: password 已回退到密码认证
卡在 Connecting to ... 网络/防火墙问题
卡在 Connection established 之后很久 大概率是服务端 UseDNS / GSSAPIAuthentication 反查超时

服务端也可以开调试,在前台单独跑一个实例(不影响现有 sshd):

/usr/sbin/sshd -d -p 2222        # -d 前台调试模式,换个端口避免冲突
ssh -p 2222 user1@10.0.0.2       # 另开终端连接,服务端会打印完整过程

四种报错的区分

报错 含义 通常原因 排查方向
Connection refused 包到了,端口无人监听 sshd 没启动 / 端口改了 / 只监听了 127.0.0.1 / 防火墙 REJECT 服务端 systemctl status sshd、ss -tnlp
Connection timed out 包发出去没有任何回应 防火墙 DROP / 安全组未放行 / 路由不通 / 主机关机 ping、traceroute、防火墙与云安全组规则
No route to host 本机路由表找不到去路,或对端 ARP 无响应 网关配置错误 / 目标 IP 不存在 / 不在同网段且无路由 / firewalld REJECT ip route、ip neigh、网关连通性
Permission denied 网络与服务都正常,认证失败 密码错误 / 用户不存在 / 密钥不对 / PermitRootLogin no / AllowUsers 限制 /var/log/secure、ssh -v 看认证方式

口诀:refused 找服务,timeout 找防火墙,no route 找路由,denied 找认证。


九、SSH 服务端配置与加固

9.1 两个配置文件不要搞混

文件 角色 作用
/etc/ssh/sshd_config 服务端(sshd 守护进程) 决定「别人怎么连进本机」,加固改的是它
/etc/ssh/ssh_config 客户端全局 决定「本机怎么连出去」,对所有用户生效
~/.ssh/config 客户端用户级 只对当前用户生效,优先级高于全局

多了一个 d 就从「连出去」变成「连进来」,这是初学者最容易改错的地方。

9.2 sshd_config 常用配置项详解

vim /etc/ssh/sshd_config
cp -a /etc/ssh/sshd_config /etc/ssh/sshd_config.bak.$(date +%F)   # 改之前先备份

监听相关

Port 22                       # 监听端口,可写多行同时监听多个端口
Port 2222
ListenAddress 0.0.0.0         # 监听的本机地址,默认所有 IPv4 地址
ListenAddress ::              # 所有 IPv6 地址
ListenAddress 192.168.0.183   # 只监听指定网卡地址(多网卡机器常用,内网口开放、公网口不开)
AddressFamily inet            # inet=仅 IPv4,inet6=仅 IPv6,any=两者(默认)
Protocol 2                    # 仅 CentOS 6 需要,禁用不安全的 SSH v1
配置项 说明与建议
Port 默认 22。改为高位端口可减少扫描噪音;改端口后 SELinux 需 semanage port -a -t ssh_port_t -p tcp 2222,防火墙也要放行
ListenAddress 只写内网地址是很有效的加固手段——公网上根本扫不到这个端口

认证相关

PermitRootLogin no                  # 是否允许 root 直接登录
PasswordAuthentication yes          # 是否允许密码认证
PubkeyAuthentication yes            # 是否允许公钥认证
AuthorizedKeysFile .ssh/authorized_keys    # 公钥存放位置(相对用户家目录)
PermitEmptyPasswords no             # 是否允许空密码用户登录
MaxAuthTries 3                      # 单次连接允许的认证失败次数
MaxSessions 10                      # 单条连接允许的会话数
LoginGraceTime 60                   # 登录宽限时间(秒),超时未完成认证即断开

PermitRootLogin 的取值(重点):

取值 含义
yes 允许 root 用任何方式登录(含密码),默认值,不安全
no 完全禁止 root 登录,推荐
prohibit-password 允许 root 登录但禁止密码认证,只能用密钥(旧名 without-password),需要 root 密钥自动化时的折中方案
forced-commands-only 仅允许 root 执行 authorized_keys 中用 command= 强制指定的命令,用于备份等受限自动化

LoginGraceTime 的作用:从 TCP 连上到认证完成的时间窗口。设小一点(30~60 秒)可以减少半开连接被用来做资源耗尽攻击的空间;设 0 表示不限制(不建议)。

保活相关(ClientAliveInterval 与 ClientAliveCountMax 的配合计算)

ClientAliveInterval 60        # 每 60 秒向客户端发一次探测包
ClientAliveCountMax 3         # 连续 3 次无响应则断开

超时时间 = ClientAliveInterval × ClientAliveCountMax = 60 × 3 = 180 秒。

两种相反的用途,取决于你的目标:

# 目标 A:安全策略要求空闲会话自动断开(如 10 分钟)
ClientAliveInterval 300
ClientAliveCountMax 2         # 300 × 2 = 600 秒 = 10 分钟

# 目标 B:防止 NAT/防火墙因空闲回收连接,让长连接一直活着
ClientAliveInterval 60
ClientAliveCountMax 30        # 60 × 30 = 1800 秒,配合客户端 ServerAliveInterval

ClientAliveCountMax 0 表示探测一次无响应就断开,容易在网络抖动时误杀会话。

性能相关(登录慢的两个元凶)

UseDNS no                     # 关闭对客户端 IP 的反向 DNS 解析
GSSAPIAuthentication no       # 关闭 Kerberos/GSSAPI 认证协商
  • UseDNS yes 时,sshd 收到连接后会对客户端 IP 做 PTR 反向解析,再用解析出的域名正向解析回来比对。如果 DNS 服务器不可达、或客户端 IP 没有 PTR 记录(内网 IP 基本都没有),sshd 会一直等到 DNS 超时(通常 5~30 秒)才继续认证流程。表现就是「密码提示都没出来就卡十几秒」。设为 no 可以立刻消除这个延迟。OpenSSH 7.x 起默认已是 no,但大量老配置文件里仍写着 yes。
  • GSSAPIAuthentication yes 同理:客户端会尝试 GSSAPI/Kerberos 认证,在没有 Kerberos 环境的机器上这一步注定失败,但仍要经过一轮协商与可能的 DNS 查询,白白增加延迟。绝大多数环境应设为 no。
# 验证是不是这两项导致的慢
ssh -v user@10.0.0.2          # 观察在哪一步停顿最久
time ssh user@10.0.0.2 'exit' # 改配置前后对比耗时

访问控制(白名单 / 黑名单)

AllowUsers deploy ops zhangsan            # 只允许这些用户登录,其他全部拒绝
AllowUsers deploy@192.168.0.*             # 还能限制来源网段(用户@来源)
DenyUsers test guest ftpuser              # 拒绝这些用户
AllowGroups sshusers wheel                # 只允许这些组的成员登录
DenyGroups nologin_group

匹配优先级(从先到后,命中即决定):DenyUsers → AllowUsers → DenyGroups → AllowGroups。

# 配合用户组批量管理,比逐个写用户名好维护
groupadd sshusers
usermod -aG sshusers deploy
usermod -aG sshusers zhangsan

高危提醒:AllowUsers 写错(比如漏了自己当前使用的账号)会把自己直接锁在门外,而 sshd -t 不会报错——它只检查语法,不检查逻辑。

其他常用项

Banner /etc/ssh/banner.txt    # 认证前显示的提示文件(法律警告语等)
PrintMotd yes                 # 登录后显示 /etc/motd
X11Forwarding yes             # 是否允许 X11 图形转发,无图形需求时设 no
AllowTcpForwarding no         # 禁止端口转发(防止被当跳板穿透内网)
PermitTunnel no
Subsystem sftp /usr/libexec/openssh/sftp-server    # 启用 sftp 子系统,注释掉则 sftp 不可用
LogLevel INFO                 # 日志级别
StrictModes yes               # 登录前检查家目录与 .ssh 权限,权限过松则拒绝
Compression delayed

LogLevel 取值:QUIET < FATAL < ERROR < INFO(默认)< VERBOSE < DEBUG1/2/3。

  • 排查登录问题时设 VERBOSE,日志会记录登录所用密钥的指纹,便于审计「是谁的密钥登进来的」。
  • DEBUG 级别会记录敏感信息且日志量巨大,仅临时排障使用,用完改回。

Subsystem sftp 还常用于配置「只能传文件、不能登录 Shell」的受限用户:

Subsystem sftp internal-sftp
Match Group sftponly
    ChrootDirectory /data/sftp/%u
    ForceCommand internal-sftp
    AllowTcpForwarding no
    X11Forwarding no

Match 块用于对特定用户/组/来源做差异化配置,必须写在文件末尾(Match 之后的所有配置都属于该块)。

9.3 客户端配置 ~/.ssh/config

日常运维效率提升最明显的一个文件。写好之后,ssh web1 就能带上端口、用户、密钥、跳板机全部参数。

mkdir -p ~/.ssh && chmod 700 ~/.ssh
vim ~/.ssh/config
chmod 600 ~/.ssh/config       # 权限过松 ssh 会拒绝使用该文件

完整示例:

# ===== 全局默认(写在最前面的 Host * 也可放最后,先匹配先生效)=====
Host *
    ServerAliveInterval 60          # 每 60 秒发一次保活包,防 NAT 超时断连
    ServerAliveCountMax 3           # 3 次无响应则判定断开
    TCPKeepAlive yes
    ConnectTimeout 10
    Compression yes
    ControlMaster auto              # 连接复用:同一目标第二次连接秒开
    ControlPath ~/.ssh/cm-%r@%h:%p
    ControlPersist 10m
    AddKeysToAgent yes

# ===== 生产 Web 服务器 =====
Host web1
    HostName 192.168.0.183          # 真实地址,Host 只是别名
    User deploy
    Port 2222
    IdentityFile ~/.ssh/id_rsa_prod
    IdentitiesOnly yes              # 只用指定的这把私钥,避免把所有密钥都试一遍

# ===== 跳板机本身 =====
Host bastion
    HostName 203.0.113.10
    User ops
    Port 22
    IdentityFile ~/.ssh/id_rsa_bastion

# ===== 内网机器:经跳板机访问 =====
Host db1
    HostName 10.0.0.51
    User dbadmin
    ProxyJump bastion               # OpenSSH 7.3+ 的跳板写法,等价于 -J bastion
    IdentityFile ~/.ssh/id_rsa_prod

# 老版本 OpenSSH 用 ProxyCommand 实现跳板
Host db2
    HostName 10.0.0.52
    User dbadmin
    ProxyCommand ssh -q -W %h:%p bastion

# ===== 通配匹配一批机器 =====
Host 10.0.0.*
    User root
    StrictHostKeyChecking accept-new

Host github.com
    User git
    IdentityFile ~/.ssh/id_rsa_github
    IdentitiesOnly yes

使用效果:

ssh web1                              # 自动 = ssh -p 2222 -i ~/.ssh/id_rsa_prod deploy@192.168.0.183
ssh db1                               # 自动经 bastion 跳板进内网
scp app.tar.gz web1:/tmp/             # scp / sftp / rsync 全部自动复用这些配置
rsync -av ./dist/ web1:/var/www/html/
ssh -J bastion dbadmin@10.0.0.51      # 命令行临时跳板,多级用逗号分隔:-J b1,b2
ssh -G web1                           # 打印某别名最终生效的全部配置,排错用

9.4 加固清单

① 修改默认端口(作用有限,但能降噪)

sed -i 's/^#\?Port 22/Port 2222/' /etc/ssh/sshd_config
semanage port -a -t ssh_port_t -p tcp 2222      # SELinux 必须先放行,否则 sshd 起不来
firewall-cmd --permanent --add-port=2222/tcp
firewall-cmd --reload
sshd -t && systemctl restart sshd

诚实的评价:改端口不能防定向攻击(nmap 全端口扫描几十秒就找到了),但能挡掉绝大多数只扫 22 端口的自动化脚本,/var/log/secure 会清爽非常多,有助于让真实告警不被淹没。

② 禁用 root 直接登录,改用普通用户 + sudo

# 顺序很重要:先建好普通用户并验证能登录,再禁 root
useradd deploy
passwd deploy
usermod -aG wheel deploy                  # wheel 组默认可 sudo
ssh deploy@192.168.0.183                  # 必须先验证通过!
sudo -l                                   # 确认 sudo 权限可用

# 验证成功后再改
vim /etc/ssh/sshd_config                  # PermitRootLogin no
sshd -t && systemctl restart sshd

好处:攻击者不但要猜密码,还要先猜对用户名;同时所有提权操作都会留下 sudo 审计日志。

③ 优先密钥认证,关闭密码认证

PubkeyAuthentication yes
PasswordAuthentication no
PermitEmptyPasswords no
ChallengeResponseAuthentication no        # 这一项也要关,否则可能仍能走键盘交互式密码

关闭前必须先确认密钥能登录,否则重启 sshd 后你就再也进不去了。验证命令:

ssh -o PreferredAuthentications=publickey -o PasswordAuthentication=no deploy@192.168.0.183

另外注意:关掉密码认证后 ssh-copy-id 也用不了(它依赖密码登录),新机器的公钥要通过装机镜像、cloud-init 或已有运维通道下发。

密钥的生成与分发详见 04-02-计划任务与SSH等效性.md

④ 限制来源 IP 与用户白名单

AllowUsers deploy ops
AllowUsers deploy@192.168.0.*
# 更彻底的做法:在防火墙层面只放行运维网段
firewall-cmd --permanent --zone=public --remove-service=ssh
firewall-cmd --permanent --add-rich-rule='rule family=ipv4 source address=192.168.0.0/24 service name=ssh accept'
firewall-cmd --reload

⑤ 防暴力破解

MaxAuthTries 3                # 3 次失败即断开连接,抬高单连接的试错成本
LoginGraceTime 30

MaxAuthTries 只是限制单条连接内的尝试次数,攻击者反复重连仍可继续试。要真正封禁来源 IP,用 fail2ban:

yum -y install epel-release && yum -y install fail2ban
systemctl enable --now fail2ban

配置文件说明:/etc/fail2ban/jail.conf 会在软件升级时被覆盖,自定义配置一律写在 /etc/fail2ban/jail.local(同名项覆盖 jail.conf)。

# /etc/fail2ban/jail.local
[DEFAULT]
bantime  = 3600               # 封禁 1 小时(-1 为永久)
findtime = 600                # 统计窗口 10 分钟
maxretry = 5                  # 窗口内失败 5 次即封
ignoreip = 127.0.0.1/8 192.168.0.0/24   # 白名单,必须含自己的管理网段
banaction = firewallcmd-ipset # CentOS 7 firewalld 环境用这个后端

[sshd]
enabled  = true
port     = 2222
filter   = sshd
logpath  = /var/log/secure
backend  = systemd
maxretry = 3
参数 说明
bantime 封禁持续时间;对反复作恶的 IP 可配合 bantime.increment = true 递增
findtime 在多长的时间窗口内统计失败次数
maxretry 触发封禁的失败次数阈值
ignoreip 白名单,必须写上自己的管理网段,否则极易把自己封掉
port 与 sshd_config 中的 Port 保持一致,改过端口一定要同步
logpath CentOS/RHEL 认证日志为 /var/log/secure(Debian 系为 /var/log/auth.log)
backend 日志读取方式;CentOS 7 上 systemd 比轮询文件更可靠
banaction 执行封禁的后端,firewalld 环境用 firewallcmd-ipset,纯 iptables 环境用默认的 iptables-multiport
fail2ban-client status                     # 查看所有 jail 状态
fail2ban-client status sshd                # 查看当前封禁列表与失败计数
fail2ban-client set sshd unbanip 1.2.3.4   # 手工解封(万一封了自己)
fail2ban-client set sshd banip 1.2.3.4     # 手工封禁
fail2ban-client reload                     # 重载配置

# 校验过滤规则能否匹配到日志中的失败记录(配好却不生效时先跑这个)
fail2ban-regex /var/log/secure /etc/fail2ban/filter.d/sshd.conf

⚠️ 把自己封了怎么办:先确认 ignoreip 已包含管理网段;已被封时通过控制台 / VNC / IPMI 等带外通道执行上面的 unbanip。启用密钥登录后基本不会触发失败计数,两项措施应配合使用。

⑥ 排查暴力破解的来源 IP

# 当前已建立的 SSH 连接
ss -tnp | grep :22
ss -tnp state established '( sport = :22 )'
who / w                                     # 当前登录用户与来源

# 从日志统计失败来源(核心手段)
grep 'Failed password' /var/log/secure | awk '{print $11}' | sort | uniq -c | sort -rn | head -20
grep 'Invalid user' /var/log/secure | awk '{print $10,$12}' | sort | uniq -c | sort -rn | head
grep -c 'Failed password' /var/log/secure   # 失败总次数,感受一下被扫的强度

# 成功登录记录(重点审计有没有可疑成功)
grep 'Accepted' /var/log/secure
last -20                                    # 登录历史(/var/log/wtmp)
lastb -20                                   # 失败登录历史(/var/log/btmp)
lastlog                                     # 每个用户最后一次登录时间

CentOS/RHEL 的 SSH 日志在 /var/log/secure,Debian/Ubuntu 在 /var/log/auth.log。

⑦ 权限必须正确(高频坑)

密钥认证失败最常见的原因不是密钥不对,而是权限过松。sshd 的 StrictModes yes 会检查权限,一旦发现「组或其他用户可写」就直接拒绝认证,且客户端只看到一句 Permission denied (publickey),毫无提示。

对象 要求权限 命令
家目录 ~ 不能被组/其他用户可写(755 / 700) chmod 755 ~
~/.ssh 目录 700 chmod 700 ~/.ssh
~/.ssh/authorized_keys 600(644 也能工作,但推荐 600) chmod 600 ~/.ssh/authorized_keys
私钥 ~/.ssh/id_rsa 600,否则 ssh 客户端直接报错拒绝使用 chmod 600 ~/.ssh/id_rsa
~/.ssh/config 600 chmod 600 ~/.ssh/config
属主 必须是该用户本人 chown -R user:user ~/.ssh
# 一条命令修好
chmod 700 ~/.ssh && chmod 600 ~/.ssh/authorized_keys ~/.ssh/id_rsa && chown -R $USER:$USER ~/.ssh

# 私钥权限过松时客户端的报错
# @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
# @         WARNING: UNPROTECTED PRIVATE KEY FILE!          @
# Permissions 0644 for '/root/.ssh/id_rsa' are too open.
# This private key will be ignored.

# 启用了 SELinux 时还要修上下文
restorecon -Rv ~/.ssh
ls -Zd ~/.ssh                      # 正确应为 ssh_home_t

9.5 改配置的正确流程

# 0. 【最重要】保留当前这个已登录的 SSH 会话,另开一个新窗口做验证
# 1. 备份
cp -a /etc/ssh/sshd_config /etc/ssh/sshd_config.bak.$(date +%F-%H%M)

# 2. 修改
vim /etc/ssh/sshd_config

# 3. 语法检查(不通过绝对不要重启)
sshd -t
sshd -t -f /etc/ssh/sshd_config          # 指定文件检查

# 4. 查看最终生效的值(含所有默认值,比 grep 配置文件可靠)
sshd -T | grep -E 'permitrootlogin|passwordauthentication|^port|maxauthtries|usedns|allowusers'

# 5. 生效
systemctl reload sshd                     # 优先 reload:不影响已有连接
systemctl restart sshd                    # CentOS 7+
service sshd restart                      # CentOS 6
/etc/init.d/sshd reload                   # CentOS 6

# 6. 【不可省略】在新窗口验证能登录
ssh -p 2222 deploy@192.168.0.183

为什么必须保留一个已登录会话?

sshd 重启只影响新建连接,已经建立的 SSH 会话不会被断开。所以只要你手上还留着一个活的会话,即使新配置有问题(端口错、AllowUsers 漏了自己、密码认证关了而密钥没配好),你依然可以在这个老会话里把配置改回去、把服务重新起来。

反过来,如果你改完配置就 exit 退出,而新配置恰好有问题,那么这台机器对你来说就彻底失联了——只能去机房接显示器,或者依赖云平台的 VNC / 串口控制台 / 救援模式。远程机房环境下这可能意味着几个小时的停机。

补充两条保险措施:

  • sshd -t 只检查语法,不检查逻辑。把自己排除在 AllowUsers 之外、端口和防火墙不匹配,它都不会报错,所以第 6 步的实际登录验证不可替代。
  • 临时开一个备用端口的 sshd 实例作为退路:/usr/sbin/sshd -p 2200,验证新配置无误后再杀掉它。

十、文件传输:scp / sftp / rsync

三者都走 SSH 加密通道,不需要额外部署服务,只要 sshd 能连上就能用。

10.1 scp:远程拷贝

scp(secure copy)基于 SSH,传输全程加密,用法几乎与 cp 一致,只是路径可以带 用户@主机: 前缀。

通用语法:

scp [选项] 源路径 目标路径
scp [选项] [user@]host:/remote/path /local/path      # 远程 → 本地
scp [选项] /local/path [user@]host:/remote/path      # 本地 → 远程

① 本地 → 远程

# 拷贝单个文件到远程目录
scp /share/iso/rhel-5.4-server-i386-dvd.iso 10.0.0.2:/root/Desktop/
scp /bin/bash zhangsan@10.1.1.2:/tmp/newbash          # 同时改名

# 拷贝目录必须加 -r
scp -r /share/iso/ 10.0.0.2:/root/Desktop/
scp -r /local/dir user@10.0.0.2:/remote/path

# 一次拷多个文件
scp /etc/hosts /etc/resolv.conf 10.0.0.2:/tmp/

② 远程 → 本地

# 拷贝单个文件
scp 10.0.0.2:/root/Desktop/sn.txt /root/Desktop/
scp zhangsan@192.168.0.182:/some/where/file /local/dir/file

# 拷贝目录加 -r
scp -r 10.0.0.2:/root/Desktop/ /root/Desktop/
scp -r zhangsan@192.168.0.182:/some/where/dirname /local/dir/

# 拷到当前目录
scp 10.0.0.2:/var/log/messages .

③ 指定用户

scp 文件 user1@10.0.0.2:/home/user1/
scp -r /data/backup deploy@10.0.0.2:/data/

不写 用户@ 时,scp 使用本地当前用户名去登录远程主机,用户不存在就会认证失败。

④ 远程 → 远程(本机作为中转控制)

scp user1@10.0.0.2:/tmp/a.txt user2@10.0.0.3:/tmp/
scp -3 user1@10.0.0.2:/tmp/a.txt user2@10.0.0.3:/tmp/   # -3 强制数据经本机转发

常用选项

选项 作用
-r 递归拷贝目录(传目录必须加,否则报 not a regular file)
-P 端口 指定 SSH 端口,大写 P
-p 保留源文件的修改时间、访问时间和权限模式
-C 传输时压缩,跨广域网传文本/日志效果明显,传已压缩文件反而变慢
-l 速率 限速,单位 Kbit/s(注意是比特不是字节,-l 8000 ≈ 1 MB/s)
-i 私钥 指定私钥文件
-q 静默模式,不显示进度条(脚本中常用)
-v 调试输出
-o 选项 传递 ssh 选项,如 -o StrictHostKeyChecking=no
-3 远程到远程时经本地中转
scp -P 2222 -r /data/www deploy@10.0.0.2:/var/www/       # 非默认端口传目录
scp -Cp -l 8000 bigfile.tar.gz 10.0.0.2:/data/           # 压缩 + 保留属性 + 限速约 1MB/s
scp -i ~/.ssh/id_rsa_prod app.jar deploy@10.0.0.2:/opt/

易错点:-P 与 -p 的大小写

命令 指定端口 保留属性
ssh -p 2222(小写) —
scp -P 2222(大写) -p(小写)
sftp -P 2222(大写) —
rsync 没有端口选项,用 -e 'ssh -p 2222' -p 或 -a

用小写 -p 指定端口的后果是:scp 以为你要保留文件属性,然后按默认 22 端口去连,报 Connection refused,很容易误判成服务端问题。根治办法是在 ~/.ssh/config 里为该主机写好 Port,scp 会自动读取。

路径中含空格与特殊字符的引号处理

远程路径会被远程 Shell 再解析一次,因此需要双层转义:

scp "10.0.0.2:/tmp/my file.txt" ./              # 错误:远程 Shell 拆成两个参数
scp "10.0.0.2:'/tmp/my file.txt'" ./            # 正确:内层单引号供远程 Shell 使用
scp 10.0.0.2:/tmp/my\\\ file.txt ./             # 正确:三反斜杠转义
scp '10.0.0.2:/tmp/a\ b.txt' ./                 # 正确

# 本地路径只需本地一层引号
scp "/local/my file.txt" 10.0.0.2:/tmp/

# 含 $ ( ) & 等字符同理,用单引号包住远程部分
scp "10.0.0.2:'/tmp/log\$(date).txt'" ./

含空格路径较多时,建议改用 rsync(它对引号的处理更直观)或 sftp。

目标路径为目录与为文件名时的行为差异

# 目标是已存在的目录 → 文件放进该目录,保持原名
scp a.txt 10.0.0.2:/tmp/            # 结果:/tmp/a.txt
scp a.txt 10.0.0.2:/tmp             # 同上(/tmp 是已存在目录)

# 目标是不存在的路径 → 当作文件名,即「拷贝并改名」
scp a.txt 10.0.0.2:/tmp/b.txt       # 结果:/tmp/b.txt(内容是 a.txt)
scp a.txt 10.0.0.2:/tmp/newdir/     # 报错:No such file or directory(不会自动建目录)

# 目标是已存在的文件 → 直接覆盖,无任何提示,且无法恢复
scp a.txt 10.0.0.2:/etc/hosts       # 危险:/etc/hosts 被静默覆盖

# 目录的特殊情况
scp -r /data/www 10.0.0.2:/var/           # /var/www 不存在 → 建立 /var/www
scp -r /data/www 10.0.0.2:/var/           # /var/www 已存在 → 变成 /var/www/www(嵌套!)

最后一条是 scp 传目录最常见的意外,重复执行同一条命令会不断加深嵌套层级。规避方法:明确写出目标目录名 scp -r /data/www 10.0.0.2:/var/www,或改用 rsync。

scp 从 OpenSSH 8.8 起底层默认改用 SFTP 协议,并且官方已把 scp 标记为不推荐(协议设计陈旧,存在服务端可控制客户端写入路径的风险)。新环境建议优先用 sftp 或 rsync。

10.2 sftp:交互式安全传输

sftp 10.0.0.2                       # 以当前用户登录
sftp user1@10.0.0.2                 # 指定用户
sftp -P 2222 user1@10.0.0.2         # 指定端口(大写 P)
sftp -i ~/.ssh/id_rsa user@host

登录后进入 sftp> 提示符。常用子命令(记忆规律:带 l 前缀的操作本地,不带的操作远程):

子命令 作用
ls / ll 列出远程当前目录
lls 列出本地当前目录
pwd 显示远程当前目录
lpwd 显示本地当前目录
cd 目录 切换远程目录
lcd 目录 切换本地目录
get 远程文件 [本地名] 下载文件
get -r 远程目录 下载目录(递归)
put 本地文件 [远程名] 上传文件
put -r 本地目录 上传目录
mget 文件模式 批量下载,支持通配符,如 mget *.log
mput 文件模式 批量上传,如 mput *.conf
rm 文件 删除远程文件
rmdir 目录 删除远程空目录
mkdir 目录 在远程创建目录
lmkdir 目录 在本地创建目录
rename 旧名 新名 重命名远程文件
chmod 权限 文件 修改远程文件权限
chown/chgrp 修改远程属主/属组
df -h 查看远程磁盘使用
!命令 执行本地 Shell 命令,如 !ls -l
! 临时切到本地 Shell,exit 返回
progress 开关传输进度条
bye / quit / exit 退出
help / ? 查看全部子命令

典型会话:

sftp> lcd /root/download
sftp> cd /var/log
sftp> ls
sftp> get messages
sftp> mget *.log
sftp> lcd /etc
sftp> put nginx.conf /tmp/nginx.conf
sftp> put -r /data/www
sftp> rm /tmp/old.tar.gz
sftp> mkdir /tmp/backup
sftp> bye

批处理模式 -b(脚本中免交互使用)

# 把子命令写进文件
cat > /root/sftp.batch <<'EOF'
cd /data/upload
put /tmp/report.csv
get remote.log /tmp/
bye
EOF

sftp -b /root/sftp.batch user1@10.0.0.2
sftp -b - user1@10.0.0.2 <<'EOF'          # -b - 表示从标准输入读
cd /tmp
put a.txt
bye
EOF

# 批处理默认遇错即退出;在子命令前加 - 可忽略该行错误
sftp -b batch.txt -oBatchMode=yes user@host    # BatchMode=yes 禁止密码提示,必须已配密钥

sftp 与传统 ftp 的区别

对比项 ftp sftp
底层协议 FTP(21 控制端口 + 数据端口) SSH 子系统,只用 22 一个端口
加密 明文(含用户名密码) 全程加密
需要额外服务 需装并运行 vsftpd / proftpd 不需要,sshd 自带(Subsystem sftp)
防火墙 主动/被动模式,数据端口配置麻烦 只放行 22 即可
账号 可用虚拟账号 使用系统账号
匿名访问 支持 不支持
断点续传 支持 支持(reget / reput)

结论:内部运维传文件优先 sftp;需要对外提供匿名下载、大量虚拟账号或严格的目录配额时才考虑 FTP 服务。

详见 09-03-FTP服务.md

10.3 rsync:增量同步

原理与优势

rsync 采用「快速检查 + 滚动校验(rolling checksum)」算法:

  1. 先按文件大小和修改时间筛选出可能有变化的文件(默认行为,很快)。
  2. 对需要传输的文件,把它切成固定大小的块,逐块计算弱校验(Adler-32)与强校验(MD5),只把目标端缺失或不同的块通过网络传过去。

相比 scp 的优势:

对比项 scp rsync
传输方式 每次全量传输 增量,只传差异部分
重复执行 全部重传 未变化的文件直接跳过,秒级完成
中断续传 从头开始 -P 支持断点续传
删除同步 不支持 --delete 使目标与源完全一致
排除文件 不支持 --exclude 灵活过滤
保留属性 -p 部分 -a 完整保留权限/属主/时间/软链接
限速 -l(Kbit/s) --bwlimit(KB/s)
适用场景 一次性拷贝小文件 备份、代码发布、目录镜像、定期同步

对于「每天同步一个 10 GB 目录,其中只改了 20 MB」这种场景,scp 要传 10 GB,rsync 只传约 20 MB,差距是量级的。

常用选项

选项 作用
-a 归档模式,等价于 -rlptgoD:递归 + 保留软链接、权限、时间、属组、属主、设备文件。最常用
-v 显示传输过程(-vv、-vvv 更详细)
-z 传输时压缩,跨公网传文本效果好
-P 等于 --partial --progress:显示进度条 + 保留未传完的部分文件以便续传
-r 递归(-a 已包含)
-l 保留软链接本身;-L 则把软链接转成实际文件
-h 输出使用人类可读的单位
-n / --dry-run 只试运行不实际传输,配合 -v 预览将发生什么,用 --delete 前必做
--delete 删除目标端存在但源端没有的文件,使两端完全一致
--exclude=模式 排除匹配的文件/目录,可多次使用
--exclude-from=文件 从文件读取排除列表
--include=模式 在排除规则中开例外(须写在对应 --exclude 之前)
-e 'ssh -p 端口' 指定远程 Shell 及其参数(改过 SSH 端口时必用)
--bwlimit=KB/s 限速,如 --bwlimit=5000 约 5 MB/s
--backup --backup-dir=目录 覆盖前先把旧文件备份到指定目录
-u / --update 跳过目标端比源端更新的文件
-c 强制按内容校验和比较(忽略时间/大小快速判断),慢但最可靠
--stats 传输结束后输出统计信息

结尾斜杠的关键差异(rsync 最重要的一个细节)

规则只有一句:源路径结尾有 / 表示「同步该目录里的内容」,没有 / 表示「同步该目录本身」。 目标路径的斜杠没有影响。

# 假设本地存在 /data/www/ 目录,里面有 index.html

情况 A:源路径带结尾斜杠

rsync -av /data/www/ 10.0.0.2:/var/dest/
# 结果:/var/dest/index.html
# 含义:把 www 目录「内部的内容」放到 dest 里

情况 B:源路径不带结尾斜杠

rsync -av /data/www 10.0.0.2:/var/dest/
# 结果:/var/dest/www/index.html
# 含义:把 www 这个「目录本身」放进 dest,多出一层 www

记忆方法:/data/www/ 读作「www 里的东西」,/data/www 读作「www 这个目录」。或者:带斜杠 = 内容,不带 = 目录。

这个差异配合 --delete 会造成严重后果:

# 意图:让远程 /var/www 与本地 /data/www 完全一致
rsync -av --delete /data/www  10.0.0.2:/var/www/    # 错!变成 /var/www/www/,
                                                    # 且 --delete 会删掉 /var/www 下原有全部内容
rsync -av --delete /data/www/ 10.0.0.2:/var/www/    # 对

所以任何带 --delete 的命令,第一次都必须先加 -n 试运行:

rsync -avn --delete /data/www/ 10.0.0.2:/var/www/   # 先看清楚要删哪些文件

本地与远程互传

# 本地 → 本地(也可以当增量 cp 用)
rsync -av /data/www/ /backup/www/

# 本地 → 远程(推送)
rsync -avz /data/www/ deploy@10.0.0.2:/var/www/html/
rsync -avz -e 'ssh -p 2222' /data/www/ deploy@10.0.0.2:/var/www/html/

# 远程 → 本地(拉取)
rsync -avz deploy@10.0.0.2:/var/log/nginx/ /backup/nginx-logs/
rsync -avzP -e 'ssh -p 2222 -i ~/.ssh/id_rsa_prod' deploy@10.0.0.2:/data/db.sql /backup/

# 配好 ~/.ssh/config 后可以极简
rsync -avzP /data/www/ web1:/var/www/html/

实用实例

# ① 代码发布:排除版本控制与临时文件,删除目标端多余文件
rsync -avz --delete \
  --exclude='.git/' --exclude='node_modules/' --exclude='*.log' --exclude='.env' \
  /home/dev/project/ deploy@10.0.0.2:/var/www/app/

# ② 每日备份:保留完整属性,限速避免占满带宽,出错重试
rsync -az --delete --bwlimit=5000 --stats \
  /data/ backup@10.0.0.9:/backup/$(hostname)/ >> /var/log/rsync-backup.log 2>&1

# ③ 断点续传大文件
rsync -avzP /iso/CentOS-7-x86_64-DVD.iso 10.0.0.2:/data/iso/
# 中断后重复执行同一条命令,从断点继续

# ④ 覆盖前保留旧版本,便于回滚
rsync -av --backup --backup-dir=/backup/$(date +%F) /data/www/ 10.0.0.2:/var/www/

# ⑤ 从排除列表文件读取规则
cat > /root/rsync-exclude.txt <<'EOF'
.git/
node_modules/
*.tmp
/cache/
EOF
rsync -avz --exclude-from=/root/rsync-exclude.txt /app/ 10.0.0.2:/app/

# ⑥ 只同步某类文件(include 要写在 exclude 之前)
rsync -avz --include='*/' --include='*.conf' --exclude='*' /etc/ 10.0.0.2:/backup/etc/

# ⑦ 配合 crontab 定时同步
# 0 2 * * * /usr/bin/rsync -az --delete /data/ backup@10.0.0.9:/backup/data/

10.4 三者选型对照

维度 scp sftp rsync
底层通道 SSH SSH 子系统 默认走 SSH(也可用 rsync 守护进程 873/tcp)
交互方式 一次性命令 交互式会话(可浏览目录) 一次性命令
增量传输 不支持,每次全量 不支持 支持,核心优势
断点续传 不支持 支持(reget/reput) 支持(-P)
删除同步 不支持 手工 rm --delete
排除过滤 不支持 不支持 --exclude
保留属性 -p(部分) 有限 -a(最完整)
压缩 -C -C -z
限速 -l(Kbit/s) -l --bwlimit(KB/s)
远程需安装 无(sshd 自带) 无(sshd 自带) 两端都要装 rsync
学习成本 最低 低 中(斜杠、过滤规则有坑)
适用场景 临时拷几个文件、脚本里一次性传输 不确定远程有什么文件、需要边看边传 备份、代码发布、目录镜像、定期同步

一句话选型:临时拷文件用 scp,要浏览着传用 sftp,反复同步目录一律用 rsync。

# rsync 不是默认安装的,注意两端都要装
yum -y install rsync            # CentOS
rpm -q rsync

十一、排错清单

11.1 连不上:分层排查流程

无论报什么错,都按这五层从下往上查,每一层确认通过再进入下一层。

第 1 层:物理链路与网卡状态

mii-tool eth0                    # link ok / no link(CentOS 6)
ethtool eth0 | grep -E 'Link detected|Speed|Duplex'
ip link show                     # state UP 且有 LOWER_UP 才算物理通
ip addr show                     # 确认 IP 配上了

no link / Link detected: no → 网线、光模块、交换机端口问题,不用往上查了。

第 2 层:IP 与路由(ping)

ping -c 2 127.0.0.1              # 协议栈本身
ping -c 2 本机IP                  # 本机网卡
ping -c 2 网关IP                  # 同网段与网关
ping -c 2 目标IP                  # 目标主机
ping -c 2 8.8.8.8                # 外网连通性
ping -c 2 www.baidu.com          # DNS 解析
ip route                         # 默认网关是否正确
traceroute -n 目标IP              # 断在哪一跳

注意:很多环境(云安全组、Windows 防火墙)默认禁 ICMP,ping 不通不代表 SSH 连不上,应该直接测端口。

第 3 层:端口可达性

# 客户端侧测试
telnet 10.0.0.2 22               # 出现 SSH-2.0-OpenSSH_xxx 即端口通
nc -zv 10.0.0.2 22               # Connection to 10.0.0.2 22 port succeeded!
nc -zv -w 3 10.0.0.2 22
nmap -p 22 10.0.0.2
timeout 3 bash -c 'cat < /dev/null > /dev/tcp/10.0.0.2/22' && echo open

# 服务端侧确认
ss -tnlp | grep 22
firewall-cmd --list-all          # CentOS 7+
iptables -nL                     # CentOS 6

第 4 层:sshd 服务本身

systemctl status sshd            # CentOS 7+
service sshd status              # CentOS 6
sshd -t                          # 配置语法
sshd -T | grep -E '^port|listenaddress|permitrootlogin|passwordauthentication'
journalctl -u sshd -n 50 --no-pager
tail -50 /var/log/secure

第 5 层:认证

ssh -v user@10.0.0.2                    # 看认证方式协商
grep -E 'Failed|Invalid|Accepted' /var/log/secure | tail -20
id user                                  # 用户是否存在
passwd -S user                           # 密码是否被锁(LK 表示锁定)
chage -l user                            # 密码是否过期
grep user /etc/shadow                    # 密码字段以 ! 或 * 开头表示锁定
grep user /etc/passwd                    # Shell 是否为 /sbin/nologin
ls -ld ~/.ssh ~/.ssh/authorized_keys     # 权限

分层口诀:先看灯(链路),再通 IP,再探端口,再查服务,最后查认证。

11.2 Permission denied (publickey)

报错:

Permission denied (publickey).
Permission denied (publickey,gssapi-keyex,gssapi-with-mic).

含义:网络与服务都正常,是认证阶段被拒。而且服务端只允许公钥认证(提示里没有 password),说明密码认证已被关闭。按顺序排查:

① 权限问题(最常见,占一半以上)

# 服务端上,被登录用户的家目录
ls -ld /home/deploy /home/deploy/.ssh /home/deploy/.ssh/authorized_keys

chmod 755 /home/deploy                          # 家目录不能组/其他可写
chmod 700 /home/deploy/.ssh
chmod 600 /home/deploy/.ssh/authorized_keys
chown -R deploy:deploy /home/deploy/.ssh

# 客户端上,私钥权限
chmod 600 ~/.ssh/id_rsa

StrictModes yes(默认)会因为「家目录组可写」这种看起来无关的问题直接拒绝认证,且日志里才有原因,客户端看不到:

tail -f /var/log/secure
# Authentication refused: bad ownership or modes for directory /home/deploy
# Authentication refused: bad ownership or modes for file /home/deploy/.ssh/authorized_keys

② authorized_keys 内容问题

cat ~/.ssh/authorized_keys | head            # 确认公钥确实写进去了
wc -l ~/.ssh/authorized_keys                 # 每把公钥必须占「一整行」,不能换行断开
ssh-keygen -l -f ~/.ssh/authorized_keys      # 列出其中每把公钥的指纹
ssh-keygen -l -f ~/.ssh/id_rsa.pub           # 与客户端公钥指纹对比,必须一致

常见错误:手工复制粘贴时公钥被编辑器折行、开头 ssh-rsa 少了、结尾多了空格或 ^M(从 Windows 复制)。

sed -i 's/\r$//' ~/.ssh/authorized_keys      # 清除 Windows 换行符

③ sshd_config 配置问题

sshd -T | grep -Ei 'pubkeyauth|authorizedkeysfile|permitrootlogin|allowusers|allowgroups|strictmodes'

逐项确认:

PubkeyAuthentication yes                    # 必须为 yes
AuthorizedKeysFile .ssh/authorized_keys     # 路径是否被改到别处
PermitRootLogin prohibit-password           # 登 root 时相关
AllowUsers deploy                           # 你的用户是否在白名单里
StrictModes yes

④ SELinux 上下文问题

家目录是手工创建或从别处拷来的,.ssh 的 SELinux 类型可能不对,sshd 就读不到 authorized_keys:

getenforce                                   # Enforcing 时才有此问题
ls -Zd ~/.ssh ~/.ssh/authorized_keys
# 正确:unconfined_u:object_r:ssh_home_t:s0
# 错误:unconfined_u:object_r:user_home_t:s0 或 admin_home_t

restorecon -Rv ~/.ssh                        # 修复上下文
ausearch -m avc -ts recent | grep sshd       # 查看 SELinux 拒绝记录
grep denied /var/log/audit/audit.log | tail

⑤ 客户端调试

ssh -vvv deploy@10.0.0.2 2>&1 | grep -iE 'offering|authentications|denied|key'
# Offering public key: ~/.ssh/id_rsa           客户端确实提交了这把
# Authentications that can continue: publickey  服务端拒绝后仍要求公钥 → 服务端侧问题
ssh -i ~/.ssh/id_rsa -o IdentitiesOnly=yes deploy@10.0.0.2   # 明确指定密钥排除干扰

11.3 登录很慢(卡十几秒才出密码提示)

两大原因,服务端改配置解决:

# /etc/ssh/sshd_config
UseDNS no                     # ① 关闭客户端 IP 反向 DNS 解析
GSSAPIAuthentication no       # ② 关闭 Kerberos/GSSAPI 协商
sshd -t && systemctl restart sshd        # CentOS 7+
service sshd restart                     # CentOS 6

# 验证效果
time ssh user@10.0.0.2 'exit'
ssh -v user@10.0.0.2                     # 看在哪一步停顿
  • UseDNS yes:sshd 反查客户端 IP 的 PTR 记录,内网 IP 通常没有 PTR,只能干等 DNS 超时。
  • GSSAPIAuthentication yes:客户端先尝试 Kerberos,没有 KDC 环境时这一步注定失败但仍要走完协商。

客户端侧也可以规避:

ssh -o GSSAPIAuthentication=no user@host
# ~/.ssh/config
Host *
    GSSAPIAuthentication no

第三个次要原因:/etc/hosts 里没有互绑主机名,或 /etc/resolv.conf 里配了不可达的 DNS。

# 多台服务器互绑,能显著加快连接
cat >> /etc/hosts <<'EOF'
10.0.0.2   qianyun.com
10.0.0.61  server61.com
EOF

11.4 连接一段时间自动断开

Write failed: Broken pipe
Connection to 10.0.0.2 closed by remote host.
client_loop: send disconnect: Broken pipe

三个可能的责任方,逐个排查:

① 服务端 sshd 主动踢人

sshd -T | grep -i clientalive
# clientaliveinterval 300
# clientalivecountmax 2      →  300 × 2 = 600 秒后断开
# 想让长连接保持:拉长总时长
ClientAliveInterval 60
ClientAliveCountMax 30        # 60 × 30 = 1800 秒

② Shell 层的空闲超时 TMOUT

echo $TMOUT                              # 有数值(秒)就是它在起作用
grep -rn TMOUT /etc/profile /etc/profile.d/ /etc/bashrc ~/.bashrc ~/.bash_profile

unset TMOUT                              # 当前会话临时解除
export TMOUT=0                           # 等价

TMOUT 是 bash 的内置变量,到时间会直接退出登录 Shell,表现为「一段时间不动就被踢出去了」,常见于按等级保护要求做过安全基线的机器。注意:出于合规要求设置的 TMOUT 通常带 readonly,无法 unset,此时应该用 screen / tmux 保持工作现场。

③ 中间设备(NAT、防火墙、云负载均衡)回收空闲连接

客户端侧发保活包最有效:

ssh -o ServerAliveInterval=60 -o ServerAliveCountMax=3 user@host
# ~/.ssh/config,一次配好长期有效
Host *
    ServerAliveInterval 60
    ServerAliveCountMax 3
    TCPKeepAlive yes

ServerAliveInterval(客户端发给服务端)与 ClientAliveInterval(服务端发给客户端)方向相反,两边配一个就够,通常配客户端更方便。

根治办法:用 tmux / screen

tmux new -s work                 # 新建会话
# 断线后重连
tmux attach -t work
tmux ls

screen -S work
screen -r work

网络断了会话仍在服务端运行,长时间任务(编译、数据迁移、yum update)都应该放在 tmux 里跑。

11.5 Too many authentication failures

Received disconnect from 10.0.0.2 port 22:2: Too many authentication failures
Disconnected from 10.0.0.2 port 22

成因:客户端把 ~/.ssh/ 下所有私钥(以及 ssh-agent 里加载的全部密钥)挨个提交给服务端尝试,还没试到对的那把,就先撞上了服务端的 MaxAuthTries 限制(默认 6,加固后常设为 3)。密钥越多越容易触发。

ssh-add -l                       # 看 agent 里加载了多少把密钥
ssh -v host 2>&1 | grep -i offering    # 看客户端依次提交了哪些密钥

解决办法:

# ① 明确指定密钥 + IdentitiesOnly(最推荐)
ssh -i ~/.ssh/id_rsa_prod -o IdentitiesOnly=yes deploy@10.0.0.2

# ② 临时清空 agent
ssh-add -D                       # 删除 agent 中所有密钥
ssh-add ~/.ssh/id_rsa_prod       # 只加载需要的那把

# ③ 强制只用密码认证登录一次
ssh -o PubkeyAuthentication=no -o PreferredAuthentications=password user@10.0.0.2

IdentitiesOnly yes 的含义是「只使用 IdentityFile 明确指定的密钥,忽略 agent 里的其他密钥」。写进 ~/.ssh/config 可永久解决:

Host web1
    HostName 192.168.0.183
    User deploy
    IdentityFile ~/.ssh/id_rsa_prod
    IdentitiesOnly yes

服务端侧也可适当放宽(但不建议为此把 MaxAuthTries 调很大,那会削弱防暴力破解能力)。

11.6 Host key verification failed

Host key verification failed.

本地 known_hosts 里的主机密钥与服务端出示的不一致(详见 8.3 节)。

ssh-keygen -R 10.0.0.2                   # 删掉这一条记录,重连后重新确认指纹
ssh-keygen -R "[10.0.0.2]:2222"          # 非默认端口
ssh-keygen -F 10.0.0.2                   # 先查询确认有这条记录
ssh-keyscan -t rsa,ecdsa 10.0.0.2 >> ~/.ssh/known_hosts   # 批量重建

如果是你自己刚重装了这台机器,那就是正常变更,清理即可;如果没有任何人动过这台机器,就要警惕中间人攻击,先通过带外渠道核对服务端指纹再继续。

# 到服务端上核对(对比客户端报错里显示的指纹)
ssh-keygen -lf /etc/ssh/ssh_host_rsa_key.pub
ssh-keygen -lf /etc/ssh/ssh_host_ecdsa_key.pub

11.7 Connection reset by peer

ssh_exchange_identification: read: Connection reset by peer
kex_exchange_identification: Connection closed by remote host
Connection reset by 10.0.0.2 port 22

服务端在完成握手前主动断开了连接(发了 RST)。可能原因:

原因 检查方法
被 /etc/hosts.deny(TCP Wrappers)拒绝 cat /etc/hosts.deny /etc/hosts.allow;CentOS 6 常用,7 之后 sshd 多数已不链接 libwrap
被 fail2ban / DenyHosts 封禁了你的 IP fail2ban-client status sshd;fail2ban-client set sshd unbanip 你的IP
服务端连接数达到上限 sshd -T \| grep -i maxstartups;MaxStartups 10:30:100
服务端资源耗尽(内存/文件描述符/进程数满) free -m、df -h、dmesg | tail、ulimit -n
/var 分区写满导致无法记日志 df -h /var
主机密钥文件损坏或权限错误 ls -l /etc/ssh/ssh_host_*(私钥须 600)、sshd -t
中间设备(IPS/WAF/负载均衡)重置连接 换网络环境测试,或在服务端抓包 tcpdump -i any port 22 -nn
密钥交换算法不兼容(新客户端连老服务端) ssh -o KexAlgorithms=+diffie-hellman-group1-sha1 -o HostKeyAlgorithms=+ssh-rsa user@host
# 服务端排查组合
tail -50 /var/log/secure
journalctl -u sshd -n 50 --no-pager
df -h /var; free -m
ls -l /etc/ssh/ssh_host_*
tcpdump -i any port 22 -nn -c 20        # 确认包到没到、是谁发的 RST

11.8 scp 报 not a regular file

scp /data/www 10.0.0.2:/tmp/
# /data/www: not a regular file

原因:源路径是目录,而 scp 默认只传普通文件。加 -r 递归即可:

scp -r /data/www 10.0.0.2:/tmp/

同类错误还有:

scp *.log 10.0.0.2:/tmp/          # 目录里有子目录时报同样的错,加 -r 或用 tar 打包
tar zcf - /data/www | ssh 10.0.0.2 'tar zxf - -C /tmp'    # 目录层级多时更快

另一个易混的报错:

scp: /tmp/newdir/: No such file or directory      # 目标目录不存在,scp 不会自动创建
ssh 10.0.0.2 'mkdir -p /tmp/newdir' && scp -r /data 10.0.0.2:/tmp/newdir/

11.9 rsync 结尾斜杠导致目录层级错乱

现象:同步完成后目标端变成 /var/www/www/index.html,多了一层;重复执行还会继续加深成 /var/www/www/www/。

rsync -av /data/www  10.0.0.2:/var/www/    # 错:源不带斜杠 → /var/www/www/
rsync -av /data/www/ 10.0.0.2:/var/www/    # 对:源带斜杠  → /var/www/

规则复述一遍:源路径结尾带 / = 同步目录里的内容;不带 / = 把目录本身放进去。

补救与预防:

# 已经错了:删掉多出来的那层重新同步
ssh 10.0.0.2 'rm -rf /var/www/www'
rsync -av --delete /data/www/ 10.0.0.2:/var/www/

# 预防:任何 --delete 操作先 dry-run
rsync -avn --delete /data/www/ 10.0.0.2:/var/www/ | head -30
rsync -avn --delete --stats /data/www/ 10.0.0.2:/var/www/    # 看会删多少个文件

--delete 加上斜杠写错,等于「把目标目录下原有的全部内容删掉,再塞进一个嵌套目录」,在生产环境是可以造成事故的组合,务必先试运行。

11.10 其他常见报错速查

报错 原因 处理
ssh: Could not resolve hostname xxx 主机名无法解析 检查 /etc/hosts、/etc/resolv.conf,或直接用 IP
Permission denied, please try again. 密码错误 / 用户被锁 passwd -S user、chage -l user、faillock
Received disconnect: Too many authentication failures 密钥试太多次 -o IdentitiesOnly=yes、ssh-add -D
Bad owner or permissions on ~/.ssh/config 配置文件权限过松 chmod 600 ~/.ssh/config
sign_and_send_pubkey: signing failed 私钥损坏或 agent 异常 ssh-add -D 后重新加载;检查私钥完整性
Agent admitted failure to sign using the key ssh-agent 未加载密钥 ssh-add ~/.ssh/id_rsa
no matching key exchange method found 新客户端连老服务端,算法被弃用 ssh -o KexAlgorithms=+diffie-hellman-group14-sha1 ...
no matching host key type found 同上,主机密钥算法被弃用 ssh -o HostKeyAlgorithms=+ssh-rsa ...
shell request failed on channel 0 用户 Shell 为 nologin,或服务端资源耗尽 检查 /etc/passwd 的 Shell 字段、df -h、进程数
Error, some other host already uses address x.x.x.x 重启网络时检测到 IP 冲突 换一个未被占用的 IP;arping -I eth0 IP 定位占用者
ping: unknown host www.baidu.com DNS 未配置或不可达 配 DNS1= 或 /etc/resolv.conf,重启网络
sudo: no tty present and no askpass program specified ssh 远程执行 sudo 无终端 ssh -t host 'sudo xxx'

练习

基础作业

1. 将 192.168.1.55 主机的 /var/www/html/index.html 目录下的所有内容都拷贝到本机的 /soft 目录下。

# 先确保本地目标目录存在
mkdir -p /soft

# 方式一:scp(目录必须加 -r)
scp -r 192.168.1.55:/var/www/html/index.html/* /soft/

# 若 index.html 是目录,且要连目录本身一起拷过来
scp -r 192.168.1.55:/var/www/html/index.html /soft/

# 指定用户与非默认端口
scp -r -P 2222 root@192.168.1.55:/var/www/html/index.html/* /soft/

# 方式二:rsync(推荐,可增量、可续传)
rsync -avz 192.168.1.55:/var/www/html/index.html/ /soft/

要点说明:

  • 传目录必须加 -r,否则报 not a regular file。
  • 「目录下的所有内容」对应源路径写 目录/*(scp)或 目录/(rsync,注意结尾斜杠)。
  • 不写 用户@ 时使用本地当前用户名登录远程主机。
  • 本地 /soft 不存在时 scp 会报 No such file or directory,需先 mkdir -p /soft。

2. 创建两台虚拟机,并为两台虚拟机都绑定一个永久有效的主机名。

# ===== CentOS 7 及以上 =====
# 第一台
hostnamectl set-hostname server1.qianyun.com
hostnamectl status
cat /etc/hostname                       # server1.qianyun.com

# 第二台
hostnamectl set-hostname server2.qianyun.com

# ===== CentOS 6 =====
# 临时生效
hostname server1.qianyun.com
echo "server1.qianyun.com" > /proc/sys/kernel/hostname    # 等价写法

# 永久生效:编辑 /etc/sysconfig/network
vim /etc/sysconfig/network
NETWORKING=yes
HOSTNAME=server1.qianyun.com
# 验证(CentOS 6 需重启或重新登录后 PS1 才更新)
hostname
hostname -f                             # 显示 FQDN
uname -n

要点:主机名建议写成 FQDN(Full Qualified Domain Name,全限定域名),即 主机名.域名 格式,如 server1.qianyun.com。部分软件(如 Apache)启动时若解析不到 FQDN 会报警告。

3. 将创建的两台虚拟机互绑主机名。

假设 server1 是 10.0.0.61,server2 是 10.0.0.62。

# 在 server1(10.0.0.61)上执行
cat >> /etc/hosts <<'EOF'
10.0.0.61   server1.qianyun.com  server1
10.0.0.62   server2.qianyun.com  server2
EOF

# 在 server2(10.0.0.62)上执行同样内容
cat >> /etc/hosts <<'EOF'
10.0.0.61   server1.qianyun.com  server1
10.0.0.62   server2.qianyun.com  server2
EOF

# 双向验证
# server1 上
ping -c 2 server2
ssh root@server2 hostname
# server2 上
ping -c 2 server1
ssh root@server1 hostname

完整的 /etc/hosts 示例:

127.0.0.1   localhost localhost.localdomain
::1         localhost localhost.localdomain
10.0.0.61   server1.qianyun.com  server1
10.0.0.62   server2.qianyun.com  server2

为什么要互相绑定主机名? 用 SSH 连接另一台机器时,如果服务端开了 UseDNS yes 而客户端 IP 没有 PTR 记录,sshd 会一直等到 DNS 解析超时才给出密码提示,表现为「卡几秒才出现密码提示」。多台服务器在 /etc/hosts 中互相绑定(或服务端设 UseDNS no)可显著加快连接。

名称解析顺序:系统先查 /etc/hosts,再查 DNS,由 /etc/nsswitch.conf 的 hosts: 行控制:

grep '^hosts:' /etc/nsswitch.conf
# hosts:  files dns          ← files 就是 /etc/hosts,排在 dns 前面

服务器数量很多时怎么管理主机名? 逐台维护 /etc/hosts 不现实,可选方案:

  1. 脚本批量分发统一的 /etc/hosts(for ip in ...; do scp /etc/hosts $ip:/etc/; done)
  2. 自动化运维工具(Ansible、Puppet、SaltStack)统一下发
  3. 搭建内部 DNS 服务器(规模上百台后的标准做法)
  4. 批量装机时(Kickstart / Cobbler)自动写入

动手练习

4. 为一台服务器配置静态 IP(192.168.0.183/24,网关 192.168.0.1,DNS 223.5.5.5)并验证。

# ===== CentOS 7+:编辑网卡配置文件 =====
vim /etc/sysconfig/network-scripts/ifcfg-ens33
TYPE=Ethernet
BOOTPROTO=none
NAME=ens33
DEVICE=ens33
ONBOOT=yes
IPADDR=192.168.0.183
PREFIX=24
GATEWAY=192.168.0.1
DNS1=223.5.5.5
DNS2=8.8.8.8
nmcli con reload && nmcli con up ens33      # CentOS 7+ 生效
systemctl restart network                   # 或重启网络服务

# ===== CentOS 6 =====
vim /etc/sysconfig/network-scripts/ifcfg-eth0
# BOOTPROTO=static / IPADDR / NETMASK=255.255.255.0 / GATEWAY / ONBOOT=yes / NM_CONTROLLED=no
service network restart

# ===== 验证四件事 =====
ip addr show ens33 | grep inet             # ① IP 对不对
ip route | grep default                    # ② 默认网关对不对
cat /etc/resolv.conf                       # ③ DNS 写进去了吗
ping -c 2 192.168.0.1                      # ④ 网关通不通
ping -c 2 223.5.5.5                        # 外网 IP
ping -c 2 www.baidu.com                    # DNS 解析

5. 用 nmcli 创建一个新连接并激活(不手工编辑配置文件)。

# 查看设备与连接
nmcli dev status
nmcli con show

# 创建静态 IP 连接
nmcli con add type ethernet con-name static-183 ifname ens33 \
  ipv4.method manual \
  ipv4.addresses 192.168.0.183/24 \
  ipv4.gateway 192.168.0.1 \
  ipv4.dns "223.5.5.5 8.8.8.8" \
  autoconnect yes

# 激活
nmcli con up static-183
nmcli con show static-183 | grep -E 'ipv4.addresses|ipv4.gateway|ipv4.dns'

# 修改已有连接
nmcli con mod static-183 ipv4.addresses 192.168.0.184/24
nmcli con mod static-183 +ipv4.dns 114.114.114.114
nmcli con up static-183                    # 修改后必须重新激活才生效

# 创建 DHCP 连接
nmcli con add type ethernet con-name dhcp-conn ifname ens33 ipv4.method auto

# 删除连接
nmcli con delete dhcp-conn

远程操作时用 nmcli con up(原子切换)而不是 nmcli con down + up,后者会立即断开你自己的 SSH 会话。

6. 修改 SSH 端口为 2222 并禁用 root 直接登录,然后验证。

# 【第 0 步】保留当前 SSH 会话不要退出!另开窗口验证

# ① 先创建可用的普通用户并验证能登录
useradd deploy && echo 'P@ssw0rd' | passwd --stdin deploy
usermod -aG wheel deploy
ssh deploy@192.168.0.183                   # 必须先确认能登录成功
sudo -l                                    # 确认 sudo 可用

# ② 备份并修改配置
cp -a /etc/ssh/sshd_config /etc/ssh/sshd_config.bak.$(date +%F)
vim /etc/ssh/sshd_config
Port 2222
PermitRootLogin no
PasswordAuthentication yes
UseDNS no
GSSAPIAuthentication no
MaxAuthTries 3
AllowUsers deploy
# ③ SELinux 放行新端口(否则 sshd 起不来)
semanage port -a -t ssh_port_t -p tcp 2222
semanage port -l | grep ssh_port_t

# ④ 防火墙放行
firewall-cmd --permanent --add-port=2222/tcp
firewall-cmd --reload

# ⑤ 语法检查后重启
sshd -t
sshd -T | grep -E '^port|permitrootlogin|allowusers'
systemctl restart sshd                     # CentOS 7+
service sshd restart                       # CentOS 6

# ⑥ 在新窗口验证(老会话保持不动)
ssh -p 2222 deploy@192.168.0.183           # 应该成功
ssh -p 2222 root@192.168.0.183             # 应该被拒:Permission denied
ssh 192.168.0.183                          # 应该 Connection refused(22 已不监听)
ss -tnlp | grep 2222

# ⑦ 全部验证通过后,从防火墙移除旧端口
firewall-cmd --permanent --remove-service=ssh
firewall-cmd --reload

7. 用 ssh 远程执行命令,并写一个批量执行的脚本。

# 单条与多条
ssh 10.0.0.62 date
ssh 10.0.0.62 'hostname; uptime; df -h /'
ssh 10.0.0.62 'echo $HOSTNAME'             # 单引号 → 远程展开(正确)
ssh 10.0.0.62 "echo $HOSTNAME"             # 双引号 → 本地展开(对比观察差异)

# 需要 sudo 或交互程序时加 -t
ssh -t 10.0.0.62 'sudo systemctl restart sshd'
ssh -t 10.0.0.62 'top -n 1'

# 执行本地脚本,无需上传
ssh 10.0.0.62 'bash -s' < /root/check.sh

# 批量执行脚本
cat > /root/batch.sh <<'EOF'
#!/bin/bash
HOSTS="10.0.0.61 10.0.0.62 10.0.0.63"
CMD="$*"
[ -z "$CMD" ] && { echo "用法: $0 '要执行的命令'"; exit 1; }
for h in $HOSTS; do
    echo "=============== $h ==============="
    ssh -o ConnectTimeout=5 -o StrictHostKeyChecking=accept-new \
        -o BatchMode=yes root@$h "$CMD" || echo "[FAIL] $h 连接或执行失败"
done
EOF
chmod +x /root/batch.sh

/root/batch.sh 'uptime'
/root/batch.sh 'df -h / | tail -1'
/root/batch.sh 'systemctl is-active sshd'

要点:批量脚本必须加 ConnectTimeout(避免一台不通卡住全部)和 BatchMode=yes(禁止密码提示导致脚本挂起,前提是已配好密钥)。

8. 用 ~/.ssh/config 配置跳板机访问内网主机。

mkdir -p ~/.ssh && chmod 700 ~/.ssh
vim ~/.ssh/config
chmod 600 ~/.ssh/config
Host *
    ServerAliveInterval 60
    ServerAliveCountMax 3
    ConnectTimeout 10

Host bastion
    HostName 203.0.113.10
    User ops
    Port 2222
    IdentityFile ~/.ssh/id_rsa_bastion
    IdentitiesOnly yes

Host db1
    HostName 10.0.0.51
    User dbadmin
    ProxyJump bastion
    IdentityFile ~/.ssh/id_rsa_prod
    IdentitiesOnly yes
ssh bastion                        # 先验证跳板机本身能连
ssh db1                            # 一条命令直达内网主机
ssh -G db1                         # 打印最终生效配置,确认 ProxyJump 已应用
ssh -v db1 2>&1 | grep -i proxy    # 观察跳板过程

# 命令行等价写法
ssh -J ops@203.0.113.10:2222 dbadmin@10.0.0.51
ssh -J bastion1,bastion2 user@target        # 多级跳板

# 老版本 OpenSSH(< 7.3)用 ProxyCommand
#   ProxyCommand ssh -q -W %h:%p bastion

# scp / rsync 自动复用同一配置
scp dump.sql db1:/tmp/
rsync -avzP db1:/var/lib/mysql-backup/ /backup/db1/

9. 用 scp 和 rsync 分别传输同一个目录,对比二者差异。

# 准备测试目录
mkdir -p /tmp/testdir && dd if=/dev/zero of=/tmp/testdir/big.bin bs=1M count=200
for i in $(seq 1 500); do echo "line $i" > /tmp/testdir/f$i.txt; done

# ① scp 首次传输
time scp -r /tmp/testdir 10.0.0.62:/tmp/dest-scp/
# ② scp 再传一次(内容完全没变)
time scp -r /tmp/testdir 10.0.0.62:/tmp/dest-scp/
# 观察:耗时几乎相同 → scp 每次都是全量传输
# 还会发现变成了 /tmp/dest-scp/testdir/testdir(嵌套),这是 scp 传目录的经典坑

# ③ rsync 首次传输
time rsync -avz /tmp/testdir/ 10.0.0.62:/tmp/dest-rsync/
# ④ rsync 再传一次
time rsync -avz /tmp/testdir/ 10.0.0.62:/tmp/dest-rsync/
# 观察:第二次几秒完成,输出 "sent xxx bytes",数据量极小 → 增量同步

# ⑤ 只改一个文件,再同步
echo "changed" >> /tmp/testdir/f1.txt
rsync -avz --stats /tmp/testdir/ 10.0.0.62:/tmp/dest-rsync/
# 观察 Number of regular files transferred: 1 → 只传了变化的那一个文件

# ⑥ 对比结尾斜杠差异
rsync -avn /tmp/testdir  10.0.0.62:/tmp/x/     # 预览 → /tmp/x/testdir/...
rsync -avn /tmp/testdir/ 10.0.0.62:/tmp/x/     # 预览 → /tmp/x/...

# ⑦ 验证 --delete
touch /tmp/dest-rsync-extra.txt
ssh 10.0.0.62 'touch /tmp/dest-rsync/should-be-deleted.txt'
rsync -avn --delete /tmp/testdir/ 10.0.0.62:/tmp/dest-rsync/   # 先 dry-run 看要删什么
rsync -av  --delete /tmp/testdir/ 10.0.0.62:/tmp/dest-rsync/

结论:一次性拷贝用 scp,反复同步一律用 rsync。

10. 故意把 sshd_config 改错,然后用 sshd -t 定位问题。

cp -a /etc/ssh/sshd_config /etc/ssh/sshd_config.ok

# 错误 ①:拼写错误的指令
echo 'PermitRootLogins no' >> /etc/ssh/sshd_config
sshd -t
# /etc/ssh/sshd_config line 155: Unsupported option PermitRootLogins
#(新版可能提示 Bad configuration option: PermitRootLogins)

# 错误 ②:非法取值
sed -i 's/^PermitRootLogin.*/PermitRootLogin maybe/' /etc/ssh/sshd_config
sshd -t
# /etc/ssh/sshd_config line 38: Bad yes/no/without-password/forced-commands-only argument

# 错误 ③:端口不是数字
echo 'Port abcd' >> /etc/ssh/sshd_config
sshd -t
# /etc/ssh/sshd_config line 156: Badly formatted port number.

# 错误 ④:Match 块缺少条件
echo 'Match' >> /etc/ssh/sshd_config
sshd -t
# Missing Match criteria

# 错误 ⑤:主机密钥权限错误(不是语法错,sshd -t 同样能查出来)
chmod 644 /etc/ssh/ssh_host_rsa_key
sshd -t
# @@@ WARNING: UNPROTECTED PRIVATE KEY FILE! @@@  Permissions 0644 ... are too open.
chmod 600 /etc/ssh/ssh_host_rsa_key

# 恢复
cp -a /etc/ssh/sshd_config.ok /etc/ssh/sshd_config
sshd -t && echo "配置正常"
systemctl restart sshd

sshd -t 查不出来的「逻辑错误」(必须靠实际登录验证)

# ① 把自己排除在白名单外 —— sshd -t 不报错,但重启后你就进不来了
echo 'AllowUsers someone-else' >> /etc/ssh/sshd_config
sshd -t                                    # 通过!

# ② 关掉密码认证但密钥没配好 —— sshd -t 不报错
# ③ 改了 Port 但防火墙/SELinux 没放行 —— sshd -t 不报错

# 正确流程:sshd -t 通过后,用 sshd -T 看生效值,再在新窗口实际登录验证
sshd -T | grep -Ei 'allowusers|passwordauthentication|^port|permitrootlogin'

结论:sshd -t 只检查语法与密钥文件权限,不检查逻辑。所以「保留一个已登录会话 + 新窗口验证」这两步永远不能省。


本章小结

网络配置命令速查(ip / ifconfig / nmcli 对照)

任务 ip(推荐,iproute2) ifconfig / route(net-tools,已弃用) nmcli(NetworkManager)
查看所有网卡 IP ip addr / ip a ifconfig -a nmcli dev show
查看指定网卡 ip addr show ens33 ifconfig ens33 nmcli dev show ens33
只看链路状态 ip link show ifconfig -a nmcli dev status
添加 IP ip addr add 10.0.0.5/24 dev ens33 ifconfig ens33 10.0.0.5/24 nmcli con mod X ipv4.addresses 10.0.0.5/24
删除 IP ip addr del 10.0.0.5/24 dev ens33 — nmcli con mod X -ipv4.addresses ...
添加第二个 IP(别名) ip addr add 10.0.0.6/24 dev ens33 ifconfig ens33:0 10.0.0.6/24 nmcli con mod X +ipv4.addresses 10.0.0.6/24
启用网卡 ip link set ens33 up ifconfig ens33 up / ifup ens33 nmcli dev connect ens33
禁用网卡 ip link set ens33 down ifconfig ens33 down / ifdown ens33 nmcli dev disconnect ens33
修改 MAC ip link set ens33 address 00:01:22:AB:EF:CD ifconfig ens33 hw ether 00:01:22:AB:EF:CD nmcli con mod X 802-3-ethernet.cloned-mac-address ...
修改 MTU ip link set ens33 mtu 9000 ifconfig ens33 mtu 9000 nmcli con mod X 802-3-ethernet.mtu 9000
查看路由 ip route / ip r route -n / netstat -rn nmcli con show X \| grep ipv4.route
添加默认网关 ip route add default via 10.0.0.1 route add default gw 10.0.0.1 nmcli con mod X ipv4.gateway 10.0.0.1
添加静态路由 ip route add 172.16.0.0/16 via 10.0.0.254 route add -net 172.16.0.0/16 gw 10.0.0.254 nmcli con mod X +ipv4.routes "172.16.0.0/16 10.0.0.254"
查看 ARP 表 ip neigh / ip n arp -n —
清空 ARP 缓存 ip neigh flush all arp -d IP —
查看监听端口 ss -tnlp netstat -ntlup —
查看已建立连接 ss -tnp netstat -nt —
查看统计 ip -s link netstat -i —
生效方式 立即生效,重启丢失 立即生效,重启丢失 写入配置文件,永久生效
yum -y install net-tools iproute        # ifconfig/netstat 在最小化安装中需单独装

网卡配置文件参数速查

文件位置:/etc/sysconfig/network-scripts/ifcfg-<网卡名>

参数 说明 示例
DEVICE 设备名,必须与文件名 ifcfg-xxx 后缀一致 DEVICE=eth0
NAME 连接名称(CentOS 7+) NAME=ens33
TYPE 接口类型 TYPE=Ethernet
UUID 连接唯一标识(克隆虚拟机时应删除) UUID=xxxx-xxxx
BOOTPROTO dhcp 动态 / static 或 none 静态 BOOTPROTO=none
ONBOOT 开机或重启网络时是否启用 ONBOOT=yes
IPADDR 静态 IP 地址 IPADDR=10.0.0.200
NETMASK 子网掩码(与 PREFIX 二选一) NETMASK=255.255.255.0
PREFIX 掩码位数(与 NETMASK 二选一,推荐) PREFIX=24
GATEWAY 默认网关,也可写在 /etc/sysconfig/network 中对所有网卡生效 GATEWAY=10.0.0.1
DNS1 / DNS2 DNS 服务器,会自动写入 /etc/resolv.conf DNS1=223.5.5.5
DOMAIN 搜索域 DOMAIN=qianyun.com
NM_CONTROLLED 是否受 NetworkManager 控制,CentOS 6 服务器建议 no NM_CONTROLLED=no
DEFROUTE 是否作为默认路由出口(多网卡时用) DEFROUTE=yes
PEERDNS DHCP 时是否用服务器下发的 DNS 覆盖 resolv.conf PEERDNS=no
IPV6INIT 是否启用 IPv6 IPV6INIT=no
HWADDR 绑定的 MAC 地址(克隆虚拟机时常需删除或改正) HWADDR=00:0C:29:...
MTU 最大传输单元 MTU=1500
USERCTL 普通用户是否可启停该网卡 USERCTL=no
ZONE firewalld 区域(CentOS 7+) ZONE=public

IP 别名(虚拟 IP)相关:

文件 参数 说明
ifcfg-eth0:0 DEVICE=eth0:0 + IPADDR 单个别名
ifcfg-eth0-range0 IPADDR_START / IPADDR_END / NETMASK / CLONENUM_START 批量别名,CLONENUM_START=10 表示从 eth0:10 开始编号

CentOS 6 与 7+ 差异对照

项目 CentOS 6(RHEL 6) CentOS 7 及以上
网卡命名 eth0、eth1(内核顺序) ens33、eno1、enp0s3(一致性命名)
网卡命名规则文件 /etc/udev/rules.d/70-persistent-net.rules 由 systemd/udev 按固件与总线位置生成
恢复传统 eth0 命名 默认即是 内核参数加 net.ifnames=0 biosdevname=0
网络服务 network(SysV init) NetworkManager + network(systemd)
重启网络 service network restart
/etc/init.d/network restart
systemctl restart network
nmcli con reload && nmcli con up <连接名>
启停单块网卡 ifup eth0 / ifdown eth0 nmcli dev connect/disconnect ens33(ifup/ifdown 仍可用)
重启 sshd service sshd restart
/etc/init.d/sshd restart
systemctl restart sshd
重载 sshd 配置 service sshd reload systemctl reload sshd
查看 sshd 状态 service sshd status systemctl status sshd
sshd 开机自启 chkconfig sshd on
chkconfig --list sshd
systemctl enable sshd
systemctl is-enabled sshd
启动 + 自启一步完成 需两条命令 systemctl enable --now sshd
查看主机名 hostname hostname / hostnamectl
永久改主机名 改 /etc/sysconfig/network 的 HOSTNAME= hostnamectl set-hostname xxx(写入 /etc/hostname)
防火墙 iptables(service iptables) firewalld(firewall-cmd)
端口查看 netstat -ntlup ss -tnlp(netstat 需装 net-tools)
链路检测 mii-tool、ethtool ethtool、ip link(mii-tool 多数已移除)
日志查看 tail /var/log/messages、/var/log/secure journalctl -u sshd、/var/log/secure
NetworkManager 处理 通常关掉:chkconfig NetworkManager off 保留启用,是主要管理方式
SSH 协议版本 需显式 Protocol 2 禁用 v1 已移除 v1,Protocol 指令也已废弃
UseDNS 默认值 yes(需手工改 no) no(OpenSSH 7.x 起)

SSH 客户端常用选项速查

选项 作用
-p 端口 指定端口(小写,scp/sftp 用大写 -P)
-l 用户 指定登录用户,等价于 user@host
-i 私钥 指定私钥文件
-v / -vv / -vvv 分级调试输出
-t / -tt 强制分配伪终端(sudo、top、vim 需要)
-T 禁用伪终端
-X X11 转发(受限,较安全)
-Y X11 信任转发(不安全,慎用)
-J 跳板机 跳板机(OpenSSH 7.3+),多级用逗号分隔
-W host:port 标准输入输出转发,老版本实现跳板用
-N 不执行远程命令(只做端口转发时用)
-f 后台运行
-L 本地端口:目标:目标端口 本地端口转发
-R 远程端口:目标:目标端口 远程端口转发
-D 端口 动态转发(SOCKS 代理)
-C 压缩
-q 静默模式
-o 选项=值 传递任意配置项
-G 打印某主机最终生效的配置(排错神器)
常用 -o ConnectTimeout=5、StrictHostKeyChecking=accept-new、BatchMode=yes、IdentitiesOnly=yes、ServerAliveInterval=60、UserKnownHostsFile=/dev/null

sshd_config 关键项速查

配置项 默认 建议值 说明
Port 22 高位端口 改端口需同步 SELinux + 防火墙
ListenAddress 0.0.0.0 内网地址 多网卡时只在内网口开放
Protocol 2 2 仅 CentOS 6 需写
PermitRootLogin yes no 或 prohibit-password(仅密钥)
PasswordAuthentication yes no(密钥配好后) 关闭前必须验证密钥可用
PubkeyAuthentication yes yes 公钥认证开关
AuthorizedKeysFile .ssh/authorized_keys 默认 公钥存放位置
PermitEmptyPasswords no no 绝不能开
MaxAuthTries 6 3 单连接失败次数上限
LoginGraceTime 120 30~60 认证超时(秒)
ClientAliveInterval 0 按需 探测间隔
ClientAliveCountMax 3 按需 超时 = 间隔 × 次数
UseDNS no(新版) no yes 会导致登录卡十几秒
GSSAPIAuthentication yes no 同样是登录慢的元凶
AllowUsers / AllowGroups 无 按需白名单 写错会锁死自己
DenyUsers / DenyGroups 无 按需黑名单 优先级高于 Allow
Banner none 按合规要求 认证前的警告语
X11Forwarding no(RHEL 默认 yes) 无图形需求设 no 配合客户端 -X
AllowTcpForwarding yes 按需 no 防被当跳板穿透内网
Subsystem sftp 启用 保留 注释掉则 sftp 不可用
LogLevel INFO INFO(排错 VERBOSE) VERBOSE 会记录密钥指纹
StrictModes yes yes 权限过松即拒绝认证

SSH 加固清单

  1. 改默认端口 —— 作用有限(nmap 一扫就找到),但能挡掉绝大多数只扫 22 的自动化脚本,让日志清爽、真实告警不被淹没。改端口三件套:sshd_config + semanage port + firewall-cmd。
  2. 禁用 root 直接登录 —— PermitRootLogin no,改用普通用户 + sudo。顺序:先建用户 → 验证能登录 → 验证 sudo 可用 → 才禁 root。
  3. 优先密钥认证,关闭密码认证 —— PubkeyAuthentication yes + PasswordAuthentication no + ChallengeResponseAuthentication no。关闭前必须先验证密钥能登录。
  4. 限制来源与用户 —— AllowUsers deploy@192.168.0.*、AllowGroups sshusers,或在防火墙层面用 rich-rule 只放行运维网段。
  5. 防暴力破解 —— MaxAuthTries 3 + LoginGraceTime 30 + fail2ban(maxretry / findtime / bantime,记得 ignoreip 加上自己的网段)。
  6. 审计暴力破解来源 —— grep 'Failed password' /var/log/secure | awk '{print $11}' | sort | uniq -c | sort -rn;ss -tnp | grep :22;lastb、last、lastlog。
  7. 权限必须正确 —— ~ 不能组/其他可写,~/.ssh 700,authorized_keys 600,私钥 600,属主是本人;SELinux 上下文用 restorecon -Rv ~/.ssh 修。这是 Permission denied (publickey) 的头号原因。
  8. 关闭无用功能 —— X11Forwarding no、AllowTcpForwarding no、PermitTunnel no、PermitEmptyPasswords no。
  9. 性能与体验 —— UseDNS no + GSSAPIAuthentication no,消除登录卡顿。
  10. 改配置的铁律 —— 备份 → 改 → sshd -t 语法检查 → sshd -T 看生效值 → reload/restart → 在新窗口实际登录验证,全程保留一个已登录会话。sshd -t 查不出逻辑错误(白名单漏了自己、端口没放行、密钥没配好)。

排错口诀

分层排查(自下而上五层)

先看灯,再通 IP,再探端口,再查服务,最后查认证。

链路(ethtool / ip link)→ IP 路由(ping / ip route)→ 端口(nc -zv / ss -tnlp)→ 服务(systemctl status sshd / sshd -t)→ 认证(/var/log/secure / ssh -v)

四种连接失败报错

refused 找服务,timeout 找防火墙,no route 找路由,denied 找认证。

SSH 高频问题一句话对症

症状 一句话原因
登录卡十几秒 UseDNS + GSSAPIAuthentication 都设 no
Permission denied (publickey) 九成是权限:.ssh 700、authorized_keys 600、家目录不可组写
HOST IDENTIFICATION HAS CHANGED ssh-keygen -R 主机,别删整个 known_hosts
Too many authentication failures -o IdentitiesOnly=yes 或 ssh-add -D
用一会儿就断 ClientAliveInterval(服务端)/ ServerAliveInterval(客户端)/ TMOUT(Shell)三处查
Connection reset by peer fail2ban 封了、hosts.deny、/var 满、算法不兼容
sudo: no tty present ssh -t
scp 报 not a regular file 传目录忘了 -r
scp 端口连不上 -P 是大写
rsync 目录多出一层 源路径结尾斜杠:带 / = 传内容,不带 = 传目录本身
--delete 之前 一律先 -n dry-run
改完 sshd 进不去了 永远保留一个已登录会话;sshd -t 不查逻辑错误

四条要点回顾

  • IP 配置区分临时(ip addr / ifconfig,重启丢失)与永久(ifcfg-xxx 文件 + 重启网络 / nmcli)。
  • ss -tnlp(新)/ netstat -ntlup(旧)是排查端口监听的核心命令。
  • SSH 连不上主要两类:密钥变更(清 known_hosts 单行)和服务未启动(起 sshd 并 enable)。
  • /etc/hosts 优先于 DNS 解析(由 /etc/nsswitch.conf 的 hosts: files dns 决定),多主机环境应统一规划主机名绑定,规模上百台后改用内部 DNS。
最后修改:2026 年 09 月 20 日
如果觉得我的文章对你有用,请随意赞赏