恢复 OCI Terraform 与站点网络 IaC

This commit is contained in:
2026-09-17 13:22:49 +00:00
parent a08c8a7303
commit 71eea7d8fc
41 changed files with 1786 additions and 10 deletions
@@ -0,0 +1,20 @@
---
- name: 重载 FRR daemons
ansible.builtin.systemd_service:
name: frr
state: reloaded
when: not ansible_check_mode
- name: 刷新专用防火墙
ansible.builtin.systemd_service:
name: oci-wg-firewall
daemon_reload: true
state: restarted
when: not ansible_check_mode
- name: 重启 WireGuard
ansible.builtin.systemd_service:
name: wg-quick@{{ wg_interface }}
state: restarted
when: not ansible_check_mode
- name: 应用 BGP 增量配置
ansible.builtin.include_tasks: apply-bgp.yml
when: not ansible_check_mode
@@ -0,0 +1,8 @@
---
- name: 应用受管 FRR 配置片段
ansible.builtin.command: vtysh -f /etc/frr/oci-wireguard.vtysh
register: bgp_apply
failed_when: bgp_apply.rc != 0 or '% Unknown' in bgp_apply.stdout or '% Unknown' in bgp_apply.stderr
- name: 保存完整运行配置以便重启恢复
ansible.builtin.command: vtysh -c 'write memory'
@@ -0,0 +1,32 @@
---
- name: 安装 AMD DN42 loopback 地址启动单元
ansible.builtin.copy:
dest: /etc/systemd/system/dn42-loopback.service
mode: '0644'
content: |
[Unit]
Description=DN42 router loopback addresses
Before=frr.service
[Service]
Type=oneshot
ExecStart=/usr/sbin/ip address replace 172.21.111.162/32 dev lo
ExecStart=/usr/sbin/ip -6 address replace fdd0:98df:15b0::2/128 dev lo
RemainAfterExit=yes
[Install]
WantedBy=multi-user.target
register: dn42_loopback_unit
- name: 启用 AMD DN42 loopback
ansible.builtin.systemd_service:
name: dn42-loopback
daemon_reload: true
enabled: true
state: "{{ 'restarted' if dn42_loopback_unit.changed else 'started' }}"
when: not ansible_check_mode
- name: 开启路由器 IPv6 转发
ansible.posix.sysctl:
name: net.ipv6.conf.all.forwarding
value: '1'
sysctl_file: /etc/sysctl.d/90-dn42-forwarding.conf
sysctl_set: true
@@ -0,0 +1,163 @@
---
- name: 安装 WireGuard 与 FRR
ansible.builtin.apt:
name: [wireguard-tools, frr, iptables]
state: present
update_cache: true
cache_valid_time: 3600
register: packages
retries: 3
delay: 5
until: packages is succeeded
- name: 创建 WireGuard 受限目录
ansible.builtin.file:
path: /etc/wireguard
state: directory
owner: root
group: root
mode: '0700'
- name: 在站点本机生成私钥,永不复制到控制机
ansible.builtin.shell: 'umask 077; wg genkey > /etc/wireguard/{{ wg_interface }}.key'
args:
creates: '/etc/wireguard/{{ wg_interface }}.key'
no_log: true
- name: 检查现有私钥
ansible.builtin.stat:
path: '/etc/wireguard/{{ wg_interface }}.key'
register: wg_key_file
- name: 读取可交换的公钥
ansible.builtin.shell: 'wg pubkey < /etc/wireguard/{{ wg_interface }}.key'
register: wg_public_key
changed_when: false
check_mode: false
when: wg_key_file.stat.exists
- name: 启用 BGP daemon,保留其他协议
ansible.builtin.lineinfile:
path: /etc/frr/daemons
regexp: '^bgpd='
line: bgpd=yes
notify: 重载 FRR daemons
when: not ansible_check_mode or not packages.changed
- name: 确保 FRR 运行
ansible.builtin.systemd_service:
name: frr
enabled: true
state: started
when: not ansible_check_mode
- name: 配置 DN42 路由器地址
ansible.builtin.include_tasks: dn42-loopback.yml
when: bgp_export6 is defined
- name: 开启 IPv4 转发
ansible.posix.sysctl:
name: net.ipv4.ip_forward
value: '1'
sysctl_file: /etc/sysctl.d/90-oci-wireguard.conf
sysctl_set: true
- name: 安装专用防火墙规则脚本
ansible.builtin.template:
src: firewall.sh.j2
dest: /usr/local/sbin/oci-wg-firewall
owner: root
group: root
mode: '0755'
notify: 刷新专用防火墙
- name: 安装防火墙启动单元
ansible.builtin.copy:
dest: /etc/systemd/system/oci-wg-firewall.service
mode: '0644'
content: |
[Unit]
Description=OCI WireGuard scoped forwarding (no NAT)
After=network-pre.target
Before=wg-quick@{{ wg_interface }}.service
[Service]
Type=oneshot
ExecStart=/usr/local/sbin/oci-wg-firewall
RemainAfterExit=yes
[Install]
WantedBy=multi-user.target
notify: 刷新专用防火墙
- name: 启动防火墙规则
ansible.builtin.systemd_service:
name: oci-wg-firewall
daemon_reload: true
enabled: true
state: started
when: not ansible_check_mode
- name: 写入只含公钥的隧道配置
ansible.builtin.template:
src: wg.conf.j2
dest: '/etc/wireguard/{{ wg_interface }}.conf'
owner: root
group: root
mode: '0600'
when: wg_key_file.stat.exists and (hostvars[wg_peer_host].wg_key_file | default({"stat":{"exists":false}})).stat.exists
notify: 重启 WireGuard
- name: 启动 WireGuard
ansible.builtin.systemd_service:
name: 'wg-quick@{{ wg_interface }}'
enabled: true
state: started
when: not ansible_check_mode
- name: 写入受管 BGP 增量配置
ansible.builtin.template:
src: bgp.conf.j2
dest: /etc/frr/oci-wireguard.vtysh
owner: frr
group: frr
mode: '0640'
notify: 应用 BGP 增量配置
when: not ansible_check_mode or not packages.changed
- name: 先完成配置变更再验证
ansible.builtin.meta: flush_handlers
- name: 检查 WireGuard 对端握手
ansible.builtin.command: 'wg show {{ wg_interface }} latest-handshakes'
changed_when: false
register: wg_handshake
retries: 12
delay: 5
until: wg_handshake.stdout.split() | length == 2 and (wg_handshake.stdout.split()[-1] | int) > 0
when: not ansible_check_mode
- name: 验证隧道互通
ansible.builtin.command: "{{ ('ping -6 -I ' ~ wg_interface ~ ' -c 3 -W 3 ' ~ bgp_transport_peer) if bgp_transport_peer is defined else ('ping -I ' ~ wg_interface ~ ' -c 3 -W 3 ' ~ wg_peer_address) }}"
changed_when: false
when: not ansible_check_mode
- name: 验证 BGP 建邻并收到预期前缀数量
ansible.builtin.command: vtysh -c 'show bgp summary json'
changed_when: false
register: bgp_summary
retries: 12
delay: 5
until: >-
(bgp_summary.stdout | from_json).get('ipv4Unicast', {}).get('peers', {}).get(bgp_transport_peer | default(wg_peer_address), {}).get('state') == 'Established'
and (bgp_summary.stdout | from_json).get('ipv4Unicast', {}).get('peers', {}).get(bgp_transport_peer | default(wg_peer_address), {}).get('pfxRcd', 0) == bgp_import | length
when: not ansible_check_mode
- name: 验证 IPv6 iBGP 建邻与前缀
ansible.builtin.command: vtysh -c 'show bgp summary json'
changed_when: false
register: bgp_summary6
retries: 12
delay: 5
until: >-
(bgp_summary6.stdout | from_json).get('ipv6Unicast', {}).get('peers', {}).get(bgp_transport_peer | default(wg_peer_ipv6), {}).get('state') == 'Established'
and (bgp_summary6.stdout | from_json).get('ipv6Unicast', {}).get('peers', {}).get(bgp_transport_peer | default(wg_peer_ipv6), {}).get('pfxRcd', 0) == bgp_import6 | length
when: not ansible_check_mode and bgp_import6 is defined
@@ -0,0 +1,80 @@
{% set v4_peer = bgp_transport_peer | default(wg_peer_address) %}
{% set v6_peer = bgp_transport_peer | default(wg_peer_ipv6 | default("")) %}
! vtysh -f 使用配置模式,write memory 由独立 handler 执行。
! 只重建本角色拥有的邻居和过滤器,保留既有 BGP/OSPF。
router bgp {{ bgp_asn }}
no neighbor {{ v4_peer }}
{% for peer in bgp_retired_peers | default([]) %}
no neighbor {{ peer }}
{% endfor %}
exit
no ip prefix-list OCI-WG-IN
no ip prefix-list OCI-WG-OUT
{% for prefix in bgp_import %}
ip prefix-list OCI-WG-IN seq {{ loop.index * 10 }} permit {{ prefix }}
{% endfor %}
{% for prefix in bgp_export + (bgp_summary | default([])) %}
ip prefix-list OCI-WG-OUT seq {{ loop.index * 10 }} permit {{ prefix }}
{% endfor %}
router bgp {{ bgp_asn }}
bgp router-id {{ bgp_router_id }}
neighbor {{ v4_peer }} remote-as {{ bgp_peer_asn }}
neighbor {{ v4_peer }} description OCI-WireGuard
neighbor {{ v4_peer }} update-source {{ wg_interface }}
{% if bgp_transport_peer is defined %}
neighbor {{ v4_peer }} interface {{ wg_interface }}
neighbor {{ v4_peer }} capability extended-nexthop
{% endif %}
address-family ipv4 unicast
{% for prefix in bgp_summary | default([]) %}
aggregate-address {{ prefix }}
{% endfor %}
{% for prefix in bgp_export %}
network {{ prefix }}
{% endfor %}
neighbor {{ v4_peer }} activate
neighbor {{ v4_peer }} prefix-list OCI-WG-IN in
neighbor {{ v4_peer }} prefix-list OCI-WG-OUT out
neighbor {{ v4_peer }} maximum-prefix {{ bgp_import | length }}
exit-address-family
exit
! 仅为这些 BGP 路由选择本机业务 IP;转发报文源地址保持不变,不是 NAT。
route-map OCI-WG-SOURCE permit 10
match ip address prefix-list OCI-WG-IN
set src {{ bgp_router_id }}
exit
route-map OCI-WG-SOURCE permit 100
exit
ip protocol bgp route-map OCI-WG-SOURCE
{% if bgp_export6 is defined %}
no ipv6 prefix-list OCI-WG6-IN
no ipv6 prefix-list OCI-WG6-OUT
{% for prefix in bgp_import6 %}
ipv6 prefix-list OCI-WG6-IN seq {{ loop.index * 10 }} permit {{ prefix }}
{% endfor %}
{% for prefix in bgp_export6 + (bgp_summary6 | default([])) %}
ipv6 prefix-list OCI-WG6-OUT seq {{ loop.index * 10 }} permit {{ prefix }}
{% endfor %}
router bgp {{ bgp_asn }}
neighbor {{ v6_peer }} remote-as {{ bgp_peer_asn }}
neighbor {{ v6_peer }} update-source {{ wg_interface }}
{% if bgp_transport_peer is not defined %}
address-family ipv4 unicast
no neighbor {{ v6_peer }} activate
exit-address-family
{% endif %}
address-family ipv6 unicast
{% for prefix in bgp_summary6 | default([]) %}
aggregate-address {{ prefix }}
{% endfor %}
{% for prefix in bgp_export6 %}
network {{ prefix }}
{% endfor %}
neighbor {{ v6_peer }} activate
neighbor {{ v6_peer }} prefix-list OCI-WG6-IN in
neighbor {{ v6_peer }} prefix-list OCI-WG6-OUT out
neighbor {{ v6_peer }} maximum-prefix {{ bgp_import6 | length }}
exit-address-family
exit
{% endif %}
@@ -0,0 +1,43 @@
#!/bin/sh
set -eu
# 仅重建专用链,不保存/覆盖 Docker、k3s、Tailscale 或 OCI 的其他动态规则。
for chain in OCI-WG-IN OCI-WG-FWD; do
iptables -w -nL "$chain" >/dev/null 2>&1 || iptables -w -N "$chain"
iptables -w -F "$chain"
done
iptables -w -A OCI-WG-IN -p udp --dport {{ wg_port }} -j ACCEPT
{% for prefix in [wg_peer_address ~ '/32'] + bgp_import %}
iptables -w -A OCI-WG-IN -i {{ wg_interface }} -s {{ prefix }} -j ACCEPT
{% endfor %}
{% for local_prefix in bgp_export %}
{% for remote_prefix in bgp_import %}
iptables -w -A OCI-WG-FWD -i {{ wg_interface }} -o {{ wg_lan_interface }} -s {{ remote_prefix }} -d {{ local_prefix }} -j ACCEPT
iptables -w -A OCI-WG-FWD -i {{ wg_lan_interface }} -o {{ wg_interface }} -s {{ local_prefix }} -d {{ remote_prefix }} -j ACCEPT
{% endfor %}
{% endfor %}
{% if dn42_external_interface is defined %}
# 仅允许注册地址在内部隧道与外部 DN42 之间转发,保持无 NAT。
iptables -w -A OCI-WG-FWD -i {{ wg_interface }} -o {{ dn42_external_interface }} -s {{ dn42_ipv4 }} -d 172.20.0.0/14 -j ACCEPT
iptables -w -A OCI-WG-FWD -i {{ dn42_external_interface }} -o {{ wg_interface }} -s 172.20.0.0/14 -d {{ dn42_ipv4 }} -j ACCEPT
{% endif %}
iptables -w -A OCI-WG-FWD -i {{ wg_interface }} -j DROP
iptables -w -A OCI-WG-FWD -o {{ wg_interface }} -j DROP
iptables -w -C INPUT -j OCI-WG-IN 2>/dev/null || iptables -w -I INPUT 1 -j OCI-WG-IN
iptables -w -C FORWARD -j OCI-WG-FWD 2>/dev/null || iptables -w -I FORWARD 1 -j OCI-WG-FWD
{% if bgp_import6 is defined %}
ip6tables -w -nL OCI-WG6-IN >/dev/null 2>&1 || ip6tables -w -N OCI-WG6-IN
ip6tables -w -F OCI-WG6-IN
ip6tables -w -A OCI-WG6-IN -i {{ wg_interface }} -s {{ dn42_ipv6 }} -j ACCEPT
ip6tables -w -C INPUT -j OCI-WG6-IN 2>/dev/null || ip6tables -w -I INPUT 1 -j OCI-WG6-IN
{% endif %}
{% if dn42_external_interface is defined %}
ip6tables -w -nL OCI-DN42-FWD >/dev/null 2>&1 || ip6tables -w -N OCI-DN42-FWD
ip6tables -w -F OCI-DN42-FWD
ip6tables -w -A OCI-DN42-FWD -i {{ wg_interface }} -o {{ dn42_external_interface }} -s {{ dn42_ipv6 }} -d fd00::/8 -j ACCEPT
ip6tables -w -A OCI-DN42-FWD -i {{ dn42_external_interface }} -o {{ wg_interface }} -s fd00::/8 -d {{ dn42_ipv6 }} -j ACCEPT
ip6tables -w -A OCI-DN42-FWD -i {{ dn42_external_interface }} -j DROP
ip6tables -w -A OCI-DN42-FWD -o {{ dn42_external_interface }} -j DROP
ip6tables -w -C FORWARD -j OCI-DN42-FWD 2>/dev/null || ip6tables -w -I FORWARD 1 -j OCI-DN42-FWD
{% endif %}
@@ -0,0 +1,16 @@
[Interface]
Address = {{ wg_address }}{% if wg_ipv6_address is defined %}, {{ wg_ipv6_address }}, {{ wg_linklocal_address }}{% endif %}
ListenPort = {{ wg_port }}
MTU = {{ wg_mtu }}
Table = off
# 私钥在本机生成和读取;配置模板与控制机不保存私钥。
PostUp = wg set %i private-key /etc/wireguard/{{ wg_interface }}.key
[Peer]
PublicKey = {{ hostvars[wg_peer_host].wg_public_key.stdout }}
AllowedIPs = {{ ([wg_peer_address ~ '/32'] + bgp_import + (bgp_import6 | default([])) + (['fe80::/64'] if bgp_import6 is defined else [])) | join(', ') }}
{% if wg_endpoint %}
Endpoint = {{ wg_endpoint }}
{% endif %}
PersistentKeepalive = {{ wg_keepalive }}