156 lines
5.7 KiB
Markdown
156 lines
5.7 KiB
Markdown
# 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 |