掘金 后端 ( ) • 2024-06-19 17:13

摘要

Linux 平台 Netplan, NetworkManager, Systemd-networkd 等网络管理器的基本概念和使用导读。以及多个网卡连接同一个私有网络时,因为反向路由过滤 (rp_filter) 造成的网络问题填坑。

前提

本文的实验环境基于 Ubuntu 22.04.4 LTS,安装在 KVM 虚拟机中,安装模式为 Minimal Server ,内核版本为 5.15.0-107-generic。

本文主要针对无图形界面的 Linux 服务器网络配置,介绍网络管理器的选择和基本操作,具体的网络配置参数和相应的操作命令语法,不在讨论范围内。

历史和变化

ifupdown/ifconfig 是 Linux 系统上传统的的网络管理命令行工具,根据配置文件创建网络连接。

NetworkManager 是具备守护进程,可进行自动配置的网络管理器。

Systemd-networkdSystemd 内置的网络管理器,出现时间最晚。缺少图形化操作界面,目前专注于有线网络参数配置,多适用于服务器环境

netplan 为前述两个管理后端生成配置参数,作为用户和网络配置间的抽线代理。

iproute2 操作网络接口和连接的网络管理命令行工具,也同样支持配置文件。

Netplan

netplan 是 Ubuntu 系统的默认网络管理器,严格来说 netplan 更像网络管理器的前端,因为他的工作主要是为 Systemd-networkd 生成配置信息。

netplan 服务程序通过读取 /etc/netplan/ 目录下的配置文件为 Systemd-networkd 生成运行时网络配置。系统启动时,此目录下所有的配置文件都将被加载。一个典型的 netplan 配置文件如下:


network:
  ethernets:
    enp1s0:
      addresses:
      - 192.168.1.237/24
      nameservers:
        addresses: [119.29.29.29, 180.76.76.76, 223.6.6.6]
        search: [vms.example.org]
      routes:
        - to: default
          via: 192.168.1.1
        - to: 192.168.1.0/24
          via: 192.168.1.1
          on-link: true
          table: 100
      routing-policy:
        - priority: 100
          from: 192.168.1.0/24
          table: 100
        - priority: 110
          from: 192.168.1.0/24
          table: 100
  version: 2

如果熟悉 yaml 语法,这个配置称得上简洁明了,需要启用新网络参数也简单,只需敲入 netplan apply 命令,netplan 即可搞定剩下的一切。

这个配置文件为 'enp1s0' 连接配置了:

  • IPv4 地址,每个连接都可以有多个;
  • DNS 解析服务器,只有前三个服务器配置会生效;
  • 路由,包括一个默认路由(取代了网关),和一个链路路由(直达);
  • 策略路由规则,根据源和目的选择路由表;

没有配置文件 netplan 不会做任何事情。此时 Linux 服务器所有的网络管理器都不会生效。因此最简单的配置文件就派上用场了。

netplan renderer

默认情况下 netplan 采用的后端是 Systemd-networkd。指定 renderer 为 NetworkManager, Systemd-networkd 将不再管理网络连接和接口,管理权移交至前者负责。默认情况下 NetworkManager 指定所有网络接口通过 DHCP 获取 IP 地址。

指定 renderer 为 networkd 或简单忽略此行,netplan 将继续应用 networkd 后端。

network:
  version: 2
  renderer: NetworkManager

需要注意的是,如果配置文件中指明了特定的接口,那么需要为每一个接口在配置目录中编写配置文件。

systemd-networkd-wait-online 超时问题

Netplan renderer 指定为 NetworkManager 会产生一个隐藏的副作用:服务器每次启动,都须等待 systemd-networkd-wait-online.service 服务超时(120s),造成启动缓慢。对承载业务的服务器而言这已是无法承受的代价。

