--- # Configure laconic-mia-sw01 for validator traffic relay via dedicated GRE tunnel # # Creates a NEW GRE tunnel (Tunnel100) separate from the DoubleZero-managed # Tunnel500. The DZ agent controls Tunnel500's ACL (SEC-USER-500-IN) and # overwrites any custom entries, so we cannot use it for validator traffic # with src 137.239.194.65. # # Tunnel100 uses mia-sw01's free LAN IP (209.42.167.137) as the tunnel # source, and biscayne's public IP (186.233.184.235) as the destination. # This tunnel carries traffic over the ISP uplink, completely independent # of the DoubleZero overlay. # # Inbound: was-sw01 → backbone Et4/1 → mia-sw01 → Tunnel100 → biscayne # Outbound: biscayne → Tunnel100 → mia-sw01 → backbone Et4/1 → was-sw01 # # Usage: # # Pre-flight checks only (safe, read-only) # ansible-playbook -i inventory/switches.yml playbooks/ashburn-relay-mia-sw01.yml # # # Apply config (after reviewing pre-flight output) # ansible-playbook -i inventory/switches.yml playbooks/ashburn-relay-mia-sw01.yml \ # -e apply=true # # # Commit persisted config # ansible-playbook -i inventory/switches.yml playbooks/ashburn-relay-mia-sw01.yml -e commit=true # # # Rollback # ansible-playbook -i inventory/switches.yml playbooks/ashburn-relay-mia-sw01.yml -e rollback=true - name: Configure mia-sw01 validator relay tunnel hosts: mia-sw01 gather_facts: false vars: ashburn_ip: 137.239.194.65 biscayne_ip: 186.233.184.235 apply: false commit: false rollback: false # New tunnel — not managed by DZ agent tunnel_interface: Tunnel100 tunnel_source_ip: 209.42.167.137 # mia-sw01 free LAN IP tunnel_local: 169.254.100.0 # /31 link, mia-sw01 side tunnel_remote: 169.254.100.1 # /31 link, biscayne side tunnel_acl: SEC-VALIDATOR-100-IN # Loopback for tunnel source (so it's always up) tunnel_source_lo: Loopback101 backbone_interface: Ethernet4/1 backbone_peer: 172.16.1.188 # was-sw01 backbone IP session_name: validator-tunnel checkpoint_name: pre-validator-tunnel tasks: # ------------------------------------------------------------------ # Rollback path # ------------------------------------------------------------------ - name: Rollback to checkpoint when: rollback | bool block: - name: Execute rollback arista.eos.eos_command: commands: - "rollback running-config checkpoint {{ checkpoint_name }}" - write memory register: rollback_result - name: Show rollback result ansible.builtin.debug: var: rollback_result.stdout_lines - name: End play after rollback ansible.builtin.meta: end_play # ------------------------------------------------------------------ # Commit finalization # ------------------------------------------------------------------ - name: Finalize pending session when: commit | bool block: - name: Commit session and write memory arista.eos.eos_command: commands: - "configure session {{ session_name }} commit" - write memory register: commit_result - name: Show commit result ansible.builtin.debug: var: commit_result.stdout_lines - name: End play after commit ansible.builtin.meta: end_play # ------------------------------------------------------------------ # Pre-flight checks (always run unless commit/rollback) # ------------------------------------------------------------------ - name: Check existing tunnel interfaces arista.eos.eos_command: commands: - show ip interface brief | include Tunnel register: existing_tunnels tags: [preflight] - name: Display existing tunnels ansible.builtin.debug: var: existing_tunnels.stdout_lines tags: [preflight] - name: Check if Tunnel100 already exists arista.eos.eos_command: commands: - "show running-config interfaces {{ tunnel_interface }}" register: tunnel_config tags: [preflight] - name: Display Tunnel100 config ansible.builtin.debug: var: tunnel_config.stdout_lines tags: [preflight] - name: Check if Loopback101 already exists arista.eos.eos_command: commands: - "show running-config interfaces {{ tunnel_source_lo }}" register: lo_config tags: [preflight] - name: Display Loopback101 config ansible.builtin.debug: var: lo_config.stdout_lines tags: [preflight] - name: Check route for ashburn IP arista.eos.eos_command: commands: - "show ip route {{ ashburn_ip }}" register: route_check tags: [preflight] - name: Display route check ansible.builtin.debug: var: route_check.stdout_lines tags: [preflight] - name: Pre-flight summary when: not (apply | bool) ansible.builtin.debug: msg: | === Pre-flight complete === Review the output above: 1. Does {{ tunnel_interface }} already exist? 2. Does {{ tunnel_source_lo }} already exist? 3. Current route for {{ ashburn_ip }} Planned config: - {{ tunnel_source_lo }}: {{ tunnel_source_ip }}/32 - {{ tunnel_interface }}: GRE src {{ tunnel_source_ip }} dst {{ biscayne_ip }} link address {{ tunnel_local }}/31 ACL {{ tunnel_acl }}: permit src {{ ashburn_ip }}, permit src {{ tunnel_remote }} - Route: {{ ashburn_ip }}/32 via {{ tunnel_remote }} - Outbound default for tunnel traffic: 0.0.0.0/0 via {{ backbone_interface }} {{ backbone_peer }} To apply config: ansible-playbook -i inventory/switches.yml playbooks/ashburn-relay-mia-sw01.yml \ -e apply=true tags: [preflight] - name: End play if not applying when: not (apply | bool) ansible.builtin.meta: end_play # ------------------------------------------------------------------ # Apply config via session with 5-minute auto-revert # ------------------------------------------------------------------ - name: Save checkpoint arista.eos.eos_command: commands: - "configure checkpoint save {{ checkpoint_name }}" - name: Apply config session arista.eos.eos_command: commands: - command: "configure session {{ session_name }}" # Loopback for tunnel source (always-up interface) - command: "interface {{ tunnel_source_lo }}" - command: "ip address {{ tunnel_source_ip }}/32" - command: exit # ACL for the new tunnel — we control this, DZ agent won't touch it - command: "ip access-list {{ tunnel_acl }}" - command: "counters per-entry" - command: "10 permit icmp host {{ tunnel_remote }} any" - command: "20 permit ip host {{ ashburn_ip }} any" - command: "30 permit ip host {{ tunnel_remote }} any" - command: "100 deny ip any any" - command: exit # New GRE tunnel - command: "interface {{ tunnel_interface }}" - command: "mtu 9216" - command: "ip address {{ tunnel_local }}/31" - command: "ip access-group {{ tunnel_acl }} in" - command: "tunnel mode gre" - command: "tunnel source {{ tunnel_source_ip }}" - command: "tunnel destination {{ biscayne_ip }}" - command: exit # Inbound: route ashburn IP to biscayne via the new tunnel - command: "ip route {{ ashburn_ip }}/32 {{ tunnel_remote }}" # Outbound: biscayne's traffic exits via backbone to was-sw01. # Use a specific route for the backbone peer so tunnel traffic # can reach was-sw01 without a blanket default route. # (The switch's actual default route is via Et1/1 ISP uplink.) - name: Show session diff arista.eos.eos_command: commands: - "configure session {{ session_name }}" - show session-config diffs - exit register: session_diff - name: Display session diff ansible.builtin.debug: var: session_diff.stdout_lines - name: Commit with 5-minute auto-revert arista.eos.eos_command: commands: - "configure session {{ session_name }} commit timer 00:05:00" # ------------------------------------------------------------------ # Verify # ------------------------------------------------------------------ - name: Verify config arista.eos.eos_command: commands: - "show running-config interfaces {{ tunnel_source_lo }}" - "show running-config interfaces {{ tunnel_interface }}" - "show ip access-lists {{ tunnel_acl }}" - "show ip route {{ ashburn_ip }}" - "show interfaces {{ tunnel_interface }} status" register: verify - name: Display verification ansible.builtin.debug: var: verify.stdout_lines - name: Reminder ansible.builtin.debug: msg: | === Config applied with 5-minute auto-revert === Session: {{ session_name }} Checkpoint: {{ checkpoint_name }} Changes applied: 1. {{ tunnel_source_lo }}: {{ tunnel_source_ip }}/32 2. {{ tunnel_interface }}: GRE tunnel to {{ biscayne_ip }} link {{ tunnel_local }}/31, ACL {{ tunnel_acl }} 3. Route: {{ ashburn_ip }}/32 via {{ tunnel_remote }} The config will auto-revert in 5 minutes unless committed. Verify on the switch, then commit: ansible-playbook ... -e commit=true To revert immediately: ansible-playbook ... -e rollback=true