# network-go — Docker Network & Firewall Manager A Go-based replacement for the `firewall/firewall-add` shell script. It watches `/etc/user/config/networks.json` for changes and reconciles Docker networks, container connections, and iptables firewall rules using the Docker SDK. ## Project Structure ``` network-go/ ├── main.go Entry point: watches config, orchestrates reconciliation ├── config/ │ └── config.go Parses /etc/user/config/networks.json into typed structs ├── docker/ │ └── docker.go Docker SDK wrapper (network create, container connect, │ container PID for nsenter, route management) ├── firewall/ │ └── firewall.go Orchestrator: translates policies → iptables rules ├── iptables/ │ └── iptables.go Manages iptables CLI: PREROUTING DNAT, POSTROUTING MASQUERADE, │ FORWARD ACCEPT, nsenter for container network namespaces ├── resolver/ │ └── resolver.go Resolves names → IPs using networks.json config ├── watcher/ │ └── watcher.go Periodic file change detection via MD5 hash polling ├── go.mod / go.sum Module definition with Docker SDK dependency └── .gitignore ``` ## Packages ### config - `NetworksConfig`, `NetworkConfig`, `IPConfig`, `PolicyConfig` structs matching `networks.json` - `FirewallRule` struct for resolved executable rules - Helpers: `IsIP`, `ToCIDR`, `NetworkPrefix`, `ParseCIDR`, `Load` ### docker - `Client` wrapping `github.com/docker/docker/client` - `EnsureNetwork` — creates a Docker network if it doesn't exist - `ConnectContainer` / `DisconnectContainer` — manages container ↔ network membership - `GetContainerPID` — returns the container PID for nsenter - `AddRouteInContainer` — adds routes inside a container namespace via nsenter - `WaitForContainerRunning` — polls until a container is running ### resolver - Resolves names → IPs using the `networks.json` config only - Looks up by `container_name` and `selector` in the `ips` section - Falls back to prefix matching (e.g. `smarthost-backend-1` → prefix `smarthost`) ### iptables - Auto-detects `iptables-legacy` vs `iptables` (matching shell script logic) - `EnsureIPForward` — enables `/proc/sys/net/ipv4/ip_forward` - `EnsureEstablishedRelated` — inserts ESTABLISHED,RELATED ACCEPT at top of chain - `InsertPreroutingRule` / `InsertPreroutingRuleInContainer` — DNAT rules - `InsertPostroutingMasquerade` — MASQUERADE rules - `InsertForwardAccept` — FORWARD/DOCKER-USER ACCEPT rules - Rule deletion by line-number + grep pattern matching (matching shell script) - nsenter-based execution inside container network namespaces ### firewall - `Orchestrator` ties all packages together - `ReconcileAll` runs the full reconciliation cycle: 1. Enable IP forwarding 2. Ensure all Docker networks from config 3. Connect containers to networks with assigned IPs 4. Apply firewall policies as iptables rules - Policy → rule mapping: - `from` field → FORWARD ACCEPT rule on DOCKER-USER or FORWARD chain - `nat: dnat` field → PREROUTING DNAT inside container namespace via nsenter - Interface-based rules (e.g. `wg0`) → host-level PREROUTING DNAT ### watcher - `FileWatcher` polls a file at a configurable interval - Computes MD5 hash on each tick - Fires `onChange` callback when hash changes - `Start` / `Stop` for lifecycle management ## Configuration The file `/etc/user/config/networks.json` defines: ```json { "networks": { "smarthost-loadbalancer": { "network_name": "smarthost-loadbalancer", "subnet": "172.18.103.0/24", "gateway": "172.18.103.1" } }, "ips": { "172.18.103.2": { "ip": "172.18.103.2", "container_name": "smarthostloadbalancer", "selector": "smarthostloadbalancer", "service_name": "smarthost-proxy" } }, "policies": [ { "service_name": "smarthost-proxy", "container_name": "smarthost_loadbalancer", "selector": "smarthostloadbalancer", "from": "publicbackend", "port": 80, "proto": "tcp" }, { "service_name": "smarthost-proxy", "container_name": "smarthost_loadbalancer", "selector": "smarthostloadbalancer", "name": "wireguardproxy", "iface": "wg0", "nat": "dnat", "to": "smarthostloadbalancer", "port": 80, "proto": "tcp" } ] } ``` ## Environment Variables | Variable | Default | Description | |---|---|---| | `NETWORKS_CONFIG_PATH` | `/etc/user/config/networks.json` | Path to the configuration file | | `WATCH_PERIOD_SECONDS` | `30` | Polling interval in seconds for config file changes | | `DEBUG` | `false` | Enable debug output (`1` or `true`) | ## Key Differences from Shell Script | Shell Script (`firewall-add`) | Go Implementation | |---|---| | `docker ps --format`, `docker inspect` | Docker SDK (`github.com/docker/docker`) | | `/etc/dns/hosts.local` lookup | networks.json config lookup | | `$SOURCE` / `$TARGET` env vars | `from` / `to` fields in `networks.json` | | iptables via bash + grep + awk | Go `os/exec` with structured line matching | | `nsenter -t PID -n` for container iptables | `nsenter` via `os/exec` in `iptables.Manager` | | `$ROUTE=true` + `ip route` | `docker.Client.AddRouteInContainer()` | | Manual per-rule invocation | `Orchestrator.ReconcileAll()` batch reconciliation | ## Build & Run ```bash go build -o network-go . ./network-go ``` In Docker: ```bash go build -o network-go . # Mount Docker socket and config docker run -v /var/run/docker.sock:/var/run/docker.sock \ -v /etc/user/config:/etc/user/config \ network-go