#!/bin/bash # Ashburn validator relay — runtime setup # # Called by ashburn-relay.service (After=docker.service) on boot. # Idempotent — safe to run multiple times. # # Creates GRE tunnel, loopback IP, iptables rules, and policy routing # so that validator traffic enters/exits via 137.239.194.65 (Ashburn). set -euo pipefail # GRE tunnel to mia-sw01 if ! ip tunnel show {{ tunnel_device }} 2>/dev/null; then ip tunnel add {{ tunnel_device }} mode gre \ local {{ tunnel_src }} remote {{ tunnel_dst }} ttl 64 ip addr add {{ tunnel_local_ip }}/31 dev {{ tunnel_device }} ip link set {{ tunnel_device }} up mtu 8972 fi # Ashburn IP on loopback (so kernel accepts inbound packets) ip addr show lo | grep -q '{{ ashburn_ip }}' || \ ip addr add {{ ashburn_ip }}/32 dev lo # Inbound DNAT (position 1, before Docker's ADDRTYPE LOCAL rule) for rule in \ "-p udp -d {{ ashburn_ip }} --dport {{ gossip_port }} \ -j DNAT --to-destination {{ kind_node_ip }}:{{ gossip_port }}" \ "-p tcp -d {{ ashburn_ip }} --dport {{ gossip_port }} \ -j DNAT --to-destination {{ kind_node_ip }}:{{ gossip_port }}" \ "-p udp -d {{ ashburn_ip }} \ --dport {{ dynamic_port_range_start }}:{{ dynamic_port_range_end }} \ -j DNAT --to-destination {{ kind_node_ip }}" \ ; do if ! iptables -t nat -C PREROUTING $rule 2>/dev/null; then iptables -t nat -I PREROUTING 1 $rule fi done # FORWARD: allow DNAT'd relay traffic through Docker's FORWARD chain. # Docker drops traffic to bridge networks unless explicitly accepted. # DOCKER-USER runs before all Docker chains and survives daemon restarts. for rule in \ "-p udp -d {{ kind_node_ip }} --dport {{ gossip_port }} -j ACCEPT" \ "-p tcp -d {{ kind_node_ip }} --dport {{ gossip_port }} -j ACCEPT" \ "-p udp -d {{ kind_node_ip }} \ --dport {{ dynamic_port_range_start }}:{{ dynamic_port_range_end }} \ -j ACCEPT" \ ; do if ! iptables -C DOCKER-USER $rule 2>/dev/null; then iptables -I DOCKER-USER 1 $rule fi done # Outbound mangle (fwmark for policy routing) # sport rules: gossip/repair/TVU traffic FROM validator well-known ports # dport rule: ip_echo TCP TO entrypoint port 8001 (ephemeral sport, # so sport-based rules miss it; without this the entrypoint sees # biscayne's real IP and probes that instead of the Ashburn relay IP) for rule in \ "-p udp -s {{ kind_network }} --sport {{ gossip_port }} \ -j MARK --set-mark {{ fwmark }}" \ "-p udp -s {{ kind_network }} \ --sport {{ dynamic_port_range_start }}:{{ dynamic_port_range_end }} \ -j MARK --set-mark {{ fwmark }}" \ "-p tcp -s {{ kind_network }} --sport {{ gossip_port }} \ -j MARK --set-mark {{ fwmark }}" \ "-p tcp -s {{ kind_network }} --dport {{ gossip_port }} \ -j MARK --set-mark {{ fwmark }}" \ ; do if ! iptables -t mangle -C PREROUTING $rule 2>/dev/null; then iptables -t mangle -A PREROUTING $rule fi done # Outbound SNAT (position 1, before Docker MASQUERADE) snat_rule="-m mark --mark {{ fwmark }} -j SNAT --to-source {{ ashburn_ip }}" if ! iptables -t nat -C POSTROUTING $snat_rule 2>/dev/null; then iptables -t nat -I POSTROUTING 1 $snat_rule fi # Policy routing table grep -q '^{{ rt_table_id }} {{ rt_table_name }}$' /etc/iproute2/rt_tables || \ echo "{{ rt_table_id }} {{ rt_table_name }}" >> /etc/iproute2/rt_tables ip rule show | grep -q 'fwmark 0x64 lookup ashburn' || \ ip rule add fwmark {{ fwmark }} table {{ rt_table_name }} ip route replace default \ via {{ tunnel_remote_ip }} dev {{ tunnel_device }} table {{ rt_table_name }}