feat: Add VXLAN over WireGuard support with site automation

- Enabled VXLAN over L3 via WireGuard tunnel;
- Standardized Ansible role structure for `vpn-overlay`;
- Integrated systemd service management via `vxlan-setup.service`;
- Configured per-site VXLAN overlay in `config/sit/vpn-overlay.yaml`;
This commit is contained in:
Haitao Pan 2025-04-06 00:08:17 +08:00
parent cb8dda4fed
commit d38c4770ac
7 changed files with 175 additions and 19 deletions

View File

@ -2,4 +2,4 @@
export DEBIAN_FRONTEND=noninteractive
sudo apt-get update
sudo apt-get install -y vim inetutils-ping
sudo apt-get install -y vim iputils-ping rsync

View File

@ -0,0 +1,7 @@
---
# Default VXLAN settings for site nodes
vxlan_dev_if: wg0
vxlan_vni: 100
vxlan_cidr_suffix: 16
overlay_config_path: config/sit/vpn-overlay.yaml

View File

@ -0,0 +1,76 @@
#!/bin/bash
# 安全增强版 VXLAN Overlay 脚本(支持 wg0 作为通道设备)
# 用法: ./setup_sit_vxlan.sh <dev_if> <local_ip> <remote_ip> <br0_ip> [cidr_suffix] [vxlan_id] [mtu]
set -e
DEV_IF="$1"
LOCAL_IP="$2"
REMOTE_IP="$3"
BRIDGE_IP="$4"
CIDR_SUFFIX="${5:-16}"
VNI="${6:-100}"
MTU="${7:-1400}"
if [[ -z "$DEV_IF" || -z "$LOCAL_IP" || -z "$REMOTE_IP" || -z "$BRIDGE_IP" ]]; then
echo "Usage: $0 <dev_if> <local_ip> <remote_ip> <br0_ip> [cidr_suffix] [vxlan_id] [mtu]"
exit 1
fi
VXLAN_IF="vxlan${VNI}"
BR_IF="br0"
BRIDGE_CIDR="${BRIDGE_IP}/${CIDR_SUFFIX}"
SUBNET="$(echo "$BRIDGE_IP" | cut -d. -f1-2).0.0/${CIDR_SUFFIX}"
# 自动判断 dev 是否能用于 VXLAN需支持广播
function is_vxlan_dev_usable() {
[[ -d "/sys/class/net/$1" ]] && grep -q "broadcast" "/sys/class/net/$1/flags"
}
echo "🔍 检查 $DEV_IF 是否可用于 VXLAN..."
USE_DEV_PARAM=true
if ! is_vxlan_dev_usable "$DEV_IF"; then
echo "⚠️ $DEV_IF 不支持广播,将省略 dev 参数(通过路由走隧道)"
USE_DEV_PARAM=false
fi
# 清理旧接口
for iface in "$VXLAN_IF" "$BR_IF"; do
ip link show "$iface" &>/dev/null && ip link set "$iface" down && ip link del "$iface"
done
# 创建 VXLAN 接口
echo "🛠️ 创建 VXLAN 接口 $VXLAN_IF ..."
if $USE_DEV_PARAM; then
ip link add "$VXLAN_IF" type vxlan id "$VNI" dstport 4789 local "$LOCAL_IP" remote "$REMOTE_IP" dev "$DEV_IF"
else
ip link add "$VXLAN_IF" type vxlan id "$VNI" dstport 4789 local "$LOCAL_IP" remote "$REMOTE_IP"
fi
ip link set "$VXLAN_IF" mtu "$MTU"
ip link set "$VXLAN_IF" up
# 创建 br0 桥
ip link add "$BR_IF" type bridge
ip link set "$BR_IF" mtu "$MTU"
ip link set "$VXLAN_IF" master "$BR_IF"
ip link set "$BR_IF" up
# 配置 IP
ip addr add "$BRIDGE_CIDR" dev "$BR_IF"
# 启用转发 + SNAT仅主机出网时
sysctl -w net.ipv4.ip_forward=1
iptables -t nat -C POSTROUTING -s "$SUBNET" -o "$DEV_IF" -j MASQUERADE 2>/dev/null || \
iptables -t nat -A POSTROUTING -s "$SUBNET" -o "$DEV_IF" -j MASQUERADE
# 尝试激活邻居学习
REMOTE_BR_IP="172.16.0.$(( $(echo "$BRIDGE_IP" | awk -F. '{print $4}') == 2 ? 3 : 2 ))"
ping -c 1 "$REMOTE_BR_IP" || true
# 展示结果
echo ""
echo "✅ VXLAN Overlay 已建立"
echo " - bridge: $BR_IF ($BRIDGE_CIDR)"
echo " - vxlan: $VXLAN_IF ($LOCAL_IP$REMOTE_IP, id=$VNI, mtu=$MTU)"
echo " - SNAT: $SUBNET$DEV_IF"

