紀錄一下DN42內網搭建的過程
紀錄一下DN42搭建內網的過程
鏈路隧道
首先要搞定的就是各節點的內網互連手段。
在DN42網路中,一般是使用VPN隧道。
真實網路則是光纜/網路線/無線電/wifi/衛星,當然你也可以在DN42中用這些
但DN42是用來低成本學習網路的,VPN最便宜
我自己用的VPN是我自己刻的Etherguard-VPN
這是一個Layer2 VPN,能根據單向延遲來選路,而不是雙向延遲。具體工作原理
隧道有分Layer2和Layer3,在這之上又分許多不同的軟體
L2和L3最大的差異就是是否攜帶了EtherFrame,EtherFrame裡面攜帶的是Src MacAddr和Dst MacAddr
L2 VPN有帶,L3 VPN沒帶
假設今天來了一個封包,我們本地看到這個封包,有3個下一跳可以選
如果是L2 VPN,選擇完以後,我們只須把下一跳的MacAddr填入EtherFrame,然後送入VPN即可。只建立一個隧道,後面多個下一跳很容易做到
如果是L3 VPN,因為沒有EtherFrame,我們必須對每個下一跳,分別建立各自的VPN隧道
有n個下一跳,就要建立n條隧道。然後根據下一跳,送入指定的隧道。只建立一個隧道,後面對應多個下一跳是不可能的
在我們的情境中
L2 VPN像是一台switch,所有電腦接入就在相同內網了
L3 VPN則像是單根網路線,p2p只有兩端。多台電腦互聯,就需要多根網路線了
L3 VPN,一對多連線模式也是存在的。像是openvpn/wireguard多個peer。但是在此種情境中,路由表示固定好的。wg設定檔的allowed ips,openvpn發配的ip,就已經決定好這個ip段該發去的peer節點了,由vpn server執行路由
但是DN42這裡,路由功能必須在自身節點上執行,而不是VPN server執行
所以L3 VPN只能使用p2p模式
但我自己覺得L3不是很方便,以這位群友為例,他的拓樸,內網就要配9條wg隧道
iBGP要求任意2節點的iBGP session兩兩互相可達,iBGP沒有維護內網路由的功能
L2 VPN 由VPN自身提供斷線繞路功能。但是在網路拓樸上,所有節點都是嚇一跳可達。繞路的行為被隱藏起來了
L3 VPN 只能p2p。內網全域的可達性就必須用OSPF/babel等協議維護
Layer 2 | Layer 3 |
---|---|
Zerotier | Wireguard |
n2n | IPIP |
VpnCloud | GRE |
Tinc | Tinc |
OpenVPN | OpenVPN |
作業系統是Debian 11,啟用bullseye-backports
包
echo "deb http://deb.debian.org/debian bullseye-backports main" >> /etc/apt/sources.list
apt update
安裝git
、bird 2.0.8
、golang 1.17
apt install git golang-src/bullseye-backports golang-go/bullseye-backports bird2/bullseye-backports
git clone https://github.com/KusakabeSi/EtherGuard-VPN
cd EtherGuard-VPN
make
然後請參考這篇教學,建立一個gensuper.yaml
我的情況:
Config output dir: /tmp/eg_gen
ConfigTemplate for super node: ""
ConfigTemplate for edge node: ""
Network name: dn42-egnet
Super Node:
Listen port: 14141 #赫茲雲的port
EdgeAPI prefix: /kskb_eg_net/eg_api
Endpoint(IPv4)(optional): de.hertz.vm.kskb.eu.org #赫茲雲的ip
Endpoint(IPv6)(optional): "[::]"
Endpoint(EdgeAPI): https://dn42egapi.kskb.eu.org/kskb_eg_net/eg_api #這個EdgeAPI之後會套一層argo tunnel
Edge Node:
Node IDs: "[1,9,10]"
MacAddress prefix: ""
IPv4 range: ""
IPv6 range: ""
IPv6 LL range: fe80::42:1817:1/112 # VPN接口上只放了ipv6 link-local地址,真正的地址放在dn42-dummy上面,並且透過iBGP廣播給其他節點
然後就生成了4個etherguard設定檔,一個super node,三個edge node。
./etherguard-go -mode gencfg -cfgmode super -config ~/gensuper.yaml
/tmp/eg_gen/dn42-egnet_edge01.yaml
/tmp/eg_gen/dn42-egnet_edge09.yaml
/tmp/eg_gen/dn42-egnet_edge10.yaml
/tmp/eg_gen/dn42-egnet_super.yaml
mv /tmp/eg_gen /etc/eggo
把edge分發到各台電腦上,然後在Supernode執行
./etherguard-go -config [設定檔位置] -mode super
在EdgeNode執行
./etherguard-go -config [設定檔位置] -mode edge
註冊argo tunnel,用來幫EdgeAPI加上https,還不用renew證書,太方便了
cloudflared tunnel create dn42egapi
cloudflared tunnel route dns dn42egapi dn42egapi.kskb.eu.org
cloudflared --credentials-file /root/.cloudflared/ddef52c6-ae6a-4151-a177-03168657018c.json --url http://127.0.0.1:14141 tunnel run dn42egapi
然後想辦法讓上面那些程式跑在背景+開機自啟即可。
如果抄我的作法,各位各顯神通吧,看是systemd還是tmux還是rc-local放背景
到這邊,我的鏈路層搭建完畢了
我自己是用systemd來管理服務的,以下單純紀錄一下我自己的配置
Etherguard
https://github.com/KusakabeSi/EtherGuard-VPN/tree/master/example_config/systemd
cloudflared
#/etc/systemd/system/cloudflared.service
[Unit]
Description=Argo Tunnel
After=network.target
[Service]
TimeoutStartSec=0
Type=notify
ExecStart=cloudflared --credentials-file /root/.cloudflared/ddef52c6-ae6a-4151-a177-03168657018c.json --url http://127.0.0.1:14141 tunnel run dn42egapi
Restart=on-failure
RestartSec=5s
[Install]
WantedBy=multi-user.target
啟動服務
systemctl daemon-reload
cd /etc/eggo
mv dn42-egnet_super.yaml super.yaml
mv dn42-egnet_edge10.yaml edge.yaml
systemctl start etherguard-super
systemctl start etherguard-edge
systemctl start cloudflared
systemctl enable etherguard-super
systemctl enable etherguard-edge
systemctl enable cloudflared
路由協議
接下來是路由協議的部分
ip link add dn42-dummy type dummy
ip addr add 172.22.77.33/28 dev dn42-dummy
ip addr add fd28:cb8f:4c92::33/48 dev dn42-dummy
ip link set dn42-dummy up
例如 ip addr add 192.168.0.1/24 dev eth0 ,就隱含了192.168.0.0/24 via eth0 的靜態路由
或是 ip addr add 172.22.77.33 peer 172.20.22.44 dev eth0,就隱含了 172.20.22.44/32 via eth0 的靜態路由
動態路由協議溝通時,使用隧道ip進行溝通。如果隧道ip掛了,就知道鏈路中斷,需要重新計算/分發路由
如果隧道ip==真實ip,鏈路中段也就意味著真實ip也掛了。雖然屬於真實ip,路由協議仍會幫這個ip分發新的路由。但是綁定設備隱含的/32路由具有最高優先權,導致連接仍然中斷
所以路由器和路由器之間的連線,隧道地址要和真實地址分開
隧道地址單純用來讓路由協議溝通,以及探測連接成功與否
真實ip則是路由由路由協議分發
不過iBGP其實不用link-local,可以直接用真實地址。
因為iBGP並不負責內網路由的計算,只負責在各節點間傳遞外網路由。
內網路由用ospf/babel等協議溝時,會自己用 ff02::5/ff02::1:6 這樣的ip進行溝通
在此行況下,iBGP就需要啟用multihop。就不能用鏈路地址了,必須使用真實地址
因為鏈路地址的可達性隨鏈路狀態變化,斷線就沒了。但是iBGP應該隨時保持fullmesh連線狀態
此時,就必須iBGP peer時使用真實地址,內網真實地址的路由ospf/babel分發。iBGP只管連線就好,傳遞外網路由
繼上一篇搭建好鏈路層以後,我的網路結構大致上長這樣:
為求簡我內網iGP預計僅使用iBGP,不使用OSPF/babel/Openflow/SDN等
應該不會有別人也用這段吧?不會吧?
- Static路由拒絕,不要把本地定義的Static路由寫入系統路由表
- LocalNET接受,iBGP傳來的LocalNET要寫入系統路由表
- is_self_net() 拒絕。內網部分應該由LocalNET處理
- 如果出現了(21817,11111) 屬性,就拒絕。 LocalNET只在iBGP內傳遞,不應被廣播出去
- 如果出現了(21817,*) 屬性,就刪掉。因為這是我內部使用的,不管是別人給我/我給別人的路由上,都得刪掉
KSKB-DN42 網路,6份設定檔我是手寫
Kusakabe-Neo,9個節點72份設定檔,我則是用python腳本生成了
外面的封包會先抵達任何一個邊界路由器,然後經由iBGP宣告的LocalNET抵達真正該去的地方
類似藍色箭頭,按照1 2 3的順序抵達
Anycast IP + DNS/RDNS
我是先搭建 Kusakabe-Neo 的DNS/RDNS/Anycast的,當時是參考這個範本
經過我的修修改改,弄出這些設定檔。其中一部分是bird裝好就有的
然後KSKB-Network的部分,我就打算直接抄Kusakabe-Neo弄出來的設定檔了
首先是Anycast IP,在所有節點的dn42-dummy上面。都新增Anycast IP
我決定使用 172.22.77.46 和 fd28:cb8f:4c92:bbbb::53 作為我的dns以及Anycast IP
ip addr add 172.22.77.46 dev dn42-dummy
ip addr add fd28:cb8f:4c92:bbbb::53 dev dn42-dummy
因為我們所有節點對外廣播的時候,都是宣告有這個IP的
但是這個anycast ip我並未加入bird設定檔的LocalNET裡面,所以內網並沒有廣播這個ip
如果未設定,那他就會因為找不到路,而丟棄這個封包,造成部分區域連不上anycast
所以每台都要設定,沒有內網路由,而是全部節點都把這個IP當成本地IP
不想要全部節點都配置,可以這樣改:
bird裡面新增Anycast的網段,並且設定「如果本地有Anycast,就不import。如果本地沒有,就import」
就可以讓沒有設定Anycast的節點,內網路由到最近的有Anycast的節點
但是我節點少,決定全部節點都部屬。而且多部屬一些也能增加冗餘
再來,更新一下ns資訊。以前是抄藍天的,分為ns1和ns2。
但是發現使用Anycast更好,因為原本是隨機選擇,只能附載平衡而不能fallback。
不如用Anycast,A節點掛了,同時BGP就沒了,於是會被路由到B節點上,有fallback效果
節點同時存活,也能因為Anycast自動抵達最近節點,有附載平衡的效果
首先安裝bind9
apt install bind9 bind9utils
不過...本篇就真的只是記錄了,不太具有教學性質
因為接連打這麼多,我累了。裡面的設定檔的含意,可能不會有過多的解釋
git clone https://github.com/KSKBpage/miscblog
cd miscblog/articles/20211214-Neonetwork-DNS-RDNS
mv /etc/bind/ /etc/bind_bak
cp -r bind /etc/bind
然後找出有我Kusakabe-Neo的資訊,替換成我KSKB-Network的資訊
/etc/bind
grep -Irn kskb.neo
可以看到這4個檔案包含了我Kusakabe-Neo的資訊
db.10.127.111
db.fd10.127.e00f
db.kskb.neo
named.conf.default-zones
根據KSKB-Network的資訊重命名
mv db.10.127.111 db.172.22.77.28
mv db.fd10.127.e00f db.fd28:cb8f:4c92
mv db.kskb.neo db.kskb.dn42
然後修改 named.conf.default-zones 的這些zone,都要改成指向剛剛的檔案
kskb.neo
111.127.10.in-addr.arpa
f.0.0.e.7.2.1.0.0.1.d.f.ip6.arpa
其中有一點不一樣,因為NeoNetwork我是/24 網段,但是DN42是 /28
所以要用上 rfc2317,格式不太一樣,長這樣
zone "32/28.77.22.172.in-addr.arpa." {
type master;
file "/etc/bind/db.172.22.77.32";
};
接著編輯這些檔案,改成自己的DNS紀錄。最上面有個SOA紀錄,那邊也要記得改,容易忘記
還有,PTR紀錄的ip是倒過來寫的
db.172.22.77.28
db.fd28:cb8f:4c92
db.kskb.dn42
然後,記得編輯named.conf.options 。
額外加入 check-names master ignore; ,因為預設情況 / 字元好像不能出現在zone裡面,要加上這個選項去允許他
check-names master ignore;
listen-on port 53 { 172.22.77.46; };
listen-on-v6 port 53 { fd28:cb8f:4c92:bbbb::53; };
# 為了安全性只允許自己人進行遞迴查詢
allow-recursion { 172.22.77.32/28; fd28:cb8f:4c92/48 ;};
然後把 setup_dummy.sh 改成自己的Anycast,設定開機自啟動
最後,把改好的設定檔同步到所有節點,設定好dummy interface和ip,並啟動bind9
apt install bind9 bind9utils
cd /etc
mv bind bind_bak
git clone https://github.com/kskbconfig/KSKB-Network-BIND9 bind
bind/setup_dummy.sh
echo "/etc/bind/setup_dummy.sh" >> /etc/rc.local
systemctl restart named
systemctl enable named
Looking Glass + AutoPeer
累了,紀錄下過程.. 能work即可,就當紀錄給自己看
peer資訊頁
我是用cloudflare page生成各節點的peer資訊頁
https://github.com/KSKBpage/dn42node
編輯一下xx.env,就會生成對應網頁了
Looking Glass/Autopeer
nginx 設定檔,用途是分流2個後端,looking glass和autopeermap $http_upgrade $connection_upgrade { default upgrade; '' close; } server { listen 127.0.0.1:3236; client_max_body_size 0; server_name _; # Managing literal requests to the JupyterHub front end location / { proxy_pass http://127.0.0.1:3235; proxy_set_header Host $host; proxy_set_header X-Real-Scheme $scheme; proxy_set_header X-Real-IP $remote_addr; #proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_http_version 1.1; # websocket headers proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection $connection_upgrade; } location /autopeer/ { proxy_pass http://127.0.0.1:4242; proxy_set_header Host $host; proxy_set_header X-Real-Scheme $scheme; proxy_set_header X-Real-IP $remote_addr; #proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_http_version 1.1; # websocket headers proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection $connection_upgrade; } }cloudflared 反代nginx,讓外界連線
cloudflared tunnel create tw42 cloudflared tunnel route dns tw42 tw42.kskb.eu.org
cd ~/gitrs git clone https://github.com/KusakabeSi/DN42-AutoPeer.git git clone https://github.com/KusakabeSi/bird-lg-go.git cd bird-lg-go # Build frontend binary cd frontend go build -ldflags "-w -s" -o frontend cd .. # Build proxy binary cd proxy go build -ldflags "-w -s" -o proxy cd ..
把倉庫 https://github.com/KusakabeSi/DN42-AutoPeer.git clone下來
把自己的資訊填入 my_config.json 和 my_parameters.json
注意的是 urlprefix 要填 autopeer ,和nginx 設定檔呼應
然後把下面4隻程式跑在背景。懶得寫 systemd 了,把這些塞進 /etc/rc.local 就完事了
tmux new -d -s dn42ap -c DN42-AutoPeer python3 DN42AutoPeer.py tmux new -d -s dn42cf cloudflared --credentials-file /root/.cloudflared/66fe2b05-81ff-47dc-826c-4180eb27c82d.json --url http://127.0.0.1:3236 tunnel run tw42 tmux new -d -s birdlgp -c bird-lg-go proxy/proxy --bird=/var/run/bird/bird.ctl --listen="127.0.0.1:3234" tmux new -d -s birdlgf -c bird-lg-go frontend/frontend --bgpmap-info="asn,as-name" --servers="Taiwan<127.0.0.1>" --listen="0.0.0.0:3235" --proxy-port=3234 --whois=172.22.0.43 --dns-interface="" --navbar-brand="Peer with me (Taiwan)!" --navbar-brand-url="/autopeer/"
內網路由是節點僅僅發送自己的OWNIP,還是說發送節點的OWNIP還包括自己從外部接受到的路由都發另一個節點一份?
回覆刪除