kapitola 3

Nginx, reverse proxy, DNS a HTTPS

Jak jsme oddělili veřejnou vrstvu VPS od backendu v Dockeru a přidali TLS přes Let's Encrypt.

Systémový nginx vs. nginx v containeru

INTERNET ↓ systémový nginx na VPS (reverse proxy + HTTPS) ↓ 127.0.0.1:8080 ↓ nginx v containeru (statický webserver) ↓ index.html

Nginx není automaticky reverse proxy. Reverse proxy z něj dělá až konfigurace typu proxy_pass.

sites-available vs. sites-enabled

Debianí instalace nginxu používá běžnou organizační konvenci:

/etc/nginx/sites-available/
/etc/nginx/sites-enabled/

sites-available

Tady máme uložené konfigurace webů, které na serveru existují nebo které máme připravené.

/etc/nginx/sites-available/
├── default
├── docker.kekor.cz
└── tahak.kekor.cz

Samotná přítomnost souboru v sites-available ještě neznamená, že jej nginx používá.

Název souboru také není pro nginx magický. Rozhodující je obsah konfigurace, například server_name tahak.kekor.cz;.

sites-enabled

Nginx má ve své hlavní konfiguraci nastaveno načítání konfigurací ze sites-enabled.

Proto jsou aktivní právě konfigurace dostupné přes tento adresář.

/etc/nginx/sites-enabled/
├── default
├── docker.kekor.cz
└── tahak.kekor.cz

Obvykle tam ale nedáváme kopii skutečného konfiguračního souboru. Použijeme symbolický link.

sites-enabled/tahak.kekor.cz │ │ symbolic link ▼ sites-available/tahak.kekor.cz

Výhoda je, že skutečná konfigurace existuje pouze jednou.

Web lze deaktivovat odstraněním symlinku ze sites-enabled, aniž bychom smazali jeho konfiguraci ze sites-available.

Jak přesně funguje ln -s?

Správný příkaz:

sudo ln -s \
  /etc/nginx/sites-available/tahak.kekor.cz \
  /etc/nginx/sites-enabled/tahak.kekor.cz

Syntaxe je:

ln -s TARGET LINK_NAME
Část Význam
ln vytvoření linku
-s symbolický link místo hard linku
první cesta skutečný cílový soubor
druhá cesta kde má vzniknout symlink

Výsledek ověříme:

ls -l /etc/nginx/sites-enabled/

Správně například:

tahak.kekor.cz -> /etc/nginx/sites-available/tahak.kekor.cz

Chyba: symlink ukazuje sám na sebe

Během našeho cvičení jsme omylem použili:

sudo ln -s tahak.kekor.cz \
  /etc/nginx/sites-enabled/tahak.kekor.cz

První cesta byla relativní. Relativní target symlinku se interpretuje vzhledem k adresáři, ve kterém se samotný symlink nachází.

Vzniklo tedy ve skutečnosti:

/etc/nginx/sites-enabled/tahak.kekor.cz ↓ /etc/nginx/sites-enabled/tahak.kekor.cz ↓ /etc/nginx/sites-enabled/tahak.kekor.cz ↓ ...

Nginx pak při testu skončil chybou:

Too many levels of symbolic links

Operační systém zastavil nekonečné následování symlinku.

Oprava:

sudo rm /etc/nginx/sites-enabled/tahak.kekor.cz

sudo ln -s \
  /etc/nginx/sites-available/tahak.kekor.cz \
  /etc/nginx/sites-enabled/tahak.kekor.cz
U nginx konfigurací je proto přehledné používat u targetu symlinku celou absolutní cestu.

Konfigurace jedné domény

