# Docker a firewall

Tohle je nejdůležitější stránka celé sekce o firewallu. Popisuje chování, které skoro nikdo nečeká a které tiše otevře služby do internetu.

## Problém v jedné větě

**Když publikuješ port přes Docker, ufw ho nezablokuje.**

```bash
ufw default deny incoming
ufw enable
docker run -d -p 8080:80 nginx
```

Port 8080 je teď dostupný z internetu. `ufw status` tvrdí, že příchozí provoz je zakázaný. Obojí je pravda a to je celý ten problém.

## Proč se to děje

Docker si sám zapisuje pravidla do netfilteru a dělá to v místě, kam se paket dostane **dřív**, než na něj sáhne ufw.

```mermaid
flowchart LR
    P[paket na port 8080] --> PRE[PREROUTING<br/>Docker sem dá DNAT]
    PRE --> R{pro tenhle stroj?}
    R -->|ne, je pro kontejner| FWD[FORWARD<br/>DOCKER-USER, DOCKER]
    R -->|ano| INP[INPUT<br/>tady sedí ufw]
    FWD --> K[kontejner]
```

Docker udělá DNAT už v `PREROUTING`, čímž z paketu udělá provoz **skrz** stroj místo provozu **na** stroj. Ten pak jde přes `FORWARD`, kde ufw žádná pravidla nemá — ufw pracuje v `INPUT`.

Souvislosti jsou v [Linux firewall](Linux-firewall).

Není to chyba Dockeru ani ufw. Je to důsledek toho, že spolu nekomunikují a oba mají pravdu ze svého pohledu.

## Ověř si to

Než tomu uvěříš, změř to. Z jiného stroje nebo z mobilu na datech:

```bash
nmap -Pn -p 8080 adresa.serveru
```

Nebo se podívej na skutečná pravidla místo na to, co tvrdí ufw:

```bash
iptables -L -n -v | grep -A20 DOCKER
nft list ruleset | grep -i docker
```

## Řešení 1: nepublikuj na všechna rozhraní

Nejjednodušší a nejúčinnější. Řekni Dockeru, aby port vystavil jen na loopback:

```bash
docker run -d -p 127.0.0.1:8080:80 nginx
```

V Compose:

```yaml
services:
  web:
    image: nginx
    ports:
      - "127.0.0.1:8080:80"
```

Služba je pak dostupná jen ze samotného stroje. Ven ji pustíš přes [reverse proxy](Reverse-proxy), která běží taky na tom stroji a je zajištěná normálně přes ufw.

**Tohle je správná odpověď pro naprostou většinu případů.** Nic dalšího řešit nemusíš.

Zapamatuj si rozdíl:

| Zápis | Dostupné z |
|---|---|
| `-p 8080:80` | odkudkoliv, včetně internetu |
| `-p 127.0.0.1:8080:80` | jen z tohoto stroje |
| `-p 192.168.1.5:8080:80` | jen přes konkrétní rozhraní |
| `expose: 80` (Compose) | jen z jiných kontejnerů |

## Řešení 2: DOCKER-USER

Docker záměrně nechává prázdný řetězec `DOCKER-USER`, který se vyhodnocuje **před** jeho vlastními pravidly. Sem patří tvoje omezení.

```bash
# povolit jen z domácí sítě a z VPN
iptables -I DOCKER-USER -i eth0 ! -s 192.168.1.0/24 -j DROP
iptables -I DOCKER-USER -i eth0 -s 10.10.0.0/24 -j RETURN
```

Pravidlo přežije restart Dockeru, ale ne restart stroje — ulož ho stejně jako ostatní, viz [iptables](iptables).

Na IPv6 nezapomeň, je to samostatný řetězec:

```bash
ip6tables -I DOCKER-USER -i eth0 -j DROP
```

## Řešení 3: vypnout Dockeru správu iptables

```json
/etc/docker/daemon.json
{
  "iptables": false
}
```

**Nedělej to, pokud přesně nevíš proč.** Kontejnerům tím rozbiješ odchozí síť a musíš si NAT a forwarding napsat celý ručně. Je to legitimní volba na routeru, kde firewall stavíš od základu, a špatná volba všude jinde.

## Compose a `expose`

Když spolu kontejnery komunikují ve stejné síti Compose, **nepotřebují publikované porty vůbec**. Oslovují se jménem služby:

```yaml
services:
  app:
    image: mojeapp
    expose:
      - "3000"
  db:
    image: postgres
    # žádné ports, databáze není zvenku vidět
  proxy:
    image: caddy
    ports:
      - "80:80"
      - "443:443"
```

Aplikace se na databázi připojí přes `db:5432`. Ven čouhá jen proxy.

Tohle je zdravý vzor: **jediný kontejner s publikovanými porty je reverse proxy.** Všechno ostatní zůstane vnitřní. Viz [sítě v Dockeru](Site-v-Dockeru).

## Kontrolní seznam

Když ti na stroji běží Docker:

1. Zjisti, co je skutečně vystavené: `docker ps --format "table {{.Names}}\t{{.Ports}}"`
2. Cokoliv ve tvaru `0.0.0.0:port->` je veřejné
3. Ověř zvenku pomocí `nmap`
4. Přidej `127.0.0.1:` všude, kde služba nemá být veřejná
5. Zkontroluj IPv6 zvlášť

Ten první příkaz stojí za spuštění hned teď. Většina lidí je překvapená.

## Proxmox a další

Stejný problém, jiný viník. Proxmox, libvirt a další systémy, které dělají virtuální sítě, si taky zapisují vlastní pravidla a `ufw status` je neukáže.

Univerzální pravidlo: **věř `nft list ruleset`, ne nadstavbě.** Viz [Proxmox a sítě](Proxmox-site).