引发问题的原因是:systemd-networkd-wait-online 的任务是启动过程中后台服务的依赖检查点,确保网络参数得到正确配置以后,才能继续加载后续服务。通常这不会是个问题:

  • Ubuntu 系统 netplan 的默认后端就是 Systemd-networkd ,wait-online 操作自然顺利完成;
  • 其他的系统要么仍然使用 ifupdown / ifupdown2 管理工具、要么已经预置 NetworkManager 为模式网络管理器;不存在 networkd online 问题。

解决这个问题的思路基本上有三种:

  • 禁用 networkd-dispatcher.servicenetwork-online.servicesystemd-networkd.service 等三个服务;
  • 使用 Systemd-networkd 服务管理网络
  • 按接口区分,部分由 Netplan/Sytemd-networkd 管理,部分由 NetworkManager 进行管理

由于 NetworkManager 本身已经是一个成熟且完善的网络管理机制,它本身同样提供 NetworkManager-wait-online.service, 其所处的 target 也与 networkd 的相关服务一致。故推荐使用第一种。

NetworkManager

NetworkManager 是为了使网络配置尽可能简单而开发的网络管理软件包,如果使用 DHCP,它会替换默认的路由表,目标是使网络开箱即用。

NetworkManager 由两个部分组成:

  • 一个以超级用户身份运行的守护进程
  • 前端管理程序,包括命令行工具 nmcli 和文本图形工具 nmtui

NetworkManager.conf 配置文件位于 /etc/NetworkManager/ ,自定义配置通常放置于此路径下的 Drop in 目录 conf.d

受管理的设备

多个网络管理服务管理同一个方案时,将会产生冲突

[keyfile]
# 指定特定接口名称设备设备非受管
unmanaged-devices=interface-name:enp1s0
# 指定特定 mac 地址设备非受管
unmanaged-devices=mac:52:54:00:74:79:56
# 指定特定设备类型非受管
unmanaged-devices=type:ethernet
# 指定多个设备选择条件,用分号(;)分隔
unmanaged-devices=interface-name:enp1s0;interface-name:enp7s0

此外,在 Debian 和 Ubuntu 发行版中,启用 ifupdown 插件,可以控制 /etc/network/interfaces 目录下配置文件所引用的网络设备是否受 NetworkManager 管理。

[main]
plugins=ifupdown,keyfile

[ifupdown]
managed=false

注意该配置仅针对传统方案配置文件存在时生效,对未纳入 NetworkManager 管理的其它网络设备而言,该配置项不产生任何影响。

修改配置文件后,需重新载入 NetworkManager 服务使之生效: systemctl reload NetworkManager

使用 nmclinmtui 配置网络

使用 nmtui 将启动一个基于字符的图形界面,可以完成基本的 IP 地址、网关、DNS 服务器地址等配置。

使用 nmcli 可以完成更多的高级功能配置,具体的 nmcli 命令参数相对复杂,这里给出一些最基本的 nmcli 示例。

# 显示所有连接
nmcli connection show
# 也可以简写为,以下采用简写模式
nmcli con show

# 显示连接的详细配置信息,需指定连接的名称
nmcli con show <con-name>

# 显示网络设备的概要信息,设备名称可选
nmcli dev show [<dev-name>]

# 设置 ipv4 的一般功能,以增加 ipv4 地址为例
nmcli con mod <con-name> +ipv4.addresses "192.168.1.0/24"

# 设置路由表,不带 + 号时将替换现有路由表,指定路由表编号可选
nmcli con mod <con-name> ipv4.routes "0.0.0.0/0 192.168.1.1 [table=<table-number>]"

# 设置策略路由,以指定接口流量按指定路由表模式为例
nmcli con mod <con-name> ipv4.touting-rules "priority 100 iif <con-name> table <table-number>"

# 在指定设备上应用更新
nmcli dev reapply <dev-name>

从 netplan 切换到 network 模式

完成切换过程的基本步骤是:修改 netplan 配置文件、禁用 networkd 相关服务、配置 新的网络参数。因为 Linux 服务器使用了 SSH 链接,修改网络管理器将不可避免的导致网络断开连接,以下脚本可以自动注册一次性服务,并重启计算机完成切换操作。

