From 6c874ae75480adfb7194561effdadbd120c6c5dc Mon Sep 17 00:00:00 2001 From: Haitao Pan Date: Wed, 23 Apr 2025 11:48:51 +0800 Subject: [PATCH] feat(vxlan-overlay-hub): added and modified Ansible roles - Added new role 'vxlan-overlay-hub' to ansible playbooks - Renamed vpn-overlay.yaml playbook to vpn-overlay-vxlan-site.yaml - Modified sit/vpn-overlay configuration according to the new setup --- .../vpn-overlay/vxlan/hub/defaults/main.yml | 7 ++ .../vxlan/hub/files/setup_sit_vxlan.sh | 83 +++++++++++++++++++ .../vpn-overlay/vxlan/hub/tasks/main.yml | 50 +++++++++++ .../hub/templates/vxlan-setup.service.j2 | 12 +++ ansible/playbooks/vpn-overlay-vxlan-hub.yaml | 8 ++ ...erlay.yaml => vpn-overlay-vxlan-site.yaml} | 0 config/sit/vpn-overlay.yaml | 2 +- 7 files changed, 161 insertions(+), 1 deletion(-) create mode 100644 ansible/playbooks/roles/vhosts/vpn-overlay/vxlan/hub/defaults/main.yml create mode 100644 ansible/playbooks/roles/vhosts/vpn-overlay/vxlan/hub/files/setup_sit_vxlan.sh create mode 100644 ansible/playbooks/roles/vhosts/vpn-overlay/vxlan/hub/tasks/main.yml create mode 100644 ansible/playbooks/roles/vhosts/vpn-overlay/vxlan/hub/templates/vxlan-setup.service.j2 create mode 100644 ansible/playbooks/vpn-overlay-vxlan-hub.yaml rename ansible/playbooks/{vpn-overlay.yaml => vpn-overlay-vxlan-site.yaml} (100%) diff --git a/ansible/playbooks/roles/vhosts/vpn-overlay/vxlan/hub/defaults/main.yml b/ansible/playbooks/roles/vhosts/vpn-overlay/vxlan/hub/defaults/main.yml new file mode 100644 index 00000000..6d9fb4f3 --- /dev/null +++ b/ansible/playbooks/roles/vhosts/vpn-overlay/vxlan/hub/defaults/main.yml @@ -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 + diff --git a/ansible/playbooks/roles/vhosts/vpn-overlay/vxlan/hub/files/setup_sit_vxlan.sh b/ansible/playbooks/roles/vhosts/vpn-overlay/vxlan/hub/files/setup_sit_vxlan.sh new file mode 100644 index 00000000..992620bf --- /dev/null +++ b/ansible/playbooks/roles/vhosts/vpn-overlay/vxlan/hub/files/setup_sit_vxlan.sh @@ -0,0 +1,83 @@ +#!/bin/bash +# 安全增强版 VXLAN Overlay 脚本(支持 wg0 作为通道设备 + 可选公网 DNAT 映射) +# 用法: ./setup_sit_vxlan.sh [cidr_suffix] [vxlan_id] [mtu] [expose_port] + +set -e + +DEV_IF="$1" +LOCAL_IP="$2" +REMOTE_IP="$3" +BRIDGE_IP="$4" +CIDR_SUFFIX="${5:-16}" +VNI="${6:-100}" +MTU="${7:-1400}" +EXPOSE_PORT="${8:-443}" + +if [[ -z "$DEV_IF" || -z "$LOCAL_IP" || -z "$REMOTE_IP" || -z "$BRIDGE_IP" ]]; then + echo "Usage: $0 [cidr_suffix] [vxlan_id] [mtu] [expose_port]" + 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 + +# ⚠️ 可选:仅在 EXPOSE_PORT 被定义时启用 DNAT 公网端口映射 +if [[ -n "$EXPOSE_PORT" ]]; then + echo "🌐 添加 DNAT 映射规则:公网:$EXPOSE_PORT → ${BRIDGE_IP}:443" + + iptables -t nat -C PREROUTING -p tcp -m tcp --dport "$EXPOSE_PORT" -j DNAT --to-destination "${BRIDGE_IP}:443" 2>/dev/null || \ + iptables -t nat -A PREROUTING -p tcp -m tcp --dport "$EXPOSE_PORT" -j DNAT --to-destination "${BRIDGE_IP}:443" +fi + +# 展示结果 +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" +if [[ -n "$EXPOSE_PORT" ]]; then +echo " - DNAT: 公网:$EXPOSE_PORT → ${BRIDGE_IP}:443" +fi diff --git a/ansible/playbooks/roles/vhosts/vpn-overlay/vxlan/hub/tasks/main.yml b/ansible/playbooks/roles/vhosts/vpn-overlay/vxlan/hub/tasks/main.yml new file mode 100644 index 00000000..55025c8f --- /dev/null +++ b/ansible/playbooks/roles/vhosts/vpn-overlay/vxlan/hub/tasks/main.yml @@ -0,0 +1,50 @@ +--- +- 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_hub: "{{ overlay_data.hubs | selectattr('name', 'equalto', inventory_hostname) | list | first }}" + +- name: Fail if no matching site found + fail: + msg: "当前主机 {{ inventory_hostname }} 未在 config 中定义" + when: current_hub is not defined + +- name: Set VXLAN parameters + set_fact: + vxlan_dev_if: "{{ current_hub.interface}}" + vxlan_local_ip: "{{ current_hub.local_ip }}" + vxlan_remote_ip: "{{ current_hub.remote_ip }}" + vxlan_br_ip: "{{ current_hub.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 diff --git a/ansible/playbooks/roles/vhosts/vpn-overlay/vxlan/hub/templates/vxlan-setup.service.j2 b/ansible/playbooks/roles/vhosts/vpn-overlay/vxlan/hub/templates/vxlan-setup.service.j2 new file mode 100644 index 00000000..d9313f02 --- /dev/null +++ b/ansible/playbooks/roles/vhosts/vpn-overlay/vxlan/hub/templates/vxlan-setup.service.j2 @@ -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 diff --git a/ansible/playbooks/vpn-overlay-vxlan-hub.yaml b/ansible/playbooks/vpn-overlay-vxlan-hub.yaml new file mode 100644 index 00000000..4399f42a --- /dev/null +++ b/ansible/playbooks/vpn-overlay-vxlan-hub.yaml @@ -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/hub diff --git a/ansible/playbooks/vpn-overlay.yaml b/ansible/playbooks/vpn-overlay-vxlan-site.yaml similarity index 100% rename from ansible/playbooks/vpn-overlay.yaml rename to ansible/playbooks/vpn-overlay-vxlan-site.yaml diff --git a/config/sit/vpn-overlay.yaml b/config/sit/vpn-overlay.yaml index 9cac8894..5b5d44b4 100644 --- a/config/sit/vpn-overlay.yaml +++ b/config/sit/vpn-overlay.yaml @@ -19,7 +19,7 @@ hubs: wg_ip: 172.30.0.1 br_ip: 10.253.253.1 local_ip: 172.30.0.1 - remote_ip: 172.31.0.1 + remote_ip: 172.31.0.10 xray: uuid: "18d270a9-533d-4b13-b3f1-e7f55540a9b2" relay_address: "global-proxy.onwalk.net"