前言

为什么会有这篇文章呢?其实是因为我很早之前就对“以Linux为底层的软路由系统”很感兴趣,之前在学校学的就是网络工程和系统服务部署,因此对Linux系统和网络可以说还是比较熟悉的(特别是网络,有Datacom HCIE认证)。最开始搜软路由就会出现很多的OpenWRT,iKuai,RouterOS这样的文章,不过我只玩了段时间的OpenWRT + iStoreOS。

iStoreOS虽然在某些设计上确实很方便(比如web界面配置等),但使用一段时间下来还是感觉不顺心,这玩意儿怎么这么臃肿复杂,把一些很简单的事情做的很复杂,后来想了想,可能因为OpenWRT刚开始就是为了web界面的路由器设计的,要对新手友好些,但对外来说很多功能其实没必要了,而且OpenWRT的很多功能我都可以自己手搓实现。

基于以上原因,最终选择简洁的Ubuntu Server 24.04LTS,且安装时选择最小安装来实现,为了保证在物理机上可一次性完美安装和功能实现,所有要部署的服务都会先在虚拟机上进行实验,确认没问题后才会上物理机,确保物理机系统的干净整洁(在这方面还是有洁癖的)。

另外提一嘴,2025年09月05日晚22:00,在小黄鱼上购买了一台多网口小主机(J4512)做软路由,虽然之前买过天钡家的AMD款的天钡 WTR PRO安装飞牛用到现在,但是一直没有做过软路由,因此耗“巨资”购买了它。

甚是喜欢呢,哈哈,不过夏天被动散热效率太低了,一会就发烫了,后面会整个静音风扇(类似电脑机箱里那个)就够压住了,它主要是没风带走热量,有风的话5分钟温度就降到室温了。

配置过程

以下过程均先在虚拟机上测试后才会“搬运”到物理机上,以下是虚拟机的配置列表,正好对应J4512的配置进行实验。

image

1、系统安装

关于U盘刻录系统镜像,插入机器并通过U盘启动这一步略过,不了解的可以上网查一下,教程太多了。

开机进入系统安装界面

image

选择English,随后进入到更新安装包的阶段,如果选择第一个update to the new installer那么需要确保你的机器目前已经连上网了,否则就选择第二个

随后选择键盘格式为English (US)

选择Ubuntu Server (minimized)最小化安装Ubuntu Server,然后就可以看到网卡界面了,目前连接公网的接口已经通过DHCP获取到了地址,可正常上网,其余三个接口后续要做桥接,因此先不配置。

image

跳过代理,来到软件包更新界面,如果没有要更新的,直接选择Done就行

在存储配置这一界面,为了后期分区扩容什么的方便,选择LVM安装,无需进行LVM加密

随后分区直接Done即可,因为目前设备就一块磁盘,且对于软路由来说不会存储有太多东西,因此直接一整块磁盘都做为根分区即可。

设置账户密码,随后选择安装OpenSSH Server方便后续远程连接。

到达软件包安装界面,虽然后面会用到docker,但目前系统还无法上外网,安装docker可能不顺利,因此都不选择,直接下一步。随后系统就会开始安装,因为选择的是最小安装,因此等待时间不会太久。

远程连接

安装完成重启开机后,因刚才选择了安装OpenSSH Server,因此可直接在windows的cmd中使用下面的命令远程连接到设备上(前提是和外网接口同网络)

ssh 用户名@接口地址

image

随后要做的就是提权更改root密码,切换root用户,更改ssh配置,使得root用户可使用密钥登录

sudo passwd root						# 输入一次ubuntu密码,两次要设置的root密码
su root
vim /etc/ssh/sshd_config
# 修改以下内容
Port 22
SyslogFacility AUTH
LogLevel VERBOSE
PermitRootLogin without-password
LoginGraceTime 2m
DenyUsers xxx	                                   # 禁止xxx用户登录
MaxAuthTries 3                                     # 最大认证次数3
MaxSessions 2                                      # 最大会话数2
PasswordAuthentication no                          # 禁止密码验证登录
PermitEmptyPasswords no                            # 禁止空密码验证
UseDNS no										   # 不对客户端进行DNS泛解析验证,加快SSH连接速度
PubkeyAuthentication yes                           # 开启SSH公钥认证登录
AuthorizedKeysFile .ssh/authorized_keys            # 用户登录公钥路径,如果该用户需要多设备免密登录,可以再authorized_keys文件内另起一行写入其他设备的公钥,也可以在此配置后再跟上.ssh/xxx_keys即可
RSAAuthentication yes                              # 允许RSA算法验证