View File

@ -0,0 +1,54 @@
---
- name: Load site overlay config
set_fact:
overlay_data: "{{ lookup('file', overlay_config_path) | from_yaml }}"
- name: Select current site from config
set_fact:
current_site: "{{ overlay_data.sites | selectattr('name', 'equalto', inventory_hostname) | list | first }}"
- name: Fail if no matching site found
fail:
msg: "当前主机 {{ inventory_hostname }} 未在 config 中定义"
when: current_site is not defined
- name: Select first hub as default
set_fact:
selected_hub: "{{ overlay_data.hubs[0] }}"
- name: Set VXLAN parameters
set_fact:
vxlan_dev_if: "{{ current_site.interface}}"
vxlan_local_ip: "{{ current_site.local_ip }}"
vxlan_remote_ip: "{{ current_site.remote_ip }}"
vxlan_br_ip: "{{ current_site.br_ip }}"
vxlan_cidr_suffix: "{{ overlay_data.vxlan_cidr_suffix | default(16) }}"
vxlan_vni: "{{ overlay_data.vxlan_id | default(100) }}"
- name: 使用 rsync 分发 VXLAN 脚本
synchronize:
src: "files/setup_sit_vxlan.sh"
dest: /tmp/setup_sit_vxlan.sh
mode: push
- name: 移动脚本并赋权到 /usr/local/bin
shell: |
mv /tmp/setup_sit_vxlan.sh /usr/local/bin/setup_sit_vxlan.sh
chmod +x /usr/local/bin/setup_sit_vxlan.sh
become: true
- name: Render systemd unit for VXLAN
template:
src: vxlan-setup.service.j2
dest: /etc/systemd/system/vxlan-setup.service
mode: '0644'
- name: Reload systemd daemon
systemd:
daemon_reload: true
- name: Enable and start VXLAN overlay setup
systemd:
name: vxlan-setup
enabled: true
state: started

View File

@ -0,0 +1,12 @@
[Unit]
Description=VXLAN Overlay Setup for {{ inventory_hostname }}
After=network-online.target
Wants=network-online.target
[Service]
Type=oneshot
ExecStart=/usr/local/bin/setup_sit_vxlan.sh {{ vxlan_dev_if }} {{ vxlan_local_ip }} {{ vxlan_remote_ip }} {{ vxlan_br_ip }} {{ vxlan_cidr_suffix }} {{ vxlan_vni }}
RemainAfterExit=true
[Install]
WantedBy=multi-user.target

View File

@ -0,0 +1,8 @@
- name: Run infrastructure setup
hosts: all
become: yes
gather_facts: yes
vars:
overlay_config_path: "{{ playbook_dir }}/../../config/sit/vpn-overlay.yaml"
roles:
- vhosts/vpn-overlay/vxlan/site

View File

@ -1,16 +1,16 @@
# 基础网络参数
wg_network: 10.100.0.0/24
bridge_network: 172.16.0.0/16
wg_network: 172.16.0.0/24
bridge_network: 10.253.0.0/16
vxlan_id: 100
hub_port: 51820
# 全局功能开关
features:
enable_gretap: true # 是否开启 gretap 桥接(站点接入 Hub
enable_vxlan_between_sits: true # 是否开启 vxlan 桥接(站点接入 Hub
enable_vxlan_between_hubs: true # 是否开启 Hub 之间的 VXLAN Mesh
enable_vless: true # 是否通过 VLESS 中转 WG 流量
enable_multi_hub: true # 是否支持多 Hub 架构false 则为单 Hub star 架构)
only_wireguard: false # 若为 true仅使用 WireGuard 点对点,忽略 gretap/vxlan
enable_vless: true # 是否通过 VLESS 中转 WG 流量
enable_multi_hub: true # 是否支持多 Hub 架构false 则为单 Hub star 架构)
only_wireguard: false # 若为 true仅使用 WireGuard 点对点,忽略 gretap/vxlan
# WireGuard Hub 节点配置
hubs:
@ -26,24 +26,23 @@ hubs:
# 各个站点定义
sites:
- name: site_a
wg_ip: 10.100.0.2
br_ip: 172.16.0.2
hub: hub1
public_ip: a.example.com
- name: master-1
interface: ens5
br_ip: 10.253.253.2
local_ip: 10.255.0.200
remote_ip: 10.255.0.100
vless:
enabled: true
endpoint: vless-a.example.com
enabled: false
uuid: "11111111-1111-1111-1111-111111111111"
transport: ws
tls: true
path: /wg
- name: site_b
wg_ip: 10.100.0.3
br_ip: 172.16.0.3
hub: hub1
public_ip: b.example.com
- name: slave-1
interface: ens5
br_ip: 10.253.253.3
local_ip: 10.255.0.100
remote_ip: 10.255.0.200
vless:
enabled: false # 此节点不通过 VLESS直接访问 Hub