Initial commit
This commit is contained in:
20
ansible/ansible.cfg
Normal file
20
ansible/ansible.cfg
Normal file
@@ -0,0 +1,20 @@
|
||||
[defaults]
|
||||
inventory = inventory/hosts.yml
|
||||
roles_path = roles
|
||||
host_key_checking = False
|
||||
vault_password_file = .vault_pass
|
||||
retry_files_enabled = False
|
||||
gathering = smart
|
||||
fact_caching = jsonfile
|
||||
fact_caching_connection = /tmp/ansible_facts
|
||||
fact_caching_timeout = 3600
|
||||
|
||||
[privilege_escalation]
|
||||
become = True
|
||||
become_method = sudo
|
||||
become_user = root
|
||||
become_ask_pass = False
|
||||
|
||||
[ssh_connection]
|
||||
pipelining = True
|
||||
ssh_args = -o ControlMaster=auto -o ControlPersist=60s
|
||||
7
ansible/inventory/group_vars/all.yml
Normal file
7
ansible/inventory/group_vars/all.yml
Normal file
@@ -0,0 +1,7 @@
|
||||
---
|
||||
terraform_output_file: "{{ playbook_dir }}/../../terraform/outputs.json"
|
||||
wireguard_persistent_keepalive: 25
|
||||
vxlan_ttl: 64
|
||||
vxlan_port: 4789
|
||||
exporter_port: 9586
|
||||
monitoring_hub_ip: 192.168.1.100
|
||||
28
ansible/inventory/group_vars/linux_gateways.yml
Normal file
28
ansible/inventory/group_vars/linux_gateways.yml
Normal file
@@ -0,0 +1,28 @@
|
||||
---
|
||||
# Linux-specific settings
|
||||
|
||||
# Sysctl settings
|
||||
sysctl_settings:
|
||||
net.ipv4.ip_forward: 1
|
||||
net.bridge.bridge-nf-call-iptables: 0
|
||||
net.bridge.bridge-nf-call-ip6tables: 0
|
||||
net.ipv4.conf.all.rp_filter: 0
|
||||
net.ipv4.conf.default.rp_filter: 0
|
||||
|
||||
# WireGuard
|
||||
wireguard_config_dir: /etc/wireguard
|
||||
|
||||
# Bridge settings
|
||||
bridge_stp: no
|
||||
bridge_forward_delay: 0
|
||||
|
||||
# ISC DHCP failover settings
|
||||
isc_dhcp_failover:
|
||||
max_response_delay: 30
|
||||
max_unacked_updates: 10
|
||||
mclt: 3600
|
||||
split: 128
|
||||
load_balance_max_seconds: 3
|
||||
|
||||
# Monitoring
|
||||
exporter_install_dir: /opt/wg-lab-exporter
|
||||
33
ansible/inventory/group_vars/openwrt_gateways.yml
Normal file
33
ansible/inventory/group_vars/openwrt_gateways.yml
Normal file
@@ -0,0 +1,33 @@
|
||||
---
|
||||
# OpenWrt-specific settings
|
||||
|
||||
# SSH connection
|
||||
ansible_user: root
|
||||
ansible_python_interpreter: /usr/bin/python3
|
||||
|
||||
# OpenWrt package requirements
|
||||
openwrt_required_packages:
|
||||
- wireguard-tools
|
||||
- kmod-wireguard
|
||||
- kmod-vxlan
|
||||
- ip-full
|
||||
- tcpdump
|
||||
|
||||
# UCI settings
|
||||
uci_commit_delay: 2
|
||||
|
||||
# Network reload wait time
|
||||
network_reload_wait: 10
|
||||
|
||||
# Firewall zone settings
|
||||
firewall_default_input: REJECT
|
||||
firewall_default_forward: REJECT
|
||||
firewall_default_output: ACCEPT
|
||||
|
||||
# Monitoring
|
||||
exporter_port: 9586
|
||||
exporter_path: /usr/lib/wg-lab-exporter
|
||||
|
||||
# Logging
|
||||
syslog_enabled: yes
|
||||
log_level: info
|
||||
24
ansible/inventory/hosts.yml
Normal file
24
ansible/inventory/hosts.yml
Normal file
@@ -0,0 +1,24 @@
|
||||
all:
|
||||
vars:
|
||||
ansible_user: admin
|
||||
ansible_python_interpreter: /usr/bin/python3
|
||||
|
||||
children:
|
||||
lab_gateways:
|
||||
children:
|
||||
linux_gateways:
|
||||
vars:
|
||||
ansible_become: yes
|
||||
mikrotik_gateways:
|
||||
vars:
|
||||
ansible_connection: ansible.netcommon.network_cli
|
||||
ansible_network_os: community.routeros.routeros
|
||||
openwrt_gateways:
|
||||
vars:
|
||||
ansible_user: root
|
||||
ansible_become: no
|
||||
|
||||
monitoring_hub:
|
||||
hosts:
|
||||
monitoring-hub.example.com:
|
||||
ansible_host: 192.168.1.100
|
||||
64
ansible/playbooks/add-gateway.yml
Normal file
64
ansible/playbooks/add-gateway.yml
Normal file
@@ -0,0 +1,64 @@
|
||||
---
|
||||
- name: Add Gateway to Existing Lab Network
|
||||
hosts: localhost
|
||||
gather_facts: no
|
||||
|
||||
vars_prompt:
|
||||
- name: lab_name
|
||||
prompt: "Lab network name (e.g., lab100)"
|
||||
private: no
|
||||
|
||||
- name: gateway_hostname
|
||||
prompt: "New gateway hostname"
|
||||
private: no
|
||||
|
||||
tasks:
|
||||
- name: Load Terraform outputs
|
||||
set_fact:
|
||||
tf_config: "{{ lookup('file', terraform_output_file) | from_json }}"
|
||||
|
||||
- name: Verify gateway exists in Terraform
|
||||
fail:
|
||||
msg: "Gateway {{ gateway_hostname }} not found. Add to terraform.tfvars first."
|
||||
when: gateway_hostname not in tf_config.gateway_config.value.keys()
|
||||
|
||||
- name: Display next steps
|
||||
debug:
|
||||
msg:
|
||||
- "Gateway {{ gateway_hostname }} found in configuration"
|
||||
- "Deploying to gateway..."
|
||||
|
||||
- name: Deploy to new gateway
|
||||
hosts: "{{ gateway_hostname }}"
|
||||
become: yes
|
||||
|
||||
tasks:
|
||||
- name: Load gateway config
|
||||
set_fact:
|
||||
gateway_config: "{{ hostvars['localhost']['tf_config'].gateway_config.value[inventory_hostname] }}"
|
||||
|
||||
- name: Deploy based on gateway type
|
||||
include_role:
|
||||
name: "{{ item }}"
|
||||
loop: "{{ roles_to_deploy }}"
|
||||
vars:
|
||||
roles_to_deploy: "{{ ['openwrt-common', 'openwrt-wireguard', 'openwrt-vxlan', 'openwrt-bridge', 'openwrt-dhcp', 'openwrt-firewall', 'openwrt-monitoring'] if gateway_config.type == 'openwrt' else (['mikrotik-common', 'mikrotik-wireguard', 'mikrotik-vxlan', 'mikrotik-bridge', 'mikrotik-dhcp', 'mikrotik-monitoring'] if gateway_config.type == 'mikrotik' else ['common', 'wireguard', 'vxlan', 'bridge', ('isc-dhcp' if gateway_config.dhcp_mode == 'failover' else 'dnsmasq'), 'monitoring']) }}"
|
||||
|
||||
- name: Update existing gateways with new peer
|
||||
hosts: lab_gateways
|
||||
become: yes
|
||||
|
||||
tasks:
|
||||
- name: Skip if not in same lab
|
||||
meta: end_host
|
||||
when: gateway_config.lab_name != hostvars['localhost']['lab_name']
|
||||
|
||||
- name: Skip new gateway itself
|
||||
meta: end_host
|
||||
when: inventory_hostname == hostvars['localhost']['gateway_hostname']
|
||||
|
||||
- name: Reload WireGuard configuration
|
||||
systemd:
|
||||
name: "wg-quick@wg-{{ gateway_config.lab_name }}"
|
||||
state: reloaded
|
||||
when: gateway_config.type == 'linux'
|
||||
30
ansible/playbooks/emergency-shutdown.yml
Normal file
30
ansible/playbooks/emergency-shutdown.yml
Normal file
@@ -0,0 +1,30 @@
|
||||
---
|
||||
- name: Emergency Shutdown of Lab Network
|
||||
hosts: localhost
|
||||
gather_facts: no
|
||||
|
||||
vars_prompt:
|
||||
- name: lab_name
|
||||
prompt: "Which lab network to shutdown? (e.g., lab100, or 'all')"
|
||||
private: no
|
||||
|
||||
- name: confirm_shutdown
|
||||
prompt: "CONFIRM: Type 'YES' to proceed with emergency shutdown"
|
||||
private: no
|
||||
|
||||
pre_tasks:
|
||||
- name: Validate confirmation
|
||||
fail:
|
||||
msg: "Shutdown cancelled"
|
||||
when: confirm_shutdown != "YES"
|
||||
|
||||
- name: Execute shutdown on Linux gateways
|
||||
hosts: linux_gateways
|
||||
become: yes
|
||||
|
||||
tasks:
|
||||
- name: Stop WireGuard
|
||||
systemd:
|
||||
name: "wg-quick@wg-{{ gateway_config.lab_name }}"
|
||||
state: stopped
|
||||
ignore_errors: yes
|
||||
40
ansible/playbooks/generate-road-warrior.yml
Normal file
40
ansible/playbooks/generate-road-warrior.yml
Normal file
@@ -0,0 +1,40 @@
|
||||
---
|
||||
- name: Generate Road Warrior Configurations
|
||||
hosts: localhost
|
||||
gather_facts: yes
|
||||
|
||||
tasks:
|
||||
- name: Load Terraform outputs
|
||||
set_fact:
|
||||
tf_config: "{{ lookup('file', terraform_output_file) | from_json }}"
|
||||
|
||||
- name: Create output directories
|
||||
file:
|
||||
path: "{{ rw_config_output_dir }}/{{ item.value.user_name }}"
|
||||
state: directory
|
||||
mode: '0755'
|
||||
loop: "{{ tf_config.road_warrior_configs.value | dict2items }}"
|
||||
loop_control:
|
||||
label: "{{ item.value.user_name }}"
|
||||
|
||||
- name: Generate client configs
|
||||
template:
|
||||
src: ../road-warrior/templates/client-config.conf.j2
|
||||
dest: "{{ rw_config_output_dir }}/{{ item.value.user_name }}/{{ item.value.lab_name }}.conf"
|
||||
mode: '0600'
|
||||
loop: "{{ tf_config.road_warrior_configs.value | dict2items }}"
|
||||
loop_control:
|
||||
label: "{{ item.value.user_name }}"
|
||||
|
||||
- name: Generate README
|
||||
template:
|
||||
src: ../road-warrior/templates/README.md.j2
|
||||
dest: "{{ rw_config_output_dir }}/{{ item.key }}/README.md"
|
||||
mode: '0644'
|
||||
loop: "{{ tf_config.road_warrior_configs.value | dict2items | groupby('value.user_name') | list }}"
|
||||
loop_control:
|
||||
label: "{{ item.0 }}"
|
||||
|
||||
- name: Display output location
|
||||
debug:
|
||||
msg: "Road warrior configs generated in {{ rw_config_output_dir }}"
|
||||
14
ansible/playbooks/health-check.yml
Normal file
14
ansible/playbooks/health-check.yml
Normal file
@@ -0,0 +1,14 @@
|
||||
---
|
||||
- name: Comprehensive Health Check
|
||||
hosts: lab_gateways
|
||||
become: yes
|
||||
|
||||
tasks:
|
||||
- name: Check WireGuard status
|
||||
shell: "wg show"
|
||||
register: wg_status
|
||||
when: ansible_os_family == "Debian"
|
||||
|
||||
- name: Display results
|
||||
debug:
|
||||
var: wg_status.stdout_lines
|
||||
35
ansible/playbooks/remove-gateway.yml
Normal file
35
ansible/playbooks/remove-gateway.yml
Normal file
@@ -0,0 +1,35 @@
|
||||
---
|
||||
- name: Remove Gateway from Lab Network
|
||||
hosts: localhost
|
||||
gather_facts: no
|
||||
|
||||
vars_prompt:
|
||||
- name: gateway_hostname
|
||||
prompt: "Gateway hostname to remove"
|
||||
private: no
|
||||
|
||||
- name: confirm_removal
|
||||
prompt: "Type 'REMOVE' to confirm"
|
||||
private: no
|
||||
|
||||
pre_tasks:
|
||||
- name: Validate confirmation
|
||||
fail:
|
||||
msg: "Removal cancelled"
|
||||
when: confirm_removal != "REMOVE"
|
||||
|
||||
- name: Shutdown gateway
|
||||
hosts: "{{ gateway_hostname }}"
|
||||
become: yes
|
||||
|
||||
tasks:
|
||||
- name: Stop all services
|
||||
systemd:
|
||||
name: "{{ item }}"
|
||||
state: stopped
|
||||
loop:
|
||||
- "wg-quick@wg-{{ gateway_config.lab_name }}"
|
||||
- "vxlan-{{ gateway_config.lab_name }}"
|
||||
- "bridge-{{ gateway_config.lab_name }}"
|
||||
when: gateway_config.type == 'linux'
|
||||
ignore_errors: yes
|
||||
12
ansible/playbooks/restore-lab.yml
Normal file
12
ansible/playbooks/restore-lab.yml
Normal file
@@ -0,0 +1,12 @@
|
||||
---
|
||||
- name: Restore Lab Network
|
||||
hosts: linux_gateways
|
||||
become: yes
|
||||
serial: 1
|
||||
|
||||
tasks:
|
||||
- name: Start WireGuard
|
||||
systemd:
|
||||
name: "wg-quick@wg-{{ gateway_config.lab_name }}"
|
||||
state: started
|
||||
when: ansible_os_family == "Debian"
|
||||
37
ansible/playbooks/rolling-update.yml
Normal file
37
ansible/playbooks/rolling-update.yml
Normal file
@@ -0,0 +1,37 @@
|
||||
---
|
||||
- name: Rolling Update of WireGuard Lab Gateways
|
||||
hosts: lab_gateways
|
||||
serial: 1
|
||||
become: yes
|
||||
|
||||
vars:
|
||||
health_check_retries: 10
|
||||
health_check_delay: 10
|
||||
|
||||
pre_tasks:
|
||||
- name: Load Terraform outputs
|
||||
set_fact:
|
||||
tf_config: "{{ lookup('file', terraform_output_file) | from_json }}"
|
||||
delegate_to: localhost
|
||||
run_once: yes
|
||||
|
||||
- name: Extract gateway-specific config
|
||||
set_fact:
|
||||
gateway_config: "{{ tf_config.gateway_config.value[inventory_hostname] }}"
|
||||
|
||||
tasks:
|
||||
- name: Update system packages (Linux)
|
||||
apt:
|
||||
update_cache: yes
|
||||
upgrade: dist
|
||||
when: gateway_config.type == "linux"
|
||||
|
||||
- name: Restart WireGuard
|
||||
systemd:
|
||||
name: "wg-quick@wg-{{ gateway_config.lab_name }}"
|
||||
state: restarted
|
||||
when: gateway_config.type == "linux"
|
||||
|
||||
- name: Wait and validate
|
||||
pause:
|
||||
seconds: 30
|
||||
90
ansible/playbooks/site.yml
Normal file
90
ansible/playbooks/site.yml
Normal file
@@ -0,0 +1,90 @@
|
||||
---
|
||||
- name: Deploy WireGuard Lab Overlay Network
|
||||
hosts: lab_gateways
|
||||
become: yes
|
||||
gather_facts: yes
|
||||
|
||||
pre_tasks:
|
||||
- name: Load Terraform outputs
|
||||
set_fact:
|
||||
tf_config: "{{ lookup('file', '../../terraform/outputs.json') | from_json }}"
|
||||
delegate_to: localhost
|
||||
run_once: yes
|
||||
|
||||
- name: Extract gateway-specific config
|
||||
set_fact:
|
||||
gateway_config: "{{ tf_config.gateway_config.value[inventory_hostname] }}"
|
||||
|
||||
- name: Display gateway type
|
||||
debug:
|
||||
msg: "Configuring {{ inventory_hostname }} ({{ gateway_config.type }}) for {{ gateway_config.lab_name }}"
|
||||
|
||||
tasks:
|
||||
- name: Configure Linux gateways
|
||||
block:
|
||||
- include_role:
|
||||
name: common
|
||||
- include_role:
|
||||
name: wireguard
|
||||
- include_role:
|
||||
name: vxlan
|
||||
- include_role:
|
||||
name: bridge
|
||||
- include_role:
|
||||
name: "{{ 'isc-dhcp' if gateway_config.dhcp_mode == 'failover' else 'dnsmasq' }}"
|
||||
- include_role:
|
||||
name: monitoring
|
||||
when: gateway_config.type == 'linux'
|
||||
|
||||
- name: Configure MikroTik gateways
|
||||
block:
|
||||
- include_role:
|
||||
name: mikrotik-common
|
||||
- include_role:
|
||||
name: mikrotik-wireguard
|
||||
- include_role:
|
||||
name: mikrotik-vxlan
|
||||
- include_role:
|
||||
name: mikrotik-bridge
|
||||
- include_role:
|
||||
name: mikrotik-dhcp
|
||||
- include_role:
|
||||
name: mikrotik-monitoring
|
||||
when: gateway_config.type == 'mikrotik'
|
||||
|
||||
- name: Configure OpenWrt gateways
|
||||
block:
|
||||
- include_role:
|
||||
name: openwrt-common
|
||||
- include_role:
|
||||
name: openwrt-wireguard
|
||||
- include_role:
|
||||
name: openwrt-vxlan
|
||||
- include_role:
|
||||
name: openwrt-bridge
|
||||
- include_role:
|
||||
name: openwrt-dhcp
|
||||
- include_role:
|
||||
name: openwrt-firewall
|
||||
- include_role:
|
||||
name: openwrt-monitoring
|
||||
when: gateway_config.type == 'openwrt'
|
||||
|
||||
post_tasks:
|
||||
- name: Deployment summary
|
||||
debug:
|
||||
msg:
|
||||
- "Gateway: {{ inventory_hostname }}"
|
||||
- "Type: {{ gateway_config.type }}"
|
||||
- "Lab: {{ gateway_config.lab_name }}"
|
||||
- "VNI: {{ gateway_config.vni }}"
|
||||
- "WireGuard IP: {{ gateway_config.wg_ip }}"
|
||||
- "Subnet: {{ gateway_config.subnet }}"
|
||||
- "DHCP Mode: {{ gateway_config.dhcp_mode }}"
|
||||
- "DHCP Role: {{ gateway_config.dhcp_role }}"
|
||||
|
||||
- name: Configure Monitoring Hub
|
||||
hosts: monitoring_hub
|
||||
become: yes
|
||||
roles:
|
||||
- monitoring_hub
|
||||
18
ansible/roles/bridge/tasks/main.yml
Normal file
18
ansible/roles/bridge/tasks/main.yml
Normal file
@@ -0,0 +1,18 @@
|
||||
---
|
||||
- name: Create bridge setup script
|
||||
template:
|
||||
src: setup-bridge.sh.j2
|
||||
dest: /usr/local/bin/setup-bridge-{{ gateway_config.lab_name }}.sh
|
||||
mode: '0755'
|
||||
|
||||
- name: Create systemd service for bridge
|
||||
template:
|
||||
src: bridge.service.j2
|
||||
dest: /etc/systemd/system/bridge-{{ gateway_config.lab_name }}.service
|
||||
notify: reload systemd
|
||||
|
||||
- name: Enable and start bridge service
|
||||
systemd:
|
||||
name: bridge-{{ gateway_config.lab_name }}
|
||||
enabled: yes
|
||||
state: started
|
||||
13
ansible/roles/bridge/templates/bridge.service.j2
Normal file
13
ansible/roles/bridge/templates/bridge.service.j2
Normal file
@@ -0,0 +1,13 @@
|
||||
[Unit]
|
||||
Description=Bridge for {{ gateway_config.lab_name }}
|
||||
After=vxlan-{{ gateway_config.lab_name }}.service
|
||||
Requires=vxlan-{{ gateway_config.lab_name }}.service
|
||||
|
||||
[Service]
|
||||
Type=oneshot
|
||||
RemainAfterExit=yes
|
||||
ExecStart=/usr/local/bin/setup-bridge-{{ gateway_config.lab_name }}.sh
|
||||
ExecStop=/sbin/ip link delete br-{{ gateway_config.lab_name }}
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
38
ansible/roles/bridge/templates/setup-bridge.sh.j2
Normal file
38
ansible/roles/bridge/templates/setup-bridge.sh.j2
Normal file
@@ -0,0 +1,38 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
LAB_NAME="{{ gateway_config.lab_name }}"
|
||||
VNI="{{ gateway_config.vni }}"
|
||||
BR_IF="br-${LAB_NAME}"
|
||||
VXLAN_IF="vxlan${VNI}"
|
||||
LAB_IF="{{ gateway_config.lab_if }}"
|
||||
|
||||
# Wait for VXLAN interface
|
||||
timeout=30
|
||||
while [ $timeout -gt 0 ]; do
|
||||
if ip link show ${VXLAN_IF} >/dev/null 2>&1; then
|
||||
break
|
||||
fi
|
||||
sleep 1
|
||||
timeout=$((timeout-1))
|
||||
done
|
||||
|
||||
# Create bridge
|
||||
if ! ip link show ${BR_IF} &>/dev/null; then
|
||||
ip link add ${BR_IF} type bridge
|
||||
ip link set ${BR_IF} type bridge stp_state {{ bridge_stp | ternary('1', '0') }}
|
||||
ip link set ${BR_IF} type bridge forward_delay {{ bridge_forward_delay }}
|
||||
fi
|
||||
|
||||
# Add interfaces to bridge
|
||||
ip link set ${VXLAN_IF} master ${BR_IF}
|
||||
ip link set ${LAB_IF} master ${BR_IF}
|
||||
|
||||
# Configure bridge IP (for DHCP server)
|
||||
ip addr flush dev ${BR_IF}
|
||||
ip addr add {{ gateway_config.subnet | ipaddr('1') | ipaddr('address') }}/{{ gateway_config.subnet | ipaddr('prefix') }} dev ${BR_IF}
|
||||
|
||||
# Bring everything up
|
||||
ip link set ${LAB_IF} up
|
||||
ip link set ${VXLAN_IF} up
|
||||
ip link set ${BR_IF} up
|
||||
26
ansible/roles/common/tasks/main.yml
Normal file
26
ansible/roles/common/tasks/main.yml
Normal file
@@ -0,0 +1,26 @@
|
||||
---
|
||||
- name: Install required packages
|
||||
apt:
|
||||
name:
|
||||
- wireguard
|
||||
- wireguard-tools
|
||||
- bridge-utils
|
||||
- python3-prometheus-client
|
||||
- vlan
|
||||
state: present
|
||||
update_cache: yes
|
||||
|
||||
- name: Configure sysctl settings
|
||||
sysctl:
|
||||
name: "{{ item.key }}"
|
||||
value: "{{ item.value }}"
|
||||
state: present
|
||||
reload: yes
|
||||
loop: "{{ sysctl_settings | dict2items }}"
|
||||
|
||||
- name: Ensure systemd-networkd is disabled
|
||||
systemd:
|
||||
name: systemd-networkd
|
||||
enabled: no
|
||||
state: stopped
|
||||
ignore_errors: yes
|
||||
5
ansible/roles/dnsmasq/handlers/main.yml
Normal file
5
ansible/roles/dnsmasq/handlers/main.yml
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
- name: restart dnsmasq
|
||||
systemd:
|
||||
name: dnsmasq
|
||||
state: restarted
|
||||
18
ansible/roles/dnsmasq/tasks/main.yml
Normal file
18
ansible/roles/dnsmasq/tasks/main.yml
Normal file
@@ -0,0 +1,18 @@
|
||||
---
|
||||
- name: Install dnsmasq
|
||||
apt:
|
||||
name: dnsmasq
|
||||
state: present
|
||||
|
||||
- name: Create dnsmasq configuration
|
||||
template:
|
||||
src: dnsmasq.conf.j2
|
||||
dest: /etc/dnsmasq.d/{{ gateway_config.lab_name }}.conf
|
||||
mode: '0644'
|
||||
notify: restart dnsmasq
|
||||
|
||||
- name: Enable and start dnsmasq
|
||||
systemd:
|
||||
name: dnsmasq
|
||||
enabled: yes
|
||||
state: started
|
||||
28
ansible/roles/dnsmasq/templates/dnsmasq.conf.j2
Normal file
28
ansible/roles/dnsmasq/templates/dnsmasq.conf.j2
Normal file
@@ -0,0 +1,28 @@
|
||||
# {{ gateway_config.lab_name }} DHCP configuration
|
||||
interface=br-{{ gateway_config.lab_name }}
|
||||
bind-interfaces
|
||||
|
||||
{% if gateway_config.dhcp_role == 'primary' %}
|
||||
# Primary DHCP server
|
||||
dhcp-range={{ gateway_config.subnet | ipaddr('10') | ipaddr('address') }},{{ gateway_config.subnet | ipaddr('127') | ipaddr('address') }},{{ gateway_config.subnet | ipaddr('netmask') }},12h
|
||||
{% elif gateway_config.dhcp_role == 'secondary' %}
|
||||
# Secondary DHCP server (split range)
|
||||
dhcp-range={{ gateway_config.subnet | ipaddr('128') | ipaddr('address') }},{{ gateway_config.subnet | ipaddr('245') | ipaddr('address') }},{{ gateway_config.subnet | ipaddr('netmask') }},12h
|
||||
{% else %}
|
||||
# Backup server (only responds if others fail)
|
||||
dhcp-range={{ gateway_config.subnet | ipaddr('10') | ipaddr('address') }},{{ gateway_config.subnet | ipaddr('245') | ipaddr('address') }},{{ gateway_config.subnet | ipaddr('netmask') }},12h
|
||||
{% endif %}
|
||||
|
||||
dhcp-option=option:router,{{ gateway_config.subnet | ipaddr('1') | ipaddr('address') }}
|
||||
dhcp-option=option:dns-server,{{ gateway_config.subnet | ipaddr('1') | ipaddr('address') }}
|
||||
|
||||
# Local DNS domain
|
||||
domain={{ gateway_config.lab_name }}.lab
|
||||
local=/{{ gateway_config.lab_name }}.lab/
|
||||
|
||||
# Lease file
|
||||
dhcp-leasefile=/var/lib/misc/dnsmasq.{{ gateway_config.lab_name }}.leases
|
||||
|
||||
# Logging
|
||||
log-dhcp
|
||||
log-queries
|
||||
5
ansible/roles/isc-dhcp/handlers/main.yml
Normal file
5
ansible/roles/isc-dhcp/handlers/main.yml
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
- name: restart isc-dhcp
|
||||
systemd:
|
||||
name: isc-dhcp-server
|
||||
state: restarted
|
||||
48
ansible/roles/isc-dhcp/tasks/main.yml
Normal file
48
ansible/roles/isc-dhcp/tasks/main.yml
Normal file
@@ -0,0 +1,48 @@
|
||||
---
|
||||
- name: Install ISC DHCP server
|
||||
apt:
|
||||
name: isc-dhcp-server
|
||||
state: present
|
||||
|
||||
- name: Stop and disable dnsmasq (conflicts with ISC DHCP)
|
||||
systemd:
|
||||
name: dnsmasq
|
||||
state: stopped
|
||||
enabled: no
|
||||
ignore_errors: yes
|
||||
|
||||
- name: Configure ISC DHCP server defaults
|
||||
template:
|
||||
src: isc-dhcp-server.j2
|
||||
dest: /etc/default/isc-dhcp-server
|
||||
mode: '0644'
|
||||
notify: restart isc-dhcp
|
||||
|
||||
- name: Get failover peer IP
|
||||
set_fact:
|
||||
failover_peer_ip: "{{ hostvars[item].gateway_config.wg_ip }}"
|
||||
loop: "{{ groups['lab_gateways'] }}"
|
||||
when:
|
||||
- hostvars[item].gateway_config.lab_name == gateway_config.lab_name
|
||||
- hostvars[item].gateway_config.dhcp_role in ['primary', 'secondary']
|
||||
- hostvars[item].gateway_config.dhcp_role != gateway_config.dhcp_role
|
||||
register: peer_search
|
||||
|
||||
- name: Set failover peer IP from search
|
||||
set_fact:
|
||||
failover_peer_ip: "{{ peer_search.results | selectattr('ansible_facts', 'defined') | map(attribute='ansible_facts.failover_peer_ip') | first }}"
|
||||
when: peer_search.results | selectattr('ansible_facts', 'defined') | list | length > 0
|
||||
|
||||
- name: Configure DHCP failover
|
||||
template:
|
||||
src: dhcpd.conf.j2
|
||||
dest: /etc/dhcp/dhcpd.conf
|
||||
mode: '0644'
|
||||
validate: 'dhcpd -t -cf %s'
|
||||
notify: restart isc-dhcp
|
||||
|
||||
- name: Ensure ISC DHCP is enabled and started
|
||||
systemd:
|
||||
name: isc-dhcp-server
|
||||
enabled: yes
|
||||
state: started
|
||||
56
ansible/roles/isc-dhcp/templates/dhcpd.conf.j2
Normal file
56
ansible/roles/isc-dhcp/templates/dhcpd.conf.j2
Normal file
@@ -0,0 +1,56 @@
|
||||
# ISC DHCP Configuration for {{ gateway_config.lab_name }}
|
||||
# Generated by Ansible
|
||||
|
||||
authoritative;
|
||||
ddns-update-style none;
|
||||
default-lease-time 43200; # 12 hours
|
||||
max-lease-time 86400; # 24 hours
|
||||
|
||||
# Failover configuration
|
||||
{% if gateway_config.dhcp_role == 'primary' %}
|
||||
failover peer "{{ gateway_config.lab_name }}-failover" {
|
||||
primary;
|
||||
address {{ gateway_config.wg_ip }};
|
||||
port 647;
|
||||
peer address {{ failover_peer_ip }};
|
||||
peer port 647;
|
||||
max-response-delay {{ isc_dhcp_failover.max_response_delay }};
|
||||
max-unacked-updates {{ isc_dhcp_failover.max_unacked_updates }};
|
||||
mclt {{ isc_dhcp_failover.mclt }};
|
||||
split {{ isc_dhcp_failover.split }};
|
||||
load balance max seconds {{ isc_dhcp_failover.load_balance_max_seconds }};
|
||||
}
|
||||
{% elif gateway_config.dhcp_role == 'secondary' %}
|
||||
failover peer "{{ gateway_config.lab_name }}-failover" {
|
||||
secondary;
|
||||
address {{ gateway_config.wg_ip }};
|
||||
port 647;
|
||||
peer address {{ failover_peer_ip }};
|
||||
peer port 647;
|
||||
max-response-delay {{ isc_dhcp_failover.max_response_delay }};
|
||||
max-unacked-updates {{ isc_dhcp_failover.max_unacked_updates }};
|
||||
load balance max seconds {{ isc_dhcp_failover.load_balance_max_seconds }};
|
||||
}
|
||||
{% endif %}
|
||||
|
||||
# Subnet declaration
|
||||
subnet {{ gateway_config.subnet | ipaddr('network') }} netmask {{ gateway_config.subnet | ipaddr('netmask') }} {
|
||||
option routers {{ gateway_config.subnet | ipaddr('1') | ipaddr('address') }};
|
||||
option domain-name-servers {{ gateway_config.subnet | ipaddr('1') | ipaddr('address') }};
|
||||
option domain-name "{{ gateway_config.lab_name }}.lab";
|
||||
|
||||
{% if gateway_config.dhcp_role in ['primary', 'secondary'] %}
|
||||
pool {
|
||||
failover peer "{{ gateway_config.lab_name }}-failover";
|
||||
range {{ gateway_config.subnet | ipaddr('10') | ipaddr('address') }} {{ gateway_config.subnet | ipaddr('245') | ipaddr('address') }};
|
||||
}
|
||||
{% else %}
|
||||
# Backup server - only serves when primary/secondary unavailable
|
||||
pool {
|
||||
range {{ gateway_config.subnet | ipaddr('10') | ipaddr('address') }} {{ gateway_config.subnet | ipaddr('245') | ipaddr('address') }};
|
||||
}
|
||||
{% endif %}
|
||||
}
|
||||
|
||||
# Logging
|
||||
log-facility local7;
|
||||
10
ansible/roles/isc-dhcp/templates/isc-dhcp-server.j2
Normal file
10
ansible/roles/isc-dhcp/templates/isc-dhcp-server.j2
Normal file
@@ -0,0 +1,10 @@
|
||||
# Defaults for isc-dhcp-server (sourced by /etc/init.d/isc-dhcp-server)
|
||||
|
||||
# Path to dhcpd's config file (default: /etc/dhcp/dhcpd.conf).
|
||||
DHCPDv4_CONF=/etc/dhcp/dhcpd.conf
|
||||
|
||||
# Path to dhcpd's PID file (default: /var/run/dhcpd.pid).
|
||||
DHCPDv4_PID=/var/run/dhcpd.pid
|
||||
|
||||
# On what interfaces should the DHCP server (dhcpd) serve DHCP requests?
|
||||
INTERFACESv4="br-{{ gateway_config.lab_name }}"
|
||||
16
ansible/roles/mikrotik-bridge/tasks/main.yml
Normal file
16
ansible/roles/mikrotik-bridge/tasks/main.yml
Normal file
@@ -0,0 +1,16 @@
|
||||
---
|
||||
- name: Create bridge
|
||||
community.routeros.command:
|
||||
commands:
|
||||
- /interface bridge add name="bridge-{{ gateway_config.lab_name }}" protocol-mode=none
|
||||
|
||||
- name: Add interfaces to bridge
|
||||
community.routeros.command:
|
||||
commands:
|
||||
- /interface bridge port add bridge="bridge-{{ gateway_config.lab_name }}" interface="vxlan{{ gateway_config.vni }}"
|
||||
- /interface bridge port add bridge="bridge-{{ gateway_config.lab_name }}" interface="{{ gateway_config.lab_if }}"
|
||||
|
||||
- name: Configure bridge IP
|
||||
community.routeros.command:
|
||||
commands:
|
||||
- /ip address add address={{ gateway_config.subnet | ipaddr('1') | ipaddr('address') }}/{{ gateway_config.subnet | ipaddr('prefix') }} interface="bridge-{{ gateway_config.lab_name }}"
|
||||
16
ansible/roles/mikrotik-common/tasks/main.yml
Normal file
16
ansible/roles/mikrotik-common/tasks/main.yml
Normal file
@@ -0,0 +1,16 @@
|
||||
---
|
||||
- name: Check RouterOS version
|
||||
community.routeros.command:
|
||||
commands:
|
||||
- /system resource print
|
||||
register: ros_version
|
||||
|
||||
- name: Set system identity
|
||||
community.routeros.command:
|
||||
commands:
|
||||
- /system identity set name={{ inventory_hostname }}
|
||||
|
||||
- name: Configure NTP client
|
||||
community.routeros.command:
|
||||
commands:
|
||||
- /system ntp client set enabled=yes servers=pool.ntp.org
|
||||
18
ansible/roles/mikrotik-dhcp/tasks/main.yml
Normal file
18
ansible/roles/mikrotik-dhcp/tasks/main.yml
Normal file
@@ -0,0 +1,18 @@
|
||||
---
|
||||
- name: Create DHCP pool
|
||||
community.routeros.command:
|
||||
commands:
|
||||
- /ip pool add name="pool-{{ gateway_config.lab_name }}" ranges={{ dhcp_start }}-{{ dhcp_end }}
|
||||
vars:
|
||||
dhcp_start: "{{ gateway_config.subnet | ipaddr('10') | ipaddr('address') if gateway_config.dhcp_role == 'primary' else gateway_config.subnet | ipaddr('128') | ipaddr('address') }}"
|
||||
dhcp_end: "{{ gateway_config.subnet | ipaddr('127') | ipaddr('address') if gateway_config.dhcp_role == 'primary' else gateway_config.subnet | ipaddr('245') | ipaddr('address') }}"
|
||||
|
||||
- name: Configure DHCP network
|
||||
community.routeros.command:
|
||||
commands:
|
||||
- /ip dhcp-server network add address={{ gateway_config.subnet }} gateway={{ gateway_config.subnet | ipaddr('1') | ipaddr('address') }} dns-server={{ gateway_config.subnet | ipaddr('1') | ipaddr('address') }}
|
||||
|
||||
- name: Create DHCP server
|
||||
community.routeros.command:
|
||||
commands:
|
||||
- /ip dhcp-server add name="dhcp-{{ gateway_config.lab_name }}" interface="bridge-{{ gateway_config.lab_name }}" address-pool="pool-{{ gateway_config.lab_name }}" lease-time=12h disabled=no
|
||||
11
ansible/roles/mikrotik-monitoring/tasks/main.yml
Normal file
11
ansible/roles/mikrotik-monitoring/tasks/main.yml
Normal file
@@ -0,0 +1,11 @@
|
||||
---
|
||||
- name: Enable REST API
|
||||
community.routeros.command:
|
||||
commands:
|
||||
- /ip service set api-ssl disabled=no port=8729
|
||||
|
||||
- name: Create monitoring user
|
||||
community.routeros.command:
|
||||
commands:
|
||||
- /user add name=monitoring group=read comment="Prometheus monitoring"
|
||||
ignore_errors: yes
|
||||
13
ansible/roles/mikrotik-vxlan/tasks/main.yml
Normal file
13
ansible/roles/mikrotik-vxlan/tasks/main.yml
Normal file
@@ -0,0 +1,13 @@
|
||||
---
|
||||
- name: Create VXLAN interface
|
||||
community.routeros.command:
|
||||
commands:
|
||||
- /interface vxlan add name="vxlan{{ gateway_config.vni }}" vni={{ gateway_config.vni }} interface="wg-{{ gateway_config.lab_name }}" port=4789
|
||||
|
||||
- name: Configure VXLAN vteps
|
||||
community.routeros.command:
|
||||
commands:
|
||||
- /interface vxlan vteps add interface="vxlan{{ gateway_config.vni }}" remote-ip={{ peer.wg_ip }}
|
||||
loop: "{{ gateway_config.peers }}"
|
||||
loop_control:
|
||||
loop_var: peer
|
||||
20
ansible/roles/mikrotik-wireguard/tasks/main.yml
Normal file
20
ansible/roles/mikrotik-wireguard/tasks/main.yml
Normal file
@@ -0,0 +1,20 @@
|
||||
---
|
||||
- name: Remove existing WireGuard interface
|
||||
community.routeros.command:
|
||||
commands:
|
||||
- /interface wireguard remove [find name="wg-{{ gateway_config.lab_name }}"]
|
||||
ignore_errors: yes
|
||||
|
||||
- name: Create WireGuard interface
|
||||
community.routeros.command:
|
||||
commands:
|
||||
- /interface wireguard add name="wg-{{ gateway_config.lab_name }}" listen-port={{ gateway_config.wg_listen_port }} private-key="{{ gateway_config.wg_private_key }}"
|
||||
- /ip address add address={{ gateway_config.wg_ip }}/{{ gateway_config.wg_net | ipaddr("prefix") }} interface="wg-{{ gateway_config.lab_name }}"
|
||||
|
||||
- name: Configure WireGuard peers
|
||||
community.routeros.command:
|
||||
commands:
|
||||
- /interface wireguard peers add interface="wg-{{ gateway_config.lab_name }}" public-key="{{ peer.public_key }}" endpoint-address={{ peer.endpoint }} endpoint-port={{ gateway_config.wg_listen_port }} allowed-address={{ peer.wg_ip }}/32 persistent-keepalive=25s
|
||||
loop: "{{ gateway_config.peers }}"
|
||||
loop_control:
|
||||
loop_var: peer
|
||||
24
ansible/roles/monitoring/tasks/main.yml
Normal file
24
ansible/roles/monitoring/tasks/main.yml
Normal file
@@ -0,0 +1,24 @@
|
||||
---
|
||||
- name: Create exporter directory
|
||||
file:
|
||||
path: "{{ exporter_install_dir }}"
|
||||
state: directory
|
||||
mode: '0755'
|
||||
|
||||
- name: Deploy exporter configuration
|
||||
template:
|
||||
src: exporter-config.json.j2
|
||||
dest: /etc/wg-lab-exporter/config.json
|
||||
mode: '0644'
|
||||
|
||||
- name: Create systemd service
|
||||
template:
|
||||
src: wg-lab-exporter.service.j2
|
||||
dest: /etc/systemd/system/wg-lab-exporter.service
|
||||
|
||||
- name: Enable and start exporter
|
||||
systemd:
|
||||
name: wg-lab-exporter
|
||||
enabled: yes
|
||||
state: started
|
||||
daemon_reload: yes
|
||||
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"labs": {
|
||||
"{{ gateway_config.lab_name }}": {
|
||||
"vni": {{ gateway_config.vni }},
|
||||
"lab_if": "{{ gateway_config.lab_if }}"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
[Unit]
|
||||
Description=WireGuard Lab Exporter
|
||||
After=network.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
ExecStart=/usr/bin/python3 /opt/wg-lab-exporter/wg_lab_exporter.py
|
||||
Restart=always
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
63
ansible/roles/openwrt-bridge/tasks/main.yml
Normal file
63
ansible/roles/openwrt-bridge/tasks/main.yml
Normal file
@@ -0,0 +1,63 @@
|
||||
---
|
||||
- name: Set bridge interface name
|
||||
set_fact:
|
||||
br_if: "br_{{ gateway_config.lab_name }}"
|
||||
vxlan_if: "vxlan{{ gateway_config.vni }}"
|
||||
lab_if: "{{ gateway_config.lab_if }}"
|
||||
|
||||
- name: Remove existing bridge configuration
|
||||
shell: |
|
||||
uci delete network.{{ br_if }} 2>/dev/null || true
|
||||
uci delete network.{{ br_if }}_dev 2>/dev/null || true
|
||||
uci commit network
|
||||
changed_when: true
|
||||
|
||||
- name: Create bridge device
|
||||
shell: |
|
||||
uci set network.{{ br_if }}_dev='device'
|
||||
uci set network.{{ br_if }}_dev.name='{{ br_if }}'
|
||||
uci set network.{{ br_if }}_dev.type='bridge'
|
||||
uci set network.{{ br_if }}_dev.stp='0'
|
||||
uci add_list network.{{ br_if }}_dev.ports='{{ vxlan_if }}'
|
||||
uci add_list network.{{ br_if }}_dev.ports='{{ lab_if }}'
|
||||
uci commit network
|
||||
changed_when: true
|
||||
|
||||
- name: Create bridge interface
|
||||
shell: |
|
||||
uci set network.{{ br_if }}='interface'
|
||||
uci set network.{{ br_if }}.proto='static'
|
||||
uci set network.{{ br_if }}.device='{{ br_if }}'
|
||||
uci set network.{{ br_if }}.ipaddr='{{ gateway_config.subnet | ipaddr("1") | ipaddr("address") }}'
|
||||
uci set network.{{ br_if }}.netmask='{{ gateway_config.subnet | ipaddr("netmask") }}'
|
||||
uci commit network
|
||||
changed_when: true
|
||||
|
||||
- name: Reload network configuration
|
||||
shell: /etc/init.d/network reload
|
||||
changed_when: true
|
||||
|
||||
- name: Wait for bridge to come up
|
||||
shell: ip link show {{ br_if }}
|
||||
register: bridge_check
|
||||
retries: 10
|
||||
delay: 2
|
||||
until: bridge_check.rc == 0
|
||||
changed_when: false
|
||||
|
||||
- name: Verify bridge configuration
|
||||
shell: |
|
||||
echo "=== Bridge Interface ==="
|
||||
ip link show {{ br_if }}
|
||||
echo ""
|
||||
echo "=== Bridge Members ==="
|
||||
bridge link show | grep {{ br_if }}
|
||||
echo ""
|
||||
echo "=== IP Address ==="
|
||||
ip addr show {{ br_if }}
|
||||
register: bridge_status
|
||||
changed_when: false
|
||||
|
||||
- name: Display bridge status
|
||||
debug:
|
||||
var: bridge_status.stdout_lines
|
||||
62
ansible/roles/openwrt-common/tasks/main.yml
Normal file
62
ansible/roles/openwrt-common/tasks/main.yml
Normal file
@@ -0,0 +1,62 @@
|
||||
---
|
||||
- name: Gather OpenWrt facts
|
||||
setup:
|
||||
|
||||
- name: Check OpenWrt version
|
||||
shell: ". /etc/os-release && echo $VERSION"
|
||||
register: openwrt_version
|
||||
changed_when: false
|
||||
|
||||
- name: Display OpenWrt version
|
||||
debug:
|
||||
msg: "OpenWrt version: {{ openwrt_version.stdout }}"
|
||||
|
||||
- name: Update package lists
|
||||
shell: opkg update
|
||||
changed_when: false
|
||||
|
||||
- name: Check if packages are installed
|
||||
shell: opkg list-installed | grep -E "^(wireguard-tools|kmod-wireguard|kmod-vxlan|ip-full|tcpdump) " || true
|
||||
register: installed_packages
|
||||
changed_when: false
|
||||
|
||||
- name: Install required packages
|
||||
shell: opkg install {{ item }}
|
||||
loop:
|
||||
- wireguard-tools
|
||||
- kmod-wireguard
|
||||
- kmod-vxlan
|
||||
- ip-full
|
||||
- tcpdump
|
||||
when: item not in installed_packages.stdout
|
||||
register: package_install
|
||||
failed_when: package_install.rc != 0 and 'already installed' not in package_install.stderr
|
||||
|
||||
- name: Enable IP forwarding
|
||||
shell: |
|
||||
uci set network.@globals[0].ipv6_forwarding='1' 2>/dev/null || uci add network globals
|
||||
uci set network.@globals[0].ipv6_forwarding='1'
|
||||
uci commit network
|
||||
changed_when: true
|
||||
|
||||
- name: Configure sysctl settings
|
||||
blockinfile:
|
||||
path: /etc/sysctl.conf
|
||||
create: yes
|
||||
block: |
|
||||
net.ipv4.ip_forward=1
|
||||
net.ipv4.conf.all.rp_filter=0
|
||||
net.ipv4.conf.default.rp_filter=0
|
||||
net.bridge.bridge-nf-call-iptables=0
|
||||
net.bridge.bridge-nf-call-ip6tables=0
|
||||
marker: "# {mark} ANSIBLE MANAGED - Wireguard Lab"
|
||||
|
||||
- name: Apply sysctl settings
|
||||
shell: sysctl -p
|
||||
changed_when: false
|
||||
|
||||
- name: Create configuration backup
|
||||
shell: sysupgrade -b /tmp/backup-pre-wireguard-lab-$(date +%Y%m%d-%H%M%S).tar.gz
|
||||
args:
|
||||
creates: /tmp/backup-pre-wireguard-lab-*.tar.gz
|
||||
ignore_errors: yes
|
||||
78
ansible/roles/openwrt-dhcp/tasks/main.yml
Normal file
78
ansible/roles/openwrt-dhcp/tasks/main.yml
Normal file
@@ -0,0 +1,78 @@
|
||||
---
|
||||
- name: Set DHCP configuration variables
|
||||
set_fact:
|
||||
br_if: "br_{{ gateway_config.lab_name }}"
|
||||
dhcp_tag: "{{ gateway_config.lab_name }}"
|
||||
|
||||
- name: Calculate DHCP range based on role
|
||||
set_fact:
|
||||
dhcp_start: "{{ gateway_config.subnet | ipaddr('10') | ipaddr('address') if gateway_config.dhcp_role == 'primary' else gateway_config.subnet | ipaddr('128') | ipaddr('address') }}"
|
||||
dhcp_limit: "{{ 118 if gateway_config.dhcp_role == 'primary' else 118 }}"
|
||||
when: gateway_config.dhcp_mode == "simple"
|
||||
|
||||
- name: Calculate DHCP range for failover simulation
|
||||
set_fact:
|
||||
dhcp_start: "{{ gateway_config.subnet | ipaddr('10') | ipaddr('address') }}"
|
||||
dhcp_limit: 236
|
||||
when: gateway_config.dhcp_mode == "failover"
|
||||
|
||||
- name: Remove existing DHCP configuration for this lab
|
||||
shell: |
|
||||
uci delete dhcp.{{ dhcp_tag }} 2>/dev/null || true
|
||||
uci commit dhcp
|
||||
changed_when: true
|
||||
|
||||
- name: Configure DHCP server
|
||||
shell: |
|
||||
uci set dhcp.{{ dhcp_tag }}='dhcp'
|
||||
uci set dhcp.{{ dhcp_tag }}.interface='{{ br_if }}'
|
||||
uci set dhcp.{{ dhcp_tag }}.start='{{ dhcp_start | ipaddr('host') }}'
|
||||
uci set dhcp.{{ dhcp_tag }}.limit='{{ dhcp_limit }}'
|
||||
uci set dhcp.{{ dhcp_tag }}.leasetime='12h'
|
||||
uci add_list dhcp.{{ dhcp_tag }}.dhcp_option='3,{{ gateway_config.subnet | ipaddr("1") | ipaddr("address") }}'
|
||||
uci add_list dhcp.{{ dhcp_tag }}.dhcp_option='6,{{ gateway_config.subnet | ipaddr("1") | ipaddr("address") }}'
|
||||
uci commit dhcp
|
||||
changed_when: true
|
||||
|
||||
- name: Configure DNS domain for lab network
|
||||
shell: |
|
||||
uci set dhcp.{{ dhcp_tag }}.domain='{{ gateway_config.lab_name }}.lab'
|
||||
uci set dhcp.{{ dhcp_tag }}.local='/{{ gateway_config.lab_name }}.lab/'
|
||||
uci commit dhcp
|
||||
changed_when: true
|
||||
|
||||
- name: Set DHCP authoritative mode based on role
|
||||
shell: |
|
||||
uci set dhcp.{{ dhcp_tag }}.authoritative='{{ "1" if gateway_config.dhcp_role == "primary" else "0" }}'
|
||||
uci commit dhcp
|
||||
changed_when: true
|
||||
when: gateway_config.dhcp_mode == "failover"
|
||||
|
||||
- name: Restart dnsmasq
|
||||
shell: /etc/init.d/dnsmasq restart
|
||||
changed_when: true
|
||||
|
||||
- name: Wait for dnsmasq to start
|
||||
shell: pgrep dnsmasq
|
||||
register: dnsmasq_check
|
||||
retries: 5
|
||||
delay: 2
|
||||
until: dnsmasq_check.rc == 0
|
||||
changed_when: false
|
||||
|
||||
- name: Verify DHCP configuration
|
||||
shell: |
|
||||
echo "=== DHCP Configuration ==="
|
||||
uci show dhcp.{{ dhcp_tag }}
|
||||
echo ""
|
||||
echo "=== dnsmasq Process ==="
|
||||
ps | grep dnsmasq | grep -v grep
|
||||
echo ""
|
||||
echo "=== DHCP Leases ==="
|
||||
cat /tmp/dhcp.leases 2>/dev/null || echo "No leases yet"
|
||||
register: dhcp_status
|
||||
changed_when: false
|
||||
|
||||
- name: Display DHCP status
|
||||
debug:
|
||||
var: dhcp_status.stdout_lines
|
||||
98
ansible/roles/openwrt-firewall/tasks/main.yml
Normal file
98
ansible/roles/openwrt-firewall/tasks/main.yml
Normal file
@@ -0,0 +1,98 @@
|
||||
---
|
||||
- name: Set firewall zone name
|
||||
set_fact:
|
||||
fw_zone: "{{ gateway_config.lab_name }}"
|
||||
br_if: "br_{{ gateway_config.lab_name }}"
|
||||
|
||||
- name: Remove existing firewall zone
|
||||
shell: |
|
||||
# Find and remove existing zone
|
||||
zone_idx=$(uci show firewall | grep "=zone" | grep "name='{{ fw_zone }}'" | cut -d. -f2 | cut -d= -f1)
|
||||
if [ -n "$zone_idx" ]; then
|
||||
uci delete firewall.$zone_idx
|
||||
fi
|
||||
|
||||
# Remove associated forwarding rules
|
||||
for fwd in $(uci show firewall | grep "={{ fw_zone }}" | cut -d. -f2 | cut -d= -f1); do
|
||||
uci delete firewall.$fwd 2>/dev/null || true
|
||||
done
|
||||
|
||||
uci commit firewall
|
||||
changed_when: true
|
||||
|
||||
- name: Create isolated firewall zone for lab network
|
||||
shell: |
|
||||
# Create new zone
|
||||
uci add firewall zone
|
||||
uci set firewall.@zone[-1].name='{{ fw_zone }}'
|
||||
uci set firewall.@zone[-1].input='REJECT'
|
||||
uci set firewall.@zone[-1].output='ACCEPT'
|
||||
uci set firewall.@zone[-1].forward='REJECT'
|
||||
uci set firewall.@zone[-1].network='{{ br_if }}'
|
||||
uci commit firewall
|
||||
changed_when: true
|
||||
|
||||
- name: Allow DNS and DHCP from lab to gateway
|
||||
shell: |
|
||||
# Allow DNS
|
||||
uci add firewall rule
|
||||
uci set firewall.@rule[-1].name='Allow-{{ fw_zone }}-DNS'
|
||||
uci set firewall.@rule[-1].src='{{ fw_zone }}'
|
||||
uci set firewall.@rule[-1].proto='tcp udp'
|
||||
uci set firewall.@rule[-1].dest_port='53'
|
||||
uci set firewall.@rule[-1].target='ACCEPT'
|
||||
|
||||
# Allow DHCP
|
||||
uci add firewall rule
|
||||
uci set firewall.@rule[-1].name='Allow-{{ fw_zone }}-DHCP'
|
||||
uci set firewall.@rule[-1].src='{{ fw_zone }}'
|
||||
uci set firewall.@rule[-1].proto='udp'
|
||||
uci set firewall.@rule[-1].src_port='67:68'
|
||||
uci set firewall.@rule[-1].dest_port='67:68'
|
||||
uci set firewall.@rule[-1].target='ACCEPT'
|
||||
|
||||
uci commit firewall
|
||||
changed_when: true
|
||||
|
||||
- name: Allow intra-lab forwarding
|
||||
shell: |
|
||||
uci add firewall forwarding
|
||||
uci set firewall.@forwarding[-1].src='{{ fw_zone }}'
|
||||
uci set firewall.@forwarding[-1].dest='{{ fw_zone }}'
|
||||
uci commit firewall
|
||||
changed_when: true
|
||||
|
||||
- name: Block lab to WAN/LAN
|
||||
shell: |
|
||||
# Explicit reject to WAN
|
||||
uci add firewall forwarding
|
||||
uci set firewall.@forwarding[-1].src='{{ fw_zone }}'
|
||||
uci set firewall.@forwarding[-1].dest='wan'
|
||||
uci set firewall.@forwarding[-1].enabled='0'
|
||||
|
||||
# Explicit reject to LAN
|
||||
uci add firewall forwarding
|
||||
uci set firewall.@forwarding[-1].src='{{ fw_zone }}'
|
||||
uci set firewall.@forwarding[-1].dest='lan'
|
||||
uci set firewall.@forwarding[-1].enabled='0'
|
||||
|
||||
uci commit firewall
|
||||
changed_when: true
|
||||
|
||||
- name: Reload firewall
|
||||
shell: /etc/init.d/firewall reload
|
||||
changed_when: true
|
||||
|
||||
- name: Verify firewall configuration
|
||||
shell: |
|
||||
echo "=== Firewall Zones ==="
|
||||
uci show firewall | grep "={{ fw_zone }}"
|
||||
echo ""
|
||||
echo "=== Firewall Rules ==="
|
||||
iptables -L -n -v | grep -A 5 "{{ fw_zone }}" || echo "No iptables rules yet"
|
||||
register: firewall_status
|
||||
changed_when: false
|
||||
|
||||
- name: Display firewall status
|
||||
debug:
|
||||
var: firewall_status.stdout_lines
|
||||
80
ansible/roles/openwrt-monitoring/tasks/main.yml
Normal file
80
ansible/roles/openwrt-monitoring/tasks/main.yml
Normal file
@@ -0,0 +1,80 @@
|
||||
---
|
||||
- name: Create metrics exporter directory
|
||||
file:
|
||||
path: /usr/lib/wg-lab-exporter
|
||||
state: directory
|
||||
mode: '0755'
|
||||
|
||||
- name: Deploy metrics exporter script
|
||||
template:
|
||||
src: metrics-exporter.sh.j2
|
||||
dest: /usr/lib/wg-lab-exporter/exporter.sh
|
||||
mode: '0755'
|
||||
|
||||
- name: Create exporter configuration
|
||||
template:
|
||||
src: exporter-config.j2
|
||||
dest: /etc/wg-lab-exporter.conf
|
||||
mode: '0644'
|
||||
|
||||
- name: Create uhttpd configuration for metrics endpoint
|
||||
blockinfile:
|
||||
path: /etc/uhttpd.conf
|
||||
create: yes
|
||||
marker: "# {mark} ANSIBLE MANAGED - Metrics Exporter"
|
||||
block: |
|
||||
config uhttpd 'metrics'
|
||||
list listen_http '0.0.0.0:9586'
|
||||
option home '/www-metrics'
|
||||
option cgi_prefix '/metrics'
|
||||
option script_timeout '10'
|
||||
option network_timeout '30'
|
||||
option tcp_keepalive '1'
|
||||
|
||||
- name: Create metrics web directory
|
||||
file:
|
||||
path: /www-metrics/metrics
|
||||
state: directory
|
||||
mode: '0755'
|
||||
|
||||
- name: Create CGI wrapper for metrics
|
||||
copy:
|
||||
dest: /www-metrics/metrics/index.cgi
|
||||
mode: '0755'
|
||||
content: |
|
||||
#!/bin/sh
|
||||
echo "Content-Type: text/plain"
|
||||
echo ""
|
||||
/usr/lib/wg-lab-exporter/exporter.sh
|
||||
|
||||
- name: Create cron job for metrics collection (fallback)
|
||||
cron:
|
||||
name: "WireGuard Lab Metrics Collection"
|
||||
minute: "*/5"
|
||||
job: "/usr/lib/wg-lab-exporter/exporter.sh > /tmp/wg-lab-metrics.txt"
|
||||
user: root
|
||||
|
||||
- name: Restart uhttpd
|
||||
shell: /etc/init.d/uhttpd restart
|
||||
changed_when: true
|
||||
|
||||
- name: Test metrics endpoint
|
||||
uri:
|
||||
url: "http://localhost:9586/metrics/"
|
||||
timeout: 5
|
||||
register: metrics_test
|
||||
ignore_errors: yes
|
||||
changed_when: false
|
||||
|
||||
- name: Display metrics test result
|
||||
debug:
|
||||
msg: "Metrics endpoint {{ 'OK' if metrics_test.status == 200 else 'FAILED' }}"
|
||||
|
||||
- name: Show sample metrics output
|
||||
shell: /usr/lib/wg-lab-exporter/exporter.sh | head -20
|
||||
register: metrics_sample
|
||||
changed_when: false
|
||||
|
||||
- name: Display sample metrics
|
||||
debug:
|
||||
var: metrics_sample.stdout_lines
|
||||
@@ -0,0 +1,10 @@
|
||||
# WireGuard Lab Exporter Configuration
|
||||
# Lab: {{ gateway_config.lab_name }}
|
||||
|
||||
LAB_NAME="{{ gateway_config.lab_name }}"
|
||||
VNI="{{ gateway_config.vni }}"
|
||||
|
||||
# Interfaces to monitor
|
||||
WG_INTERFACES="wg_{{ gateway_config.lab_name }}"
|
||||
BRIDGE_INTERFACES="br_{{ gateway_config.lab_name }}"
|
||||
VXLAN_INTERFACES="vxlan{{ gateway_config.vni }}"
|
||||
@@ -0,0 +1,105 @@
|
||||
#!/bin/sh
|
||||
# Prometheus metrics exporter for WireGuard Lab Overlay
|
||||
# Generated by Ansible
|
||||
|
||||
. /etc/wg-lab-exporter.conf
|
||||
|
||||
# Helper function to output metrics
|
||||
metric() {
|
||||
echo "$1 $2"
|
||||
}
|
||||
|
||||
metric_label() {
|
||||
echo "$1{$2} $3"
|
||||
}
|
||||
|
||||
# Current timestamp
|
||||
TIMESTAMP=$(date +%s)
|
||||
|
||||
echo "# HELP wireguard_tunnel_up WireGuard tunnel status (1=up, 0=down)"
|
||||
echo "# TYPE wireguard_tunnel_up gauge"
|
||||
|
||||
# Check WireGuard tunnels
|
||||
for WG_IF in ${WG_INTERFACES}; do
|
||||
if ip link show ${WG_IF} >/dev/null 2>&1; then
|
||||
# Get peer information
|
||||
wg show ${WG_IF} dump | tail -n +2 | while IFS=$'\t' read pubkey psk endpoint allowed_ip latest_handshake rx_bytes tx_bytes persistent_keepalive; do
|
||||
# Calculate handshake age
|
||||
if [ "$latest_handshake" != "0" ]; then
|
||||
handshake_age=$((TIMESTAMP - latest_handshake))
|
||||
if [ $handshake_age -lt 180 ]; then
|
||||
status=1
|
||||
else
|
||||
status=0
|
||||
fi
|
||||
else
|
||||
status=0
|
||||
handshake_age=-1
|
||||
fi
|
||||
|
||||
peer_short=$(echo $pubkey | cut -c1-16)
|
||||
|
||||
metric_label "wireguard_tunnel_up" "lab=\"${LAB_NAME}\",interface=\"${WG_IF}\",peer=\"${peer_short}\"" "$status"
|
||||
metric_label "wireguard_latest_handshake_seconds" "lab=\"${LAB_NAME}\",interface=\"${WG_IF}\",peer=\"${peer_short}\"" "$handshake_age"
|
||||
metric_label "wireguard_rx_bytes_total" "lab=\"${LAB_NAME}\",interface=\"${WG_IF}\",peer=\"${peer_short}\"" "$rx_bytes"
|
||||
metric_label "wireguard_tx_bytes_total" "lab=\"${LAB_NAME}\",interface=\"${WG_IF}\",peer=\"${peer_short}\"" "$tx_bytes"
|
||||
done
|
||||
else
|
||||
metric_label "wireguard_tunnel_up" "lab=\"${LAB_NAME}\",interface=\"${WG_IF}\",peer=\"all\"" "0"
|
||||
fi
|
||||
done
|
||||
|
||||
echo ""
|
||||
echo "# HELP bridge_fdb_entries Number of FDB entries in bridge"
|
||||
echo "# TYPE bridge_fdb_entries gauge"
|
||||
|
||||
# Bridge FDB entries
|
||||
for BR_IF in ${BRIDGE_INTERFACES}; do
|
||||
if ip link show ${BR_IF} >/dev/null 2>&1; then
|
||||
fdb_count=$(bridge fdb show br ${BR_IF} 2>/dev/null | wc -l)
|
||||
metric_label "bridge_fdb_entries" "lab=\"${LAB_NAME}\",bridge=\"${BR_IF}\"" "$fdb_count"
|
||||
fi
|
||||
done
|
||||
|
||||
echo ""
|
||||
echo "# HELP vxlan_rx_packets_total VXLAN received packets"
|
||||
echo "# TYPE vxlan_rx_packets_total counter"
|
||||
echo "# HELP vxlan_tx_packets_total VXLAN transmitted packets"
|
||||
echo "# TYPE vxlan_tx_packets_total counter"
|
||||
|
||||
# VXLAN statistics
|
||||
for VXLAN_IF in ${VXLAN_INTERFACES}; do
|
||||
if [ -f "/sys/class/net/${VXLAN_IF}/statistics/rx_packets" ]; then
|
||||
rx_packets=$(cat /sys/class/net/${VXLAN_IF}/statistics/rx_packets)
|
||||
tx_packets=$(cat /sys/class/net/${VXLAN_IF}/statistics/tx_packets)
|
||||
rx_bytes=$(cat /sys/class/net/${VXLAN_IF}/statistics/rx_bytes)
|
||||
tx_bytes=$(cat /sys/class/net/${VXLAN_IF}/statistics/tx_bytes)
|
||||
|
||||
metric_label "vxlan_rx_packets_total" "lab=\"${LAB_NAME}\",vni=\"${VNI}\"" "$rx_packets"
|
||||
metric_label "vxlan_tx_packets_total" "lab=\"${LAB_NAME}\",vni=\"${VNI}\"" "$tx_packets"
|
||||
metric_label "vxlan_rx_bytes_total" "lab=\"${LAB_NAME}\",vni=\"${VNI}\"" "$rx_bytes"
|
||||
metric_label "vxlan_tx_bytes_total" "lab=\"${LAB_NAME}\",vni=\"${VNI}\"" "$tx_bytes"
|
||||
fi
|
||||
done
|
||||
|
||||
echo ""
|
||||
echo "# HELP dhcp_leases_active Number of active DHCP leases"
|
||||
echo "# TYPE dhcp_leases_active gauge"
|
||||
|
||||
# DHCP lease count
|
||||
if [ -f "/tmp/dhcp.leases" ]; then
|
||||
lease_count=$(wc -l < /tmp/dhcp.leases)
|
||||
metric_label "dhcp_leases_active" "lab=\"${LAB_NAME}\"" "$lease_count"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "# HELP openwrt_system_uptime_seconds System uptime in seconds"
|
||||
echo "# TYPE openwrt_system_uptime_seconds counter"
|
||||
uptime_seconds=$(cat /proc/uptime | cut -d' ' -f1 | cut -d. -f1)
|
||||
metric "openwrt_system_uptime_seconds" "$uptime_seconds"
|
||||
|
||||
echo ""
|
||||
echo "# HELP openwrt_memory_free_bytes Free memory in bytes"
|
||||
echo "# TYPE openwrt_memory_free_bytes gauge"
|
||||
mem_free=$(free | grep Mem | awk '{print $4}')
|
||||
metric "openwrt_memory_free_bytes" "$((mem_free * 1024))"
|
||||
62
ansible/roles/openwrt-vxlan/tasks/main.yml
Normal file
62
ansible/roles/openwrt-vxlan/tasks/main.yml
Normal file
@@ -0,0 +1,62 @@
|
||||
---
|
||||
- name: Set VXLAN interface name
|
||||
set_fact:
|
||||
vxlan_if: "vxlan{{ gateway_config.vni }}"
|
||||
wg_if: "wg_{{ gateway_config.lab_name }}"
|
||||
|
||||
- name: Remove existing VXLAN interface
|
||||
shell: |
|
||||
uci delete network.{{ vxlan_if }} 2>/dev/null || true
|
||||
uci commit network
|
||||
changed_when: true
|
||||
|
||||
- name: Create VXLAN interface using ip command (OpenWrt native VXLAN)
|
||||
shell: |
|
||||
# Remove if exists
|
||||
ip link delete {{ vxlan_if }} 2>/dev/null || true
|
||||
|
||||
# Create VXLAN interface
|
||||
ip link add {{ vxlan_if }} type vxlan \
|
||||
id {{ gateway_config.vni }} \
|
||||
dev {{ wg_if }} \
|
||||
dstport 4789 \
|
||||
nolearning \
|
||||
proxy
|
||||
|
||||
# Bring up interface
|
||||
ip link set {{ vxlan_if }} up
|
||||
changed_when: true
|
||||
|
||||
- name: Add static FDB entries for peers
|
||||
shell: |
|
||||
# Clear existing FDB entries for this interface
|
||||
bridge fdb show dev {{ vxlan_if }} | grep 00:00:00:00:00:00 | while read line; do
|
||||
bridge fdb del $(echo $line | awk '{print $1}') dev {{ vxlan_if }} 2>/dev/null || true
|
||||
done
|
||||
|
||||
# Add FDB entries for all peers
|
||||
bridge fdb append 00:00:00:00:00:00 dev {{ vxlan_if }} dst {{ peer.wg_ip }}
|
||||
loop: "{{ gateway_config.peers }}"
|
||||
loop_control:
|
||||
loop_var: peer
|
||||
label: "{{ peer.to }}"
|
||||
changed_when: true
|
||||
|
||||
- name: Create VXLAN startup script
|
||||
template:
|
||||
src: vxlan-startup.sh.j2
|
||||
dest: /etc/init.d/vxlan-{{ gateway_config.lab_name }}
|
||||
mode: '0755'
|
||||
|
||||
- name: Enable VXLAN startup script
|
||||
shell: /etc/init.d/vxlan-{{ gateway_config.lab_name }} enable
|
||||
changed_when: true
|
||||
|
||||
- name: Verify VXLAN interface
|
||||
shell: ip -d link show {{ vxlan_if }}
|
||||
register: vxlan_status
|
||||
changed_when: false
|
||||
|
||||
- name: Display VXLAN status
|
||||
debug:
|
||||
var: vxlan_status.stdout_lines
|
||||
56
ansible/roles/openwrt-vxlan/templates/vxlan-startup.sh.j2
Normal file
56
ansible/roles/openwrt-vxlan/templates/vxlan-startup.sh.j2
Normal file
@@ -0,0 +1,56 @@
|
||||
#!/bin/sh /etc/rc.common
|
||||
# VXLAN startup script for {{ gateway_config.lab_name }}
|
||||
|
||||
START=99
|
||||
STOP=10
|
||||
|
||||
USE_PROCD=1
|
||||
|
||||
LAB_NAME="{{ gateway_config.lab_name }}"
|
||||
VNI="{{ gateway_config.vni }}"
|
||||
WG_IF="wg_${LAB_NAME}"
|
||||
VXLAN_IF="vxlan${VNI}"
|
||||
|
||||
start_service() {
|
||||
# Wait for WireGuard interface
|
||||
timeout=30
|
||||
while [ $timeout -gt 0 ]; do
|
||||
if ip link show ${WG_IF} >/dev/null 2>&1; then
|
||||
break
|
||||
fi
|
||||
sleep 1
|
||||
timeout=$((timeout-1))
|
||||
done
|
||||
|
||||
# Remove if exists
|
||||
ip link delete ${VXLAN_IF} 2>/dev/null || true
|
||||
|
||||
# Create VXLAN interface
|
||||
ip link add ${VXLAN_IF} type vxlan \
|
||||
id ${VNI} \
|
||||
dev ${WG_IF} \
|
||||
dstport 4789 \
|
||||
nolearning \
|
||||
proxy
|
||||
|
||||
# Add FDB entries
|
||||
{% for peer in gateway_config.peers %}
|
||||
bridge fdb append 00:00:00:00:00:00 dev ${VXLAN_IF} dst {{ peer.wg_ip }}
|
||||
{% endfor %}
|
||||
|
||||
# Bring up interface
|
||||
ip link set ${VXLAN_IF} up
|
||||
|
||||
logger -t vxlan-${LAB_NAME} "VXLAN ${VXLAN_IF} started"
|
||||
}
|
||||
|
||||
stop_service() {
|
||||
ip link delete ${VXLAN_IF} 2>/dev/null || true
|
||||
logger -t vxlan-${LAB_NAME} "VXLAN ${VXLAN_IF} stopped"
|
||||
}
|
||||
|
||||
restart() {
|
||||
stop_service
|
||||
sleep 2
|
||||
start_service
|
||||
}
|
||||
86
ansible/roles/openwrt-wireguard/tasks/main.yml
Normal file
86
ansible/roles/openwrt-wireguard/tasks/main.yml
Normal file
@@ -0,0 +1,86 @@
|
||||
---
|
||||
- name: Set WireGuard interface name
|
||||
set_fact:
|
||||
wg_if: "wg_{{ gateway_config.lab_name }}"
|
||||
wg_port: "{{ gateway_config.wg_listen_port }}"
|
||||
|
||||
- name: Remove existing WireGuard interface configuration
|
||||
shell: |
|
||||
uci delete network.{{ wg_if }} 2>/dev/null || true
|
||||
uci commit network
|
||||
changed_when: true
|
||||
|
||||
- name: Create WireGuard interface
|
||||
shell: |
|
||||
uci set network.{{ wg_if }}=interface
|
||||
uci set network.{{ wg_if }}.proto='wireguard'
|
||||
uci set network.{{ wg_if }}.private_key='{{ gateway_config.wg_private_key }}'
|
||||
uci set network.{{ wg_if }}.listen_port='{{ wg_port }}'
|
||||
uci add_list network.{{ wg_if }}.addresses='{{ gateway_config.wg_ip }}/{{ gateway_config.wg_net | ipaddr("prefix") }}'
|
||||
uci commit network
|
||||
changed_when: true
|
||||
|
||||
- name: Remove existing WireGuard peers
|
||||
shell: |
|
||||
for peer in $(uci show network | grep "={{ wg_if }}_peer" | cut -d. -f2 | cut -d= -f1); do
|
||||
uci delete network.$peer
|
||||
done
|
||||
uci commit network
|
||||
changed_when: true
|
||||
ignore_errors: yes
|
||||
|
||||
- name: Configure WireGuard gateway peers
|
||||
shell: |
|
||||
peer_name="{{ wg_if }}_peer{{ idx }}"
|
||||
uci set network.$peer_name=wireguard_{{ wg_if }}
|
||||
uci set network.$peer_name.public_key='{{ peer.public_key }}'
|
||||
uci set network.$peer_name.endpoint_host='{{ peer.endpoint }}'
|
||||
uci set network.$peer_name.endpoint_port='{{ wg_port }}'
|
||||
uci set network.$peer_name.persistent_keepalive='25'
|
||||
uci add_list network.$peer_name.allowed_ips='{{ peer.wg_ip }}/32'
|
||||
uci set network.$peer_name.route_allowed_ips='0'
|
||||
uci commit network
|
||||
loop: "{{ gateway_config.peers }}"
|
||||
loop_control:
|
||||
index_var: idx
|
||||
loop_var: peer
|
||||
label: "{{ peer.to }}"
|
||||
changed_when: true
|
||||
|
||||
- name: Configure WireGuard road warrior peers
|
||||
shell: |
|
||||
peer_name="{{ wg_if }}_rw{{ idx }}"
|
||||
uci set network.$peer_name=wireguard_{{ wg_if }}
|
||||
uci set network.$peer_name.public_key='{{ client.public_key }}'
|
||||
uci add_list network.$peer_name.allowed_ips='{{ client.wg_ip }}/32'
|
||||
uci set network.$peer_name.route_allowed_ips='0'
|
||||
uci set network.$peer_name.description='{{ client.email }}'
|
||||
uci commit network
|
||||
loop: "{{ gateway_config.rw_clients }}"
|
||||
loop_control:
|
||||
index_var: idx
|
||||
loop_var: client
|
||||
label: "{{ client.email }}"
|
||||
when: gateway_config.road_warrior and gateway_config.rw_clients | length > 0
|
||||
changed_when: true
|
||||
|
||||
- name: Reload network configuration
|
||||
shell: /etc/init.d/network reload
|
||||
changed_when: true
|
||||
|
||||
- name: Wait for WireGuard interface to come up
|
||||
shell: ip link show {{ wg_if }}
|
||||
register: wg_link_check
|
||||
retries: 10
|
||||
delay: 2
|
||||
until: wg_link_check.rc == 0
|
||||
changed_when: false
|
||||
|
||||
- name: Verify WireGuard is running
|
||||
shell: wg show {{ wg_if }}
|
||||
register: wg_status
|
||||
changed_when: false
|
||||
|
||||
- name: Display WireGuard status
|
||||
debug:
|
||||
var: wg_status.stdout_lines
|
||||
4
ansible/roles/vxlan/handlers/main.yml
Normal file
4
ansible/roles/vxlan/handlers/main.yml
Normal file
@@ -0,0 +1,4 @@
|
||||
---
|
||||
- name: reload systemd
|
||||
systemd:
|
||||
daemon_reload: yes
|
||||
19
ansible/roles/vxlan/tasks/main.yml
Normal file
19
ansible/roles/vxlan/tasks/main.yml
Normal file
@@ -0,0 +1,19 @@
|
||||
---
|
||||
- name: Create VXLAN interface setup script
|
||||
template:
|
||||
src: setup-vxlan.sh.j2
|
||||
dest: /usr/local/bin/setup-vxlan-{{ gateway_config.lab_name }}.sh
|
||||
mode: '0755'
|
||||
|
||||
- name: Create systemd service for VXLAN
|
||||
template:
|
||||
src: vxlan.service.j2
|
||||
dest: /etc/systemd/system/vxlan-{{ gateway_config.lab_name }}.service
|
||||
notify: reload systemd
|
||||
|
||||
- name: Enable and start VXLAN service
|
||||
systemd:
|
||||
name: vxlan-{{ gateway_config.lab_name }}
|
||||
enabled: yes
|
||||
state: started
|
||||
daemon_reload: yes
|
||||
35
ansible/roles/vxlan/templates/setup-vxlan.sh.j2
Normal file
35
ansible/roles/vxlan/templates/setup-vxlan.sh.j2
Normal file
@@ -0,0 +1,35 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
LAB_NAME="{{ gateway_config.lab_name }}"
|
||||
VNI="{{ gateway_config.vni }}"
|
||||
WG_IF="wg-${LAB_NAME}"
|
||||
VXLAN_IF="vxlan${VNI}"
|
||||
|
||||
# Wait for WireGuard interface
|
||||
timeout=30
|
||||
while [ $timeout -gt 0 ]; do
|
||||
if ip link show ${WG_IF} &>/dev/null; then
|
||||
break
|
||||
fi
|
||||
sleep 1
|
||||
((timeout--))
|
||||
done
|
||||
|
||||
# Create VXLAN interface
|
||||
if ! ip link show ${VXLAN_IF} &>/dev/null; then
|
||||
ip link add ${VXLAN_IF} type vxlan \
|
||||
id ${VNI} \
|
||||
dev ${WG_IF} \
|
||||
dstport {{ vxlan_port }} \
|
||||
nolearning \
|
||||
proxy
|
||||
fi
|
||||
|
||||
# Configure VXLAN peers (FDB entries)
|
||||
{% for peer in gateway_config.peers %}
|
||||
bridge fdb append 00:00:00:00:00:00 dev ${VXLAN_IF} dst {{ peer.wg_ip }}
|
||||
{% endfor %}
|
||||
|
||||
# Bring up interface
|
||||
ip link set ${VXLAN_IF} up
|
||||
13
ansible/roles/vxlan/templates/vxlan.service.j2
Normal file
13
ansible/roles/vxlan/templates/vxlan.service.j2
Normal file
@@ -0,0 +1,13 @@
|
||||
[Unit]
|
||||
Description=VXLAN interface for {{ gateway_config.lab_name }}
|
||||
After=wg-quick@wg-{{ gateway_config.lab_name }}.service
|
||||
Requires=wg-quick@wg-{{ gateway_config.lab_name }}.service
|
||||
|
||||
[Service]
|
||||
Type=oneshot
|
||||
RemainAfterExit=yes
|
||||
ExecStart=/usr/local/bin/setup-vxlan-{{ gateway_config.lab_name }}.sh
|
||||
ExecStop=/sbin/ip link delete vxlan{{ gateway_config.vni }}
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
5
ansible/roles/wireguard/handlers/main.yml
Normal file
5
ansible/roles/wireguard/handlers/main.yml
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
- name: restart wireguard
|
||||
systemd:
|
||||
name: "wg-quick@wg-{{ gateway_config.lab_name }}"
|
||||
state: restarted
|
||||
19
ansible/roles/wireguard/tasks/main.yml
Normal file
19
ansible/roles/wireguard/tasks/main.yml
Normal file
@@ -0,0 +1,19 @@
|
||||
---
|
||||
- name: Create WireGuard config directory
|
||||
file:
|
||||
path: "{{ wireguard_config_dir }}"
|
||||
state: directory
|
||||
mode: '0700'
|
||||
|
||||
- name: Generate WireGuard configuration
|
||||
template:
|
||||
src: wg-interface.conf.j2
|
||||
dest: "{{ wireguard_config_dir }}/wg-{{ gateway_config.lab_name }}.conf"
|
||||
mode: '0600'
|
||||
notify: restart wireguard
|
||||
|
||||
- name: Enable WireGuard interface
|
||||
systemd:
|
||||
name: "wg-quick@wg-{{ gateway_config.lab_name }}"
|
||||
enabled: yes
|
||||
state: started
|
||||
31
ansible/roles/wireguard/templates/wg-interface.conf.j2
Normal file
31
ansible/roles/wireguard/templates/wg-interface.conf.j2
Normal file
@@ -0,0 +1,31 @@
|
||||
[Interface]
|
||||
Address = {{ gateway_config.wg_ip }}/{{ gateway_config.wg_net | ipaddr('prefix') }}
|
||||
ListenPort = {{ gateway_config.wg_listen_port }}
|
||||
PrivateKey = {{ gateway_config.wg_private_key }}
|
||||
|
||||
# Disable default route
|
||||
Table = off
|
||||
|
||||
# Post-up: Add route for WireGuard mesh network only
|
||||
PostUp = ip route add {{ gateway_config.wg_net }} dev wg-{{ gateway_config.lab_name }}
|
||||
|
||||
{% for peer in gateway_config.peers %}
|
||||
[Peer]
|
||||
# {{ peer.to }}
|
||||
PublicKey = {{ peer.public_key }}
|
||||
Endpoint = {{ peer.endpoint }}:{{ gateway_config.wg_listen_port }}
|
||||
AllowedIPs = {{ peer.wg_ip }}/32
|
||||
PersistentKeepalive = {{ wireguard_persistent_keepalive }}
|
||||
|
||||
{% endfor %}
|
||||
|
||||
{% if gateway_config.road_warrior and gateway_config.rw_clients | length > 0 %}
|
||||
# Road Warrior Clients
|
||||
{% for client in gateway_config.rw_clients %}
|
||||
[Peer]
|
||||
# {{ client.email }}
|
||||
PublicKey = {{ client.public_key }}
|
||||
AllowedIPs = {{ client.wg_ip }}/32
|
||||
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
29
ansible/tasks/health-check.yml
Normal file
29
ansible/tasks/health-check.yml
Normal file
@@ -0,0 +1,29 @@
|
||||
---
|
||||
- name: Check WireGuard status (Linux)
|
||||
shell: "wg show wg-{{ gateway_config.lab_name }}"
|
||||
register: wg_status_linux
|
||||
changed_when: false
|
||||
failed_when: false
|
||||
when: gateway_config.type == 'linux'
|
||||
|
||||
- name: Check VXLAN interface (Linux)
|
||||
shell: "ip link show vxlan{{ gateway_config.vni }}"
|
||||
register: vxlan_status
|
||||
changed_when: false
|
||||
failed_when: false
|
||||
when: gateway_config.type == 'linux'
|
||||
|
||||
- name: Check bridge (Linux)
|
||||
shell: "ip link show br-{{ gateway_config.lab_name }}"
|
||||
register: bridge_status
|
||||
changed_when: false
|
||||
failed_when: false
|
||||
when: gateway_config.type == 'linux'
|
||||
|
||||
- name: Set health status
|
||||
set_fact:
|
||||
health_check_results:
|
||||
wireguard: "{{ 'PASS' if wg_status_linux.rc == 0 else 'FAIL' }}"
|
||||
vxlan: "{{ 'PASS' if vxlan_status.rc == 0 else 'FAIL' }}"
|
||||
bridge: "{{ 'PASS' if bridge_status.rc == 0 else 'FAIL' }}"
|
||||
when: gateway_config.type == 'linux'
|
||||
14
ansible/tasks/validate-gateway.yml
Normal file
14
ansible/tasks/validate-gateway.yml
Normal file
@@ -0,0 +1,14 @@
|
||||
---
|
||||
- name: Validate gateway configuration
|
||||
debug:
|
||||
msg: "Validating {{ inventory_hostname }}"
|
||||
|
||||
- name: Check WireGuard peers connectivity
|
||||
shell: "ping -c 3 -W 2 {{ peer.wg_ip }}"
|
||||
loop: "{{ gateway_config.peers }}"
|
||||
loop_control:
|
||||
loop_var: peer
|
||||
register: ping_results
|
||||
failed_when: false
|
||||
changed_when: false
|
||||
when: gateway_config.type == 'linux'
|
||||
Reference in New Issue
Block a user