在windows终端上生成ssh-key

ssh-keygen -t rsa -b 4096 							# 一直回车即可

会在C:\users\用户名\.ssh目录下生成一个私钥文件和公钥文件(pub结尾),使用文本文档打开pub文件,复制全部内容到Ubuntu的/root/.ssh/authorized_keys文件内,重启ssh后即可,尝试使用ssh登录可看到无需输入密码就可以进入系统

systemctl restart ssh
systemctl enable ssh

image

2、安装常用工具

apt update && apt install iputils-ping traceroute unzip wget curl dnsutils iproute2 net-tools -y

3、命令行补全

安装bash-completion

apt install bash-completion -y

在/etc/bash.bashrc文件下添加脚本命令

if ! shopt -oq posix; then
  if [ -f /usr/share/bash-completion/bash_completion ]; then
    . /usr/share/bash-completion/bash_completion
  elif [ -f /etc/bash_completion ]; then
    . /etc/bash_completion
  fi
fi

退出当前用户终端重新进入即可

4、开启内核的路由转发功能

net.ipv4.ip_forward = 1
net.ipv6.conf.all.forwarding = 1
net.ipv4.tcp_congestion_control = bbr

5、安装NetworkManager管理网络

apt install network-manager -y

目前Ubuntu Server默认管理网络的工具是netplan,其实是不太方便的,配置network-manager管理网络

vim /etc/NetworkManager/NetworkManager.conf
# 修改下面内容
[ifupdown]
managed=true
vim /etc/netplan/00-installer-config.yaml                # 每个系统的文件名都不一样,我这里叫00-installer-config.yaml
# 在下添加,注意开头与 version:2 对齐:
renderer: NetworkManager

应用netplay并重启NetworkManager(可能会断网,做好准备)

netplay apply
systemctl restart NetworkManager

随后查看nmcli接管情况

nmcli con show

出现下面类似的输出说明接管完成

image

修改各网卡的Connect Name为网卡名称,为方便可选择使用Network Manager的伪图形化界面快速修改

nmtui

效果如下

image

6、创建桥接网卡

现代光猫、软路由的LAN网卡其实本质上就是一个虚拟的桥接网卡,类似与Switch(交换机),通过将物理网卡绑定在桥接网卡下实现交换机二层网络接口的功能(默认网卡是三层网络,每个接口必须属于一个独立的IP段)

创建虚拟网卡LAN

nmcli con add \
 con-name lan \
 type bridge \
 ifname lan \
 ipv4.method manual \
 ipv4.addr 192.168.100.254/24 \
 ipv6.method auto \
 stp no

这里我们关闭了STP,因为当LAN接口下没有任何物理端口up时,LAN接口处于关闭状态,接入端口后STP需要经过2个Forwarding时间(30秒)才能进入UP状态,即便进入UP状态,还需要DHCP服务反应一段时间,这就会造成终端在开机或者睡眠结束后无法立马获取到地址上网,需要等待1-2分钟,这是比较痛苦的。

注意:这里如果是在虚拟机中,确保添加的三个内网网卡不属于同一个vmnet,否则,STP不开启的情况下,你将尝到广播风暴的味道~但同时,现实中我们关闭了STP也要确保软路由下接的只有三层接口(终端、路由器等),如果两个接口接同一个交换机,一定要开STP或者聚合接口,要不然也是会环路的

将三张局域网网卡绑定到桥接网卡lan下

nmcli con modify ens34 master lan
nmcli con modify ens35 master lan
nmcli con modify ens36 master lan

image

image

7、配置轻量DHCP服务器

这里有两种轻量选择,一种是dns和dhcp都具备的dnsmasq,另一种是转为dhcp准备的udhcp。后面因为要做流量分流,国内流量和国外流量分开走,需要路由分流和DNS分流,因此需要后面肯定要安装对应的dns服务,但是这里不选择dnsmasq,因为它在处理大量域名列表的时候太过臃肿,CPU压力大,源于它的设计问题。后面再说,先说DHCP。

