feat: ashburn validator relay playbooks
Three playbooks for routing all validator traffic through 137.239.194.65: - was-sw01: Loopback101 + PBR redirect on Et1/1 (already applied/committed) Will be simplified to a static route in next iteration. - mia-sw01: ACL permit for src 137.239.194.65 on Tunnel500 + default route in vrf1 via egress-vrf default to was-sw01 backbone. No PBR needed — per-tunnel ACLs already scope what enters vrf1. - biscayne: DNAT inbound (137.239.194.65 → kind node), SNAT + policy routing outbound (validator sport 8001,9000-9025 → doublezero0 GRE). Inbound already applied. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>fix/kind-mount-propagation
parent
dd29257dd8
commit
6841d5e3c3
|
|
@ -0,0 +1,356 @@
|
|||
---
|
||||
# Configure biscayne for Ashburn validator relay
|
||||
#
|
||||
# Sets up inbound DNAT (137.239.194.65 → kind node) and outbound SNAT +
|
||||
# policy routing (validator traffic → doublezero0 → mia-sw01 → was-sw01).
|
||||
#
|
||||
# Usage:
|
||||
# # Full setup (inbound + outbound)
|
||||
# ansible-playbook playbooks/ashburn-relay-biscayne.yml
|
||||
#
|
||||
# # Inbound only (DNAT rules)
|
||||
# ansible-playbook playbooks/ashburn-relay-biscayne.yml -t inbound
|
||||
#
|
||||
# # Outbound only (SNAT + policy routing)
|
||||
# ansible-playbook playbooks/ashburn-relay-biscayne.yml -t outbound
|
||||
#
|
||||
# # Pre-flight checks only
|
||||
# ansible-playbook playbooks/ashburn-relay-biscayne.yml -t preflight
|
||||
#
|
||||
# # Rollback
|
||||
# ansible-playbook playbooks/ashburn-relay-biscayne.yml -e rollback=true
|
||||
|
||||
- name: Configure biscayne Ashburn validator relay
|
||||
hosts: biscayne
|
||||
gather_facts: false
|
||||
|
||||
vars:
|
||||
ashburn_ip: 137.239.194.65
|
||||
kind_node_ip: 172.20.0.2
|
||||
kind_network: 172.20.0.0/16
|
||||
tunnel_gateway: 169.254.7.6
|
||||
tunnel_device: doublezero0
|
||||
fwmark: 100
|
||||
rt_table_name: ashburn
|
||||
rt_table_id: 100
|
||||
gossip_port: 8001
|
||||
dynamic_port_range_start: 9000
|
||||
dynamic_port_range_end: 9025
|
||||
rollback: false
|
||||
|
||||
tasks:
|
||||
# ------------------------------------------------------------------
|
||||
# Rollback
|
||||
# ------------------------------------------------------------------
|
||||
- name: Rollback all Ashburn relay rules
|
||||
when: rollback | bool
|
||||
block:
|
||||
- name: Remove Ashburn IP from loopback
|
||||
ansible.builtin.command:
|
||||
cmd: ip addr del {{ ashburn_ip }}/32 dev lo
|
||||
failed_when: false
|
||||
|
||||
- name: Remove inbound DNAT rules
|
||||
ansible.builtin.shell:
|
||||
cmd: |
|
||||
set -o pipefail
|
||||
iptables -t nat -D PREROUTING -p udp -d {{ ashburn_ip }} --dport {{ gossip_port }} -j DNAT --to-destination {{ kind_node_ip }}:{{ gossip_port }} 2>/dev/null || true
|
||||
iptables -t nat -D PREROUTING -p tcp -d {{ ashburn_ip }} --dport {{ gossip_port }} -j DNAT --to-destination {{ kind_node_ip }}:{{ gossip_port }} 2>/dev/null || true
|
||||
iptables -t nat -D PREROUTING -p udp -d {{ ashburn_ip }} --dport {{ dynamic_port_range_start }}:{{ dynamic_port_range_end }} -j DNAT --to-destination {{ kind_node_ip }} 2>/dev/null || true
|
||||
executable: /bin/bash
|
||||
|
||||
- name: Remove outbound mangle rules
|
||||
ansible.builtin.shell:
|
||||
cmd: |
|
||||
set -o pipefail
|
||||
iptables -t mangle -D PREROUTING -s {{ kind_network }} -p udp --sport {{ gossip_port }} -j MARK --set-mark {{ fwmark }} 2>/dev/null || true
|
||||
iptables -t mangle -D PREROUTING -s {{ kind_network }} -p udp --sport {{ dynamic_port_range_start }}:{{ dynamic_port_range_end }} -j MARK --set-mark {{ fwmark }} 2>/dev/null || true
|
||||
iptables -t mangle -D PREROUTING -s {{ kind_network }} -p tcp --sport {{ gossip_port }} -j MARK --set-mark {{ fwmark }} 2>/dev/null || true
|
||||
executable: /bin/bash
|
||||
|
||||
- name: Remove outbound SNAT rule
|
||||
ansible.builtin.shell:
|
||||
cmd: iptables -t nat -D POSTROUTING -m mark --mark {{ fwmark }} -j SNAT --to-source {{ ashburn_ip }} 2>/dev/null || true
|
||||
executable: /bin/bash
|
||||
|
||||
- name: Remove policy routing
|
||||
ansible.builtin.shell:
|
||||
cmd: |
|
||||
ip rule del fwmark {{ fwmark }} table {{ rt_table_name }} 2>/dev/null || true
|
||||
ip route del default table {{ rt_table_name }} 2>/dev/null || true
|
||||
executable: /bin/bash
|
||||
|
||||
- name: Persist cleaned iptables
|
||||
ansible.builtin.command:
|
||||
cmd: netfilter-persistent save
|
||||
|
||||
- name: Remove if-up.d script
|
||||
ansible.builtin.file:
|
||||
path: /etc/network/if-up.d/ashburn-routing
|
||||
state: absent
|
||||
|
||||
- name: Rollback complete
|
||||
ansible.builtin.debug:
|
||||
msg: "Ashburn relay rules removed. Old SHRED-RELAY DNAT (64.92.84.81:20000) is still in place."
|
||||
|
||||
- name: End play after rollback
|
||||
ansible.builtin.meta: end_play
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# Pre-flight checks
|
||||
# ------------------------------------------------------------------
|
||||
- name: Check doublezero0 tunnel is up
|
||||
ansible.builtin.command:
|
||||
cmd: ip link show {{ tunnel_device }}
|
||||
register: tunnel_status
|
||||
changed_when: false
|
||||
failed_when: "'UP' not in tunnel_status.stdout"
|
||||
tags: [preflight, inbound, outbound]
|
||||
|
||||
- name: Check kind node is reachable
|
||||
ansible.builtin.command:
|
||||
cmd: ping -c 1 -W 2 {{ kind_node_ip }}
|
||||
register: kind_ping
|
||||
changed_when: false
|
||||
failed_when: kind_ping.rc != 0
|
||||
tags: [preflight, inbound]
|
||||
|
||||
- name: Verify Docker preserves source ports (5 sec sample)
|
||||
ansible.builtin.shell:
|
||||
cmd: |
|
||||
set -o pipefail
|
||||
# Check if any validator traffic is flowing with original sport
|
||||
timeout 5 tcpdump -i br-cf46a62ab5b2 -nn -c 5 'udp src port 8001 or udp src portrange 9000-9025' 2>&1 | tail -5 || echo "No validator traffic captured in 5s (validator may not be running)"
|
||||
executable: /bin/bash
|
||||
register: sport_check
|
||||
changed_when: false
|
||||
failed_when: false
|
||||
tags: [preflight]
|
||||
|
||||
- name: Show sport preservation check
|
||||
ansible.builtin.debug:
|
||||
var: sport_check.stdout_lines
|
||||
tags: [preflight]
|
||||
|
||||
- name: Show existing iptables nat rules
|
||||
ansible.builtin.shell:
|
||||
cmd: iptables -t nat -L -v -n --line-numbers | head -60
|
||||
executable: /bin/bash
|
||||
register: existing_nat
|
||||
changed_when: false
|
||||
tags: [preflight]
|
||||
|
||||
- name: Display existing NAT rules
|
||||
ansible.builtin.debug:
|
||||
var: existing_nat.stdout_lines
|
||||
tags: [preflight]
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# Inbound: DNAT for 137.239.194.65 → kind node
|
||||
# ------------------------------------------------------------------
|
||||
- name: Add Ashburn IP to loopback
|
||||
ansible.builtin.command:
|
||||
cmd: ip addr add {{ ashburn_ip }}/32 dev lo
|
||||
register: add_ip
|
||||
changed_when: add_ip.rc == 0
|
||||
failed_when: "add_ip.rc != 0 and 'RTNETLINK answers: File exists' not in add_ip.stderr"
|
||||
tags: [inbound]
|
||||
|
||||
- name: Add DNAT for gossip UDP
|
||||
ansible.builtin.iptables:
|
||||
table: nat
|
||||
chain: PREROUTING
|
||||
protocol: udp
|
||||
destination: "{{ ashburn_ip }}"
|
||||
destination_port: "{{ gossip_port }}"
|
||||
jump: DNAT
|
||||
to_destination: "{{ kind_node_ip }}:{{ gossip_port }}"
|
||||
tags: [inbound]
|
||||
|
||||
- name: Add DNAT for gossip TCP
|
||||
ansible.builtin.iptables:
|
||||
table: nat
|
||||
chain: PREROUTING
|
||||
protocol: tcp
|
||||
destination: "{{ ashburn_ip }}"
|
||||
destination_port: "{{ gossip_port }}"
|
||||
jump: DNAT
|
||||
to_destination: "{{ kind_node_ip }}:{{ gossip_port }}"
|
||||
tags: [inbound]
|
||||
|
||||
- name: Add DNAT for dynamic ports (UDP 9000-9025)
|
||||
ansible.builtin.iptables:
|
||||
table: nat
|
||||
chain: PREROUTING
|
||||
protocol: udp
|
||||
destination: "{{ ashburn_ip }}"
|
||||
destination_port: "{{ dynamic_port_range_start }}:{{ dynamic_port_range_end }}"
|
||||
jump: DNAT
|
||||
to_destination: "{{ kind_node_ip }}"
|
||||
tags: [inbound]
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# Outbound: fwmark + SNAT + policy routing
|
||||
# ------------------------------------------------------------------
|
||||
- name: Mark outbound validator UDP gossip traffic
|
||||
ansible.builtin.iptables:
|
||||
table: mangle
|
||||
chain: PREROUTING
|
||||
protocol: udp
|
||||
source: "{{ kind_network }}"
|
||||
source_port: "{{ gossip_port }}"
|
||||
jump: MARK
|
||||
set_mark: "{{ fwmark }}"
|
||||
tags: [outbound]
|
||||
|
||||
- name: Mark outbound validator UDP dynamic port traffic
|
||||
ansible.builtin.iptables:
|
||||
table: mangle
|
||||
chain: PREROUTING
|
||||
protocol: udp
|
||||
source: "{{ kind_network }}"
|
||||
source_port: "{{ dynamic_port_range_start }}:{{ dynamic_port_range_end }}"
|
||||
jump: MARK
|
||||
set_mark: "{{ fwmark }}"
|
||||
tags: [outbound]
|
||||
|
||||
- name: Mark outbound validator TCP gossip traffic
|
||||
ansible.builtin.iptables:
|
||||
table: mangle
|
||||
chain: PREROUTING
|
||||
protocol: tcp
|
||||
source: "{{ kind_network }}"
|
||||
source_port: "{{ gossip_port }}"
|
||||
jump: MARK
|
||||
set_mark: "{{ fwmark }}"
|
||||
tags: [outbound]
|
||||
|
||||
- name: SNAT marked traffic to Ashburn IP (before Docker MASQUERADE)
|
||||
ansible.builtin.shell:
|
||||
cmd: |
|
||||
set -o pipefail
|
||||
# Check if rule already exists
|
||||
if iptables -t nat -C POSTROUTING -m mark --mark {{ fwmark }} -j SNAT --to-source {{ ashburn_ip }} 2>/dev/null; then
|
||||
echo "SNAT rule already exists"
|
||||
else
|
||||
iptables -t nat -I POSTROUTING 1 -m mark --mark {{ fwmark }} -j SNAT --to-source {{ ashburn_ip }}
|
||||
echo "SNAT rule inserted at position 1"
|
||||
fi
|
||||
executable: /bin/bash
|
||||
register: snat_result
|
||||
changed_when: "'inserted' in snat_result.stdout"
|
||||
tags: [outbound]
|
||||
|
||||
- name: Show SNAT result
|
||||
ansible.builtin.debug:
|
||||
var: snat_result.stdout
|
||||
tags: [outbound]
|
||||
|
||||
- name: Ensure rt_tables entry exists
|
||||
ansible.builtin.lineinfile:
|
||||
path: /etc/iproute2/rt_tables
|
||||
line: "{{ rt_table_id }} {{ rt_table_name }}"
|
||||
regexp: "^{{ rt_table_id }}\\s"
|
||||
tags: [outbound]
|
||||
|
||||
- name: Add policy routing rule for fwmark
|
||||
ansible.builtin.shell:
|
||||
cmd: |
|
||||
if ip rule show | grep -q 'fwmark 0x64 lookup ashburn'; then
|
||||
echo "rule already exists"
|
||||
else
|
||||
ip rule add fwmark {{ fwmark }} table {{ rt_table_name }}
|
||||
echo "rule added"
|
||||
fi
|
||||
executable: /bin/bash
|
||||
register: rule_result
|
||||
changed_when: "'added' in rule_result.stdout"
|
||||
tags: [outbound]
|
||||
|
||||
- name: Add default route via doublezero0 in ashburn table
|
||||
ansible.builtin.shell:
|
||||
cmd: ip route replace default via {{ tunnel_gateway }} dev {{ tunnel_device }} table {{ rt_table_name }}
|
||||
executable: /bin/bash
|
||||
changed_when: true
|
||||
tags: [outbound]
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# Persistence
|
||||
# ------------------------------------------------------------------
|
||||
- name: Save iptables rules
|
||||
ansible.builtin.command:
|
||||
cmd: netfilter-persistent save
|
||||
tags: [inbound, outbound]
|
||||
|
||||
- name: Install if-up.d persistence script
|
||||
ansible.builtin.copy:
|
||||
src: files/ashburn-routing-ifup.sh
|
||||
dest: /etc/network/if-up.d/ashburn-routing
|
||||
mode: '0755'
|
||||
owner: root
|
||||
group: root
|
||||
tags: [outbound]
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# Verification
|
||||
# ------------------------------------------------------------------
|
||||
- name: Show NAT rules
|
||||
ansible.builtin.shell:
|
||||
cmd: iptables -t nat -L -v -n --line-numbers 2>&1 | head -40
|
||||
executable: /bin/bash
|
||||
register: nat_rules
|
||||
changed_when: false
|
||||
tags: [inbound, outbound]
|
||||
|
||||
- name: Show mangle rules
|
||||
ansible.builtin.shell:
|
||||
cmd: iptables -t mangle -L -v -n 2>&1
|
||||
executable: /bin/bash
|
||||
register: mangle_rules
|
||||
changed_when: false
|
||||
tags: [outbound]
|
||||
|
||||
- name: Show policy routing
|
||||
ansible.builtin.shell:
|
||||
cmd: |
|
||||
echo "=== ip rule ==="
|
||||
ip rule show
|
||||
echo ""
|
||||
echo "=== ashburn routing table ==="
|
||||
ip route show table {{ rt_table_name }} 2>/dev/null || echo "table empty"
|
||||
executable: /bin/bash
|
||||
register: routing_info
|
||||
changed_when: false
|
||||
tags: [outbound]
|
||||
|
||||
- name: Show loopback addresses
|
||||
ansible.builtin.shell:
|
||||
cmd: ip addr show lo | grep inet
|
||||
executable: /bin/bash
|
||||
register: lo_addrs
|
||||
changed_when: false
|
||||
tags: [inbound]
|
||||
|
||||
- name: Display verification
|
||||
ansible.builtin.debug:
|
||||
msg:
|
||||
nat_rules: "{{ nat_rules.stdout_lines }}"
|
||||
mangle_rules: "{{ mangle_rules.stdout_lines | default([]) }}"
|
||||
routing: "{{ routing_info.stdout_lines | default([]) }}"
|
||||
loopback: "{{ lo_addrs.stdout_lines }}"
|
||||
tags: [inbound, outbound]
|
||||
|
||||
- name: Summary
|
||||
ansible.builtin.debug:
|
||||
msg: |
|
||||
=== Ashburn Relay Setup Complete ===
|
||||
Ashburn IP: {{ ashburn_ip }} (on lo)
|
||||
Inbound DNAT: {{ ashburn_ip }}:8001,9000-9025 → {{ kind_node_ip }}
|
||||
Outbound SNAT: {{ kind_network }} sport 8001,9000-9025 → {{ ashburn_ip }}
|
||||
Policy route: fwmark {{ fwmark }} → table {{ rt_table_name }} → via {{ tunnel_gateway }} dev {{ tunnel_device }}
|
||||
Persisted: iptables-persistent + /etc/network/if-up.d/ashburn-routing
|
||||
|
||||
Next steps:
|
||||
1. Verify inbound: ping {{ ashburn_ip }} from external host
|
||||
2. Verify outbound: tcpdump on was-sw01 for src {{ ashburn_ip }}
|
||||
3. Check validator gossip ContactInfo shows {{ ashburn_ip }} for all addresses
|
||||
|
|
@ -0,0 +1,215 @@
|
|||
---
|
||||
# Configure laconic-mia-sw01 for outbound validator traffic redirect
|
||||
#
|
||||
# Redirects outbound traffic from biscayne (src 137.239.194.65) arriving
|
||||
# via the doublezero0 GRE tunnel to was-sw01 via the backbone, preventing
|
||||
# BCP38 drops at mia-sw01's ISP uplink.
|
||||
#
|
||||
# Approach: The existing per-tunnel ACL (SEC-USER-500-IN) controls what
|
||||
# traffic enters vrf1 from Tunnel500. We add 137.239.194.65 to the ACL
|
||||
# and add a default route in vrf1 via egress-vrf default pointing to
|
||||
# was-sw01's backbone IP. No PBR needed — the ACL is the filter.
|
||||
#
|
||||
# The other vrf1 tunnels (502, 504, 505) have their own ACLs that only
|
||||
# permit their specific source IPs, so the default route won't affect them.
|
||||
#
|
||||
# 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 outbound validator redirect
|
||||
hosts: mia-sw01
|
||||
gather_facts: false
|
||||
|
||||
vars:
|
||||
ashburn_ip: 137.239.194.65
|
||||
apply: false
|
||||
commit: false
|
||||
rollback: false
|
||||
tunnel_interface: Tunnel500
|
||||
tunnel_vrf: vrf1
|
||||
tunnel_acl: SEC-USER-500-IN
|
||||
backbone_interface: Ethernet4/1
|
||||
session_name: validator-outbound
|
||||
checkpoint_name: pre-validator-outbound
|
||||
|
||||
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: Show tunnel interface config
|
||||
arista.eos.eos_command:
|
||||
commands:
|
||||
- "show running-config interfaces {{ tunnel_interface }}"
|
||||
register: tunnel_config
|
||||
tags: [preflight]
|
||||
|
||||
- name: Display tunnel config
|
||||
ansible.builtin.debug:
|
||||
var: tunnel_config.stdout_lines
|
||||
tags: [preflight]
|
||||
|
||||
- name: Show tunnel ACL
|
||||
arista.eos.eos_command:
|
||||
commands:
|
||||
- "show running-config | section ip access-list {{ tunnel_acl }}"
|
||||
register: acl_config
|
||||
tags: [preflight]
|
||||
|
||||
- name: Display tunnel ACL
|
||||
ansible.builtin.debug:
|
||||
var: acl_config.stdout_lines
|
||||
tags: [preflight]
|
||||
|
||||
- name: Check VRF routing
|
||||
arista.eos.eos_command:
|
||||
commands:
|
||||
- "show ip route vrf {{ tunnel_vrf }} 0.0.0.0/0"
|
||||
- "show ip route vrf {{ tunnel_vrf }} {{ backbone_peer }}"
|
||||
- "show ip route {{ backbone_peer }}"
|
||||
register: vrf_routing
|
||||
tags: [preflight]
|
||||
|
||||
- name: Display VRF routing check
|
||||
ansible.builtin.debug:
|
||||
var: vrf_routing.stdout_lines
|
||||
tags: [preflight]
|
||||
|
||||
- name: Pre-flight summary
|
||||
when: not (apply | bool)
|
||||
ansible.builtin.debug:
|
||||
msg: |
|
||||
=== Pre-flight complete ===
|
||||
Review the output above:
|
||||
1. {{ tunnel_interface }} ACL ({{ tunnel_acl }}): does it permit src {{ ashburn_ip }}?
|
||||
2. {{ tunnel_vrf }} default route: does one exist?
|
||||
3. Backbone nexthop {{ backbone_peer }}: reachable in default VRF?
|
||||
|
||||
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 }}"
|
||||
# Permit Ashburn IP through the tunnel ACL (insert before deny)
|
||||
- command: "ip access-list {{ tunnel_acl }}"
|
||||
- command: "45 permit ip host {{ ashburn_ip }} any"
|
||||
- command: exit
|
||||
# Default route in vrf1 via backbone to was-sw01 (egress-vrf default)
|
||||
# Safe because per-tunnel ACLs already restrict what enters vrf1
|
||||
- command: "ip route vrf {{ tunnel_vrf }} 0.0.0.0/0 egress-vrf default {{ backbone_interface }} {{ backbone_peer }}"
|
||||
|
||||
- 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 | section ip access-list {{ tunnel_acl }}"
|
||||
- "show ip route vrf {{ tunnel_vrf }} 0.0.0.0/0"
|
||||
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. ACL {{ tunnel_acl }}: added "45 permit ip host {{ ashburn_ip }} any"
|
||||
2. Default route in {{ tunnel_vrf }}: 0.0.0.0/0 egress-vrf default {{ backbone_interface }} {{ backbone_peer }}
|
||||
|
||||
The config will auto-revert in 5 minutes unless committed.
|
||||
Verify on the switch, then commit:
|
||||
configure session {{ session_name }} commit
|
||||
write memory
|
||||
|
||||
To revert immediately:
|
||||
ansible-playbook ... -e rollback=true
|
||||
|
|
@ -0,0 +1,197 @@
|
|||
---
|
||||
# Configure laconic-was-sw01 for full validator traffic relay
|
||||
#
|
||||
# Replaces the old SHRED-RELAY (TVU-only, port 20000) with VALIDATOR-RELAY
|
||||
# covering all validator ports (8001, 9000-9025). Adds Loopback101 for
|
||||
# 137.239.194.65.
|
||||
#
|
||||
# Uses EOS config session with 5-minute auto-revert for safety.
|
||||
# After verification, run with -e commit=true to finalize.
|
||||
#
|
||||
# Usage:
|
||||
# ansible-playbook -i inventory/switches.yml playbooks/ashburn-relay-was-sw01.yml
|
||||
# ansible-playbook -i inventory/switches.yml playbooks/ashburn-relay-was-sw01.yml -e commit=true
|
||||
# ansible-playbook -i inventory/switches.yml playbooks/ashburn-relay-was-sw01.yml -e rollback=true
|
||||
|
||||
- name: Configure was-sw01 inbound validator relay
|
||||
hosts: was-sw01
|
||||
gather_facts: false
|
||||
|
||||
vars:
|
||||
ashburn_ip: 137.239.194.65
|
||||
commit: false
|
||||
rollback: false
|
||||
session_name: validator-relay
|
||||
checkpoint_name: pre-validator-relay
|
||||
|
||||
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-checks
|
||||
# ------------------------------------------------------------------
|
||||
- name: Show current traffic-policy on Et1/1
|
||||
arista.eos.eos_command:
|
||||
commands:
|
||||
- show running-config interfaces Ethernet1/1
|
||||
register: et1_config
|
||||
|
||||
- name: Show current config
|
||||
ansible.builtin.debug:
|
||||
var: et1_config.stdout_lines
|
||||
|
||||
- name: Show existing PBR policy on Et1/1
|
||||
arista.eos.eos_command:
|
||||
commands:
|
||||
- "show running-config | include service-policy"
|
||||
register: existing_pbr
|
||||
|
||||
- name: Show existing PBR config
|
||||
ansible.builtin.debug:
|
||||
var: existing_pbr.stdout_lines
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# Save checkpoint
|
||||
# ------------------------------------------------------------------
|
||||
- name: Save checkpoint for rollback
|
||||
arista.eos.eos_command:
|
||||
commands:
|
||||
- "configure checkpoint save {{ checkpoint_name }}"
|
||||
register: checkpoint_result
|
||||
|
||||
- name: Show checkpoint result
|
||||
ansible.builtin.debug:
|
||||
var: checkpoint_result.stdout_lines
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# Apply via config session with 5-minute auto-revert
|
||||
#
|
||||
# eos_config writes directly to running-config, bypassing sessions.
|
||||
# Use eos_command with raw CLI to get the safety net.
|
||||
# ------------------------------------------------------------------
|
||||
- name: Apply config session with auto-revert
|
||||
arista.eos.eos_command:
|
||||
commands:
|
||||
# Enter named config session
|
||||
- command: "configure session {{ session_name }}"
|
||||
# Loopback101 for Ashburn IP
|
||||
- command: interface Loopback101
|
||||
- command: "ip address {{ ashburn_ip }}/32"
|
||||
- command: exit
|
||||
# ACL covering all validator ports
|
||||
- command: ip access-list VALIDATOR-RELAY-ACL
|
||||
- command: 10 permit udp any any eq 8001
|
||||
- command: 20 permit udp any any range 9000 9025
|
||||
- command: 30 permit tcp any any eq 8001
|
||||
- command: exit
|
||||
# PBR class-map referencing the ACL
|
||||
- command: class-map type pbr match-any VALIDATOR-RELAY-CLASS
|
||||
- command: match ip access-group VALIDATOR-RELAY-ACL
|
||||
- command: exit
|
||||
# PBR policy-map with nexthop redirect
|
||||
- command: policy-map type pbr VALIDATOR-RELAY
|
||||
- command: class VALIDATOR-RELAY-CLASS
|
||||
- command: "set nexthop {{ backbone_peer }}"
|
||||
- command: exit
|
||||
- command: exit
|
||||
# Apply PBR policy on Et1/1
|
||||
- command: interface Ethernet1/1
|
||||
- command: service-policy type pbr input VALIDATOR-RELAY
|
||||
- command: exit
|
||||
tags: [config]
|
||||
|
||||
- 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"
|
||||
tags: [config]
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# Verify
|
||||
# ------------------------------------------------------------------
|
||||
- name: Show PBR policy on Et1/1
|
||||
arista.eos.eos_command:
|
||||
commands:
|
||||
- show running-config interfaces Ethernet1/1
|
||||
- show running-config section policy-map
|
||||
- show ip interface Loopback101
|
||||
register: pbr_interface
|
||||
|
||||
- name: Display verification
|
||||
ansible.builtin.debug:
|
||||
var: pbr_interface.stdout_lines
|
||||
|
||||
- name: Show Loopback101
|
||||
arista.eos.eos_command:
|
||||
commands:
|
||||
- show ip interface Loopback101
|
||||
register: lo101
|
||||
|
||||
- name: Display Loopback101
|
||||
ansible.builtin.debug:
|
||||
var: lo101.stdout_lines
|
||||
|
||||
- name: Reminder
|
||||
ansible.builtin.debug:
|
||||
msg: |
|
||||
=== Config applied with 5-minute auto-revert ===
|
||||
Session: {{ session_name }}
|
||||
Checkpoint: {{ checkpoint_name }}
|
||||
|
||||
The config will auto-revert in 5 minutes unless committed.
|
||||
Verify PBR policy is applied, then commit from the switch CLI:
|
||||
configure session {{ session_name }} commit
|
||||
write memory
|
||||
|
||||
To revert immediately:
|
||||
ansible-playbook ... -e rollback=true
|
||||
Loading…
Reference in New Issue