diff --git a/.gitignore b/.gitignore index fc0d3f2..1d73ba9 100644 --- a/.gitignore +++ b/.gitignore @@ -5,3 +5,5 @@ __pycache__/ *.pyc +gobgp/gobgpd.conf +gobgp-evpn/gobgpd.conf diff --git a/cml/gobgp_peering_config.py b/cml/gobgp_peering_config.py index 4e8b537..32a299b 100644 --- a/cml/gobgp_peering_config.py +++ b/cml/gobgp_peering_config.py @@ -33,14 +33,32 @@ policy from the cores. To stop the feed instantly without touching router config, `docker compose stop gobgp` -- the eBGP sessions drop and the full table is withdrawn fleet-wide within seconds. See gobgp/README.md. """ +import os import sys import time import paramiko + +def _env_default(key, default, dotenv=".env"): + """Resolve a value from os.environ or the repo-root .env, else default.""" + v = os.environ.get(key) + if v: + return v + try: + with open(dotenv) as fh: + for line in fh: + s = line.strip() + if s and not s.startswith("#") and s.startswith(f"{key}="): + return s.split("=", 1)[1].strip().strip('"').strip("'") + except FileNotFoundError: + pass + return default + + # GoBGP runs network_mode: host, so it sources BGP TCP from the host's real -# interface IP (10.40.40.202) -- NOT its router-id 10.40.40.250. The cores -# must peer with the host IP. -GOBGP_IP = "10.40.40.202" +# interface IP -- NOT its router-id. The cores must peer with the host IP. +# Resolved from $HOST_IP or the HOST_IP= line in repo-root .env. +GOBGP_IP = _env_default("HOST_IP", "10.40.40.202") GOBGP_AS = "65001" # Additive config, built per core (asn = that core's local BGP AS: diff --git a/cml/proxmox_bmp_config.py b/cml/proxmox_bmp_config.py index e161607..6291b13 100644 --- a/cml/proxmox_bmp_config.py +++ b/cml/proxmox_bmp_config.py @@ -18,12 +18,31 @@ Verify afterwards in OpenBMP: -c "SELECT name, ip_address, bgp_id, isconnected FROM routers ORDER BY name;" """ +import os import sys import time import paramiko + +def _env_default(key, default, dotenv=".env"): + """Resolve a value from os.environ or the repo-root .env, else default.""" + v = os.environ.get(key) + if v: + return v + try: + with open(dotenv) as fh: + for line in fh: + s = line.strip() + if s and not s.startswith("#") and s.startswith(f"{key}="): + return s.split("=", 1)[1].strip().strip('"').strip("'") + except FileNotFoundError: + pass + return default + + # --- BMP collector --------------------------------------------------------- -COLLECTOR_HOST = "10.40.40.202" +# Resolved from $HOST_IP or the HOST_IP= line in repo-root .env. +COLLECTOR_HOST = _env_default("HOST_IP", "10.40.40.202") COLLECTOR_PORT = "5000" # `bmp server 1` block — flat formal form, identical to the ESXi lab. diff --git a/docker-compose.yml b/docker-compose.yml index 411d251..b635650 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -476,12 +476,16 @@ services: # Host networking: the daemon uses the host's real IPv4 + IPv6 stack, so # both the v4 and v6 eBGP sessions to AS57355 source from the host's # public addresses (no Docker IPv6/NAT plumbing). BMP still reaches the - # collector on 10.40.40.202:5000 (its published port). + # collector on $HOST_IP:5000 (its published port). network_mode: host depends_on: - collector - # gobgpd reads /config/gobgpd.conf; the same mount carries mrt-refresh.sh - # and the cached MRT dumps it downloads. + # gobgpd.conf.tmpl (in git, with __HOST_IP__ placeholders) is rendered to + # gobgpd.conf (gitignored) by setup.sh using $HOST_IP from .env. The + # gobgp image is distroless (no shell), so we cannot render at container + # start -- the rendered .conf must exist on the host before `docker + # compose up`. The same /config mount also carries mrt-refresh.sh and + # cached MRT dumps. volumes: - ./gobgp:/config command: ["gobgpd", "-f", "/config/gobgpd.conf", "-t", "toml"] @@ -498,6 +502,8 @@ services: image: jauderho/gobgp:v4.5.0 depends_on: - collector + # gobgpd.conf is rendered by setup.sh from gobgpd.conf.tmpl. See gobgp/ + # service notes above. volumes: - ./gobgp-evpn:/config command: ["gobgpd", "-f", "/config/gobgpd.conf", "-t", "toml"] @@ -534,7 +540,7 @@ services: depends_on: - psql environment: - - PG_DSN=host=10.40.40.202 port=5432 dbname=openbmp user=openbmp password=${POSTGRES_PASSWORD:-openbmp} + - PG_DSN=host=${HOST_IP:-10.40.40.202} port=5432 dbname=openbmp user=openbmp password=${POSTGRES_PASSWORD:-openbmp} - POLL_INTERVAL=900 - ROUTER_USER=webui - ROUTER_PASS=cisco diff --git a/gobgp-evpn/gobgpd.conf b/gobgp-evpn/gobgpd.conf.tmpl similarity index 97% rename from gobgp-evpn/gobgpd.conf rename to gobgp-evpn/gobgpd.conf.tmpl index f0835d4..a884df7 100644 --- a/gobgp-evpn/gobgpd.conf +++ b/gobgp-evpn/gobgpd.conf.tmpl @@ -21,7 +21,7 @@ # --- BMP export to the OpenBMP collector ------------------------------------ [[bmp-servers]] [bmp-servers.config] - address = "10.40.40.202" + address = "__HOST_IP__" port = 5000 # local-rib: the injected EVPN routes live in the loc-rib (there are no # BGP peers / no adj-rib-in), so export the local RIB. diff --git a/gobgp/gobgpd.conf b/gobgp/gobgpd.conf.tmpl similarity index 98% rename from gobgp/gobgpd.conf rename to gobgp/gobgpd.conf.tmpl index 6b92a31..65eefdc 100644 --- a/gobgp/gobgpd.conf +++ b/gobgp/gobgpd.conf.tmpl @@ -77,7 +77,7 @@ # --- Neighbor: CML CORE-01 (AS65020) ---------------------------------------- # GoBGP initiates outbound to the core's mgmt IP (reachable from the docker # host -- the cores already reach the host for BMP). GoBGP sources the session -# from the host IP 10.40.40.202. eBGP multihop: the host is several hops from +# from the host IP __HOST_IP__. eBGP multihop: the host is several hops from # the core. Default export policy (accept) re-advertises the full Bromirski # table to the core. prefix-limit is a safety cap on what the core can send # back (its lab routes only -- small). @@ -164,7 +164,7 @@ # routes, pre import-policy) -- consistent with the rest of the OpenBMP fleet. [[bmp-servers]] [bmp-servers.config] - address = "10.40.40.202" + address = "__HOST_IP__" port = 5000 route-monitoring-policy = "pre-policy" statistics-timeout = 3600 diff --git a/obmp-rib-poller/poller.py b/obmp-rib-poller/poller.py index 8c06d0b..6181a9c 100644 --- a/obmp-rib-poller/poller.py +++ b/obmp-rib-poller/poller.py @@ -36,7 +36,8 @@ from ncclient import manager PG_DSN = os.environ.get( "PG_DSN", - "host=10.40.40.202 port=5432 dbname=openbmp user=openbmp password=openbmp", + f"host={os.environ.get('HOST_IP', 'localhost')} port=5432 " + "dbname=openbmp user=openbmp password=openbmp", ) POLL_INTERVAL = int(os.environ.get("POLL_INTERVAL", "900")) R_USER = os.environ.get("ROUTER_USER", "webui") diff --git a/setup.sh b/setup.sh index 796eaf4..216338d 100755 --- a/setup.sh +++ b/setup.sh @@ -111,6 +111,16 @@ else echo "authelia/users_database.yml exists — left untouched" fi +# --- gobgpd.conf rendering (host-side -- gobgp image is distroless) --------- +# Render gobgp{,-evpn}/gobgpd.conf from gobgpd.conf.tmpl, substituting +# __HOST_IP__ with $HOST_IP. The rendered .conf is gitignored. +for d in gobgp gobgp-evpn; do + if [ -f "$d/gobgpd.conf.tmpl" ]; then + sed -e "s/__HOST_IP__/$HOST_IP/g" "$d/gobgpd.conf.tmpl" > "$d/gobgpd.conf" + echo "Rendered $d/gobgpd.conf (HOST_IP=$HOST_IP)" + fi +done + # --- images ----------------------------------------------------------------- echo "Pulling and building images ..." docker compose pull --quiet