这里选择udhcpd是因为他非常的轻量,开发时就是为了嵌入式设备的小型化设备设计的,因此在运行时几乎不消耗资源。

安装udhcpd

apt install udhcpd -y

配置UDHCP服务器

# /etc/udhcpd.conf

# 指定要下发的地址范围
start          192.168.10.100
end            192.168.10.200

# 请确保这里是要监听DHCP请求的网络接口,例如 eth0, wlan0 等
interface       lan 

# DHCP租约文件,所有租约都将存储在这里。
lease_file     /var/lib/misc/udhcpd.leases
max_leases      101

# 指定到期时间
opt     lease   864000     # 10 days of seconds

# 指定要下发的掩码和网关
opt     subnet  255.255.255.0
opt     router  192.168.10.254

# 指定下发的DNS,如果后面要做DNS分流,一定也要指定dns是本设备
opt     dns     192.168.10.254
opt     dns     223.5.5.5

# 指定光播地址
opt     broadcast       192.168.10.255

# 指定下发的域
opt     domain  home

# 也可以指定静态下发地址
#static_lease  00:60:08:11:CE:4E 192.168.1.55
#static_lease  AA:BB:CC:DD:EE:FF 192.168.1.75
systemctl restart udhcpd
systemctl enable udhcpd

修改启动顺序为NetworkManager之后

由于udhcpd是在NetworkManager前启动的,而udhcpd中的lan接口又是NetworkManager创建的,因此会导致udhcpd在系统启动时无法得知lan接口导致启动失败,将其调整为NetworkManager后启动

vim /usr/lib/systemd/system/dnsmasq.service
# 修改以下内容
After=NetworkManager-wait-online.service
Wants=NetworkManager-wait-online.service

重新加载配置

systemctl daemon-reload

8、配置smartdns服务

之前说了dnsmasq在处理大量域名列表的时候太过臃肿,CPU压力大。因此选用smartdns服务,smartdns在设计时就保证了即使处理万级的域名列表也很轻松,特别是我们后面要配置的域名列表内有11万的国内域名列表。11万行的列表,dnsmasq处理时会有很长时间的CPU沾满的情况,绝对不适合,而smartdns即使处理11万行域名列表也可以较为轻松应对。

为什么要进行dns分流?因为国内大厂的站点都有CDN和DNS分流解析,国内DNS服务器和国外DNS服务器解析出来的IP地址不一样,国外的DNS走他们的海外节点,国内的DNS走国内的节点。因此,要进行分流的话,必须要做DNS分流。

安装smartdns

apt install smartdns -y

获取国内域名列表

cd /etc/smartdns
wget -O cn.conf https://raw.githubusercontent.com/felixonmars/dnsmasq-china-list/refs/heads/master/accelerated-domains.china.conf

可以先复制到windows电脑内用记事本打开,Ctrl + h 进行替换,把文件内的server=/换成namerserver ,/114.114.114.114换成/china 然后在放到ubuntu内

将.cn加入到列表的最上方,这样如果访问的是.cn域名的站点服务器直接从文件最上方就能匹配到,不需要往下寻找,消耗CPU资源了

