Setup Software Router
最近想设置一个自己的服务器,一方面合理利用宽带的公网IP资源,可以在服务器上运行一些常规的服务,一方面能够替换掉TPlink路由器,作为一个家庭网关。遂规划搭建一个软路由。
规划
当前服务器有两个有线网卡,eno1
和 enp2s0
,一个wifi网卡 wlp3s0
。
将 eno1 作为 WAN 口进行pppoe与服务商连接,enp2s0 作为有线 LAN 口对内网提供服务。wlp3s0 提供2.4G/5G wifi 作为WLAN使用。
$ ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
inet6 ::1/128 scope host
valid_lft forever preferred_lft forever
2: enp2s0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP group default qlen 1000
link/ether 70:85:c2:a9:d5:02 brd ff:ff:ff:ff:ff:ff
inet6 fe80::7285:c2ff:fea9:d502/64 scope link
valid_lft forever preferred_lft forever
3: eno1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
link/ether 70:85:c2:a9:d5:00 brd ff:ff:ff:ff:ff:ff
inet6 fe80::7285:c2ff:fea9:d500/64 scope link
valid_lft forever preferred_lft forever
4: wlp3s0: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN group default qlen 1000
link/ether 3c:6a:a7:a0:d6:10 brd ff:ff:ff:ff:ff:ff
netplan setup
ubuntu server 默认使用 systemd-network 配合 netplan 进行网络管理。自身非常精简,仅提供了常用的功能。
此外 cloud-init 也是使用的 netplan 的配置格式进行配置的,所以先研究了一下 netplan 相关。
先看官方文档:netplan reference
还可以看示例配置:netplan examples
$ ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
inet6 ::1/128 scope host
valid_lft forever preferred_lft forever
2: enp2s0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP group default qlen 1000
link/ether 70:85:c2:a9:d5:02 brd ff:ff:ff:ff:ff:ff
inet 192.168.101.6/24 brd 192.168.101.255 scope global enp2s0
valid_lft forever preferred_lft forever
inet6 fe80::7285:c2ff:fea9:d502/64 scope link
valid_lft forever preferred_lft forever
3: eno1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
link/ether 70:85:c2:a9:d5:00 brd ff:ff:ff:ff:ff:ff
inet 192.168.1.5/24 brd 192.168.1.255 scope global dynamic eno1
valid_lft 86231sec preferred_lft 86231sec
inet6 fe80::7285:c2ff:fea9:d500/64 scope link
valid_lft forever preferred_lft forever
4: wlp3s0: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN group default qlen 1000
link/ether 3c:6a:a7:a0:d6:10 brd ff:ff:ff:ff:ff:ff
/etc/netplan/interfaces.yaml
network:
version: 2
ethernets:
enp2s0:
dhcp4: true
eno1:
dhcp4: true
pppoe setup
netplan 不支持设置 pppoe,因 networkd 也不支持设置 pppoe 。
参见add pppoe support to systemd-networkd·Issue481,因networkd认为pppoe作为"老旧"的技术,且不再云环境中常用,所以在其路标上不计划该特性。(我觉得有道理,能够坚持在cloud方向做好)
转而使用 ppp 进行pppoe配置。推荐配合 networkd-dispatcher 使用,该组件是类似于 ifupdown 的hook,也提供了存放hook脚本的地方。
推荐查看官方文档: networkd-dispatcher
决定在 /etc/networkd-dispatcher/carrier.d
中编写脚本,在eno1端口负载时进行pppoe连接。
安装 ppp
sudo apt-get install ppp
推荐查看官方文档: PPP github或者gitweb on ozlabs.org或者 man pppd
以了解如何使用。其中官方文档对 pppoe 提供了详细说明 README.pppoe
编写 pppoe config /etc/ppp/peers/eno1
plugin rp-pppoe.so nic-eno1
ifname ppp0
user "CDXXXXXXXX"
noipdefault
usepeerdns
defaultroute
persist
noauth
+ipv6 ipv6cp-use-ipaddr
配置 pap-secrets /etc/ppp/pap-secrets
,为了某种安全考虑,ppp将密码文件与配置文件分开存放,使用时会根据配置中的 username
在 secrets 中寻找对应的密码进行使用。
sudo sh -c 'echo "CDXXXXXXXX * XXXXXXXX" >> /etc/ppp/pap-secrets'
编写 hook script /etc/networkd-dispatcher/carrier.d/setup-pppoe.sh
#!/bin/env sh
# interface to pppoe workload
INTERFACE=eno1
if [ "${IFACE}" = "${INTERFACE}" ] ; then
echo "running pon ${INTERFACE}..."
pon ${INTERFACE}
fi
pon 命令由 ppp 提供,其本质是一个shell script,感兴趣的可以
cat $(which pon)
看一下内容
ddns setup
pppoe 由于是家用的,每次获取到的公网地址随机,因此需要使用ddns来动态解析以获取我们真实的地址。以后服务器的服务可以基于该dns进行使用。
ddns 使用的是多年前的 dnspod 服务(现已经被腾讯云收购,不过还可以正常使用),Dnspod API文档
创建 hook script /etc/networkd-dispatcher/routable.d/setup-ddns.sh
,也可在我的github上etc/openwrt查看该script内容。
#!/bin/sh
INTERFACE=ppp0 #networkd-dispacher IFACE - interface that triggered the event
LOGIN_TOKEN="ID,TOKEN"
DOMAIN="<DOMAIN>"
SUB_DOMAIN="<SUB_DOMAIN>"
DNSPOD_HOST=https://dnsapi.cn
RECORED_LIST_PATH=Record.List
RECORED_UPDATE_PATH=Record.Modify
getRecord(){
curl -s -X POST -d "login_token=${LOGIN_TOKEN}&format=json&domain=${DOMAIN}" ${DNSPOD_HOST}/${RECORED_LIST_PATH} | jq '.records[]|select(.name=="'${SUB_DOMAIN}'" and .type=="A")'
}
updateRecord(){
curl -s -X POST -d "login_token=${LOGIN_TOKEN}&format=json&domain=${DOMAIN}&sub_domain=${SUB_DOMAIN}&record_id=${1}&record_type=A&record_line_id=0&value=${2}" ${DNSPOD_HOST}/${RECORED_UPDATE_PATH}
}
record=$(getRecord)
externalIP=$ADDR #networkd-dispacher ADDR - the ipv4 address of the device
if [ "${IFACE}" != "${INTERFACE}" ] ; then
echo "skip event ${STATE} ${IFACE} ${ADDR}"
exit
fi
if [ "$(echo "$record"|jq -r '.value')" != "$externalIP" ] ; then
echo "dns record value [$(echo "$record" | jq -r .value )] is out of date, updating to [$externalIP]"
updateRecord "$(echo "$record"|jq -r .id)" "${externalIP}"
else
echo "dns record value [$(echo "$record" | jq -r .value )] is up to date, nothing todo"
fi
网友评论