Initial commit
This commit is contained in:
3
.vscode/settings.json
vendored
Normal file
3
.vscode/settings.json
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"ansible.python.interpreterPath": "/usr/bin/python"
|
||||
}
|
||||
269
GETTING_STARTED.md
Normal file
269
GETTING_STARTED.md
Normal file
@@ -0,0 +1,269 @@
|
||||
# Getting Started with WireGuard Lab Overlay
|
||||
|
||||
## Prerequisites
|
||||
|
||||
### On Management Server
|
||||
|
||||
```bash
|
||||
# Ubuntu/Debian
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y \
|
||||
terraform \
|
||||
ansible \
|
||||
python3-pip \
|
||||
git \
|
||||
docker.io \
|
||||
docker-compose
|
||||
|
||||
# Install Ansible collections
|
||||
ansible-galaxy collection install community.routeros
|
||||
ansible-galaxy collection install ansible.netcommon
|
||||
|
||||
# Install Python packages
|
||||
pip3 install librouteros prometheus-client
|
||||
```
|
||||
|
||||
### On Linux Gateways
|
||||
|
||||
```bash
|
||||
# Ensure WireGuard is available
|
||||
sudo apt-get install -y wireguard wireguard-tools bridge-utils python3
|
||||
```
|
||||
|
||||
### On MikroTik Routers
|
||||
|
||||
- RouterOS 7.6 or higher
|
||||
- API access enabled
|
||||
- Admin credentials
|
||||
|
||||
## Initial Setup
|
||||
|
||||
### Step 1: Configure Your Lab Networks
|
||||
|
||||
1. Copy the example configuration:
|
||||
```bash
|
||||
cd terraform
|
||||
cp terraform.tfvars.example terraform.tfvars
|
||||
```
|
||||
|
||||
2. Edit `terraform.tfvars` with your lab networks:
|
||||
```hcl
|
||||
lab_networks = {
|
||||
lab100 = {
|
||||
vni = 100
|
||||
subnet = "10.100.0.0/24"
|
||||
wireguard_net = "172.16.100.0/24"
|
||||
dhcp_mode = "simple" # or "failover"
|
||||
road_warrior = false # Enable later
|
||||
gateways = [
|
||||
{
|
||||
hostname = "lab100-gw1"
|
||||
type = "linux"
|
||||
mgmt_ip = "192.168.1.11" # Change to your IPs
|
||||
api_port = 22
|
||||
lab_if = "eth1" # Interface facing lab devices
|
||||
dhcp_role = "primary"
|
||||
},
|
||||
{
|
||||
hostname = "lab100-gw2"
|
||||
type = "linux"
|
||||
mgmt_ip = "192.168.1.12"
|
||||
api_port = 22
|
||||
lab_if = "eth1"
|
||||
dhcp_role = "secondary"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Step 2: Set Up Ansible
|
||||
|
||||
1. Configure SSH access to your gateways:
|
||||
```bash
|
||||
ssh-copy-id admin@192.168.1.11
|
||||
ssh-copy-id admin@192.168.1.12
|
||||
```
|
||||
|
||||
2. For MikroTik devices, create vault password:
|
||||
```bash
|
||||
cd ansible
|
||||
echo "your-vault-password" > .vault_pass
|
||||
chmod 600 .vault_pass
|
||||
```
|
||||
|
||||
3. Encrypt MikroTik credentials:
|
||||
```bash
|
||||
ansible-vault create inventory/group_vars/mikrotik_gateways.yml
|
||||
```
|
||||
|
||||
Add:
|
||||
```yaml
|
||||
ansible_password: your-mikrotik-password
|
||||
```
|
||||
|
||||
### Step 3: Deploy
|
||||
|
||||
#### Option A: Automated (Recommended)
|
||||
|
||||
```bash
|
||||
./deploy.sh
|
||||
```
|
||||
|
||||
#### Option B: Manual Steps
|
||||
|
||||
```bash
|
||||
# 1. Initialize and apply Terraform
|
||||
cd terraform
|
||||
terraform init
|
||||
terraform apply
|
||||
terraform output -json > outputs.json
|
||||
|
||||
# 2. Generate Ansible inventory
|
||||
terraform output ansible_inventory > ../ansible/inventory/hosts.yml
|
||||
|
||||
# 3. Deploy to gateways
|
||||
cd ../ansible
|
||||
ansible-playbook -i inventory/hosts.yml playbooks/site.yml
|
||||
|
||||
# 4. Start monitoring
|
||||
cd ../monitoring
|
||||
docker-compose up -d
|
||||
```
|
||||
|
||||
### Step 4: Verify Deployment
|
||||
|
||||
```bash
|
||||
# Check gateway health
|
||||
cd ansible
|
||||
ansible-playbook playbooks/health-check.yml
|
||||
|
||||
# Check WireGuard tunnels
|
||||
ansible linux_gateways -m shell -a "wg show"
|
||||
|
||||
# Check bridges
|
||||
ansible linux_gateways -m shell -a "ip link show type bridge"
|
||||
```
|
||||
|
||||
## Common Tasks
|
||||
|
||||
### Add Road Warrior Access
|
||||
|
||||
1. Edit `terraform/terraform.tfvars`:
|
||||
```hcl
|
||||
lab_networks = {
|
||||
lab100 = {
|
||||
road_warrior = true # Enable this
|
||||
# ...
|
||||
}
|
||||
}
|
||||
|
||||
road_warrior_users = {
|
||||
"jdoe" = {
|
||||
email = "john.doe@company.com"
|
||||
labs = ["lab100"]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
2. Apply changes:
|
||||
```bash
|
||||
cd terraform
|
||||
terraform apply
|
||||
cd ../ansible
|
||||
ansible-playbook playbooks/generate-road-warrior.yml
|
||||
```
|
||||
|
||||
3. Distribute configs:
|
||||
```bash
|
||||
ls ../road-warrior/generated/jdoe/
|
||||
# lab100.conf - WireGuard config file
|
||||
# lab100-qr.html - QR code for mobile devices
|
||||
```
|
||||
|
||||
### Add New Lab Network
|
||||
|
||||
```bash
|
||||
ansible-playbook playbooks/add-lab-network.yml
|
||||
```
|
||||
|
||||
Follow the prompts to configure the new lab.
|
||||
|
||||
### Emergency Shutdown
|
||||
|
||||
```bash
|
||||
ansible-playbook playbooks/emergency-shutdown.yml
|
||||
```
|
||||
|
||||
### Restore Lab
|
||||
|
||||
```bash
|
||||
ansible-playbook playbooks/restore-lab.yml
|
||||
```
|
||||
|
||||
## Monitoring
|
||||
|
||||
Access the monitoring stack:
|
||||
|
||||
- **Grafana**: http://your-server:3000
|
||||
- Username: `admin`
|
||||
- Password: `admin`
|
||||
|
||||
- **Prometheus**: http://your-server:9090
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### WireGuard tunnel not coming up
|
||||
|
||||
```bash
|
||||
# On gateway:
|
||||
sudo wg show
|
||||
sudo journalctl -u wg-quick@wg-lab100 -n 50
|
||||
|
||||
# Check connectivity
|
||||
ping <peer-wireguard-ip>
|
||||
```
|
||||
|
||||
### DHCP not working
|
||||
|
||||
```bash
|
||||
# Check DHCP server
|
||||
sudo systemctl status dnsmasq
|
||||
# or for failover mode:
|
||||
sudo systemctl status isc-dhcp-server
|
||||
|
||||
# Check leases
|
||||
sudo tail -f /var/lib/misc/dnsmasq.lab100.leases
|
||||
```
|
||||
|
||||
### Can't reach lab devices
|
||||
|
||||
1. Check bridge is up:
|
||||
```bash
|
||||
ip link show br-lab100
|
||||
```
|
||||
|
||||
2. Check VXLAN interface:
|
||||
```bash
|
||||
ip -d link show vxlan100
|
||||
```
|
||||
|
||||
3. Check FDB entries:
|
||||
```bash
|
||||
bridge fdb show br br-lab100
|
||||
```
|
||||
|
||||
## Next Steps
|
||||
|
||||
- Read `docs/architecture.md` for detailed architecture
|
||||
- Read `docs/operations.md` for operational procedures
|
||||
- Set up alerting in Grafana
|
||||
- Configure backup for Terraform state
|
||||
- Implement proper secret management (HashiCorp Vault)
|
||||
|
||||
## Support
|
||||
|
||||
For issues:
|
||||
1. Check logs: `journalctl -u wg-quick@wg-lab*`
|
||||
2. Run health check: `ansible-playbook playbooks/health-check.yml`
|
||||
3. Review documentation in `docs/`
|
||||
153
OPENWRT_CHANGELOG.md
Normal file
153
OPENWRT_CHANGELOG.md
Normal file
@@ -0,0 +1,153 @@
|
||||
# OpenWrt Integration - Change Log
|
||||
|
||||
## What's New
|
||||
|
||||
### OpenWrt Gateway Support
|
||||
|
||||
This version adds complete support for OpenWrt routers as lab network gateways, making them fully interoperable with Linux VMs and MikroTik routers.
|
||||
|
||||
### New Ansible Roles
|
||||
|
||||
1. **openwrt-common** - System preparation and package installation
|
||||
2. **openwrt-wireguard** - UCI-based WireGuard configuration
|
||||
3. **openwrt-vxlan** - VXLAN interface setup with FDB management
|
||||
4. **openwrt-bridge** - Bridge configuration via UCI
|
||||
5. **openwrt-dhcp** - dnsmasq DHCP server configuration
|
||||
6. **openwrt-firewall** - Automatic firewall zone creation and isolation
|
||||
7. **openwrt-monitoring** - Lightweight shell-based Prometheus exporter
|
||||
|
||||
### Key Features
|
||||
|
||||
- **UCI Configuration**: All settings managed via OpenWrt's UCI system
|
||||
- **Auto Package Installation**: Automatic installation of required packages:
|
||||
- wireguard-tools
|
||||
- kmod-wireguard
|
||||
- kmod-vxlan
|
||||
- ip-full
|
||||
- tcpdump
|
||||
- **Firewall Isolation**: Each lab network gets isolated firewall zone
|
||||
- **Metrics Export**: Lightweight shell script exporter on port 9586
|
||||
- **Full Mesh Support**: OpenWrt gateways mesh with Linux and MikroTik
|
||||
- **DHCP Modes**: Support for both simple (split range) and failover simulation
|
||||
|
||||
### Architecture Support
|
||||
|
||||
- **x86_64**: PC Engines APU, Protectli Vault, generic x86
|
||||
- **ARM**: GL.iNet, Turris Omnia, Raspberry Pi
|
||||
- **MIPS**: Various TP-Link, Ubiquiti models (device-dependent)
|
||||
|
||||
### Updated Files
|
||||
|
||||
**Terraform:**
|
||||
- `terraform/outputs.tf` - Added openwrt_gateways inventory group
|
||||
- `terraform/terraform.tfvars.example` - Added OpenWrt gateway examples
|
||||
|
||||
**Ansible:**
|
||||
- `ansible/playbooks/site.yml` - Added OpenWrt configuration block
|
||||
- `ansible/inventory/hosts.yml` - Added openwrt_gateways group
|
||||
- `ansible/inventory/group_vars/openwrt_gateways.yml` - OpenWrt-specific vars
|
||||
|
||||
**Documentation:**
|
||||
- `docs/openwrt-setup.md` - Complete OpenWrt setup guide
|
||||
- `docs/gateway-comparison.md` - Comparison of all three gateway types
|
||||
- `README.md` - Updated with OpenWrt references
|
||||
|
||||
### Compatibility
|
||||
|
||||
- **OpenWrt Version**: 23.05 or later (stable releases)
|
||||
- **Minimum Hardware**: 128MB RAM, 128MB flash (256MB+ recommended)
|
||||
- **Tested Platforms**: x86_64, ARM (Cortex-A series)
|
||||
- **Interoperability**: Fully compatible with Linux and MikroTik gateways
|
||||
|
||||
### Usage Example
|
||||
|
||||
```hcl
|
||||
# terraform/terraform.tfvars
|
||||
lab_networks = {
|
||||
lab100 = {
|
||||
vni = 100
|
||||
subnet = "10.100.0.0/24"
|
||||
wireguard_net = "172.16.100.0/24"
|
||||
dhcp_mode = "simple"
|
||||
road_warrior = false
|
||||
gateways = [
|
||||
{
|
||||
hostname = "lab100-linux1"
|
||||
type = "linux"
|
||||
mgmt_ip = "192.168.1.11"
|
||||
api_port = 22
|
||||
lab_if = "eth1"
|
||||
dhcp_role = "primary"
|
||||
},
|
||||
{
|
||||
hostname = "lab100-openwrt1"
|
||||
type = "openwrt" # ← OpenWrt gateway
|
||||
mgmt_ip = "192.168.1.14"
|
||||
api_port = 22
|
||||
lab_if = "eth1" # Adjust to your OpenWrt interface
|
||||
dhcp_role = "secondary"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Deploy:
|
||||
```bash
|
||||
./deploy.sh
|
||||
```
|
||||
|
||||
### Testing Checklist
|
||||
|
||||
After deploying an OpenWrt gateway:
|
||||
|
||||
- [ ] WireGuard tunnel established: `wg show`
|
||||
- [ ] VXLAN interface up: `ip -d link show vxlan100`
|
||||
- [ ] Bridge operational: `bridge link show`
|
||||
- [ ] DHCP serving: `cat /tmp/dhcp.leases`
|
||||
- [ ] Firewall zone created: `uci show firewall | grep lab100`
|
||||
- [ ] Metrics exported: `curl http://<openwrt-ip>:9586/metrics/`
|
||||
- [ ] Can ping peer WireGuard IPs
|
||||
- [ ] Lab devices getting DHCP addresses
|
||||
|
||||
### Known Limitations
|
||||
|
||||
1. **DHCP Failover**: OpenWrt uses simulated failover (split ranges), not true ISC DHCP failover protocol
|
||||
2. **Resource Constraints**: Performance varies significantly based on hardware
|
||||
3. **Package Availability**: Some older/exotic architectures may lack VXLAN support
|
||||
4. **Web UI**: No LuCI integration (CLI/Ansible only)
|
||||
|
||||
### Migration Notes
|
||||
|
||||
If upgrading from a version without OpenWrt support:
|
||||
|
||||
1. Update Terraform: `terraform init -upgrade`
|
||||
2. Run Terraform plan to see new inventory structure
|
||||
3. No changes needed for existing Linux/MikroTik gateways
|
||||
4. Add OpenWrt gateways incrementally
|
||||
|
||||
### Performance Expectations
|
||||
|
||||
| Hardware Class | WireGuard Throughput | Concurrent Devices |
|
||||
|----------------|---------------------|-------------------|
|
||||
| x86_64 (modern) | 500 Mbps - 2 Gbps | 50-100+ |
|
||||
| ARM (Cortex-A) | 100-300 Mbps | 20-50 |
|
||||
| MIPS (consumer) | 50-150 Mbps | 10-20 |
|
||||
|
||||
### Support
|
||||
|
||||
See:
|
||||
- `docs/openwrt-setup.md` for detailed setup instructions
|
||||
- `docs/gateway-comparison.md` for choosing gateway types
|
||||
- `docs/operations.md` for troubleshooting
|
||||
|
||||
## Breaking Changes
|
||||
|
||||
None - fully backward compatible with existing deployments.
|
||||
|
||||
## Next Features (Future)
|
||||
|
||||
- LuCI web UI integration (optional)
|
||||
- Support for OpenWrt snapshot builds
|
||||
- WiFi AP mode integration for wireless lab devices
|
||||
- Advanced QoS policies for lab networks
|
||||
160
README.md
Normal file
160
README.md
Normal file
@@ -0,0 +1,160 @@
|
||||
# WireGuard Lab Overlay Network
|
||||
|
||||
Automated deployment of WireGuard-based overlay networks for lab environments with support for Linux and MikroTik gateways.
|
||||
|
||||
## Features
|
||||
|
||||
- **Multi-Vendor Support**: Linux VMs, MikroTik routers, and OpenWrt devices
|
||||
- **Layer 2 Overlay**: VXLAN over WireGuard for L2 connectivity
|
||||
- **DHCP Options**: ISC DHCP with failover OR dnsmasq with split ranges
|
||||
- **Road Warrior Access**: Built-in support for mobile/laptop clients
|
||||
- **Monitoring**: Prometheus exporters and Grafana dashboards
|
||||
- **Firewall Isolation**: Automatic firewall zones for OpenWrt gateways
|
||||
- **Full Automation**: Terraform + Ansible deployment
|
||||
|
||||
## Architecture
|
||||
|
||||
- **WireGuard**: Encrypted mesh tunnels between gateways
|
||||
- **VXLAN**: Layer 2 extension over WireGuard
|
||||
- **Linux Bridge/MikroTik Bridge**: Local device attachment
|
||||
- **DHCP**: Redundant DHCP servers per lab network
|
||||
- **Monitoring Hub**: Centralized Prometheus + Grafana
|
||||
|
||||
## Quick Start
|
||||
|
||||
### 1. Prerequisites
|
||||
|
||||
```bash
|
||||
# Install required tools
|
||||
apt-get install terraform ansible python3-pip
|
||||
|
||||
# Install Ansible collections
|
||||
ansible-galaxy collection install community.routeros
|
||||
ansible-galaxy collection install ansible.netcommon
|
||||
|
||||
# Install Python dependencies
|
||||
pip3 install librouteros prometheus-client
|
||||
```
|
||||
|
||||
### 2. Configure Lab Networks
|
||||
|
||||
Edit `terraform/terraform.tfvars`:
|
||||
|
||||
```hcl
|
||||
lab_networks = {
|
||||
lab100 = {
|
||||
vni = 100
|
||||
subnet = "10.100.0.0/24"
|
||||
wireguard_net = "172.16.100.0/24"
|
||||
dhcp_mode = "failover"
|
||||
road_warrior = true
|
||||
gateways = [
|
||||
# Define your gateways here
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 3. Deploy
|
||||
|
||||
```bash
|
||||
# Run deployment script
|
||||
./deploy.sh
|
||||
```
|
||||
|
||||
Or manual steps:
|
||||
|
||||
```bash
|
||||
# Initialize Terraform
|
||||
cd terraform
|
||||
terraform init
|
||||
terraform apply
|
||||
terraform output -json > outputs.json
|
||||
|
||||
# Deploy to gateways
|
||||
cd ../ansible
|
||||
ansible-playbook -i inventory/hosts.yml playbooks/site.yml
|
||||
|
||||
# Generate road warrior configs
|
||||
ansible-playbook -i inventory/hosts.yml playbooks/generate-road-warrior.yml
|
||||
|
||||
# Start monitoring
|
||||
cd ../monitoring
|
||||
docker-compose up -d
|
||||
```
|
||||
|
||||
## Operational Playbooks
|
||||
|
||||
### Rolling Update
|
||||
```bash
|
||||
ansible-playbook playbooks/rolling-update.yml
|
||||
```
|
||||
|
||||
### Health Check
|
||||
```bash
|
||||
ansible-playbook playbooks/health-check.yml
|
||||
```
|
||||
|
||||
### Emergency Shutdown
|
||||
```bash
|
||||
ansible-playbook playbooks/emergency-shutdown.yml
|
||||
```
|
||||
|
||||
### Restore Lab
|
||||
```bash
|
||||
ansible-playbook playbooks/restore-lab.yml
|
||||
```
|
||||
|
||||
### Add Gateway
|
||||
```bash
|
||||
ansible-playbook playbooks/add-gateway.yml
|
||||
```
|
||||
|
||||
### Remove Gateway
|
||||
```bash
|
||||
ansible-playbook playbooks/remove-gateway.yml
|
||||
```
|
||||
|
||||
## Directory Structure
|
||||
|
||||
```
|
||||
wireguard-lab-overlay/
|
||||
├── terraform/ # Infrastructure state
|
||||
├── ansible/ # Configuration management
|
||||
│ ├── roles/ # Ansible roles (Linux & MikroTik)
|
||||
│ ├── playbooks/ # Operational playbooks
|
||||
│ └── inventory/ # Inventory and variables
|
||||
├── monitoring/ # Monitoring stack
|
||||
│ ├── prometheus/
|
||||
│ ├── grafana/
|
||||
│ └── docker-compose.yml
|
||||
├── road-warrior/ # Client configurations
|
||||
└── docs/ # Documentation
|
||||
```
|
||||
|
||||
## Monitoring
|
||||
|
||||
Access monitoring:
|
||||
- Grafana: http://monitoring-hub:3000 (admin/admin)
|
||||
- Prometheus: http://monitoring-hub:9090
|
||||
|
||||
## Security Notes
|
||||
|
||||
- WireGuard private keys are stored in Terraform state (use remote backend with encryption)
|
||||
- Consider using HashiCorp Vault or AWS Secrets Manager for production
|
||||
- Ansible Vault is used for MikroTik credentials
|
||||
- Road warrior configs should be distributed securely
|
||||
|
||||
## Support
|
||||
|
||||
For issues and questions, refer to:
|
||||
- `docs/architecture.md` - Detailed architecture
|
||||
- `docs/deployment.md` - Deployment guide
|
||||
- `docs/operations.md` - Operations manual
|
||||
- `docs/mikrotik-setup.md` - MikroTik specific guide
|
||||
- `docs/openwrt-setup.md` - OpenWrt specific guide
|
||||
- `docs/gateway-comparison.md` - Gateway type comparison
|
||||
|
||||
## License
|
||||
|
||||
Internal use only - Company proprietary
|
||||
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'
|
||||
39
create_all_files.sh
Executable file
39
create_all_files.sh
Executable file
@@ -0,0 +1,39 @@
|
||||
#!/bin/bash
|
||||
# Script to create all remaining files
|
||||
|
||||
BASE_DIR="/home/claude/wireguard-lab-overlay"
|
||||
|
||||
# Create all operational playbooks
|
||||
cat > "$BASE_DIR/ansible/playbooks/site.yml" << 'EOF'
|
||||
---
|
||||
- name: Deploy WireGuard Lab Overlay Network
|
||||
hosts: lab_gateways
|
||||
become: 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] }}"
|
||||
|
||||
roles:
|
||||
- common
|
||||
- wireguard
|
||||
- vxlan
|
||||
- bridge
|
||||
- dnsmasq
|
||||
- monitoring
|
||||
|
||||
- name: Configure Monitoring Hub
|
||||
hosts: monitoring_hub
|
||||
become: yes
|
||||
roles:
|
||||
- monitoring_hub
|
||||
EOF
|
||||
|
||||
# Continue with more files...
|
||||
45
deploy.sh
Executable file
45
deploy.sh
Executable file
@@ -0,0 +1,45 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
cd "$SCRIPT_DIR"
|
||||
|
||||
echo "=== WireGuard Lab Overlay Network Deployment ==="
|
||||
|
||||
# Step 1: Terraform
|
||||
echo ""
|
||||
echo "[1/5] Running Terraform..."
|
||||
cd terraform
|
||||
terraform init
|
||||
terraform plan -out=tfplan
|
||||
terraform apply tfplan
|
||||
terraform output -json > outputs.json
|
||||
cd ..
|
||||
|
||||
# Step 2: Ansible inventory generation
|
||||
echo ""
|
||||
echo "[2/5] Generating Ansible inventory..."
|
||||
cd ansible
|
||||
terraform -chdir=../terraform output ansible_inventory > inventory/hosts.yml
|
||||
|
||||
# Step 3: Deploy to gateways
|
||||
echo ""
|
||||
echo "[3/5] Deploying to gateways..."
|
||||
ansible-playbook -i inventory/hosts.yml playbooks/site.yml
|
||||
|
||||
# Step 4: Generate road warrior configs
|
||||
echo ""
|
||||
echo "[4/5] Generating road warrior configurations..."
|
||||
ansible-playbook -i inventory/hosts.yml playbooks/generate-road-warrior.yml
|
||||
|
||||
# Step 5: Deploy monitoring hub
|
||||
echo ""
|
||||
echo "[5/5] Deploying monitoring hub..."
|
||||
cd ../monitoring
|
||||
docker-compose up -d
|
||||
|
||||
echo ""
|
||||
echo "=== Deployment Complete ==="
|
||||
echo "Grafana: http://$(hostname -I | awk '{print $1}'):3000 (admin/admin)"
|
||||
echo "Prometheus: http://$(hostname -I | awk '{print $1}'):9090"
|
||||
echo "Road warrior configs: $SCRIPT_DIR/road-warrior/generated/"
|
||||
43
docs/architecture.md
Normal file
43
docs/architecture.md
Normal file
@@ -0,0 +1,43 @@
|
||||
# WireGuard Lab Overlay Network Architecture
|
||||
|
||||
## Overview
|
||||
|
||||
This solution provides automated deployment of isolated Layer 2 networks across corporate infrastructure using WireGuard and VXLAN.
|
||||
|
||||
## Components
|
||||
|
||||
### WireGuard Mesh
|
||||
- Full mesh topology between gateways within each lab network
|
||||
- Encrypted tunnels over corporate network
|
||||
- No routing of lab traffic through corporate infrastructure
|
||||
|
||||
### VXLAN Overlay
|
||||
- Provides Layer 2 connectivity over WireGuard Layer 3 tunnels
|
||||
- BUM traffic replication to all peers
|
||||
- Support for up to 10 gateways per lab
|
||||
|
||||
### Gateway Types
|
||||
- **Linux VMs**: Full-featured with ISC DHCP failover support
|
||||
- **MikroTik Routers**: Managed via RouterOS API
|
||||
|
||||
### DHCP Modes
|
||||
- **Failover**: ISC DHCP with active-passive failover (Linux only)
|
||||
- **Simple**: dnsmasq with split address ranges
|
||||
|
||||
### Monitoring
|
||||
- Prometheus exporters on each gateway
|
||||
- Centralized Grafana dashboards
|
||||
- Metrics: tunnel status, bandwidth, DHCP leases, bridge stats
|
||||
|
||||
## Network Flow
|
||||
|
||||
```
|
||||
Lab Device → Linux Bridge/MikroTik Bridge → VXLAN → WireGuard → Corporate Network → WireGuard → VXLAN → Bridge → Lab Device
|
||||
```
|
||||
|
||||
## Security
|
||||
|
||||
- All lab traffic encrypted via WireGuard
|
||||
- Complete isolation from corporate network
|
||||
- Road warrior clients get direct lab access
|
||||
- No inter-lab routing
|
||||
405
docs/design-discussion.md
Normal file
405
docs/design-discussion.md
Normal file
@@ -0,0 +1,405 @@
|
||||
# Design Discussion and Requirements
|
||||
|
||||
This document captures the key design decisions, requirements, and architectural discussions that shaped the WireGuard Lab Overlay Network solution.
|
||||
|
||||
## Initial Requirements
|
||||
|
||||
### Core Objective
|
||||
Automated deployment of a WireGuard-based overlay network that connects local lab room components at Layer 2 across the company network, without exposing lab devices to the corporate network.
|
||||
|
||||
### Key Requirements
|
||||
1. **Layer 2 Connectivity**: Lab devices plugged into gateway routers/switches should communicate at L2 across the company network
|
||||
2. **Network Isolation**: Lab traffic must NOT have connectivity to company network
|
||||
3. **Gateway Flexibility**: Support Linux VMs, MikroTik routers, and OpenWrt devices
|
||||
4. **Scalability**: Up to 10 gateways per lab network, 10-20 independent lab networks
|
||||
5. **Automation**: Ansible or Terraform for automated deployment
|
||||
6. **Ease of Deployment**: Solution should be easily deployable
|
||||
7. **Future Requirements**:
|
||||
- Microsegmentation capability
|
||||
- Road warrior access for office notebooks
|
||||
- Central management
|
||||
- Central monitoring/visibility
|
||||
|
||||
### Lab Network Specifications
|
||||
- **Max gateways per lab**: 10
|
||||
- **Total lab networks**: 10-20 independent networks
|
||||
- **DHCP**: Lab devices use DHCP, each lab has own IP range
|
||||
- **Gateway types**: Linux VMs (primary), MikroTik routers, OpenWrt devices
|
||||
- **Multiple NICs**: Gateways have multiple network interfaces
|
||||
- **No VM provisioning**: Configure existing VMs/routers only
|
||||
|
||||
## Architecture Decisions
|
||||
|
||||
### 1. WireGuard + VXLAN Approach
|
||||
|
||||
**Decision**: Use VXLAN over WireGuard instead of direct WireGuard bridging.
|
||||
|
||||
**Rationale**:
|
||||
- WireGuard is L3-only natively
|
||||
- VXLAN provides true L2 extension with broadcast/multicast support
|
||||
- Clean separation of concerns:
|
||||
- WireGuard = Encrypted L3 transport mesh
|
||||
- VXLAN = L2 overlay with BUM traffic handling
|
||||
- Bridge = Local device attachment
|
||||
|
||||
**Alternative Considered**: Direct bridging of WireGuard interfaces
|
||||
- **Rejected because**: More complex, less clean architecture, harder to troubleshoot
|
||||
|
||||
### 2. Full Mesh Topology
|
||||
|
||||
**Decision**: All gateways within a lab network form a full WireGuard mesh.
|
||||
|
||||
**Rationale**:
|
||||
- No single point of failure
|
||||
- Optimal path between any two gateways
|
||||
- Scales well up to 10 gateways (45 tunnels max)
|
||||
- Simpler than hub-and-spoke for small deployments
|
||||
|
||||
**Calculation**: n gateways = n×(n-1)/2 tunnels
|
||||
- 3 gateways = 3 tunnels
|
||||
- 5 gateways = 10 tunnels
|
||||
- 10 gateways = 45 tunnels (acceptable)
|
||||
|
||||
**Alternative Considered**: Hub-and-spoke with central gateway
|
||||
- **Rejected because**: Single point of failure, suboptimal routing, more complex failover
|
||||
|
||||
### 3. Separate WireGuard Interface per Lab
|
||||
|
||||
**Decision**: Each lab network gets its own WireGuard interface (wg-lab100, wg-lab200, etc.)
|
||||
|
||||
**Rationale**:
|
||||
- Complete isolation between lab networks
|
||||
- Easier troubleshooting (can bring down one lab without affecting others)
|
||||
- Simplified microsegmentation in future
|
||||
- Clear network boundaries
|
||||
|
||||
**Alternative Considered**: Single WireGuard interface with policy routing
|
||||
- **Rejected because**: Complex routing, harder to isolate, debugging nightmare
|
||||
|
||||
### 4. Terraform for State, Ansible for Configuration
|
||||
|
||||
**Decision**: Use Terraform for topology/state management, Ansible for device configuration.
|
||||
|
||||
**Rationale**:
|
||||
|
||||
**Terraform handles:**
|
||||
- IP address allocation (deterministic, conflict-free)
|
||||
- WireGuard key generation and tracking
|
||||
- Peer matrix calculation (combinatorial complexity)
|
||||
- Road warrior client generation
|
||||
- State persistence
|
||||
|
||||
**Ansible handles:**
|
||||
- Platform-specific configuration (UCI vs systemd vs RouterOS)
|
||||
- Service deployment and management
|
||||
- Operational tasks (updates, health checks, emergency procedures)
|
||||
|
||||
**Why not pure Ansible?**
|
||||
- Peer matrix calculation becomes complex Jinja2 templates
|
||||
- No built-in state for keys/IPs
|
||||
- Key generation requires custom modules
|
||||
- Change detection harder without state
|
||||
|
||||
**Why not pure Terraform?**
|
||||
- Terraform providers for network devices are limited
|
||||
- Configuration management is Ansible's strength
|
||||
- Operational playbooks natural in Ansible
|
||||
|
||||
### 5. DHCP Strategy
|
||||
|
||||
**Decision**: Offer two modes - ISC DHCP failover (Linux only) and dnsmasq split-range (all platforms)
|
||||
|
||||
**Rationale**:
|
||||
|
||||
**ISC DHCP Failover Mode:**
|
||||
- True active-passive DHCP failover
|
||||
- Lease synchronization
|
||||
- IP address continuity across failovers
|
||||
- Linux-only (not available on MikroTik/OpenWrt)
|
||||
|
||||
**dnsmasq Split-Range Mode:**
|
||||
- Works on all three platforms
|
||||
- Simpler configuration
|
||||
- Primary serves 10-127, Secondary serves 128-245
|
||||
- Good enough for most lab scenarios
|
||||
- No lease synchronization but acceptable for labs
|
||||
|
||||
**Why both?**
|
||||
- Flexibility for different requirements
|
||||
- Some labs need true failover (production-like testing)
|
||||
- Other labs can use simpler approach
|
||||
|
||||
### 6. Multi-Vendor Support
|
||||
|
||||
**Decision**: Support Linux, MikroTik, and OpenWrt as first-class citizens.
|
||||
|
||||
**Rationale**:
|
||||
|
||||
**Linux (VMs):**
|
||||
- Most flexible and powerful
|
||||
- Best for primary/critical gateways
|
||||
- Full feature support (ISC DHCP failover)
|
||||
- Extensive monitoring capabilities
|
||||
|
||||
**MikroTik:**
|
||||
- Purpose-built routing hardware
|
||||
- Cost-effective
|
||||
- Low power consumption
|
||||
- Common in network labs
|
||||
- Proven reliability
|
||||
|
||||
**OpenWrt:**
|
||||
- Wide hardware support (x86, ARM, MIPS)
|
||||
- Repurposing existing hardware
|
||||
- Open source requirement for some environments
|
||||
- Budget-friendly option
|
||||
- Can integrate WiFi AP functionality
|
||||
|
||||
**Implementation Strategy:**
|
||||
- Common interface across all gateway types
|
||||
- Role-based Ansible deployment
|
||||
- Gateway type determines which roles execute
|
||||
- Full interoperability (mix types in same lab)
|
||||
|
||||
### 7. Road Warrior Support
|
||||
|
||||
**Decision**: Build road warrior support into core architecture from start.
|
||||
|
||||
**Rationale**:
|
||||
- Common requirement (laptops accessing lab devices)
|
||||
- Easier to design in from start than retrofit
|
||||
- IP allocation strategy accounts for it
|
||||
- WireGuard well-suited for road warrior use
|
||||
|
||||
**Implementation**:
|
||||
- Gateways allow road warrior clients as peers
|
||||
- Clients assigned IPs from WireGuard subnet (after gateway IPs)
|
||||
- Configuration generation automated via Ansible
|
||||
- QR codes for mobile devices
|
||||
|
||||
### 8. Monitoring Architecture
|
||||
|
||||
**Decision**: Prometheus exporters on each gateway, centralized Grafana.
|
||||
|
||||
**Rationale**:
|
||||
- Industry-standard monitoring stack
|
||||
- Pull-based metrics collection
|
||||
- Platform-specific exporters:
|
||||
- Linux: Python Prometheus client
|
||||
- MikroTik: REST API polling
|
||||
- OpenWrt: Shell script exporter
|
||||
|
||||
**Metrics Collected**:
|
||||
- WireGuard tunnel status (up/down)
|
||||
- Latest handshake timing
|
||||
- Rx/Tx bytes per tunnel
|
||||
- VXLAN interface statistics
|
||||
- Bridge FDB entry count
|
||||
- DHCP active lease count
|
||||
|
||||
### 9. Firewall Isolation (OpenWrt-specific)
|
||||
|
||||
**Decision**: Automatically create isolated firewall zones for each lab network on OpenWrt.
|
||||
|
||||
**Rationale**:
|
||||
- OpenWrt requires explicit firewall configuration
|
||||
- Each lab needs its own zone
|
||||
- Prevent lab-to-corporate traffic
|
||||
- Allow intra-lab forwarding
|
||||
- Allow DHCP/DNS to gateway
|
||||
|
||||
**Firewall Rules Created**:
|
||||
```
|
||||
Zone: lab100
|
||||
Input: REJECT (except DNS/DHCP)
|
||||
Forward: REJECT (except to same zone)
|
||||
Output: ACCEPT
|
||||
|
||||
Forwarding:
|
||||
lab100 → lab100: ACCEPT
|
||||
lab100 → lan: REJECT
|
||||
lab100 → wan: REJECT
|
||||
```
|
||||
|
||||
### 10. Configuration Decoupling
|
||||
|
||||
**Decision**: Make deployment independent of existing infrastructure.
|
||||
|
||||
**Rationale**:
|
||||
- Don't assume pre-existing Ansible inventory
|
||||
- Don't depend on existing Terraform state
|
||||
- Self-contained solution
|
||||
- Easy to integrate into existing automation later
|
||||
|
||||
**Implementation**:
|
||||
- Fresh Terraform state
|
||||
- Self-generated Ansible inventory
|
||||
- Standalone playbooks
|
||||
- Can be integrated into larger automation framework if needed
|
||||
|
||||
## OpenWrt Integration Discussion
|
||||
|
||||
### Requirements for OpenWrt Support
|
||||
|
||||
When OpenWrt support was requested, the following questions were clarified:
|
||||
|
||||
**1. OpenWrt Version**: 23.05 or latest stable
|
||||
**2. Architectures**: x86 (Intel), ARM, potentially MIPS
|
||||
**3. Router Independence**: Should work regardless of specific router model
|
||||
**4. Management**: SSH-based first, API if beneficial
|
||||
**5. Package Management**: Auto-install missing packages
|
||||
**6. DHCP Strategy**: Pros/cons analysis requested → chose dnsmasq
|
||||
**7. Network Config**: UCI-based configuration
|
||||
**8. Firewall**: Create new zones per lab network
|
||||
**9. Monitoring**: Lightweight shell script tools
|
||||
**10. Interoperability**: Must work with Linux/MikroTik
|
||||
**11. Ansible Modules**: Best option selected (shell + UCI commands)
|
||||
**12. Storage**: Not a constraint (appropriate hardware selection)
|
||||
|
||||
### OpenWrt Design Decisions
|
||||
|
||||
**UCI Configuration Method**:
|
||||
- Native OpenWrt approach
|
||||
- Atomic commits
|
||||
- Rollback capability
|
||||
- CLI-friendly
|
||||
|
||||
**Package Installation Strategy**:
|
||||
```
|
||||
Required packages:
|
||||
- wireguard-tools (WireGuard CLI)
|
||||
- kmod-wireguard (kernel module)
|
||||
- kmod-vxlan (VXLAN support)
|
||||
- ip-full (iproute2 with VXLAN)
|
||||
- tcpdump (troubleshooting)
|
||||
```
|
||||
|
||||
**Firewall Zone Isolation**:
|
||||
- Each lab gets isolated zone
|
||||
- Prevents lab → corporate traffic
|
||||
- Allows lab → lab forwarding
|
||||
- Gateway provides DNS/DHCP
|
||||
|
||||
**Monitoring Approach**:
|
||||
- Shell script Prometheus exporter
|
||||
- Lightweight (no Python needed)
|
||||
- Served via uhttpd on port 9586
|
||||
- Collects essential metrics
|
||||
|
||||
## Rejected Alternatives
|
||||
|
||||
### 1. GRE/IPIP Instead of VXLAN
|
||||
**Rejected**: Less flexible, no native L2 support, older technology
|
||||
|
||||
### 2. OpenVPN Instead of WireGuard
|
||||
**Rejected**: Slower, more complex, heavier resource usage, older codebase
|
||||
|
||||
### 3. Single Gateway with VLANs
|
||||
**Rejected**: Single point of failure, doesn't meet redundancy requirements
|
||||
|
||||
### 4. Cloud-Managed Solution (Tailscale/ZeroTier)
|
||||
**Rejected**: External dependency, cost, limited control, privacy concerns
|
||||
|
||||
### 5. Pure iptables/nftables Firewall on Linux
|
||||
**Rejected for OpenWrt**: UCI firewall is native and more maintainable
|
||||
|
||||
### 6. LuCI Web UI Integration
|
||||
**Deferred**: CLI/Ansible-first approach, could add later as enhancement
|
||||
|
||||
## Known Limitations and Trade-offs
|
||||
|
||||
### 1. DHCP Failover on Non-Linux Platforms
|
||||
**Limitation**: True DHCP failover only on Linux (ISC DHCP)
|
||||
**Trade-off**: MikroTik/OpenWrt use split ranges (acceptable for labs)
|
||||
**Mitigation**: Can use Linux for critical labs requiring true failover
|
||||
|
||||
### 2. VXLAN Performance Overhead
|
||||
**Limitation**: Double encapsulation (VXLAN + WireGuard) adds overhead
|
||||
**Trade-off**: ~5-10% throughput reduction vs single encapsulation
|
||||
**Mitigation**: Modern CPUs handle this well, acceptable for lab use
|
||||
|
||||
### 3. Broadcast Traffic Amplification
|
||||
**Limitation**: BUM traffic replicated to all peers in mesh
|
||||
**Trade-off**: With 10 gateways, broadcast amplified 10x
|
||||
**Mitigation**: 10 gateways max limit, modern switches handle this
|
||||
|
||||
### 4. WireGuard Key Rotation
|
||||
**Limitation**: No automatic key rotation implemented
|
||||
**Trade-off**: Manual process to regenerate and redistribute keys
|
||||
**Mitigation**: Keys stored in Terraform state, can be regenerated and redeployed
|
||||
|
||||
### 5. No Inter-Lab Routing
|
||||
**Limitation**: Labs are completely isolated from each other
|
||||
**Trade-off**: Can't route between lab100 and lab200
|
||||
**Mitigation**: Design requirement (isolation), can add selective routing later
|
||||
|
||||
### 6. Road Warrior Scalability
|
||||
**Limitation**: Each gateway needs peer config for each RW client
|
||||
**Trade-off**: 100 RW clients = 100 peer configs per gateway
|
||||
**Mitigation**: Acceptable for expected scale, can optimize if needed
|
||||
|
||||
## Future Enhancements Considered
|
||||
|
||||
### Near-Term (Feasible)
|
||||
1. **Automated key rotation** - Periodic WireGuard key regeneration
|
||||
2. **Dynamic peer discovery** - Reduce configuration for large meshes
|
||||
3. **QoS policies** - Traffic prioritization for lab networks
|
||||
4. **WiFi AP mode** - OpenWrt as wireless access point for labs
|
||||
5. **LuCI integration** - Web UI for OpenWrt management
|
||||
|
||||
### Medium-Term (Requires Development)
|
||||
1. **Microsegmentation** - Finer-grained network isolation
|
||||
2. **Inter-lab routing** - Selective routing between specific labs
|
||||
3. **IPAM integration** - NetBox/Nautobot for IP management
|
||||
4. **Centralized logging** - Log aggregation from all gateways
|
||||
5. **Alerting rules** - Prometheus alerts for tunnel failures
|
||||
|
||||
### Long-Term (Architectural Changes)
|
||||
1. **BGP-based routing** - For very large deployments
|
||||
2. **Multi-site support** - Gateways across multiple datacenters
|
||||
3. **IPv6 support** - Dual-stack overlay networks
|
||||
4. **Hardware offload** - WireGuard hardware acceleration
|
||||
|
||||
## Design Principles Applied
|
||||
|
||||
Throughout the design process, we adhered to these principles:
|
||||
|
||||
1. **Simplicity over complexity** - Use proven technologies, avoid over-engineering
|
||||
2. **Automation first** - Everything deployable via code
|
||||
3. **Platform agnostic** - Support multiple gateway types equally
|
||||
4. **Fail-safe defaults** - Network isolation by default, explicit allowlisting
|
||||
5. **Observability** - Built-in monitoring from day one
|
||||
6. **Operational friendly** - Include operational playbooks, not just deployment
|
||||
7. **Documentation** - Comprehensive guides for all platforms
|
||||
8. **Backward compatible** - New features don't break existing deployments
|
||||
|
||||
## Technical Debt and Known Issues
|
||||
|
||||
### Acknowledged but Accepted
|
||||
1. **Terraform state security**: Keys stored in state (recommend remote backend with encryption)
|
||||
2. **No automated backups**: Operational playbooks include manual backup procedures
|
||||
3. **Hard-coded ports**: WireGuard listen port = 51820 + VNI (could be configurable)
|
||||
4. **No certificate-based auth**: Relies on SSH keys (acceptable for lab environments)
|
||||
|
||||
### To Be Addressed
|
||||
1. **Error handling**: Some playbooks could have more robust error handling
|
||||
2. **Idempotency**: A few tasks could be more idempotent
|
||||
3. **Testing**: No automated integration tests (manual testing only)
|
||||
4. **Rollback**: No automated rollback on failed deployments
|
||||
|
||||
## Conclusion
|
||||
|
||||
This design represents a pragmatic balance between:
|
||||
- **Flexibility** (multiple platforms, multiple DHCP modes, road warrior support)
|
||||
- **Simplicity** (standard technologies, clear architecture)
|
||||
- **Automation** (minimal manual intervention)
|
||||
- **Reliability** (full mesh, redundant DHCP, monitoring)
|
||||
- **Maintainability** (good documentation, operational playbooks)
|
||||
|
||||
The solution meets all initial requirements and provides a foundation for future enhancements while remaining production-ready today.
|
||||
|
||||
---
|
||||
|
||||
**Document Version**: 1.0
|
||||
**Last Updated**: 2025-01-30
|
||||
**Maintained By**: Network Architecture Team
|
||||
278
docs/gateway-comparison.md
Normal file
278
docs/gateway-comparison.md
Normal file
@@ -0,0 +1,278 @@
|
||||
# Gateway Type Comparison
|
||||
|
||||
## Overview
|
||||
|
||||
The WireGuard Lab Overlay solution supports three gateway types: **Linux**, **MikroTik RouterOS**, and **OpenWrt**. All types are fully interoperable and can be mixed within the same lab network.
|
||||
|
||||
## Feature Comparison
|
||||
|
||||
| Feature | Linux | MikroTik | OpenWrt |
|
||||
|---------|-------|----------|---------|
|
||||
| **WireGuard Support** | ✅ Native | ✅ Native (RouterOS 7+) | ✅ Native |
|
||||
| **VXLAN Support** | ✅ Native | ✅ Native | ✅ via kmod-vxlan |
|
||||
| **DHCP Failover** | ✅ ISC DHCP | ⚠️ Simulated | ⚠️ Simulated |
|
||||
| **DHCP Simple/Split** | ✅ dnsmasq | ✅ Native DHCP | ✅ dnsmasq |
|
||||
| **Firewall Isolation** | ✅ iptables/nftables | ✅ Native firewall | ✅ UCI firewall |
|
||||
| **Monitoring** | ✅ Python exporter | ✅ API/SNMP | ✅ Shell script |
|
||||
| **Ansible Management** | ✅ Excellent | ✅ Good | ✅ Excellent |
|
||||
| **Resource Usage** | Medium | Low | Low |
|
||||
| **Configuration Method** | systemd/files | RouterOS CLI/API | UCI |
|
||||
|
||||
## When to Use Each Type
|
||||
|
||||
### Linux (VM or Physical)
|
||||
|
||||
**Best for:**
|
||||
- ✅ Primary/critical gateways requiring true DHCP failover
|
||||
- ✅ High-throughput requirements (10G+)
|
||||
- ✅ Complex routing/NAT scenarios
|
||||
- ✅ Advanced monitoring and logging
|
||||
- ✅ Situations requiring custom software
|
||||
|
||||
**Advantages:**
|
||||
- Most flexible and powerful
|
||||
- Best monitoring capabilities
|
||||
- True DHCP failover support
|
||||
- Easy troubleshooting with standard Linux tools
|
||||
- Can run additional services
|
||||
|
||||
**Disadvantages:**
|
||||
- Requires more resources (RAM, storage)
|
||||
- VM overhead if virtualized
|
||||
- More complex to secure
|
||||
|
||||
**Typical Specs:**
|
||||
- vCPU: 2+
|
||||
- RAM: 2GB+
|
||||
- Storage: 20GB+
|
||||
|
||||
### MikroTik RouterOS
|
||||
|
||||
**Best for:**
|
||||
- ✅ Branch/remote site gateways
|
||||
- ✅ Cost-effective hardware deployment
|
||||
- ✅ Existing MikroTik infrastructure
|
||||
- ✅ Environments with limited power/cooling
|
||||
- ✅ Sites requiring hardware redundancy
|
||||
|
||||
**Advantages:**
|
||||
- Purpose-built routing hardware
|
||||
- Low power consumption
|
||||
- Excellent price/performance
|
||||
- Built-in redundancy features (RouterOS version dependent)
|
||||
- GUI management option (WinBox/WebFig)
|
||||
|
||||
**Disadvantages:**
|
||||
- Proprietary OS
|
||||
- Learning curve for RouterOS
|
||||
- Limited customization
|
||||
- Monitoring via API (not as rich as Linux)
|
||||
|
||||
**Typical Models:**
|
||||
- CCR series (enterprise)
|
||||
- RB series (SOHO/branch)
|
||||
- Minimum: RB750Gr3 or better
|
||||
|
||||
### OpenWrt
|
||||
|
||||
**Best for:**
|
||||
- ✅ Lab edge/access gateways
|
||||
- ✅ Repurposing existing hardware
|
||||
- ✅ WiFi-enabled access points for labs
|
||||
- ✅ Budget-conscious deployments
|
||||
- ✅ Learning/testing environments
|
||||
|
||||
**Advantages:**
|
||||
- Runs on wide variety of hardware (x86, ARM, MIPS)
|
||||
- Open source and highly customizable
|
||||
- Excellent package ecosystem
|
||||
- Good community support
|
||||
- Can combine routing + WiFi AP functions
|
||||
|
||||
**Disadvantages:**
|
||||
- Varies by hardware (performance, stability)
|
||||
- Flash/RAM limitations on some devices
|
||||
- Web UI less polished than RouterOS
|
||||
- Package dependencies can be complex
|
||||
|
||||
**Typical Hardware:**
|
||||
- x86_64: PC Engines APU, Protectli Vault
|
||||
- ARM: GL.iNet, Turris Omnia
|
||||
- Purpose-built: TP-Link, Ubiquiti (compatible models)
|
||||
|
||||
## Mixed Deployments
|
||||
|
||||
You can mix gateway types within a single lab network. Here's a recommended pattern:
|
||||
|
||||
### Pattern 1: High Availability Core
|
||||
```hcl
|
||||
lab100 = {
|
||||
gateways = [
|
||||
{ hostname = "lab100-linux1", type = "linux", dhcp_role = "primary" },
|
||||
{ hostname = "lab100-linux2", type = "linux", dhcp_role = "secondary" },
|
||||
{ hostname = "lab100-openwrt1", type = "openwrt", dhcp_role = "backup" }
|
||||
]
|
||||
}
|
||||
```
|
||||
- Linux gateways provide true DHCP failover
|
||||
- OpenWrt provides additional redundancy
|
||||
|
||||
### Pattern 2: Cost-Optimized
|
||||
```hcl
|
||||
lab200 = {
|
||||
gateways = [
|
||||
{ hostname = "lab200-mikrotik1", type = "mikrotik", dhcp_role = "primary" },
|
||||
{ hostname = "lab200-mikrotik2", type = "mikrotik", dhcp_role = "secondary" }
|
||||
]
|
||||
}
|
||||
```
|
||||
- All MikroTik for consistency
|
||||
- Lower hardware costs
|
||||
- Lower power consumption
|
||||
|
||||
### Pattern 3: Hybrid
|
||||
```hcl
|
||||
lab300 = {
|
||||
gateways = [
|
||||
{ hostname = "lab300-linux1", type = "linux", dhcp_role = "primary" },
|
||||
{ hostname = "lab300-mikrotik1", type = "mikrotik", dhcp_role = "backup" },
|
||||
{ hostname = "lab300-openwrt1", type = "openwrt", dhcp_role = "backup" }
|
||||
]
|
||||
}
|
||||
```
|
||||
- Linux core for features
|
||||
- MikroTik for proven reliability
|
||||
- OpenWrt for flexibility
|
||||
|
||||
## DHCP Mode Recommendations
|
||||
|
||||
### ISC DHCP Failover Mode (`dhcp_mode = "failover"`)
|
||||
|
||||
**Use when:**
|
||||
- Critical lab requiring lease continuity
|
||||
- Active-passive DHCP failover needed
|
||||
- Only Linux gateways (primary + secondary)
|
||||
|
||||
**Configuration:**
|
||||
```hcl
|
||||
dhcp_mode = "failover"
|
||||
gateways = [
|
||||
{ type = "linux", dhcp_role = "primary" },
|
||||
{ type = "linux", dhcp_role = "secondary" },
|
||||
{ type = "...", dhcp_role = "backup" } # Any type for additional backup
|
||||
]
|
||||
```
|
||||
|
||||
### Simple/Split Range Mode (`dhcp_mode = "simple"`)
|
||||
|
||||
**Use when:**
|
||||
- Any gateway types in use
|
||||
- DHCP IP address persistence not critical
|
||||
- Simpler configuration preferred
|
||||
|
||||
**Configuration:**
|
||||
```hcl
|
||||
dhcp_mode = "simple"
|
||||
gateways = [
|
||||
{ type = "...", dhcp_role = "primary" }, # Serves .10-.127
|
||||
{ type = "...", dhcp_role = "secondary" }, # Serves .128-.245
|
||||
{ type = "...", dhcp_role = "backup" } # Serves full range (standby)
|
||||
]
|
||||
```
|
||||
|
||||
## Performance Expectations
|
||||
|
||||
### WireGuard Throughput
|
||||
|
||||
| Gateway Type | Expected Throughput | Notes |
|
||||
|--------------|---------------------|-------|
|
||||
| Linux (modern CPU) | 2-10 Gbps | Depends on CPU/NICs |
|
||||
| MikroTik CCR | 1-5 Gbps | Varies by model |
|
||||
| MikroTik RB | 100-500 Mbps | Consumer models |
|
||||
| OpenWrt x86_64 | 500 Mbps - 2 Gbps | Hardware dependent |
|
||||
| OpenWrt ARM | 50-300 Mbps | Limited by CPU |
|
||||
|
||||
### Recommendations by Lab Size
|
||||
|
||||
| Lab Devices | Recommended Primary | Recommended Secondary |
|
||||
|-------------|---------------------|----------------------|
|
||||
| 1-10 | OpenWrt | OpenWrt |
|
||||
| 10-50 | MikroTik or Linux | MikroTik or OpenWrt |
|
||||
| 50-100 | Linux | Linux or MikroTik |
|
||||
| 100+ | Linux (multi-core) | Linux |
|
||||
|
||||
## Configuration Management
|
||||
|
||||
### Ansible Roles Per Type
|
||||
|
||||
| Task | Linux Role | MikroTik Role | OpenWrt Role |
|
||||
|------|-----------|---------------|--------------|
|
||||
| Common Setup | `common` | `mikrotik-common` | `openwrt-common` |
|
||||
| WireGuard | `wireguard` | `mikrotik-wireguard` | `openwrt-wireguard` |
|
||||
| VXLAN | `vxlan` | `mikrotik-vxlan` | `openwrt-vxlan` |
|
||||
| Bridge | `bridge` | `mikrotik-bridge` | `openwrt-bridge` |
|
||||
| DHCP | `isc-dhcp` or `dnsmasq` | `mikrotik-dhcp` | `openwrt-dhcp` |
|
||||
| Firewall | Implicit in roles | `mikrotik-firewall` | `openwrt-firewall` |
|
||||
| Monitoring | `monitoring` | `mikrotik-monitoring` | `openwrt-monitoring` |
|
||||
|
||||
All roles present the same logical interface - Ansible playbooks work identically regardless of gateway mix.
|
||||
|
||||
## Monitoring Comparison
|
||||
|
||||
### Metrics Collection
|
||||
|
||||
**Linux:**
|
||||
- Python Prometheus exporter
|
||||
- Most detailed metrics
|
||||
- Real-time collection
|
||||
- Port 9586
|
||||
|
||||
**MikroTik:**
|
||||
- REST API polling
|
||||
- SNMP (optional)
|
||||
- Good coverage
|
||||
- Requires external collector
|
||||
|
||||
**OpenWrt:**
|
||||
- Shell script exporter
|
||||
- Lightweight
|
||||
- Essential metrics
|
||||
- Port 9586 (uhttpd)
|
||||
|
||||
### Available Metrics (All Types)
|
||||
|
||||
- ✅ WireGuard tunnel status
|
||||
- ✅ WireGuard handshake timing
|
||||
- ✅ WireGuard rx/tx bytes
|
||||
- ✅ VXLAN interface stats
|
||||
- ✅ Bridge FDB entries
|
||||
- ✅ DHCP lease count
|
||||
- ✅ System uptime/memory
|
||||
|
||||
## Summary Recommendations
|
||||
|
||||
### Choose Linux when:
|
||||
- You need true DHCP failover
|
||||
- Performance is critical (>1 Gbps)
|
||||
- Advanced features required
|
||||
- Resources are not constrained
|
||||
|
||||
### Choose MikroTik when:
|
||||
- Budget-conscious hardware deployment
|
||||
- Power efficiency matters
|
||||
- RouterOS expertise available
|
||||
- Proven routing platform needed
|
||||
|
||||
### Choose OpenWrt when:
|
||||
- Repurposing existing hardware
|
||||
- Open source requirement
|
||||
- WiFi AP integration needed
|
||||
- Learning/lab environment
|
||||
|
||||
### Mix them when:
|
||||
- Different sites have different constraints
|
||||
- Migrating between platforms
|
||||
- Cost/performance optimization needed
|
||||
- Maximum redundancy required
|
||||
|
||||
All three types work together seamlessly in the same lab network!
|
||||
399
docs/openwrt-setup.md
Normal file
399
docs/openwrt-setup.md
Normal file
@@ -0,0 +1,399 @@
|
||||
# OpenWrt Gateway Setup Guide
|
||||
|
||||
## Prerequisites
|
||||
|
||||
### OpenWrt Version
|
||||
- **Minimum**: OpenWrt 23.05 (stable)
|
||||
- **Recommended**: Latest stable release
|
||||
- **Architectures**: x86_64, ARM, MIPS supported
|
||||
|
||||
### Hardware Requirements
|
||||
- **Flash**: Minimum 128MB (256MB+ recommended)
|
||||
- **RAM**: Minimum 128MB (256MB+ recommended)
|
||||
- **Network Interfaces**: At least 2 (one for management, one for lab devices)
|
||||
|
||||
## Initial OpenWrt Preparation
|
||||
|
||||
### 1. Fresh OpenWrt Installation
|
||||
|
||||
Ensure you have a clean OpenWrt installation with network access.
|
||||
|
||||
```bash
|
||||
# From your management workstation
|
||||
ssh root@<openwrt-ip>
|
||||
```
|
||||
|
||||
### 2. Configure Management Network
|
||||
|
||||
Ensure the router is reachable from your management server:
|
||||
|
||||
```bash
|
||||
# On OpenWrt router
|
||||
uci set network.lan.ipaddr='192.168.1.21'
|
||||
uci set network.lan.netmask='255.255.255.0'
|
||||
uci commit network
|
||||
/etc/init.d/network restart
|
||||
```
|
||||
|
||||
### 3. Install Required Packages
|
||||
|
||||
The Ansible playbook will auto-install these, but you can pre-install:
|
||||
|
||||
```bash
|
||||
opkg update
|
||||
opkg install wireguard-tools kmod-wireguard kmod-vxlan ip-full tcpdump
|
||||
```
|
||||
|
||||
### 4. Enable SSH Access
|
||||
|
||||
Ensure SSH is accessible:
|
||||
|
||||
```bash
|
||||
# Set root password if not already set
|
||||
passwd
|
||||
|
||||
# Enable SSH
|
||||
/etc/init.d/dropbear enable
|
||||
/etc/init.d/dropbear start
|
||||
```
|
||||
|
||||
### 5. Configure SSH Keys (Recommended)
|
||||
|
||||
From your management server:
|
||||
|
||||
```bash
|
||||
ssh-copy-id root@192.168.1.21
|
||||
```
|
||||
|
||||
## Network Interface Configuration
|
||||
|
||||
### Identifying Interfaces
|
||||
|
||||
List available interfaces:
|
||||
|
||||
```bash
|
||||
# On OpenWrt
|
||||
ip link show
|
||||
```
|
||||
|
||||
Common interface names:
|
||||
- **x86_64**: `eth0`, `eth1`, `eth2`
|
||||
- **ARM/embedded**: `lan1`, `lan2`, `wan`
|
||||
- **VLAN-aware**: `eth0.1`, `eth0.2`
|
||||
|
||||
### Recommended Setup
|
||||
|
||||
```
|
||||
┌──────────────────┐
|
||||
│ OpenWrt Router │
|
||||
│ │
|
||||
│ eth0 ───────────┼──→ Corporate Network (Management)
|
||||
│ eth1 ───────────┼──→ Lab Devices (Physical Connection)
|
||||
│ │
|
||||
│ wg_lab100 ──────┼──→ WireGuard Overlay
|
||||
│ vxlan100 ───────┼──→ VXLAN over WireGuard
|
||||
│ br_lab100 ──────┼──→ Bridge (eth1 + vxlan100)
|
||||
└──────────────────┘
|
||||
```
|
||||
|
||||
## Deployment via Ansible
|
||||
|
||||
### 1. Update Terraform Configuration
|
||||
|
||||
Edit `terraform/terraform.tfvars`:
|
||||
|
||||
```hcl
|
||||
lab_networks = {
|
||||
lab100 = {
|
||||
vni = 100
|
||||
subnet = "10.100.0.0/24"
|
||||
wireguard_net = "172.16.100.0/24"
|
||||
dhcp_mode = "simple"
|
||||
road_warrior = false
|
||||
gateways = [
|
||||
{
|
||||
hostname = "lab100-openwrt1"
|
||||
type = "openwrt" # Important: set to 'openwrt'
|
||||
mgmt_ip = "192.168.1.21"
|
||||
api_port = 22
|
||||
lab_if = "eth1" # Interface facing lab devices
|
||||
dhcp_role = "primary"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 2. Deploy
|
||||
|
||||
```bash
|
||||
# From project root
|
||||
./deploy.sh
|
||||
```
|
||||
|
||||
Or manually:
|
||||
|
||||
```bash
|
||||
cd terraform
|
||||
terraform apply
|
||||
cd ../ansible
|
||||
ansible-playbook -i inventory/hosts.yml playbooks/site.yml
|
||||
```
|
||||
|
||||
## Verification
|
||||
|
||||
### 1. Check WireGuard Status
|
||||
|
||||
```bash
|
||||
# On OpenWrt router
|
||||
wg show
|
||||
```
|
||||
|
||||
Expected output:
|
||||
```
|
||||
interface: wg_lab100
|
||||
public key: <key>
|
||||
private key: (hidden)
|
||||
listening port: 51920
|
||||
|
||||
peer: <peer-public-key>
|
||||
endpoint: 192.168.1.11:51920
|
||||
allowed ips: 172.16.100.1/32
|
||||
latest handshake: 30 seconds ago
|
||||
transfer: 5.2 KiB received, 4.8 KiB sent
|
||||
```
|
||||
|
||||
### 2. Check VXLAN Interface
|
||||
|
||||
```bash
|
||||
ip -d link show vxlan100
|
||||
```
|
||||
|
||||
Expected output:
|
||||
```
|
||||
vxlan100@wg_lab100: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1450
|
||||
link/ether ... brd ff:ff:ff:ff:ff:ff
|
||||
vxlan id 100 dev wg_lab100 dstport 4789 ...
|
||||
```
|
||||
|
||||
### 3. Check Bridge
|
||||
|
||||
```bash
|
||||
bridge link show | grep br_lab100
|
||||
```
|
||||
|
||||
Expected output:
|
||||
```
|
||||
3: eth1: <BROADCAST,MULTICAST,UP,LOWER_UP> master br_lab100
|
||||
4: vxlan100: <BROADCAST,MULTICAST,UP,LOWER_UP> master br_lab100
|
||||
```
|
||||
|
||||
### 4. Check DHCP
|
||||
|
||||
```bash
|
||||
# Show DHCP configuration
|
||||
uci show dhcp.lab100
|
||||
|
||||
# Check active leases
|
||||
cat /tmp/dhcp.leases
|
||||
```
|
||||
|
||||
### 5. Check Firewall
|
||||
|
||||
```bash
|
||||
# Show firewall zones
|
||||
uci show firewall | grep lab100
|
||||
|
||||
# Check iptables rules
|
||||
iptables -L -n -v | grep lab100
|
||||
```
|
||||
|
||||
### 6. Test Connectivity
|
||||
|
||||
```bash
|
||||
# Ping peer WireGuard IP
|
||||
ping 172.16.100.1
|
||||
|
||||
# Test DHCP by connecting a lab device to eth1
|
||||
# The device should get an IP in 10.100.0.0/24
|
||||
```
|
||||
|
||||
## Monitoring
|
||||
|
||||
### Access Metrics
|
||||
|
||||
The OpenWrt gateway exposes Prometheus metrics on port 9586:
|
||||
|
||||
```bash
|
||||
# From management server
|
||||
curl http://192.168.1.21:9586/metrics/
|
||||
```
|
||||
|
||||
Sample output:
|
||||
```
|
||||
# HELP wireguard_tunnel_up WireGuard tunnel status
|
||||
# TYPE wireguard_tunnel_up gauge
|
||||
wireguard_tunnel_up{lab="lab100",interface="wg_lab100",peer="abcd1234"} 1
|
||||
...
|
||||
```
|
||||
|
||||
### View Logs
|
||||
|
||||
```bash
|
||||
# On OpenWrt
|
||||
logread | grep -E 'wireguard|vxlan|lab100'
|
||||
|
||||
# Follow logs in real-time
|
||||
logread -f | grep -E 'wireguard|vxlan|lab100'
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### WireGuard Not Starting
|
||||
|
||||
```bash
|
||||
# Check kernel modules
|
||||
lsmod | grep wireguard
|
||||
|
||||
# Reload network
|
||||
/etc/init.d/network reload
|
||||
|
||||
# Check logs
|
||||
logread | grep wireguard
|
||||
```
|
||||
|
||||
### VXLAN Not Working
|
||||
|
||||
```bash
|
||||
# Manually recreate VXLAN
|
||||
/etc/init.d/vxlan-lab100 restart
|
||||
|
||||
# Check if WireGuard is up first
|
||||
ip link show wg_lab100
|
||||
```
|
||||
|
||||
### DHCP Not Assigning IPs
|
||||
|
||||
```bash
|
||||
# Restart dnsmasq
|
||||
/etc/init.d/dnsmasq restart
|
||||
|
||||
# Check configuration
|
||||
uci show dhcp.lab100
|
||||
|
||||
# Test with static IP first
|
||||
ip addr add 10.100.0.50/24 dev br_lab100
|
||||
```
|
||||
|
||||
### Firewall Blocking Traffic
|
||||
|
||||
```bash
|
||||
# Temporarily disable firewall for testing
|
||||
/etc/init.d/firewall stop
|
||||
|
||||
# Check rules
|
||||
iptables -L -n -v
|
||||
|
||||
# Re-enable
|
||||
/etc/init.d/firewall start
|
||||
```
|
||||
|
||||
### Package Installation Failures
|
||||
|
||||
```bash
|
||||
# Update package lists
|
||||
opkg update
|
||||
|
||||
# Check available space
|
||||
df -h
|
||||
|
||||
# Free up space if needed
|
||||
opkg remove <unnecessary-packages>
|
||||
```
|
||||
|
||||
## Performance Tuning
|
||||
|
||||
### For High-Throughput Labs
|
||||
|
||||
```bash
|
||||
# Increase network buffers
|
||||
sysctl -w net.core.rmem_max=16777216
|
||||
sysctl -w net.core.wmem_max=16777216
|
||||
|
||||
# Make permanent
|
||||
echo "net.core.rmem_max=16777216" >> /etc/sysctl.conf
|
||||
echo "net.core.wmem_max=16777216" >> /etc/sysctl.conf
|
||||
```
|
||||
|
||||
### For Low-Resource Devices
|
||||
|
||||
```bash
|
||||
# Reduce WireGuard keepalive
|
||||
uci set network.wg_lab100_peer0.persistent_keepalive='60'
|
||||
uci commit network
|
||||
|
||||
# Limit DHCP lease time
|
||||
uci set dhcp.lab100.leasetime='1h'
|
||||
uci commit dhcp
|
||||
```
|
||||
|
||||
## Backup and Recovery
|
||||
|
||||
### Create Backup
|
||||
|
||||
```bash
|
||||
# Full configuration backup
|
||||
sysupgrade -b /tmp/backup-$(date +%Y%m%d).tar.gz
|
||||
|
||||
# Download backup
|
||||
scp root@192.168.1.21:/tmp/backup-*.tar.gz ./
|
||||
```
|
||||
|
||||
### Restore Backup
|
||||
|
||||
```bash
|
||||
# Upload backup
|
||||
scp backup-20250130.tar.gz root@192.168.1.21:/tmp/
|
||||
|
||||
# Restore
|
||||
sysupgrade -r /tmp/backup-20250130.tar.gz
|
||||
```
|
||||
|
||||
## Integration with Multi-Vendor Labs
|
||||
|
||||
OpenWrt gateways are fully compatible with Linux and MikroTik gateways in the same lab network:
|
||||
|
||||
```hcl
|
||||
# Example mixed gateway lab
|
||||
lab_networks = {
|
||||
lab100 = {
|
||||
gateways = [
|
||||
{ hostname = "gw1", type = "linux" },
|
||||
{ hostname = "rt1", type = "mikrotik" },
|
||||
{ hostname = "ap1", type = "openwrt" } # All three work together!
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
All three gateway types:
|
||||
- ✅ Form full mesh WireGuard tunnels with each other
|
||||
- ✅ Participate in VXLAN overlay
|
||||
- ✅ Provide DHCP services (with split ranges or failover)
|
||||
- ✅ Export metrics to centralized Prometheus
|
||||
- ✅ Can be managed from same Ansible playbooks
|
||||
|
||||
## Next Steps
|
||||
|
||||
- Enable road warrior access: Edit `terraform.tfvars` and set `road_warrior = true`
|
||||
- Add monitoring dashboards in Grafana
|
||||
- Configure alerting for tunnel failures
|
||||
- Set up automated backups via cron
|
||||
|
||||
## Support
|
||||
|
||||
For OpenWrt-specific issues:
|
||||
- OpenWrt documentation: https://openwrt.org/docs
|
||||
- Check `/var/log/messages` for system logs
|
||||
- Use `logread -f` for real-time debugging
|
||||
- Run health check: `ansible-playbook playbooks/health-check.yml`
|
||||
61
docs/operations.md
Normal file
61
docs/operations.md
Normal file
@@ -0,0 +1,61 @@
|
||||
# Operations Manual
|
||||
|
||||
## Daily Operations
|
||||
|
||||
### Health Checks
|
||||
```bash
|
||||
ansible-playbook playbooks/health-check.yml
|
||||
```
|
||||
|
||||
### Rolling Updates
|
||||
```bash
|
||||
ansible-playbook playbooks/rolling-update.yml
|
||||
```
|
||||
|
||||
## Emergency Procedures
|
||||
|
||||
### Emergency Shutdown
|
||||
```bash
|
||||
ansible-playbook playbooks/emergency-shutdown.yml
|
||||
```
|
||||
|
||||
### Restore After Shutdown
|
||||
```bash
|
||||
ansible-playbook playbooks/restore-lab.yml
|
||||
```
|
||||
|
||||
## Adding/Removing Components
|
||||
|
||||
### Add New Lab Network
|
||||
1. Edit `terraform/terraform.tfvars`
|
||||
2. Run `terraform apply`
|
||||
3. Run `ansible-playbook playbooks/site.yml`
|
||||
|
||||
### Add Gateway to Existing Lab
|
||||
1. Edit `terraform/terraform.tfvars`
|
||||
2. Run `terraform apply`
|
||||
3. Run `ansible-playbook playbooks/add-gateway.yml`
|
||||
|
||||
### Remove Gateway
|
||||
```bash
|
||||
ansible-playbook playbooks/remove-gateway.yml
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### WireGuard Tunnel Down
|
||||
1. Check WireGuard status: `wg show`
|
||||
2. Verify peer connectivity: `ping <peer_wg_ip>`
|
||||
3. Check firewall rules
|
||||
4. Restart WireGuard: `systemctl restart wg-quick@wg-labXXX`
|
||||
|
||||
### DHCP Not Working
|
||||
1. Check DHCP server status
|
||||
2. Verify bridge is up
|
||||
3. Check DHCP lease file
|
||||
4. Test with static IP first
|
||||
|
||||
### VXLAN Issues
|
||||
1. Verify WireGuard tunnels are up first
|
||||
2. Check VXLAN interface: `ip -d link show vxlanXXX`
|
||||
3. Verify FDB entries: `bridge fdb show`
|
||||
27
monitoring/docker-compose.yml
Normal file
27
monitoring/docker-compose.yml
Normal file
@@ -0,0 +1,27 @@
|
||||
version: '3.8'
|
||||
|
||||
services:
|
||||
prometheus:
|
||||
image: prom/prometheus:latest
|
||||
container_name: prometheus
|
||||
volumes:
|
||||
- ./prometheus/prometheus.yml:/etc/prometheus/prometheus.yml
|
||||
- prometheus_data:/prometheus
|
||||
ports:
|
||||
- "9090:9090"
|
||||
restart: unless-stopped
|
||||
|
||||
grafana:
|
||||
image: grafana/grafana:latest
|
||||
container_name: grafana
|
||||
volumes:
|
||||
- grafana_data:/var/lib/grafana
|
||||
environment:
|
||||
- GF_SECURITY_ADMIN_PASSWORD=admin
|
||||
ports:
|
||||
- "3000:3000"
|
||||
restart: unless-stopped
|
||||
|
||||
volumes:
|
||||
prometheus_data:
|
||||
grafana_data:
|
||||
38
monitoring/exporter/wg_lab_exporter.py
Executable file
38
monitoring/exporter/wg_lab_exporter.py
Executable file
@@ -0,0 +1,38 @@
|
||||
#!/usr/bin/env python3
|
||||
"""WireGuard Lab Exporter for Prometheus"""
|
||||
import subprocess
|
||||
import time
|
||||
from prometheus_client import start_http_server
|
||||
from prometheus_client.core import GaugeMetricFamily, CounterMetricFamily, REGISTRY
|
||||
import json
|
||||
|
||||
class WireGuardLabCollector:
|
||||
def __init__(self):
|
||||
try:
|
||||
with open('/etc/wg-lab-exporter/config.json') as f:
|
||||
self.config = json.load(f)
|
||||
except:
|
||||
self.config = {'labs': {}}
|
||||
|
||||
def collect(self):
|
||||
wg_up = GaugeMetricFamily('wireguard_tunnel_up', 'Tunnel status', labels=['lab', 'peer'])
|
||||
|
||||
for lab_name in self.config.get('labs', {}):
|
||||
try:
|
||||
result = subprocess.run(['wg', 'show', f'wg-{lab_name}'],
|
||||
capture_output=True, text=True, timeout=5)
|
||||
if result.returncode == 0:
|
||||
wg_up.add_metric([lab_name, 'all'], 1)
|
||||
else:
|
||||
wg_up.add_metric([lab_name, 'all'], 0)
|
||||
except:
|
||||
wg_up.add_metric([lab_name, 'all'], 0)
|
||||
|
||||
yield wg_up
|
||||
|
||||
if __name__ == '__main__':
|
||||
REGISTRY.register(WireGuardLabCollector())
|
||||
start_http_server(9586)
|
||||
print("Exporter started on port 9586")
|
||||
while True:
|
||||
time.sleep(60)
|
||||
8
monitoring/prometheus/prometheus.yml
Normal file
8
monitoring/prometheus/prometheus.yml
Normal file
@@ -0,0 +1,8 @@
|
||||
global:
|
||||
scrape_interval: 30s
|
||||
evaluation_interval: 30s
|
||||
|
||||
scrape_configs:
|
||||
- job_name: 'wireguard-linux'
|
||||
static_configs:
|
||||
- targets: ['192.168.1.11:9586', '192.168.1.13:9586']
|
||||
33
road-warrior/templates/README.md.j2
Normal file
33
road-warrior/templates/README.md.j2
Normal file
@@ -0,0 +1,33 @@
|
||||
# WireGuard Lab Access - {{ item.key }}
|
||||
|
||||
## Your Lab Networks
|
||||
|
||||
You have access to the following lab networks:
|
||||
|
||||
{% for lab in road_warrior_users[item.key].labs %}
|
||||
- **{{ lab }}**: Configuration file `{{ lab }}.conf`
|
||||
{% endfor %}
|
||||
|
||||
## Setup Instructions
|
||||
|
||||
### Desktop/Laptop (Linux/Mac/Windows)
|
||||
|
||||
1. Install WireGuard: https://www.wireguard.com/install/
|
||||
2. Import the `.conf` file for your lab
|
||||
3. Activate the connection
|
||||
|
||||
### Mobile (iOS/Android)
|
||||
|
||||
1. Install WireGuard app from App Store/Play Store
|
||||
2. Open the `{{ item.key }}-qr.html` file
|
||||
3. Scan the QR code with the app
|
||||
|
||||
## Security Notes
|
||||
|
||||
- Keep these configuration files secure
|
||||
- Do NOT share your private key
|
||||
- Report lost devices immediately
|
||||
|
||||
## Support
|
||||
|
||||
For issues, contact: IT Support
|
||||
18
road-warrior/templates/client-config.conf.j2
Normal file
18
road-warrior/templates/client-config.conf.j2
Normal file
@@ -0,0 +1,18 @@
|
||||
[Interface]
|
||||
# WireGuard configuration for {{ item.value.email }}
|
||||
# Lab Network: {{ item.value.lab_name }}
|
||||
# Generated: {{ ansible_date_time.iso8601 }}
|
||||
|
||||
PrivateKey = {{ item.value.wg_private_key }}
|
||||
Address = {{ item.value.wg_ip }}/32
|
||||
DNS = 8.8.8.8, 8.8.4.4
|
||||
|
||||
{% for peer in item.value.peers %}
|
||||
[Peer]
|
||||
# Gateway: {{ peer.gateway }}
|
||||
PublicKey = {{ peer.public_key }}
|
||||
Endpoint = {{ peer.endpoint }}:{{ peer.listen_port }}
|
||||
AllowedIPs = {{ item.value.subnet }}
|
||||
PersistentKeepalive = 25
|
||||
|
||||
{% endfor %}
|
||||
22
terraform/.terraform.lock.hcl
generated
Normal file
22
terraform/.terraform.lock.hcl
generated
Normal file
@@ -0,0 +1,22 @@
|
||||
# This file is maintained automatically by "terraform init".
|
||||
# Manual edits may be lost in future updates.
|
||||
|
||||
provider "registry.terraform.io/hashicorp/random" {
|
||||
version = "3.8.1"
|
||||
constraints = "~> 3.5"
|
||||
hashes = [
|
||||
"h1:m2y2fw9SBQ6+e7pNhi3+qsh8bYNmqkL89BulzH7uK3U=",
|
||||
"zh:08dd03b918c7b55713026037c5400c48af5b9f468f483463321bd18e17b907b4",
|
||||
"zh:0eee654a5542dc1d41920bbf2419032d6f0d5625b03bd81339e5b33394a3e0ae",
|
||||
"zh:229665ddf060aa0ed315597908483eee5b818a17d09b6417a0f52fd9405c4f57",
|
||||
"zh:2469d2e48f28076254a2a3fc327f184914566d9e40c5780b8d96ebf7205f8bc0",
|
||||
"zh:37d7eb334d9561f335e748280f5535a384a88675af9a9eac439d4cfd663bcb66",
|
||||
"zh:741101426a2f2c52dee37122f0f4a2f2d6af6d852cb1db634480a86398fa3511",
|
||||
"zh:78d5eefdd9e494defcb3c68d282b8f96630502cac21d1ea161f53cfe9bb483b3",
|
||||
"zh:a902473f08ef8df62cfe6116bd6c157070a93f66622384300de235a533e9d4a9",
|
||||
"zh:b85c511a23e57a2147355932b3b6dce2a11e856b941165793a0c3d7578d94d05",
|
||||
"zh:c5172226d18eaac95b1daac80172287b69d4ce32750c82ad77fa0768be4ea4b8",
|
||||
"zh:dab4434dba34aad569b0bc243c2d3f3ff86dd7740def373f2a49816bd2ff819b",
|
||||
"zh:f49fd62aa8c5525a5c17abd51e27ca5e213881d58882fd42fec4a545b53c9699",
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,375 @@
|
||||
Copyright (c) 2017 HashiCorp, Inc.
|
||||
|
||||
Mozilla Public License Version 2.0
|
||||
==================================
|
||||
|
||||
1. Definitions
|
||||
--------------
|
||||
|
||||
1.1. "Contributor"
|
||||
means each individual or legal entity that creates, contributes to
|
||||
the creation of, or owns Covered Software.
|
||||
|
||||
1.2. "Contributor Version"
|
||||
means the combination of the Contributions of others (if any) used
|
||||
by a Contributor and that particular Contributor's Contribution.
|
||||
|
||||
1.3. "Contribution"
|
||||
means Covered Software of a particular Contributor.
|
||||
|
||||
1.4. "Covered Software"
|
||||
means Source Code Form to which the initial Contributor has attached
|
||||
the notice in Exhibit A, the Executable Form of such Source Code
|
||||
Form, and Modifications of such Source Code Form, in each case
|
||||
including portions thereof.
|
||||
|
||||
1.5. "Incompatible With Secondary Licenses"
|
||||
means
|
||||
|
||||
(a) that the initial Contributor has attached the notice described
|
||||
in Exhibit B to the Covered Software; or
|
||||
|
||||
(b) that the Covered Software was made available under the terms of
|
||||
version 1.1 or earlier of the License, but not also under the
|
||||
terms of a Secondary License.
|
||||
|
||||
1.6. "Executable Form"
|
||||
means any form of the work other than Source Code Form.
|
||||
|
||||
1.7. "Larger Work"
|
||||
means a work that combines Covered Software with other material, in
|
||||
a separate file or files, that is not Covered Software.
|
||||
|
||||
1.8. "License"
|
||||
means this document.
|
||||
|
||||
1.9. "Licensable"
|
||||
means having the right to grant, to the maximum extent possible,
|
||||
whether at the time of the initial grant or subsequently, any and
|
||||
all of the rights conveyed by this License.
|
||||
|
||||
1.10. "Modifications"
|
||||
means any of the following:
|
||||
|
||||
(a) any file in Source Code Form that results from an addition to,
|
||||
deletion from, or modification of the contents of Covered
|
||||
Software; or
|
||||
|
||||
(b) any new file in Source Code Form that contains any Covered
|
||||
Software.
|
||||
|
||||
1.11. "Patent Claims" of a Contributor
|
||||
means any patent claim(s), including without limitation, method,
|
||||
process, and apparatus claims, in any patent Licensable by such
|
||||
Contributor that would be infringed, but for the grant of the
|
||||
License, by the making, using, selling, offering for sale, having
|
||||
made, import, or transfer of either its Contributions or its
|
||||
Contributor Version.
|
||||
|
||||
1.12. "Secondary License"
|
||||
means either the GNU General Public License, Version 2.0, the GNU
|
||||
Lesser General Public License, Version 2.1, the GNU Affero General
|
||||
Public License, Version 3.0, or any later versions of those
|
||||
licenses.
|
||||
|
||||
1.13. "Source Code Form"
|
||||
means the form of the work preferred for making modifications.
|
||||
|
||||
1.14. "You" (or "Your")
|
||||
means an individual or a legal entity exercising rights under this
|
||||
License. For legal entities, "You" includes any entity that
|
||||
controls, is controlled by, or is under common control with You. For
|
||||
purposes of this definition, "control" means (a) the power, direct
|
||||
or indirect, to cause the direction or management of such entity,
|
||||
whether by contract or otherwise, or (b) ownership of more than
|
||||
fifty percent (50%) of the outstanding shares or beneficial
|
||||
ownership of such entity.
|
||||
|
||||
2. License Grants and Conditions
|
||||
--------------------------------
|
||||
|
||||
2.1. Grants
|
||||
|
||||
Each Contributor hereby grants You a world-wide, royalty-free,
|
||||
non-exclusive license:
|
||||
|
||||
(a) under intellectual property rights (other than patent or trademark)
|
||||
Licensable by such Contributor to use, reproduce, make available,
|
||||
modify, display, perform, distribute, and otherwise exploit its
|
||||
Contributions, either on an unmodified basis, with Modifications, or
|
||||
as part of a Larger Work; and
|
||||
|
||||
(b) under Patent Claims of such Contributor to make, use, sell, offer
|
||||
for sale, have made, import, and otherwise transfer either its
|
||||
Contributions or its Contributor Version.
|
||||
|
||||
2.2. Effective Date
|
||||
|
||||
The licenses granted in Section 2.1 with respect to any Contribution
|
||||
become effective for each Contribution on the date the Contributor first
|
||||
distributes such Contribution.
|
||||
|
||||
2.3. Limitations on Grant Scope
|
||||
|
||||
The licenses granted in this Section 2 are the only rights granted under
|
||||
this License. No additional rights or licenses will be implied from the
|
||||
distribution or licensing of Covered Software under this License.
|
||||
Notwithstanding Section 2.1(b) above, no patent license is granted by a
|
||||
Contributor:
|
||||
|
||||
(a) for any code that a Contributor has removed from Covered Software;
|
||||
or
|
||||
|
||||
(b) for infringements caused by: (i) Your and any other third party's
|
||||
modifications of Covered Software, or (ii) the combination of its
|
||||
Contributions with other software (except as part of its Contributor
|
||||
Version); or
|
||||
|
||||
(c) under Patent Claims infringed by Covered Software in the absence of
|
||||
its Contributions.
|
||||
|
||||
This License does not grant any rights in the trademarks, service marks,
|
||||
or logos of any Contributor (except as may be necessary to comply with
|
||||
the notice requirements in Section 3.4).
|
||||
|
||||
2.4. Subsequent Licenses
|
||||
|
||||
No Contributor makes additional grants as a result of Your choice to
|
||||
distribute the Covered Software under a subsequent version of this
|
||||
License (see Section 10.2) or under the terms of a Secondary License (if
|
||||
permitted under the terms of Section 3.3).
|
||||
|
||||
2.5. Representation
|
||||
|
||||
Each Contributor represents that the Contributor believes its
|
||||
Contributions are its original creation(s) or it has sufficient rights
|
||||
to grant the rights to its Contributions conveyed by this License.
|
||||
|
||||
2.6. Fair Use
|
||||
|
||||
This License is not intended to limit any rights You have under
|
||||
applicable copyright doctrines of fair use, fair dealing, or other
|
||||
equivalents.
|
||||
|
||||
2.7. Conditions
|
||||
|
||||
Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted
|
||||
in Section 2.1.
|
||||
|
||||
3. Responsibilities
|
||||
-------------------
|
||||
|
||||
3.1. Distribution of Source Form
|
||||
|
||||
All distribution of Covered Software in Source Code Form, including any
|
||||
Modifications that You create or to which You contribute, must be under
|
||||
the terms of this License. You must inform recipients that the Source
|
||||
Code Form of the Covered Software is governed by the terms of this
|
||||
License, and how they can obtain a copy of this License. You may not
|
||||
attempt to alter or restrict the recipients' rights in the Source Code
|
||||
Form.
|
||||
|
||||
3.2. Distribution of Executable Form
|
||||
|
||||
If You distribute Covered Software in Executable Form then:
|
||||
|
||||
(a) such Covered Software must also be made available in Source Code
|
||||
Form, as described in Section 3.1, and You must inform recipients of
|
||||
the Executable Form how they can obtain a copy of such Source Code
|
||||
Form by reasonable means in a timely manner, at a charge no more
|
||||
than the cost of distribution to the recipient; and
|
||||
|
||||
(b) You may distribute such Executable Form under the terms of this
|
||||
License, or sublicense it under different terms, provided that the
|
||||
license for the Executable Form does not attempt to limit or alter
|
||||
the recipients' rights in the Source Code Form under this License.
|
||||
|
||||
3.3. Distribution of a Larger Work
|
||||
|
||||
You may create and distribute a Larger Work under terms of Your choice,
|
||||
provided that You also comply with the requirements of this License for
|
||||
the Covered Software. If the Larger Work is a combination of Covered
|
||||
Software with a work governed by one or more Secondary Licenses, and the
|
||||
Covered Software is not Incompatible With Secondary Licenses, this
|
||||
License permits You to additionally distribute such Covered Software
|
||||
under the terms of such Secondary License(s), so that the recipient of
|
||||
the Larger Work may, at their option, further distribute the Covered
|
||||
Software under the terms of either this License or such Secondary
|
||||
License(s).
|
||||
|
||||
3.4. Notices
|
||||
|
||||
You may not remove or alter the substance of any license notices
|
||||
(including copyright notices, patent notices, disclaimers of warranty,
|
||||
or limitations of liability) contained within the Source Code Form of
|
||||
the Covered Software, except that You may alter any license notices to
|
||||
the extent required to remedy known factual inaccuracies.
|
||||
|
||||
3.5. Application of Additional Terms
|
||||
|
||||
You may choose to offer, and to charge a fee for, warranty, support,
|
||||
indemnity or liability obligations to one or more recipients of Covered
|
||||
Software. However, You may do so only on Your own behalf, and not on
|
||||
behalf of any Contributor. You must make it absolutely clear that any
|
||||
such warranty, support, indemnity, or liability obligation is offered by
|
||||
You alone, and You hereby agree to indemnify every Contributor for any
|
||||
liability incurred by such Contributor as a result of warranty, support,
|
||||
indemnity or liability terms You offer. You may include additional
|
||||
disclaimers of warranty and limitations of liability specific to any
|
||||
jurisdiction.
|
||||
|
||||
4. Inability to Comply Due to Statute or Regulation
|
||||
---------------------------------------------------
|
||||
|
||||
If it is impossible for You to comply with any of the terms of this
|
||||
License with respect to some or all of the Covered Software due to
|
||||
statute, judicial order, or regulation then You must: (a) comply with
|
||||
the terms of this License to the maximum extent possible; and (b)
|
||||
describe the limitations and the code they affect. Such description must
|
||||
be placed in a text file included with all distributions of the Covered
|
||||
Software under this License. Except to the extent prohibited by statute
|
||||
or regulation, such description must be sufficiently detailed for a
|
||||
recipient of ordinary skill to be able to understand it.
|
||||
|
||||
5. Termination
|
||||
--------------
|
||||
|
||||
5.1. The rights granted under this License will terminate automatically
|
||||
if You fail to comply with any of its terms. However, if You become
|
||||
compliant, then the rights granted under this License from a particular
|
||||
Contributor are reinstated (a) provisionally, unless and until such
|
||||
Contributor explicitly and finally terminates Your grants, and (b) on an
|
||||
ongoing basis, if such Contributor fails to notify You of the
|
||||
non-compliance by some reasonable means prior to 60 days after You have
|
||||
come back into compliance. Moreover, Your grants from a particular
|
||||
Contributor are reinstated on an ongoing basis if such Contributor
|
||||
notifies You of the non-compliance by some reasonable means, this is the
|
||||
first time You have received notice of non-compliance with this License
|
||||
from such Contributor, and You become compliant prior to 30 days after
|
||||
Your receipt of the notice.
|
||||
|
||||
5.2. If You initiate litigation against any entity by asserting a patent
|
||||
infringement claim (excluding declaratory judgment actions,
|
||||
counter-claims, and cross-claims) alleging that a Contributor Version
|
||||
directly or indirectly infringes any patent, then the rights granted to
|
||||
You by any and all Contributors for the Covered Software under Section
|
||||
2.1 of this License shall terminate.
|
||||
|
||||
5.3. In the event of termination under Sections 5.1 or 5.2 above, all
|
||||
end user license agreements (excluding distributors and resellers) which
|
||||
have been validly granted by You or Your distributors under this License
|
||||
prior to termination shall survive termination.
|
||||
|
||||
************************************************************************
|
||||
* *
|
||||
* 6. Disclaimer of Warranty *
|
||||
* ------------------------- *
|
||||
* *
|
||||
* Covered Software is provided under this License on an "as is" *
|
||||
* basis, without warranty of any kind, either expressed, implied, or *
|
||||
* statutory, including, without limitation, warranties that the *
|
||||
* Covered Software is free of defects, merchantable, fit for a *
|
||||
* particular purpose or non-infringing. The entire risk as to the *
|
||||
* quality and performance of the Covered Software is with You. *
|
||||
* Should any Covered Software prove defective in any respect, You *
|
||||
* (not any Contributor) assume the cost of any necessary servicing, *
|
||||
* repair, or correction. This disclaimer of warranty constitutes an *
|
||||
* essential part of this License. No use of any Covered Software is *
|
||||
* authorized under this License except under this disclaimer. *
|
||||
* *
|
||||
************************************************************************
|
||||
|
||||
************************************************************************
|
||||
* *
|
||||
* 7. Limitation of Liability *
|
||||
* -------------------------- *
|
||||
* *
|
||||
* Under no circumstances and under no legal theory, whether tort *
|
||||
* (including negligence), contract, or otherwise, shall any *
|
||||
* Contributor, or anyone who distributes Covered Software as *
|
||||
* permitted above, be liable to You for any direct, indirect, *
|
||||
* special, incidental, or consequential damages of any character *
|
||||
* including, without limitation, damages for lost profits, loss of *
|
||||
* goodwill, work stoppage, computer failure or malfunction, or any *
|
||||
* and all other commercial damages or losses, even if such party *
|
||||
* shall have been informed of the possibility of such damages. This *
|
||||
* limitation of liability shall not apply to liability for death or *
|
||||
* personal injury resulting from such party's negligence to the *
|
||||
* extent applicable law prohibits such limitation. Some *
|
||||
* jurisdictions do not allow the exclusion or limitation of *
|
||||
* incidental or consequential damages, so this exclusion and *
|
||||
* limitation may not apply to You. *
|
||||
* *
|
||||
************************************************************************
|
||||
|
||||
8. Litigation
|
||||
-------------
|
||||
|
||||
Any litigation relating to this License may be brought only in the
|
||||
courts of a jurisdiction where the defendant maintains its principal
|
||||
place of business and such litigation shall be governed by laws of that
|
||||
jurisdiction, without reference to its conflict-of-law provisions.
|
||||
Nothing in this Section shall prevent a party's ability to bring
|
||||
cross-claims or counter-claims.
|
||||
|
||||
9. Miscellaneous
|
||||
----------------
|
||||
|
||||
This License represents the complete agreement concerning the subject
|
||||
matter hereof. If any provision of this License is held to be
|
||||
unenforceable, such provision shall be reformed only to the extent
|
||||
necessary to make it enforceable. Any law or regulation which provides
|
||||
that the language of a contract shall be construed against the drafter
|
||||
shall not be used to construe this License against a Contributor.
|
||||
|
||||
10. Versions of the License
|
||||
---------------------------
|
||||
|
||||
10.1. New Versions
|
||||
|
||||
Mozilla Foundation is the license steward. Except as provided in Section
|
||||
10.3, no one other than the license steward has the right to modify or
|
||||
publish new versions of this License. Each version will be given a
|
||||
distinguishing version number.
|
||||
|
||||
10.2. Effect of New Versions
|
||||
|
||||
You may distribute the Covered Software under the terms of the version
|
||||
of the License under which You originally received the Covered Software,
|
||||
or under the terms of any subsequent version published by the license
|
||||
steward.
|
||||
|
||||
10.3. Modified Versions
|
||||
|
||||
If you create software not governed by this License, and you want to
|
||||
create a new license for such software, you may create and use a
|
||||
modified version of this License if you rename the license and remove
|
||||
any references to the name of the license steward (except to note that
|
||||
such modified license differs from this License).
|
||||
|
||||
10.4. Distributing Source Code Form that is Incompatible With Secondary
|
||||
Licenses
|
||||
|
||||
If You choose to distribute Source Code Form that is Incompatible With
|
||||
Secondary Licenses under the terms of this version of the License, the
|
||||
notice described in Exhibit B of this License must be attached.
|
||||
|
||||
Exhibit A - Source Code Form License Notice
|
||||
-------------------------------------------
|
||||
|
||||
This Source Code Form is subject to the terms of the Mozilla Public
|
||||
License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
If it is not possible or desirable to put the notice in a particular
|
||||
file, then You may include the notice in a location (such as a LICENSE
|
||||
file in a relevant directory) where a recipient would be likely to look
|
||||
for such a notice.
|
||||
|
||||
You may add additional accurate notices of copyright ownership.
|
||||
|
||||
Exhibit B - "Incompatible With Secondary Licenses" Notice
|
||||
---------------------------------------------------------
|
||||
|
||||
This Source Code Form is "Incompatible With Secondary Licenses", as
|
||||
defined by the Mozilla Public License, v. 2.0.
|
||||
Binary file not shown.
12
terraform/.terraform/terraform.tfstate
Normal file
12
terraform/.terraform/terraform.tfstate
Normal file
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"version": 3,
|
||||
"terraform_version": "1.14.4",
|
||||
"backend": {
|
||||
"type": "local",
|
||||
"config": {
|
||||
"path": "terraform.tfstate",
|
||||
"workspace_dir": null
|
||||
},
|
||||
"hash": 73024536
|
||||
}
|
||||
}
|
||||
125
terraform/main.tf
Normal file
125
terraform/main.tf
Normal file
@@ -0,0 +1,125 @@
|
||||
terraform {
|
||||
required_providers {
|
||||
random = {
|
||||
source = "hashicorp/random"
|
||||
version = "~> 3.5"
|
||||
}
|
||||
}
|
||||
backend "local" {
|
||||
path = "terraform.tfstate"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
# Generate WireGuard keypairs for gateways
|
||||
resource "random_password" "wg_private_key" {
|
||||
for_each = local.all_gateways
|
||||
length = 44
|
||||
special = false
|
||||
}
|
||||
|
||||
# Generate WireGuard keypairs for road warrior clients
|
||||
resource "random_password" "rw_private_key" {
|
||||
for_each = local.road_warrior_clients
|
||||
length = 44
|
||||
special = false
|
||||
}
|
||||
|
||||
locals {
|
||||
# Flatten all gateways
|
||||
all_gateways = merge([
|
||||
for lab_name, lab in var.lab_networks : {
|
||||
for gw in lab.gateways :
|
||||
"${lab_name}-${gw.hostname}" => merge(gw, {
|
||||
lab_name = lab_name
|
||||
vni = lab.vni
|
||||
wg_net = lab.wireguard_net
|
||||
subnet = lab.subnet
|
||||
dhcp_mode = lab.dhcp_mode
|
||||
road_warrior = lab.road_warrior
|
||||
})
|
||||
}
|
||||
]...)
|
||||
|
||||
# Generate road warrior client entries
|
||||
road_warrior_clients = merge([
|
||||
for user_name, user in var.road_warrior_users : {
|
||||
for lab in user.labs :
|
||||
"${user_name}-${lab}" => {
|
||||
user_name = user_name
|
||||
email = user.email
|
||||
lab_name = lab
|
||||
vni = var.lab_networks[lab].vni
|
||||
wg_net = var.lab_networks[lab].wireguard_net
|
||||
subnet = var.lab_networks[lab].subnet
|
||||
}
|
||||
}
|
||||
]...)
|
||||
|
||||
# Calculate WireGuard IP assignments
|
||||
gateway_wg_ips = {
|
||||
for key, gw in local.all_gateways :
|
||||
key => cidrhost(gw.wg_net, index(var.lab_networks[gw.lab_name].gateways,
|
||||
[for g in var.lab_networks[gw.lab_name].gateways : g if g.hostname == gw.hostname][0]
|
||||
) + 1)
|
||||
}
|
||||
|
||||
# Road warrior IP assignments (start after gateways)
|
||||
rw_wg_ips = {
|
||||
for key, rw in local.road_warrior_clients :
|
||||
key => cidrhost(rw.wg_net,
|
||||
length(var.lab_networks[rw.lab_name].gateways) +
|
||||
index([for k, v in local.road_warrior_clients : k if v.lab_name == rw.lab_name], key) + 10
|
||||
)
|
||||
}
|
||||
|
||||
# Generate peer matrices (gateways only mesh with each other, not RW clients)
|
||||
wg_gateway_peers = {
|
||||
for lab_name, lab in var.lab_networks : lab_name => [
|
||||
for i, gw1 in lab.gateways : [
|
||||
for j, gw2 in lab.gateways :
|
||||
{
|
||||
from = gw1.hostname
|
||||
to = gw2.hostname
|
||||
to_type = gw2.type
|
||||
endpoint = gw2.mgmt_ip
|
||||
wg_ip = local.gateway_wg_ips["${lab_name}-${gw2.hostname}"]
|
||||
public_key = random_password.wg_private_key["${lab_name}-${gw2.hostname}"].result
|
||||
} if i != j
|
||||
]
|
||||
]
|
||||
}
|
||||
|
||||
# Road warrior peers (each RW client peers with all gateways in their labs)
|
||||
wg_rw_peers = {
|
||||
for key, rw in local.road_warrior_clients :
|
||||
key => [
|
||||
for gw in var.lab_networks[rw.lab_name].gateways :
|
||||
{
|
||||
gateway = gw.hostname
|
||||
gw_type = gw.type
|
||||
endpoint = gw.mgmt_ip
|
||||
wg_ip = local.gateway_wg_ips["${rw.lab_name}-${gw.hostname}"]
|
||||
public_key = random_password.wg_private_key["${rw.lab_name}-${gw.hostname}"].result
|
||||
listen_port = 51820 + var.lab_networks[rw.lab_name].vni
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
# Compute public keys from private keys (simplified for demo)
|
||||
resource "random_id" "wg_public_key" {
|
||||
for_each = random_password.wg_private_key
|
||||
byte_length = 32
|
||||
keepers = {
|
||||
private_key = each.value.result
|
||||
}
|
||||
}
|
||||
|
||||
resource "random_id" "rw_public_key" {
|
||||
for_each = random_password.rw_private_key
|
||||
byte_length = 32
|
||||
keepers = {
|
||||
private_key = each.value.result
|
||||
}
|
||||
}
|
||||
842
terraform/outputs.json
Normal file
842
terraform/outputs.json
Normal file
@@ -0,0 +1,842 @@
|
||||
{
|
||||
"ansible_inventory": {
|
||||
"sensitive": false,
|
||||
"type": "string",
|
||||
"value": "\"all\":\n \"children\":\n \"linux_gateways\":\n \"hosts\":\n \"lab100-gw1\":\n \"ansible_host\": \"192.168.1.11\"\n \"lab_name\": \"lab100\"\n \"mikrotik_gateways\":\n \"hosts\":\n \"lab100-rt1\":\n \"ansible_connection\": \"network_cli\"\n \"ansible_host\": \"192.168.1.12\"\n \"ansible_network_os\": \"routeros\"\n \"ansible_port\": 8728\n \"lab_name\": \"lab100\"\n \"openwrt_gateways\":\n \"hosts\":\n \"lab100-openwrt1\":\n \"ansible_host\": \"192.168.1.14\"\n \"ansible_port\": 22\n \"lab_name\": \"lab100\"\n \"lab200-openwrt1\":\n \"ansible_host\": \"192.168.1.21\"\n \"ansible_port\": 22\n \"lab_name\": \"lab200\"\n \"lab200-openwrt2\":\n \"ansible_host\": \"192.168.1.22\"\n \"ansible_port\": 22\n \"lab_name\": \"lab200\"\n"
|
||||
},
|
||||
"gateway_config": {
|
||||
"sensitive": true,
|
||||
"type": [
|
||||
"object",
|
||||
{
|
||||
"lab100-lab100-gw1": [
|
||||
"object",
|
||||
{
|
||||
"api_port": "number",
|
||||
"dhcp_mode": "string",
|
||||
"dhcp_role": "string",
|
||||
"hostname": "string",
|
||||
"lab_if": "string",
|
||||
"lab_name": "string",
|
||||
"mgmt_ip": "string",
|
||||
"peers": [
|
||||
"tuple",
|
||||
[
|
||||
[
|
||||
"object",
|
||||
{
|
||||
"endpoint": "string",
|
||||
"from": "string",
|
||||
"public_key": "string",
|
||||
"to": "string",
|
||||
"to_type": "string",
|
||||
"wg_ip": "string"
|
||||
}
|
||||
],
|
||||
[
|
||||
"object",
|
||||
{
|
||||
"endpoint": "string",
|
||||
"from": "string",
|
||||
"public_key": "string",
|
||||
"to": "string",
|
||||
"to_type": "string",
|
||||
"wg_ip": "string"
|
||||
}
|
||||
]
|
||||
]
|
||||
],
|
||||
"road_warrior": "bool",
|
||||
"rw_clients": [
|
||||
"list",
|
||||
[
|
||||
"object",
|
||||
{
|
||||
"email": "string",
|
||||
"public_key": "string",
|
||||
"user_name": "string",
|
||||
"wg_ip": "string"
|
||||
}
|
||||
]
|
||||
],
|
||||
"subnet": "string",
|
||||
"type": "string",
|
||||
"vni": "number",
|
||||
"wg_ip": "string",
|
||||
"wg_listen_port": "number",
|
||||
"wg_private_key": "string",
|
||||
"wg_public_key": "string"
|
||||
}
|
||||
],
|
||||
"lab100-lab100-openwrt1": [
|
||||
"object",
|
||||
{
|
||||
"api_port": "number",
|
||||
"dhcp_mode": "string",
|
||||
"dhcp_role": "string",
|
||||
"hostname": "string",
|
||||
"lab_if": "string",
|
||||
"lab_name": "string",
|
||||
"mgmt_ip": "string",
|
||||
"peers": [
|
||||
"tuple",
|
||||
[
|
||||
[
|
||||
"object",
|
||||
{
|
||||
"endpoint": "string",
|
||||
"from": "string",
|
||||
"public_key": "string",
|
||||
"to": "string",
|
||||
"to_type": "string",
|
||||
"wg_ip": "string"
|
||||
}
|
||||
],
|
||||
[
|
||||
"object",
|
||||
{
|
||||
"endpoint": "string",
|
||||
"from": "string",
|
||||
"public_key": "string",
|
||||
"to": "string",
|
||||
"to_type": "string",
|
||||
"wg_ip": "string"
|
||||
}
|
||||
]
|
||||
]
|
||||
],
|
||||
"road_warrior": "bool",
|
||||
"rw_clients": [
|
||||
"list",
|
||||
[
|
||||
"object",
|
||||
{
|
||||
"email": "string",
|
||||
"public_key": "string",
|
||||
"user_name": "string",
|
||||
"wg_ip": "string"
|
||||
}
|
||||
]
|
||||
],
|
||||
"subnet": "string",
|
||||
"type": "string",
|
||||
"vni": "number",
|
||||
"wg_ip": "string",
|
||||
"wg_listen_port": "number",
|
||||
"wg_private_key": "string",
|
||||
"wg_public_key": "string"
|
||||
}
|
||||
],
|
||||
"lab100-lab100-rt1": [
|
||||
"object",
|
||||
{
|
||||
"api_port": "number",
|
||||
"dhcp_mode": "string",
|
||||
"dhcp_role": "string",
|
||||
"hostname": "string",
|
||||
"lab_if": "string",
|
||||
"lab_name": "string",
|
||||
"mgmt_ip": "string",
|
||||
"peers": [
|
||||
"tuple",
|
||||
[
|
||||
[
|
||||
"object",
|
||||
{
|
||||
"endpoint": "string",
|
||||
"from": "string",
|
||||
"public_key": "string",
|
||||
"to": "string",
|
||||
"to_type": "string",
|
||||
"wg_ip": "string"
|
||||
}
|
||||
],
|
||||
[
|
||||
"object",
|
||||
{
|
||||
"endpoint": "string",
|
||||
"from": "string",
|
||||
"public_key": "string",
|
||||
"to": "string",
|
||||
"to_type": "string",
|
||||
"wg_ip": "string"
|
||||
}
|
||||
]
|
||||
]
|
||||
],
|
||||
"road_warrior": "bool",
|
||||
"rw_clients": [
|
||||
"list",
|
||||
[
|
||||
"object",
|
||||
{
|
||||
"email": "string",
|
||||
"public_key": "string",
|
||||
"user_name": "string",
|
||||
"wg_ip": "string"
|
||||
}
|
||||
]
|
||||
],
|
||||
"subnet": "string",
|
||||
"type": "string",
|
||||
"vni": "number",
|
||||
"wg_ip": "string",
|
||||
"wg_listen_port": "number",
|
||||
"wg_private_key": "string",
|
||||
"wg_public_key": "string"
|
||||
}
|
||||
],
|
||||
"lab200-lab200-openwrt1": [
|
||||
"object",
|
||||
{
|
||||
"api_port": "number",
|
||||
"dhcp_mode": "string",
|
||||
"dhcp_role": "string",
|
||||
"hostname": "string",
|
||||
"lab_if": "string",
|
||||
"lab_name": "string",
|
||||
"mgmt_ip": "string",
|
||||
"peers": [
|
||||
"tuple",
|
||||
[
|
||||
[
|
||||
"object",
|
||||
{
|
||||
"endpoint": "string",
|
||||
"from": "string",
|
||||
"public_key": "string",
|
||||
"to": "string",
|
||||
"to_type": "string",
|
||||
"wg_ip": "string"
|
||||
}
|
||||
]
|
||||
]
|
||||
],
|
||||
"road_warrior": "bool",
|
||||
"rw_clients": [
|
||||
"list",
|
||||
[
|
||||
"object",
|
||||
{
|
||||
"email": "string",
|
||||
"public_key": "string",
|
||||
"user_name": "string",
|
||||
"wg_ip": "string"
|
||||
}
|
||||
]
|
||||
],
|
||||
"subnet": "string",
|
||||
"type": "string",
|
||||
"vni": "number",
|
||||
"wg_ip": "string",
|
||||
"wg_listen_port": "number",
|
||||
"wg_private_key": "string",
|
||||
"wg_public_key": "string"
|
||||
}
|
||||
],
|
||||
"lab200-lab200-openwrt2": [
|
||||
"object",
|
||||
{
|
||||
"api_port": "number",
|
||||
"dhcp_mode": "string",
|
||||
"dhcp_role": "string",
|
||||
"hostname": "string",
|
||||
"lab_if": "string",
|
||||
"lab_name": "string",
|
||||
"mgmt_ip": "string",
|
||||
"peers": [
|
||||
"tuple",
|
||||
[
|
||||
[
|
||||
"object",
|
||||
{
|
||||
"endpoint": "string",
|
||||
"from": "string",
|
||||
"public_key": "string",
|
||||
"to": "string",
|
||||
"to_type": "string",
|
||||
"wg_ip": "string"
|
||||
}
|
||||
]
|
||||
]
|
||||
],
|
||||
"road_warrior": "bool",
|
||||
"rw_clients": [
|
||||
"list",
|
||||
[
|
||||
"object",
|
||||
{
|
||||
"email": "string",
|
||||
"public_key": "string",
|
||||
"user_name": "string",
|
||||
"wg_ip": "string"
|
||||
}
|
||||
]
|
||||
],
|
||||
"subnet": "string",
|
||||
"type": "string",
|
||||
"vni": "number",
|
||||
"wg_ip": "string",
|
||||
"wg_listen_port": "number",
|
||||
"wg_private_key": "string",
|
||||
"wg_public_key": "string"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"value": {
|
||||
"lab100-lab100-gw1": {
|
||||
"api_port": 22,
|
||||
"dhcp_mode": "failover",
|
||||
"dhcp_role": "primary",
|
||||
"hostname": "lab100-gw1",
|
||||
"lab_if": "eth1",
|
||||
"lab_name": "lab100",
|
||||
"mgmt_ip": "192.168.1.11",
|
||||
"peers": [
|
||||
{
|
||||
"endpoint": "192.168.1.12",
|
||||
"from": "lab100-gw1",
|
||||
"public_key": "uRgOFY6Lml5bm1SkpMnJCDR7tqBu8U8KWinrzvwPwtq8",
|
||||
"to": "lab100-rt1",
|
||||
"to_type": "mikrotik",
|
||||
"wg_ip": "172.16.100.2"
|
||||
},
|
||||
{
|
||||
"endpoint": "192.168.1.14",
|
||||
"from": "lab100-gw1",
|
||||
"public_key": "kJ8w03s95KxcROZ4MDn0JGeqeZLBhkKoduoaiPIWvRUm",
|
||||
"to": "lab100-openwrt1",
|
||||
"to_type": "openwrt",
|
||||
"wg_ip": "172.16.100.3"
|
||||
}
|
||||
],
|
||||
"road_warrior": true,
|
||||
"rw_clients": [
|
||||
{
|
||||
"email": "alice.smith@company.com",
|
||||
"public_key": "e678c242ea2f29548224f4b469030be3bb9a65c6858fdf58d8eadc2c3305d91f",
|
||||
"user_name": "asmith",
|
||||
"wg_ip": "172.16.100.13"
|
||||
},
|
||||
{
|
||||
"email": "lutz.finsterle@mahle.com",
|
||||
"public_key": "bd99a73acd6f6d7c925996d2aaa4aa322e7443d1bd857580b2cfbbf7dd6086ab",
|
||||
"user_name": "finstl1",
|
||||
"wg_ip": "172.16.100.14"
|
||||
},
|
||||
{
|
||||
"email": "john.doe@company.com",
|
||||
"public_key": "8d25d50ed5012d300f52770233c7517cd589d9be9f761e8bf4612dc904369c23",
|
||||
"user_name": "jdoe",
|
||||
"wg_ip": "172.16.100.15"
|
||||
}
|
||||
],
|
||||
"subnet": "10.100.0.0/24",
|
||||
"type": "linux",
|
||||
"vni": 100,
|
||||
"wg_ip": "172.16.100.1",
|
||||
"wg_listen_port": 51920,
|
||||
"wg_private_key": "XivDZJP1Jt0hMzkg28XcbImykp2OepiajCdnSThpJ4EN",
|
||||
"wg_public_key": "54c1598be162afda063f4dd3ba968f94ac3a79998b441c9dcebef3e1f800edc9"
|
||||
},
|
||||
"lab100-lab100-openwrt1": {
|
||||
"api_port": 22,
|
||||
"dhcp_mode": "failover",
|
||||
"dhcp_role": "backup",
|
||||
"hostname": "lab100-openwrt1",
|
||||
"lab_if": "eth1",
|
||||
"lab_name": "lab100",
|
||||
"mgmt_ip": "192.168.1.14",
|
||||
"peers": [
|
||||
{
|
||||
"endpoint": "192.168.1.11",
|
||||
"from": "lab100-openwrt1",
|
||||
"public_key": "XivDZJP1Jt0hMzkg28XcbImykp2OepiajCdnSThpJ4EN",
|
||||
"to": "lab100-gw1",
|
||||
"to_type": "linux",
|
||||
"wg_ip": "172.16.100.1"
|
||||
},
|
||||
{
|
||||
"endpoint": "192.168.1.12",
|
||||
"from": "lab100-openwrt1",
|
||||
"public_key": "uRgOFY6Lml5bm1SkpMnJCDR7tqBu8U8KWinrzvwPwtq8",
|
||||
"to": "lab100-rt1",
|
||||
"to_type": "mikrotik",
|
||||
"wg_ip": "172.16.100.2"
|
||||
}
|
||||
],
|
||||
"road_warrior": true,
|
||||
"rw_clients": [
|
||||
{
|
||||
"email": "alice.smith@company.com",
|
||||
"public_key": "e678c242ea2f29548224f4b469030be3bb9a65c6858fdf58d8eadc2c3305d91f",
|
||||
"user_name": "asmith",
|
||||
"wg_ip": "172.16.100.13"
|
||||
},
|
||||
{
|
||||
"email": "lutz.finsterle@mahle.com",
|
||||
"public_key": "bd99a73acd6f6d7c925996d2aaa4aa322e7443d1bd857580b2cfbbf7dd6086ab",
|
||||
"user_name": "finstl1",
|
||||
"wg_ip": "172.16.100.14"
|
||||
},
|
||||
{
|
||||
"email": "john.doe@company.com",
|
||||
"public_key": "8d25d50ed5012d300f52770233c7517cd589d9be9f761e8bf4612dc904369c23",
|
||||
"user_name": "jdoe",
|
||||
"wg_ip": "172.16.100.15"
|
||||
}
|
||||
],
|
||||
"subnet": "10.100.0.0/24",
|
||||
"type": "openwrt",
|
||||
"vni": 100,
|
||||
"wg_ip": "172.16.100.3",
|
||||
"wg_listen_port": 51920,
|
||||
"wg_private_key": "kJ8w03s95KxcROZ4MDn0JGeqeZLBhkKoduoaiPIWvRUm",
|
||||
"wg_public_key": "121507f174a5822f22f1e939b2940dfdb83aa6f2ed2f9d9032a02435f2fe1838"
|
||||
},
|
||||
"lab100-lab100-rt1": {
|
||||
"api_port": 8728,
|
||||
"dhcp_mode": "failover",
|
||||
"dhcp_role": "secondary",
|
||||
"hostname": "lab100-rt1",
|
||||
"lab_if": "ether2",
|
||||
"lab_name": "lab100",
|
||||
"mgmt_ip": "192.168.1.12",
|
||||
"peers": [
|
||||
{
|
||||
"endpoint": "192.168.1.11",
|
||||
"from": "lab100-rt1",
|
||||
"public_key": "XivDZJP1Jt0hMzkg28XcbImykp2OepiajCdnSThpJ4EN",
|
||||
"to": "lab100-gw1",
|
||||
"to_type": "linux",
|
||||
"wg_ip": "172.16.100.1"
|
||||
},
|
||||
{
|
||||
"endpoint": "192.168.1.14",
|
||||
"from": "lab100-rt1",
|
||||
"public_key": "kJ8w03s95KxcROZ4MDn0JGeqeZLBhkKoduoaiPIWvRUm",
|
||||
"to": "lab100-openwrt1",
|
||||
"to_type": "openwrt",
|
||||
"wg_ip": "172.16.100.3"
|
||||
}
|
||||
],
|
||||
"road_warrior": true,
|
||||
"rw_clients": [
|
||||
{
|
||||
"email": "alice.smith@company.com",
|
||||
"public_key": "e678c242ea2f29548224f4b469030be3bb9a65c6858fdf58d8eadc2c3305d91f",
|
||||
"user_name": "asmith",
|
||||
"wg_ip": "172.16.100.13"
|
||||
},
|
||||
{
|
||||
"email": "lutz.finsterle@mahle.com",
|
||||
"public_key": "bd99a73acd6f6d7c925996d2aaa4aa322e7443d1bd857580b2cfbbf7dd6086ab",
|
||||
"user_name": "finstl1",
|
||||
"wg_ip": "172.16.100.14"
|
||||
},
|
||||
{
|
||||
"email": "john.doe@company.com",
|
||||
"public_key": "8d25d50ed5012d300f52770233c7517cd589d9be9f761e8bf4612dc904369c23",
|
||||
"user_name": "jdoe",
|
||||
"wg_ip": "172.16.100.15"
|
||||
}
|
||||
],
|
||||
"subnet": "10.100.0.0/24",
|
||||
"type": "mikrotik",
|
||||
"vni": 100,
|
||||
"wg_ip": "172.16.100.2",
|
||||
"wg_listen_port": 51920,
|
||||
"wg_private_key": "uRgOFY6Lml5bm1SkpMnJCDR7tqBu8U8KWinrzvwPwtq8",
|
||||
"wg_public_key": "0ad05299182076819239dc37e57e02fc1ce7ff1fca122c797bf891802da5940f"
|
||||
},
|
||||
"lab200-lab200-openwrt1": {
|
||||
"api_port": 22,
|
||||
"dhcp_mode": "simple",
|
||||
"dhcp_role": "primary",
|
||||
"hostname": "lab200-openwrt1",
|
||||
"lab_if": "lan1",
|
||||
"lab_name": "lab200",
|
||||
"mgmt_ip": "192.168.1.21",
|
||||
"peers": [
|
||||
{
|
||||
"endpoint": "192.168.1.22",
|
||||
"from": "lab200-openwrt1",
|
||||
"public_key": "LbBa8hG48wqJCOPQgEi4LKIWE5nrK1HiLQdsIlbxblMW",
|
||||
"to": "lab200-openwrt2",
|
||||
"to_type": "openwrt",
|
||||
"wg_ip": "172.16.200.2"
|
||||
}
|
||||
],
|
||||
"road_warrior": false,
|
||||
"rw_clients": [],
|
||||
"subnet": "10.200.0.0/24",
|
||||
"type": "openwrt",
|
||||
"vni": 200,
|
||||
"wg_ip": "172.16.200.1",
|
||||
"wg_listen_port": 52020,
|
||||
"wg_private_key": "AQMhcavQwY7RBBk3qnDBP3sxjTGie6Fw6QUvwnLXI3Yo",
|
||||
"wg_public_key": "1045e3900850df7aba0061f1fd0dde7e96c2c6b44903b5c769308c5a89ba5116"
|
||||
},
|
||||
"lab200-lab200-openwrt2": {
|
||||
"api_port": 22,
|
||||
"dhcp_mode": "simple",
|
||||
"dhcp_role": "secondary",
|
||||
"hostname": "lab200-openwrt2",
|
||||
"lab_if": "lan1",
|
||||
"lab_name": "lab200",
|
||||
"mgmt_ip": "192.168.1.22",
|
||||
"peers": [
|
||||
{
|
||||
"endpoint": "192.168.1.21",
|
||||
"from": "lab200-openwrt2",
|
||||
"public_key": "AQMhcavQwY7RBBk3qnDBP3sxjTGie6Fw6QUvwnLXI3Yo",
|
||||
"to": "lab200-openwrt1",
|
||||
"to_type": "openwrt",
|
||||
"wg_ip": "172.16.200.1"
|
||||
}
|
||||
],
|
||||
"road_warrior": false,
|
||||
"rw_clients": [],
|
||||
"subnet": "10.200.0.0/24",
|
||||
"type": "openwrt",
|
||||
"vni": 200,
|
||||
"wg_ip": "172.16.200.2",
|
||||
"wg_listen_port": 52020,
|
||||
"wg_private_key": "LbBa8hG48wqJCOPQgEi4LKIWE5nrK1HiLQdsIlbxblMW",
|
||||
"wg_public_key": "33e4ef4b6e8dbcf6b78ba1652eed1941149578a308429776a82b779db70ff179"
|
||||
}
|
||||
}
|
||||
},
|
||||
"road_warrior_configs": {
|
||||
"sensitive": true,
|
||||
"type": [
|
||||
"object",
|
||||
{
|
||||
"asmith-lab100": [
|
||||
"object",
|
||||
{
|
||||
"email": "string",
|
||||
"lab_name": "string",
|
||||
"peers": [
|
||||
"tuple",
|
||||
[
|
||||
[
|
||||
"object",
|
||||
{
|
||||
"endpoint": "string",
|
||||
"gateway": "string",
|
||||
"gw_type": "string",
|
||||
"listen_port": "number",
|
||||
"public_key": "string",
|
||||
"wg_ip": "string"
|
||||
}
|
||||
],
|
||||
[
|
||||
"object",
|
||||
{
|
||||
"endpoint": "string",
|
||||
"gateway": "string",
|
||||
"gw_type": "string",
|
||||
"listen_port": "number",
|
||||
"public_key": "string",
|
||||
"wg_ip": "string"
|
||||
}
|
||||
],
|
||||
[
|
||||
"object",
|
||||
{
|
||||
"endpoint": "string",
|
||||
"gateway": "string",
|
||||
"gw_type": "string",
|
||||
"listen_port": "number",
|
||||
"public_key": "string",
|
||||
"wg_ip": "string"
|
||||
}
|
||||
]
|
||||
]
|
||||
],
|
||||
"subnet": "string",
|
||||
"user_name": "string",
|
||||
"wg_ip": "string",
|
||||
"wg_private_key": "string",
|
||||
"wg_public_key": "string"
|
||||
}
|
||||
],
|
||||
"finstl1-lab100": [
|
||||
"object",
|
||||
{
|
||||
"email": "string",
|
||||
"lab_name": "string",
|
||||
"peers": [
|
||||
"tuple",
|
||||
[
|
||||
[
|
||||
"object",
|
||||
{
|
||||
"endpoint": "string",
|
||||
"gateway": "string",
|
||||
"gw_type": "string",
|
||||
"listen_port": "number",
|
||||
"public_key": "string",
|
||||
"wg_ip": "string"
|
||||
}
|
||||
],
|
||||
[
|
||||
"object",
|
||||
{
|
||||
"endpoint": "string",
|
||||
"gateway": "string",
|
||||
"gw_type": "string",
|
||||
"listen_port": "number",
|
||||
"public_key": "string",
|
||||
"wg_ip": "string"
|
||||
}
|
||||
],
|
||||
[
|
||||
"object",
|
||||
{
|
||||
"endpoint": "string",
|
||||
"gateway": "string",
|
||||
"gw_type": "string",
|
||||
"listen_port": "number",
|
||||
"public_key": "string",
|
||||
"wg_ip": "string"
|
||||
}
|
||||
]
|
||||
]
|
||||
],
|
||||
"subnet": "string",
|
||||
"user_name": "string",
|
||||
"wg_ip": "string",
|
||||
"wg_private_key": "string",
|
||||
"wg_public_key": "string"
|
||||
}
|
||||
],
|
||||
"finstl1-lab200": [
|
||||
"object",
|
||||
{
|
||||
"email": "string",
|
||||
"lab_name": "string",
|
||||
"peers": [
|
||||
"tuple",
|
||||
[
|
||||
[
|
||||
"object",
|
||||
{
|
||||
"endpoint": "string",
|
||||
"gateway": "string",
|
||||
"gw_type": "string",
|
||||
"listen_port": "number",
|
||||
"public_key": "string",
|
||||
"wg_ip": "string"
|
||||
}
|
||||
],
|
||||
[
|
||||
"object",
|
||||
{
|
||||
"endpoint": "string",
|
||||
"gateway": "string",
|
||||
"gw_type": "string",
|
||||
"listen_port": "number",
|
||||
"public_key": "string",
|
||||
"wg_ip": "string"
|
||||
}
|
||||
]
|
||||
]
|
||||
],
|
||||
"subnet": "string",
|
||||
"user_name": "string",
|
||||
"wg_ip": "string",
|
||||
"wg_private_key": "string",
|
||||
"wg_public_key": "string"
|
||||
}
|
||||
],
|
||||
"jdoe-lab100": [
|
||||
"object",
|
||||
{
|
||||
"email": "string",
|
||||
"lab_name": "string",
|
||||
"peers": [
|
||||
"tuple",
|
||||
[
|
||||
[
|
||||
"object",
|
||||
{
|
||||
"endpoint": "string",
|
||||
"gateway": "string",
|
||||
"gw_type": "string",
|
||||
"listen_port": "number",
|
||||
"public_key": "string",
|
||||
"wg_ip": "string"
|
||||
}
|
||||
],
|
||||
[
|
||||
"object",
|
||||
{
|
||||
"endpoint": "string",
|
||||
"gateway": "string",
|
||||
"gw_type": "string",
|
||||
"listen_port": "number",
|
||||
"public_key": "string",
|
||||
"wg_ip": "string"
|
||||
}
|
||||
],
|
||||
[
|
||||
"object",
|
||||
{
|
||||
"endpoint": "string",
|
||||
"gateway": "string",
|
||||
"gw_type": "string",
|
||||
"listen_port": "number",
|
||||
"public_key": "string",
|
||||
"wg_ip": "string"
|
||||
}
|
||||
]
|
||||
]
|
||||
],
|
||||
"subnet": "string",
|
||||
"user_name": "string",
|
||||
"wg_ip": "string",
|
||||
"wg_private_key": "string",
|
||||
"wg_public_key": "string"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"value": {
|
||||
"asmith-lab100": {
|
||||
"email": "alice.smith@company.com",
|
||||
"lab_name": "lab100",
|
||||
"peers": [
|
||||
{
|
||||
"endpoint": "192.168.1.11",
|
||||
"gateway": "lab100-gw1",
|
||||
"gw_type": "linux",
|
||||
"listen_port": 51920,
|
||||
"public_key": "XivDZJP1Jt0hMzkg28XcbImykp2OepiajCdnSThpJ4EN",
|
||||
"wg_ip": "172.16.100.1"
|
||||
},
|
||||
{
|
||||
"endpoint": "192.168.1.12",
|
||||
"gateway": "lab100-rt1",
|
||||
"gw_type": "mikrotik",
|
||||
"listen_port": 51920,
|
||||
"public_key": "uRgOFY6Lml5bm1SkpMnJCDR7tqBu8U8KWinrzvwPwtq8",
|
||||
"wg_ip": "172.16.100.2"
|
||||
},
|
||||
{
|
||||
"endpoint": "192.168.1.14",
|
||||
"gateway": "lab100-openwrt1",
|
||||
"gw_type": "openwrt",
|
||||
"listen_port": 51920,
|
||||
"public_key": "kJ8w03s95KxcROZ4MDn0JGeqeZLBhkKoduoaiPIWvRUm",
|
||||
"wg_ip": "172.16.100.3"
|
||||
}
|
||||
],
|
||||
"subnet": "10.100.0.0/24",
|
||||
"user_name": "asmith",
|
||||
"wg_ip": "172.16.100.13",
|
||||
"wg_private_key": "WzAAWAf9paoVPnRblDVBMUqfL2ieuMDeaEwr8xLgP3Hm",
|
||||
"wg_public_key": "e678c242ea2f29548224f4b469030be3bb9a65c6858fdf58d8eadc2c3305d91f"
|
||||
},
|
||||
"finstl1-lab100": {
|
||||
"email": "lutz.finsterle@mahle.com",
|
||||
"lab_name": "lab100",
|
||||
"peers": [
|
||||
{
|
||||
"endpoint": "192.168.1.11",
|
||||
"gateway": "lab100-gw1",
|
||||
"gw_type": "linux",
|
||||
"listen_port": 51920,
|
||||
"public_key": "XivDZJP1Jt0hMzkg28XcbImykp2OepiajCdnSThpJ4EN",
|
||||
"wg_ip": "172.16.100.1"
|
||||
},
|
||||
{
|
||||
"endpoint": "192.168.1.12",
|
||||
"gateway": "lab100-rt1",
|
||||
"gw_type": "mikrotik",
|
||||
"listen_port": 51920,
|
||||
"public_key": "uRgOFY6Lml5bm1SkpMnJCDR7tqBu8U8KWinrzvwPwtq8",
|
||||
"wg_ip": "172.16.100.2"
|
||||
},
|
||||
{
|
||||
"endpoint": "192.168.1.14",
|
||||
"gateway": "lab100-openwrt1",
|
||||
"gw_type": "openwrt",
|
||||
"listen_port": 51920,
|
||||
"public_key": "kJ8w03s95KxcROZ4MDn0JGeqeZLBhkKoduoaiPIWvRUm",
|
||||
"wg_ip": "172.16.100.3"
|
||||
}
|
||||
],
|
||||
"subnet": "10.100.0.0/24",
|
||||
"user_name": "finstl1",
|
||||
"wg_ip": "172.16.100.14",
|
||||
"wg_private_key": "qGbsVHrl6vOdqrVN0xxWzbzgJUGCCb6MZt4FEhZUS6WV",
|
||||
"wg_public_key": "bd99a73acd6f6d7c925996d2aaa4aa322e7443d1bd857580b2cfbbf7dd6086ab"
|
||||
},
|
||||
"finstl1-lab200": {
|
||||
"email": "lutz.finsterle@mahle.com",
|
||||
"lab_name": "lab200",
|
||||
"peers": [
|
||||
{
|
||||
"endpoint": "192.168.1.21",
|
||||
"gateway": "lab200-openwrt1",
|
||||
"gw_type": "openwrt",
|
||||
"listen_port": 52020,
|
||||
"public_key": "AQMhcavQwY7RBBk3qnDBP3sxjTGie6Fw6QUvwnLXI3Yo",
|
||||
"wg_ip": "172.16.200.1"
|
||||
},
|
||||
{
|
||||
"endpoint": "192.168.1.22",
|
||||
"gateway": "lab200-openwrt2",
|
||||
"gw_type": "openwrt",
|
||||
"listen_port": 52020,
|
||||
"public_key": "LbBa8hG48wqJCOPQgEi4LKIWE5nrK1HiLQdsIlbxblMW",
|
||||
"wg_ip": "172.16.200.2"
|
||||
}
|
||||
],
|
||||
"subnet": "10.200.0.0/24",
|
||||
"user_name": "finstl1",
|
||||
"wg_ip": "172.16.200.12",
|
||||
"wg_private_key": "NsbuQIZK3NSpl8PlFcr25W11qH7PYplhxku2YjMGMh6j",
|
||||
"wg_public_key": "d64d224ad309c89ebb46b5626fe3392d7be16cede395dedf75d44665778f4dcf"
|
||||
},
|
||||
"jdoe-lab100": {
|
||||
"email": "john.doe@company.com",
|
||||
"lab_name": "lab100",
|
||||
"peers": [
|
||||
{
|
||||
"endpoint": "192.168.1.11",
|
||||
"gateway": "lab100-gw1",
|
||||
"gw_type": "linux",
|
||||
"listen_port": 51920,
|
||||
"public_key": "XivDZJP1Jt0hMzkg28XcbImykp2OepiajCdnSThpJ4EN",
|
||||
"wg_ip": "172.16.100.1"
|
||||
},
|
||||
{
|
||||
"endpoint": "192.168.1.12",
|
||||
"gateway": "lab100-rt1",
|
||||
"gw_type": "mikrotik",
|
||||
"listen_port": 51920,
|
||||
"public_key": "uRgOFY6Lml5bm1SkpMnJCDR7tqBu8U8KWinrzvwPwtq8",
|
||||
"wg_ip": "172.16.100.2"
|
||||
},
|
||||
{
|
||||
"endpoint": "192.168.1.14",
|
||||
"gateway": "lab100-openwrt1",
|
||||
"gw_type": "openwrt",
|
||||
"listen_port": 51920,
|
||||
"public_key": "kJ8w03s95KxcROZ4MDn0JGeqeZLBhkKoduoaiPIWvRUm",
|
||||
"wg_ip": "172.16.100.3"
|
||||
}
|
||||
],
|
||||
"subnet": "10.100.0.0/24",
|
||||
"user_name": "jdoe",
|
||||
"wg_ip": "172.16.100.15",
|
||||
"wg_private_key": "yDIq0PioXao7aLR0e6FIhQH58NkFSH8q1EDSqvDIE6K1",
|
||||
"wg_public_key": "8d25d50ed5012d300f52770233c7517cd589d9be9f761e8bf4612dc904369c23"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
92
terraform/outputs.tf
Normal file
92
terraform/outputs.tf
Normal file
@@ -0,0 +1,92 @@
|
||||
output "gateway_config" {
|
||||
value = {
|
||||
for key, gw in local.all_gateways : key => {
|
||||
hostname = gw.hostname
|
||||
type = gw.type
|
||||
mgmt_ip = gw.mgmt_ip
|
||||
api_port = gw.api_port
|
||||
lab_name = gw.lab_name
|
||||
vni = gw.vni
|
||||
subnet = gw.subnet
|
||||
lab_if = gw.lab_if
|
||||
dhcp_role = gw.dhcp_role
|
||||
dhcp_mode = gw.dhcp_mode
|
||||
road_warrior = gw.road_warrior
|
||||
wg_private_key = random_password.wg_private_key[key].result
|
||||
wg_public_key = random_id.wg_public_key[key].hex
|
||||
wg_ip = local.gateway_wg_ips[key]
|
||||
wg_listen_port = 51820 + gw.vni
|
||||
peers = [
|
||||
for peer_list in local.wg_gateway_peers[gw.lab_name] :
|
||||
peer_list if peer_list[0].from == gw.hostname
|
||||
][0]
|
||||
# Road warrior clients that should be allowed
|
||||
rw_clients = gw.road_warrior ? [
|
||||
for rw_key, rw in local.road_warrior_clients :
|
||||
{
|
||||
user_name = rw.user_name
|
||||
email = rw.email
|
||||
wg_ip = local.rw_wg_ips[rw_key]
|
||||
public_key = random_id.rw_public_key[rw_key].hex
|
||||
} if rw.lab_name == gw.lab_name
|
||||
] : []
|
||||
}
|
||||
}
|
||||
sensitive = true
|
||||
}
|
||||
|
||||
output "road_warrior_configs" {
|
||||
value = {
|
||||
for key, rw in local.road_warrior_clients : key => {
|
||||
user_name = rw.user_name
|
||||
email = rw.email
|
||||
lab_name = rw.lab_name
|
||||
subnet = rw.subnet
|
||||
wg_private_key = random_password.rw_private_key[key].result
|
||||
wg_public_key = random_id.rw_public_key[key].hex
|
||||
wg_ip = local.rw_wg_ips[key]
|
||||
peers = local.wg_rw_peers[key]
|
||||
}
|
||||
}
|
||||
sensitive = true
|
||||
}
|
||||
|
||||
output "ansible_inventory" {
|
||||
value = yamlencode({
|
||||
all = {
|
||||
children = {
|
||||
linux_gateways = {
|
||||
hosts = {
|
||||
for key, gw in local.all_gateways :
|
||||
gw.hostname => {
|
||||
ansible_host = gw.mgmt_ip
|
||||
lab_name = gw.lab_name
|
||||
} if gw.type == "linux"
|
||||
}
|
||||
}
|
||||
mikrotik_gateways = {
|
||||
hosts = {
|
||||
for key, gw in local.all_gateways :
|
||||
gw.hostname => {
|
||||
ansible_host = gw.mgmt_ip
|
||||
ansible_connection = "network_cli"
|
||||
ansible_network_os = "routeros"
|
||||
ansible_port = gw.api_port
|
||||
lab_name = gw.lab_name
|
||||
} if gw.type == "mikrotik"
|
||||
}
|
||||
}
|
||||
openwrt_gateways = {
|
||||
hosts = {
|
||||
for key, gw in local.all_gateways :
|
||||
gw.hostname => {
|
||||
ansible_host = gw.mgmt_ip
|
||||
ansible_port = gw.api_port
|
||||
lab_name = gw.lab_name
|
||||
} if gw.type == "openwrt"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
1521
terraform/terraform.tfstate
Normal file
1521
terraform/terraform.tfstate
Normal file
File diff suppressed because it is too large
Load Diff
1194
terraform/terraform.tfstate.backup
Normal file
1194
terraform/terraform.tfstate.backup
Normal file
File diff suppressed because it is too large
Load Diff
76
terraform/terraform.tfvars
Normal file
76
terraform/terraform.tfvars
Normal file
@@ -0,0 +1,76 @@
|
||||
lab_networks = {
|
||||
lab100 = {
|
||||
vni = 100
|
||||
subnet = "10.100.0.0/24"
|
||||
wireguard_net = "172.16.100.0/24"
|
||||
dhcp_mode = "failover" # ISC DHCP with failover
|
||||
road_warrior = true
|
||||
gateways = [
|
||||
{
|
||||
hostname = "lab100-gw1"
|
||||
type = "linux"
|
||||
mgmt_ip = "192.168.1.11"
|
||||
api_port = 22
|
||||
lab_if = "eth1"
|
||||
dhcp_role = "primary"
|
||||
},
|
||||
{
|
||||
hostname = "lab100-rt1"
|
||||
type = "mikrotik"
|
||||
mgmt_ip = "192.168.1.12"
|
||||
api_port = 8728
|
||||
lab_if = "ether2"
|
||||
dhcp_role = "secondary"
|
||||
},
|
||||
{
|
||||
hostname = "lab100-openwrt1"
|
||||
type = "openwrt"
|
||||
mgmt_ip = "192.168.1.14"
|
||||
api_port = 22
|
||||
lab_if = "eth1"
|
||||
dhcp_role = "backup"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
lab200 = {
|
||||
vni = 200
|
||||
subnet = "10.200.0.0/24"
|
||||
wireguard_net = "172.16.200.0/24"
|
||||
dhcp_mode = "simple" # dnsmasq with split ranges
|
||||
road_warrior = false
|
||||
gateways = [
|
||||
{
|
||||
hostname = "lab200-openwrt1"
|
||||
type = "openwrt"
|
||||
mgmt_ip = "192.168.1.21"
|
||||
api_port = 22
|
||||
lab_if = "lan1"
|
||||
dhcp_role = "primary"
|
||||
},
|
||||
{
|
||||
hostname = "lab200-openwrt2"
|
||||
type = "openwrt"
|
||||
mgmt_ip = "192.168.1.22"
|
||||
api_port = 22
|
||||
lab_if = "lan1"
|
||||
dhcp_role = "secondary"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
road_warrior_users = {
|
||||
"jdoe" = {
|
||||
email = "john.doe@company.com"
|
||||
labs = ["lab100"]
|
||||
}
|
||||
"asmith" = {
|
||||
email = "alice.smith@company.com"
|
||||
labs = ["lab100"]
|
||||
}
|
||||
"finstl1" = {
|
||||
email = "lutz.finsterle@mahle.com"
|
||||
labs = ["lab100","lab200"]
|
||||
}
|
||||
}
|
||||
72
terraform/terraform.tfvars.example
Normal file
72
terraform/terraform.tfvars.example
Normal file
@@ -0,0 +1,72 @@
|
||||
lab_networks = {
|
||||
lab100 = {
|
||||
vni = 100
|
||||
subnet = "10.100.0.0/24"
|
||||
wireguard_net = "172.16.100.0/24"
|
||||
dhcp_mode = "failover" # ISC DHCP with failover
|
||||
road_warrior = true
|
||||
gateways = [
|
||||
{
|
||||
hostname = "lab100-gw1"
|
||||
type = "linux"
|
||||
mgmt_ip = "192.168.1.11"
|
||||
api_port = 22
|
||||
lab_if = "eth1"
|
||||
dhcp_role = "primary"
|
||||
},
|
||||
{
|
||||
hostname = "lab100-rt1"
|
||||
type = "mikrotik"
|
||||
mgmt_ip = "192.168.1.12"
|
||||
api_port = 8728
|
||||
lab_if = "ether2"
|
||||
dhcp_role = "secondary"
|
||||
},
|
||||
{
|
||||
hostname = "lab100-openwrt1"
|
||||
type = "openwrt"
|
||||
mgmt_ip = "192.168.1.14"
|
||||
api_port = 22
|
||||
lab_if = "eth1"
|
||||
dhcp_role = "backup"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
lab200 = {
|
||||
vni = 200
|
||||
subnet = "10.200.0.0/24"
|
||||
wireguard_net = "172.16.200.0/24"
|
||||
dhcp_mode = "simple" # dnsmasq with split ranges
|
||||
road_warrior = false
|
||||
gateways = [
|
||||
{
|
||||
hostname = "lab200-openwrt1"
|
||||
type = "openwrt"
|
||||
mgmt_ip = "192.168.1.21"
|
||||
api_port = 22
|
||||
lab_if = "lan1"
|
||||
dhcp_role = "primary"
|
||||
},
|
||||
{
|
||||
hostname = "lab200-openwrt2"
|
||||
type = "openwrt"
|
||||
mgmt_ip = "192.168.1.22"
|
||||
api_port = 22
|
||||
lab_if = "lan1"
|
||||
dhcp_role = "secondary"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
road_warrior_users = {
|
||||
"jdoe" = {
|
||||
email = "john.doe@company.com"
|
||||
labs = ["lab100"]
|
||||
}
|
||||
"asmith" = {
|
||||
email = "alice.smith@company.com"
|
||||
labs = ["lab100"]
|
||||
}
|
||||
}
|
||||
27
terraform/variables.tf
Normal file
27
terraform/variables.tf
Normal file
@@ -0,0 +1,27 @@
|
||||
variable "lab_networks" {
|
||||
description = "Map of lab networks with their configuration"
|
||||
type = map(object({
|
||||
vni = number
|
||||
subnet = string
|
||||
wireguard_net = string
|
||||
dhcp_mode = string
|
||||
road_warrior = bool
|
||||
gateways = list(object({
|
||||
hostname = string
|
||||
type = string
|
||||
mgmt_ip = string
|
||||
api_port = number
|
||||
lab_if = string
|
||||
dhcp_role = string
|
||||
}))
|
||||
}))
|
||||
}
|
||||
|
||||
variable "road_warrior_users" {
|
||||
description = "Map of road warrior users and their lab access"
|
||||
type = map(object({
|
||||
email = string
|
||||
labs = list(string)
|
||||
}))
|
||||
default = {}
|
||||
}
|
||||
Reference in New Issue
Block a user