# Service Binding Hardening — 0.0.0.0 → 127.0.0.1 Migration

## Scenario

Security audit revealed several services listening on `0.0.0.0` (all interfaces, internet-reachable). None of them needed public access — all clients connect through Tailscale or a reverse proxy.

## Audit command

```bash
ss -tlnp4 | grep "0.0.0.0:" | grep -v "127.0.0.1"
```

## Services migrated

| Service | Port | Original Binding | New Binding | Method |
|---------|------|-----------------|-------------|--------|
| Open WebUI | 3081 | `0.0.0.0:3081` (host networking) | `127.0.0.1:3081` (bridge) | Docker compose: `network_mode: host` → bridge + `extra_hosts` |
| Hermes Dashboard | 9119 | `0.0.0.0:9119` | `127.0.0.1:9119` | Kill + restart with `--host 127.0.0.1` |
| llama-swap | 9292 | `*:9292` (IPv4+IPv6) | `127.0.0.1:9292` | Config change + systemd update + restart |

## Open WebUI — host to bridge migration

### Before (`network_mode: host`)
```yaml
    network_mode: host
    environment:
      - OPENAI_API_BASE_URL=http://127.0.0.1:9292/v1
      - HERMES_API_URL=http://127.0.0.1:18789
      - SEARXNG_QUERY_URL=http://127.0.0.1:8080/search?q=<query>&format=json
```

### After (bridge networking)
```yaml
    ports:
      - "127.0.0.1:3081:3081"
    extra_hosts:
      - "host.docker.internal:host-gateway"
    environment:
      - OPENAI_API_BASE_URL=http://host.docker.internal:9292/v1
      - HERMES_API_URL=http://host.docker.internal:18789
      - SEARXNG_QUERY_URL=http://host.docker.internal:8080/search?q=<query>&format=json
```

Key change: all `127.0.0.1` URLs inside env vars become `host.docker.internal` since the container can't reach `127.0.0.1` on the host from a bridge network.

## Stale host process cleanup

After the Docker container was recreated from host networking to bridge, the OLD host-level process was still running:

```
root  557199  /usr/local/bin/python3 -m uvicorn open_webui.main:app --host 0.0.0.0 --port 3081
```

This process continued serving on `0.0.0.0:3081` even though the Docker container was now on `127.0.0.1:3081`. The fix: `sudo kill <PID>`.
