# Full Server Port Exposure Audit — Case Study

## Session Context
Conducted on a self-hosted Debian server (debian-ai) running Docker containers (nexstream-scraper, open-webui, searxng), Tailscale Funnel, Hermes gateway/dashboard, and llama-swap LLM proxy.

## Methodology

### 1. Identify all publicly reachable services
```bash
ss -tlnp4 | grep LISTEN
```

### 2. Classify by bind address
- **0.0.0.0:PORT** — reachable from the public internet (unless blocked by iptables)
- **127.0.0.1:PORT** — safe, localhost-only
- **100.x.x.x:PORT** — Tailscale IP, reachable only within tailnet
- ***:PORT** — reachable on ALL interfaces including IPv6

### 3. Check iptables — is the DROP policy actually enforced?
```bash
sudo iptables -L INPUT -n | head -10
sudo iptables -L ts-input -n | head -10
```

**Critical finding on this server:** The Tailscale `ts-input` chain has:
```
ACCEPT all -- 0.0.0.0/0 0.0.0.0/0
```
This means ALL incoming traffic is accepted regardless of source, making the INPUT DROP policy irrelevant. Any service bound to 0.0.0.0 is reachable from the internet.

### 4. Check Tailscale funnel/serve exposure
```bash
tailscale funnel status 2>/dev/null
tailscale serve status 2>/dev/null
```

### 5. Cross-reference with Docker port mappings
```bash
docker ps --format "table {{.Names}}\t{{.Image}}\t{{.Ports}}"
```

## Findings (This Server)

### Services on 0.0.0.0 (EXTERNALLY REACHABLE — needed fixing)
| Port | Service | Risk | Fix |
|------|---------|------|-----|
| **3081** | Open WebUI (direct, bypassing nginx) | 🔴 HIGH — ran as root, no rate limiting | Switch `network_mode: host` → bridge + `127.0.0.1:3081` |
| **9119** | Hermes dashboard (no auth) | 🔴 HIGH — full management access | Restart with `--host 127.0.0.1` |
| **9292** | llama-swap LLM proxy | 🔴 HIGH — anyone could send inference | Update config `listen: :9292` → `listen: 127.0.0.1:9292` + restart |

### Services on 127.0.0.1 (SAFE — no changes needed)
| Port | Service | Notes |
|------|---------|-------|
| **3089** | nginx → scraper proxy | Newly added, rate-limited |
| **8080** | SearXNG | Internal |
| **6379** | Valkey/Redis | Internal |
| **5432** | PostgreSQL | Internal |
| **18789** | Hermes gateway | Already internal |
| **5037** | ADB | Internal |

### Services on 0.0.0.0 (ACCEPTABLE — left as-is)
| Port | Service | Why OK |
|------|---------|--------|
| **80** | nginx → Open WebUI | Expected web service |
| **22** | SSH | PasswordAuth disabled, fail2ban active |
| **4096** | opencode IDE | User explicitly using it |
| **443** | Tailscale Funnel | Tailscale IP only |

## Migration Patterns Applied

### Pattern 1: Docker container with `network_mode: host` → bridge
**Service:** Open WebUI (port 3081)

```yaml
# Before
network_mode: host
environment:
  - OPENAI_API_BASE_URL=http://127.0.0.1:9292/v1
  - SEARXNG_QUERY_URL=http://127.0.0.1:8080/...

# After  
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
  - SEARXNG_QUERY_URL=http://host.docker.internal:8080/...
```

### Pattern 2: Standalone process with CLI flag
**Service:** Hermes dashboard (port 9119)
```bash
kill <PID>
hermes dashboard --host 127.0.0.1 --port 9119 --no-open --insecure
```

### Pattern 3: Systemd service + config file
**Service:** llama-swap (port 9292)

Update config file:
```yaml
# ~/.config/llama-swap/config.yaml
listen: 127.0.0.1:9292  # was :9292
```

Update systemd unit:
```bash
sudo sed -i 's/--listen :9292/--listen 127.0.0.1:9292/' /etc/systemd/system/llama-swap.service
```

Apply:
```bash
sudo systemctl daemon-reload && sudo systemctl restart llama-swap
```

## Verification

After each migration, verify:
```bash
# Service is listening on 127.0.0.1 only
ss -tlnp4 | grep <PORT> | grep "127.0.0.1"

# Service is NOT on 0.0.0.0
ss -tlnp4 | grep <PORT> | grep -v "127.0.0.1"

# Service is functional
curl -s -o /dev/null -w "%{http_code}" http://127.0.0.1:<PORT>/health

# External services (Funnel, nginx) still work
curl -s -o /dev/null -w "%{http_code}" https://<funnel-url>/health
```