PART-I: 修改本机主机名,并配置本地地址解析

MYHOSTNAME=kavms10-h4v3
hostnamectl hostname ${MYHOSTNAME}
cat << EOF > /etc/hosts
127.0.0.1 localhost
127.0.1.1 ${MYHOSTNAME}.vms.example.org ${MYHOSTNAME}

192.168.1.231 kmvms01-h2v1.vms.example.org kmvms01-h2v1
# Other hosts ....

# The following lines are desirable for IPv6 capable hosts
::1     ip6-localhost ip6-loopback
fe00::0 ip6-localnet
ff00::0 ip6-mcastprefix
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters
EOF

PART-II:备份 netplan 的默认配置文件,并指定 netplan 采用 NetworkManager 作为网络管理后端

{
SCNAME=yhamcit_init_ip_addr

DEFCONFIL=`ls /etc/netplan/00-* | xargs basename `

for CONFIL in `ls /etc/netplan/ | grep -v ${DEFCONFIL}`; do rm -f /etc/netplan/${CONFIL}; done

[ -f /etc/netplan//etc/netplan/${DEFCONFIL}.bak ] || mv /etc/netplan/${DEFCONFIL} /etc/netplan/${DEFCONFIL}.bak

cat << EOF > /etc/netplan/${DEFCONFIL}
# This file describes the network interfaces available on your system
# For more information, see netplan(5).
# Set and change netplan renderer to NetworkManager GUI tool
network:
  version: 2
  renderer: NetworkManager
EOF
}

PART-III: 禁用 networkd 服务守护进程

systemctl disable networkd-dispatcher.service network-online.service systemd-networkd.service

PART-IV:生成网络初始化配置守护进程

{
LSBSCFILE=/etc/init.d/${SCNAME}
cat << "EOF" > ${LSBSCFILE}
#! /usr/bin/bash

### BEGIN INIT INFO
# Provides:          ${SCNAME}
# Required-Start:    $network $NetworkManager
# Required-Stop:     $network $NetworkManager
# Default-Start:     2 3 4 5
# Default-Stop:      0 1 6
# Short-Description: make ip address for networkmanager
### END INIT INFO

EOF

# 遍历当前网卡
for CONN in `ip a | sed -nr 's#^.*inet (.*) brd .*(enp[[:digit:]]s[[:digit:]])$#\2;\1#gp'`; do

IFNAME=`echo ${CONN} | awk -F\; '{print $1}'`
CIDR=`echo ${CONN} | awk -F\; '{print $2}'`

# 生成修改连接的命令,包括:修改连接名称、设置 IP、网关、路由、和路由策略
echo "CONAME=\`nmcli con show | grep ${IFNAME} | sed -En 's#^(.*)  ([0-9a-f]+\-?)+ +ethernet  .*\$#\1#gp'\`" >> ${LSBSCFILE}
echo "nmcli con mod \"\${CONAME}\" con-name ${IFNAME}" >> ${LSBSCFILE}

echo "nmcli con mod ${IFNAME} ipv4.addresses \"${CIDR}\"" >> ${LSBSCFILE}

echo "nmcli con mod ${IFNAME} ipv4.dns "119.29.29.29"" >> ${LSBSCFILE}
echo "nmcli con mod ${IFNAME} +ipv4.dns "180.76.76.76"" >> ${LSBSCFILE}
echo "nmcli con mod ${IFNAME} +ipv4.dns "223.6.6.6"" >> ${LSBSCFILE}

SUBN=`echo ${CIDR} | sed -En 's#^((\.?[[:digit:]]{1,3}){3}).*(\/[[:digit:]]{2})$#\1#gp'`
ADDR=`echo ${CIDR} | sed -En 's#^((\.?[[:digit:]]{1,3}){3}).*(\/[[:digit:]]{2})$#\2#gp'`
MASK=`echo ${CIDR} | sed -En 's#^((\.?[[:digit:]]{1,3}){3}).*(\/[[:digit:]]{2})$#\3#gp'`
TABLID=$(( ${ADDR:1} / 100 ))

echo "nmcli con mod ${IFNAME} ipv4.gateway ${SUBN}.254" >> ${LSBSCFILE}

echo "nmcli con mod ${IFNAME} ipv4.method manual" >> ${LSBSCFILE}

echo "nmcli con mod ${IFNAME} ipv4.routes \"0.0.0.0/0 ${SUBN}.254 table=1${TABLID}\"" >> ${LSBSCFILE}
echo "nmcli con mod ${IFNAME} +ipv4.routes \"${SUBN}.0${MASK} ${SUBN}.254 table=1${TABLID}\"" >> ${LSBSCFILE}

echo "nmcli con mod ${IFNAME} ipv4.routing-rules \"priority $(( 105 + ${TABLID} * 5)) iif ${IFNAME} table 1${TABLID}\"" >> ${LSBSCFILE}
echo "nmcli con mod ${IFNAME} +ipv4.routing-rules \"priority $(( 115 + ${TABLID} * 5)) from ${SUBN}.0${MASK} table 1${TABLID}\"" >> ${LSBSCFILE}
echo "nmcli con mod ${IFNAME} +ipv4.routing-rules \"priority $(( 125 + ${TABLID} * 5)) to ${SUBN}.0${MASK} table 1${TABLID}\"" >> ${LSBSCFILE}

# 网络参数生效
echo "nmcli dev reapply ${IFNAME}" >> ${LSBSCFILE}

done

cat << EOF >> ${LSBSCFILE}
# 守护脚本执行结束,清理自身服务
systemctl disable ${SCNAME}
update-rc.d -f ${SCNAME} remove
rm -f \`find / -name *${SCNAME}*\`

EOF
}

PART-V: 初始化服务制作完毕,注册服务

chmod +x ${LSBSCFILE}
update-rc.d ${SCNAME} defaults

reboot

以上代码 PART-IV 将在系统重启后运行,其它都会在本脚本中立刻生效。将素有内容放置在 '{}' 一次性执行整个代码块,或放置在脚本中运行。重启后会切换到 NetworkManager 管理器下,同时保留原 IP 地址。

全部完成后,再次重启将完全抹掉初始化服务的痕迹。

Systemd-networkd

networkd 作为 Systemd 的一部分,其配置文件保存于 /etc/systemd/network/ 或其它 Systemd 配置文件目录的 network 子目录下,配置方式与 NetworkManager 类似。由于可采用 netplan 进行配置,故不再展开讨论。

对策略路由的支持

networkd 同样可配置基于规则的策略路由, 具体内容参考networkd man(8),ArchLinux 关于 systemd-networkd的实用手册。

命令行工具

同时 networkd 也附带了有用查询网络连接状态的命令行工具 networkctl,但是该工具目前没有设置网络参数的功能。

多连接网络配置

在服务网络中,难以避免的需要管理/维护/控制链路和业务流量链路物理隔离。当 Linux 服务器有两个及以上的连接接入同一个私有网络,即便两个接口具有不同的 CIDR 地址段,仍然可能发生部分网段不通的问题。

假设有位于第三网段的目标地址,则同时在两个链路上存在默认路由,使目标链路多路可达。这种情况会触发反向路径过滤,导致匪夷所思的网络问题,甚至 iptables 日志和接口 tcpdump 都能看到数据发送,但数据包就是不会出现在网络中。

rp_filter

Linux 内核当中的反向路径过滤开关(rp_filter)打开时,Linux 通过反向路由查询,检查收到的数据包源IP是否可路由、是否最佳路由,如果没有通过验证,则丢弃数据包,设计的目的是防范IP地址欺骗攻击。rp_filter提供三种模式供配置:

  • 0 - 不验证
  • 1 - 严格模式(Strict mode):根据 RFC3704 的定义,对每个收到的数据包,查询反向路由,如果数据包入口和反向路由出口不一致,则不通过
  • 2 - 松散模式(Loose mode):根据 RFC3704 的定义,对每个收到的数据包,查询反向路由,如果任何接口都不可达,则不通过

反向路由过滤功能配置文件分为全局和网卡,分别位于路径: /proc/sys/net/ipv4/conf/all/rp_filter 和 /proc/sys/net/ipv4/conf/NIC接口/rp_filter

临时禁用 rp_filter

解除 rp_filter 限制,直观的做法是禁用此功能,但是在企业级网络设备中,交换机和路由器同样会做反向路径检查,盲目的关闭此功能可能埋下更为隐蔽的缺陷。可临时禁用此功能用于检查。

# kernel default setting
sysctl -w net.ipv4.conf.default.rp_filter =1
# global setting
sysctl -w net.ipv4.conf.all.rp_filter =1
# per nic setting
sysctl -w net.ipv4.conf.enp1s0.rp_filter =1
# other nics

永久禁用 rp_filter

永久禁用此功能须慎重,修改 /etc/sysctl.conf 文件并重启,rp_filter 功能将永久关闭。

net.ipv4.conf.default.rp_filter =1
net.ipv4.conf.all.rp_filter =1
net.ipv4.conf.enp1s0.rp_filter =1

使用 nmcli 配置策略路由

配置默认路由和本地链路路由到指定路由表,包含默认路由、链路直达路由两种基本情况。

### Example of a route for the locally connected subnet
# nmcli con mod enp7s0 +ipv4.routes "192.168.0.0/24 table=10"

### Example of a route to send a remote subnet (10.0.0.0/24) via a specific gateway (192.168.0.254)
# nmcli con mod enp7s0 +ipv4.routes "10.0.0.0/24 192.168.0.254 table=10"

通过规则限制流量分流至指定路由表,包含从指定接口进入,源自/目的指定网段的流量三种规则。

### Anything which comes in this interface, send to table 10
# nmcli con mod enp7s0 ipv4.routing-rules "priority 100 iif enp7s0 table 10"

### Anything from this system's local IP, send to table 10
# nmcli con mod enp7s0 +ipv4.routing-rules "priority 110 from 192.168.0.2 table 10"

### Anything to the local subnet, send to table 10
# nmcli con mod enp7s0 +ipv4.routing-rules "priority 120 to 192.168.0.0/24 table 10"

设置完毕检查无误后启用

nmcli con show enp7s0
nmcli dev reapply enp7s0

需注意,路由规则的优先级应全局差异配置,而非对应连接差异配置

配置策略路由的说明

netplan +/ networkd 同样可以配置策略路由,通常情况下三层流量可以正常工作。但是这种方案缺少配置二层流量规则的能力,即配置从特定接口进入的流量规则。配置文件中的 from/destination 指令只能匹配 IP 地址,容易引起 TCP/UPD/IP 正常但是 PING 不通的奇怪现象。

其它内核开关

当多个连接接入同一个网络时,还应关注 proxy_arparp_filter,这两个开关分别控制服务是否对对已知的外部 IP 地址进行 ARP 代答或内部 IP 通过不可路由接口代答。这两个开关可能导致 Linux 服务器从非预期的接口进行通信,造成网络设备设置的障碍。

  • 开启 proxy_arp ,如果请求中的 IP 不是本机网卡接口的地址,该地址路由可达,则会以自己的 MAC 地址进行回复;否则不回复。
  • 开启 arp_filter,请求中的 IP 是本机接口的地址,arp 客户端地址通过接收接口路由可达, 则应答此arp,否则不应答。**

参考资料

How exactly are NetworkManager, networkd, netplan, ifupdown2, and iproute2 interacting?

NetworkManager.conf

Debian - NetworkManager