nameserver /*.cn/china

配置smartdns

vim /etc/smartdns/smartdns.conf
# 在lan接口监听dns查询
bind :53 -device lan

# 创建china组且不放入default组,在default组内的server在为明确指定域名对应的dns服务器时,会使用default组内的dns服务器
server 114.114.114.114  -group china -exclude-default-group
server 223.5.5.5  -group china -exclude-default-group
server 223.6.6.6  -group china -exclude-default-group

# 创建global组,并在default组内,当没有明确的域名对应的dns服务器时,会使用default组内的dns服务进行查询
server 8.8.8.8 -group global
server 8.8.4.4 -group global
server 1.1.1.1 -group global

# 指定域名列表路径
conf-file /etc/smartdns/cn.conf

# 指定域名解析缓存大小,缓存到内存中,后续有相同的查询的话就不需要在文件内查询了
cache-size 65536
prefetch-domain yes

使得resovectl不监听系统网卡53端口,但是还是处理上游dns服务器,防止和dnsmasq冲突

vim /etc/systemd/resolved.conf
DNSStubListener=no
systemctl restart systemd-resolved.service

由于smartdns 是在NetworkManager前启动的,而dnsmasq中的lan接口又是NetworkManager创建的,因此会导致dnsmasq在系统启动时无法得知lan接口导致启动失败,将其调整为NetworkManager后启动

vim /usr/lib/systemd/system/smartdns.service
# 修改以下内容
After=NetworkManager-wait-online.service
Wants=NetworkManager-wait-online.service

重新加载配置

systemctl daemon-reload

9、使用OpenVPN进行科学上网和流量分流

篇幅太长,看我之前的文章

https://blog.opennw.cn/archives/openvpn-you-hua#15%E3%80%81%E5%AE%9E%E7%8E%B0-openvpn-%E5%88%86%E6%B5%81%E5%B9%B6%E8%87%AA%E5%8A%A8%E6%9B%B4%E6%96%B0%E8%B7%AF%E7%94%B1

这里要注意,安装openvpn-dco的话,有小部分概率tun网卡使用会不顺畅,具体看文章开头,目前只在inter cpu遇到过,而且我配置了十几台服务器了,只见过这一次。

10、调整系统启动时间

ubuntu18以后使用netplan管理网络,会有一个毛病,就是netplan配置的网络在系统启动时systemd-networkd-wait-online这个服务无法正常监听到网络启动,导致超时启动失败,导致系统启动时间有2分钟超时时间。可以将其超时时间控制在10秒,虽然可以关闭它,但是为了以后产生不必要的麻烦,还是调整超时时间吧

mkdir /etc/systemd/system/systemd-networkd-wait-online.service.d
vim /etc/systemd/system/systemd-networkd-wait-online.service.d/override.conf
# 加入以下内容
[Service]
TimeoutStartSec=10sec

重新加载配置

systemctl daemon-reload

重启系统即可发现开机速度快了

11、配置防火墙和NAT

以下是nftables防火墙的加速数据包转发、NAT和有状态包过滤防护配置,内容太大了,不单独解释,没学过的自行查资料

vim /etc/nftables.conf
#!/usr/sbin/nft -f

flush ruleset

# 预定义配置
define lan_int={ lan, wlp6s0 }
define wan_int={ enp1s0 }
define vpn_ip={ 10.10.10.0/24, 10.10.20.0/24, 10.10.30.0/24  }

table inet filter {
		# flowtables,数据包卸载功能,匹配的数据包可绕过prerouting、routing、forwarding和postrouting hook直接跳出netfilter,减少cpu消耗
        flowtable myft {
                hook ingress priority filter;
                devices = { enp1s0, lan, wlp6s0 }
                counter
        }


        chain input {
                type filter hook input priority filter; policy drop;

				# 放行已建立连接和与连接相关的连接(有状态防火墙)
                ct state { established, related } accept

                iif "lo" accept

                iif $lan_int accept

                ip saddr $vpn_ip accept

                iif $wan_int ip protocol tcp tcp dport { 22 } accept
        }

        chain forward {
                type filter hook forward priority filter; policy drop;

				# 将已建立连接和与连接相关的连接调用在加速数据转发的flowtable中(要放在accept之前,否则直接放行了,不会被匹配)
                ct state { established, related } flow add @myft
				# 放行已建立连接和与连接相关的连接(有状态防火墙)
                ct state { established, related } accept

                iif $lan_int accept

                ip saddr $vpn_ip accept

        }

        chain output {
                type filter hook output priority filter; policy accept;

                ct state { established, related } accept
        }

        chain postrouting {
                type nat hook postrouting priority 100; policy accept;

				# 出接口非环回接口和LAN接口时进行SNAT伪装
                oif !={ lo, lan } masquerade

                ct state { established, related } accept
}
}

加载nftables规则并设置为开机自启动

nft -f /etc/nftables.conf
systemctl enable nftables

同样的还是修改nftabels的启动顺序在NetworkManager之后

vim /usr/lib/systemd/system/nftables.service
# 修改以下内容,并
After=NetworkManager-wait-online.service
Wants=NetworkManager-wait-online.service
systemctl daemon-reload

12、安装docker

国内安装docker会受限制,因此使用阿里云软件源进行安装

# 从阿里云镜像源下载 Docker 官方 GPG 密钥(国内访问快,无 SSL 连接问题)
# 并转换为 apt 可识别的密钥格式,保存到系统密钥目录
curl -fsSL https://mirrors.aliyun.com/docker-ce/linux/ubuntu/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg
# 生成适配当前系统架构和版本的阿里云 Docker 源配置
# 其中:
# - $(dpkg --print-architecture) 自动获取系统架构(如 amd64)
# - $(lsb_release -cs) 自动获取 Ubuntu 系统版本代号(如 jammy、focal)
# - signed-by 指定已添加的阿里云密钥路径,确保源验证通过
echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://mirrors.aliyun.com/docker-ce/linux/ubuntu $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
# 更新软件包索引(此时会从阿里云源获取信息,速度快且稳定)
sudo apt update

# 安装 Docker 引擎及相关组件(docker-ce 为 Docker 引擎,docker-ce-cli 为命令行工具,containerd.io 为容器运行时)
sudo apt install docker-ce docker-ce-cli containerd.io

# 配置docker开机自启动
sudo systemctl enable docker

检验docker是否安装

docker info
-----------------------------------------------------------------------
Client: Docker Engine - Community
 Version:    28.4.0
 Context:    default
 Debug Mode: false
 Plugins:
  buildx: Docker Buildx (Docker Inc.)
    Version:  v0.27.0
    Path:     /usr/libexec/docker/cli-plugins/docker-buildx
  compose: Docker Compose (Docker Inc.)
    Version:  v2.39.2
    Path:     /usr/libexec/docker/cli-plugins/docker-compose
.......................

关闭docker自动生成防火墙配置,后面我们自己配置,并且设置docker默认桥接网卡的ip地址

vim /etc/docker/daemon.json
{
  "iptables": false,
  "ip6tables": false,
  "bip": "192.168.17.1/24"
}

重启docker生效

systemctl restart docker

13、配置IPv6相关内容

在有IPv6地址的环境下,我们为了方便访问内网设备,肯定是要获取IPv6地址的,外网接口从运营商获取到ICMPv6的RA报文,从中提取IPv6前缀并根据eui64生成一个IPv6地址。更重要的是,我们还要通过这个获取到的前缀为局域网内的设备下发IPv6地址。

以上前提:光猫是桥接模式,由ubuntu路由器复制PPPOE拨号,得到运营商下发的PD前缀才可,大多为56或50。否则最多获取到/64的地址,/64的地址是不能继续拆分向下分发的,因为拆分的话就到/65了,而下发ipv6地址最少要求的是/64;

如果ubuntu路由器只能通过RS/RA获取到/64的地址,那么,LAN接口下的设备是无缘公网IPv6地址了,只能WAN接口获取/64地址,然后LAN接口配置/64的私网地址下发RA消息,使得局域网获取到一个私网的/64前缀生成地址。如果希望WAN侧主机通过IPv6访问内网主机,需要做DNAT才可以。

配置外网接口获取IPv6地址

nmcli con modify ens33 ipv6.method auto \
 ipv6.addr-gen-mode eui64 \
 ipv6.ip6-privacy 0
nmcli con up ens33				# 重启外网接口然后使用ip addr查看接口是否获取到IPv6地址

随后进行LAN接口配置

nmcli con modify lan ipv6.method shared \
 ipv6.address fd00:192:168:100::1/64
nmcli con up lan				# 重启内网接口

随后LAN接口下的终端主机在开启IPv6功能后即可通过ICMPv6的RS和RA报文获取对应的IPV6地址和IPv6 DNS信息

image

image

14、配置无线网络

相关配置也不再赘述,看我之前文章

https://blog.opennw.cn/archives/networkmanager#%E4%BA%8C%E3%80%81nmcli-%E7%9A%84%E7%9B%B8%E5%85%B3%E9%85%8D%E7%BD%AE(%E5%90%8E%E7%BD%AE%E5%8F%82%E6%95%B0%E5%9D%87%E5%8F%AF%E7%BC%A9%E5%86%99)

这里要注意的是,如果创建了AP模式的网卡向外发送信号,使用nmcli con up 无线连接名称时,可能无法启动会报错,与网卡默认的安全模式有关,使用以下命令修改后重新打开即可

nmcli connection modify xxxx 802-11-wireless-security.pmf 1
nmcli con up 无线连接名称