diff --git a/README.md b/README.md index 1653d51..fd7aff3 100644 --- a/README.md +++ b/README.md @@ -41,6 +41,7 @@ The suite is organized into three independent but complementary scripts, each ma - Enforces inter-VLAN isolation by default (forward chain policy drop); specific cross-VLAN traffic is permitted via `inter_vlan_exceptions` - Masquerades outbound traffic for all non-WireGuard VLANs automatically - Auto-detects active container bridge interfaces (Docker, Podman, libvirt, etc.) and adds forward rules so VLAN clients can reach containerized services +- Auto-detects active container bridge interfaces and adds DNS listening on each bridge IP, so containers can reach the local DNS resolver during builds and at runtime (container services, e.g. Docker, Podman, must be running at the time of `--apply`) - Installs a `systemd` boot service (`core-nat.service`) to re-apply firewall rules on every boot - Co-exists with Docker (does not touch Docker-managed `nat`/`filter` tables) - Generates FreeRADIUS `clients.conf` and `users` files from `core.json` reservations, enabling dynamic VLAN assignment via MAC Authentication Bypass (MAB) for both wired (802.1X) and wireless clients diff --git a/core.py b/core.py index 784f274..82098df 100644 --- a/core.py +++ b/core.py @@ -983,6 +983,11 @@ def build_vlan_dnsmasq_conf(vlan, data): line("bind-interfaces") line(f"listen-address={gateway}") line(f"interface={iface}") + if is_physical(vlan): + bridge_ips = get_container_bridge_ips() + for bridge, ip in bridge_ips.items(): + line(f"interface={bridge}") + line(f"listen-address={ip}") line() if not is_wg(vlan): @@ -1320,6 +1325,33 @@ def get_container_bridges(): except Exception: return [] +def get_container_bridge_ips(): + """Return {ifname: ip} for all active container bridge interfaces. + Used to add listen-address directives to the physical VLAN's dnsmasq + instance so containers can reach the local DNS resolver. + Works universally for Docker, Podman, LXC, libvirt, etc. + """ + try: + result = subprocess.run( + ["ip", "-j", "addr", "show", "type", "bridge"], + capture_output=True, text=True, timeout=5 + ) + if result.returncode != 0: + return {} + import json as _json + links = _json.loads(result.stdout) + out = {} + for l in links: + if l.get("operstate") != "UP": + continue + for addr in l.get("addr_info", []): + if addr.get("family") == "inet": + out[l["ifname"]] = addr["local"] + break + return out + except Exception: + return {} + def apply_dnsmasq_instances(data, dry_run=False, start_if_needed=True): """Write per-VLAN dnsmasq configs and service units. diff --git a/ddns.json b/ddns.json index 1319827..81330ae 100644 --- a/ddns.json +++ b/ddns.json @@ -1,51 +1,40 @@ { "general": { - "log_max_kb": 512, - "log_errors_only": false + "log_max_kb": 1024, + "log_errors_only": false, + "ip_check_services": [ + "https://api.ipify.org", + "https://ifconfig.me/ip", + "https://icanhazip.com", + "https://api4.my-ip.io/ip", + "https://ipv4.icanhazip.com", + "https://checkip.amazonaws.com", + "https://ipinfo.io/ip", + "https://ipecho.net/plain", + "https://ident.me", + "https://myip.dnsomatic.com", + "https://wtfismyip.com/text" + ], + "timer_interval": "10m" }, - "providers": [ { "description": "No-IP Main Account", + "provider": "noip", "enabled": true, - "type": "noip", - "username": "your-noip-username", - "password": "your-noip-password", + "username": "your-username", + "password": "your-password", "hostnames": [ - "myhome.ddns.net" - ], - "timer_interval": "5m", - "ip_check_services": [ - "https://api.ipify.org", - "https://ipv4.icanhazip.com", - "https://checkip.amazonaws.com", - "https://myip.dnsomatic.com", - "https://api4.my-ip.io/ip", - "https://ipinfo.io/ip", - "https://ip4.seeip.org", - "https://ipv4bot.whatismyipaddress.com", - "http://checkip.dyndns.org/" + "grotke.ddns.net" ] }, { - "description": "DuckDNS Account", + "description": "DuckDNS Main Account", + "provider": "duckdns", "enabled": false, - "type": "duckdns", "token": "your-duckdns-token", "subdomains": [ - "myhome" - ], - "timer_interval": "5m", - "ip_check_services": [ - "https://api.ipify.org", - "https://ipv4.icanhazip.com", - "https://checkip.amazonaws.com", - "https://myip.dnsomatic.com", - "https://api4.my-ip.io/ip", - "https://ipinfo.io/ip", - "https://ip4.seeip.org", - "https://ipv4bot.whatismyipaddress.com", - "http://checkip.dyndns.org/" + "yoursubdomain" ] } ]