server {
    listen 80;
    listen [::]:80;

    server_name docker.example.cz;

    location / {
        proxy_pass http://127.0.0.1:8080;

        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}

server_name vybírá virtual host podle HTTP hlavičky Host. Díky tomu může jeden VPS a jedna veřejná IP obsluhovat mnoho různých domén.

app1.example.cz → nginx → 127.0.0.1:8080 → container A app2.example.cz → nginx → 127.0.0.1:8081 → container B app3.example.cz → nginx → 127.0.0.1:8082 → container C

Jak přesně funguje ln -s?

Správný příkaz:

sudo ln -s \
  /etc/nginx/sites-available/tahak.kekor.cz \
  /etc/nginx/sites-enabled/tahak.kekor.cz

Syntaxe je:

ln -s TARGET LINK_NAME
Část Význam
ln vytvoření linku
-s symbolický link místo hard linku
první cesta skutečný cílový soubor
druhá cesta kde má vzniknout symlink

Výsledek ověříme:

ls -l /etc/nginx/sites-enabled/

Správně například:

tahak.kekor.cz -> /etc/nginx/sites-available/tahak.kekor.cz

Chyba: symlink ukazuje sám na sebe

Během našeho cvičení jsme omylem použili:

sudo ln -s tahak.kekor.cz \
  /etc/nginx/sites-enabled/tahak.kekor.cz

První cesta byla relativní. Relativní target symlinku se interpretuje vzhledem k adresáři, ve kterém se samotný symlink nachází.

Vzniklo tedy ve skutečnosti:

/etc/nginx/sites-enabled/tahak.kekor.cz ↓ /etc/nginx/sites-enabled/tahak.kekor.cz ↓ /etc/nginx/sites-enabled/tahak.kekor.cz ↓ ...

Nginx pak při testu skončil chybou:

Too many levels of symbolic links

Operační systém zastavil nekonečné následování symlinku.

Oprava:

sudo rm /etc/nginx/sites-enabled/tahak.kekor.cz

sudo ln -s \
  /etc/nginx/sites-available/tahak.kekor.cz \
  /etc/nginx/sites-enabled/tahak.kekor.cz
U nginx konfigurací je proto přehledné používat u targetu symlinku celou absolutní cestu.

Proxy hlavičky

HlavičkaCo předává backendu
Hostpůvodní doménu
X-Real-IPIP návštěvníka
X-Forwarded-Forřetězec proxy/IP přes které request prošel
X-Forwarded-Protozda klient přišel přes http nebo https

Kontrola a reload nginxu

sudo nginx -t
sudo systemctl reload nginx

Nejdřív syntaktický test, až potom reload. Je to bezpečnější než slepý restart.

DNS

U správce domény jsme vytvořili A záznam:

docker.example.cz   A   YOUR_VPS_IP

Ověření:

Resolve-DnsName docker.example.cz
nslookup docker.example.cz
curl -v http://docker.example.cz

HTTP request obsahuje Host: docker.example.cz, podle něhož nginx vybere správný server block.

Let's Encrypt, ACME a Certbot

Let's Encrypt

Certifikační autorita, která vydává certifikát.

ACME

Automatizovaný protokol pro vydání/obnovu certifikátu.

Certbot

ACME klient na našem VPS.

nginx

Server, který certifikát skutečně používá pro TLS.

sudo apt install certbot python3-certbot-nginx
certbot --version
sudo certbot --nginx -d docker.example.cz

TLS termination

browser │ HTTPS :443 │ šifrovaně ▼ systémový nginx │ certifikát + private key │ TLS zde končí ▼ HTTP na 127.0.0.1:8080 ▼ Docker container

Backend může běžet přes obyčejné HTTP, protože komunikace zůstává uvnitř VPS na loopbacku.

Co Certbot přidal do nginxu

listen 443 ssl;
ssl_certificate /etc/letsencrypt/live/docker.example.cz/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/docker.example.cz/privkey.pem;

fullchain.pem je veřejný certifikát + certifikační řetězec. privkey.pem je tajný soukromý klíč.

HTTP port 80 se pak typicky používá jen pro redirect:

return 301 https://$host$request_uri;

Ověření HTTPS

curl -I http://docker.example.cz
# očekáváme 301

curl -I https://docker.example.cz
# očekáváme 200

Automatické obnovování certifikátu

systemctl status certbot.timer
sudo certbot renew --dry-run

Timer běží pravidelně, ale nový certifikát se nevydává při každém spuštění. Certbot obnovuje až certifikáty, které se blíží expiraci.