# TLS a HTTPS prakticky

Reverse proxy je z velké části správa TLS, takže bez téhle stránky nedávají ty další smysl. Nejde tu o kryptografii, ale o věci, které tě reálně potkají v konfiguraci.

Když tě zajímá, co se pod tím děje, je to na [TLS handshake krok za krokem](TLS-handshake).

## Co TLS řeší

Tři věci naráz, a je užitečné je oddělovat:

**Šifrování** — nikdo po cestě nepřečte obsah.

**Integrita** — nikdo po cestě obsah nezmění, aniž by to bylo poznat.

**Autenticita** — mluvíš se serverem, za který se vydává. Tohle dělá certifikát a je to ta část, která nejčastěji zlobí.

Šifrování bez autenticity je k ničemu. Kdyby ti stačilo šifrování, útočník se prostě postaví doprostřed a šifruje s tebou sám.

## Průběh spojení

```mermaid
sequenceDiagram
    participant K as Klient
    participant S as Server
    K->>S: ClientHello (umím tyto šifry, chci doménu X)
    S->>K: ServerHello + certifikát
    K->>K: ověří podpis proti CA a datum platnosti
    K->>S: výměna klíčů
    Note over K,S: dál je vše šifrované
    K->>S: GET / HTTP/1.1
```

To „chci doménu X" v prvním kroku je **SNI** a je klíčové pro pochopení reverse proxy.

Tohle je zjednodušení. V TLS 1.3 přiloží klient svou část výměny klíčů rovnou do prvního paketu, takže celý handshake trvá jedno kolo a certifikát letí už zašifrovaný — rozbor je na [TLS handshake krok za krokem](TLS-handshake), samotná výměna na [Diffie-Hellman](Diffie-Hellman).

## SNI

Na jedné IP adrese může běžet sto webů. Server ale musí vybrat certifikát **dřív**, než se spojení zašifruje a než uvidí HTTP hlavičku `Host`.

Řeší to SNI: klient v úvodním nešifrovaném pozdravu pošle jméno domény, na kterou se chce připojit. Podle toho server vybere certifikát.

Praktické důsledky:

**Jméno domény jde po drátě nešifrovaně.** Obsah nikdo nepřečte, ale to, na které weby chodíš, ano. Řeší to až Encrypted Client Hello, které se teprve nasazuje.

**Reverse proxy může podle SNI rozhodovat, aniž by TLS rozbalovala.** Tomu se říká SNI routing.

**Bez SNI dostaneš výchozí certifikát.** Když se připojíš na IP adresu místo na jméno, server neví, co chceš, a pošle první certifikát v pořadí — proto to varování v prohlížeči.

## Řetěz certifikátů

Certifikát sám o sobě nic nedokazuje. Důvěra se odvozuje řetězem:

```
kořenová CA (v prohlížeči a v OS)
    └── mezilehlá CA
            └── tvůj certifikát pro example.cz
```

Server musí poslat **svůj certifikát i mezilehlé**. Kořenový ne, ten už klient má.

Tohle je nejčastější chyba při ručním nasazení certifikátů. Projeví se tak, že web funguje v prohlížeči na desktopu (ten si mezilehlý certifikát domyslí z keše) a nefunguje v `curl`, na mobilu nebo v jiné aplikaci.

Ověř si to:

```bash
openssl s_client -connect example.cz:443 -servername example.cz < /dev/null
```

V hlavičce výstupu je `Certificate chain` — musí tam být víc než jedna položka. A na konci `Verify return code: 0 (ok)`.

Online kontrola: [ssllabs.com/ssltest](https://www.ssllabs.com/ssltest/), která ti ukáže i „chain issues".

[Caddy](Caddy) a [Traefik](Traefik) tenhle problém nemají, protože řetěz sestavují samy.

## Verze a šifry

Jednoduše: **TLS 1.2 a 1.3, nic staršího.** TLS 1.0 a 1.1 jsou vyřazené, SSL v jakékoliv verzi je prolomené.

Rozumná konfigurace pro nginx:

```nginx
ssl_protocols TLSv1.2 TLSv1.3;
ssl_prefer_server_ciphers off;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 1d;
ssl_session_tickets off;
```

U TLS 1.3 se seznam šifer neladí — protokol nabízí jen bezpečné. To `ssl_prefer_server_ciphers off` je dnes doporučení, protože klient obvykle ví lépe, co má hardwarově akcelerované.

To `ssl_session_tickets off` na posledním řádku není kosmetika: tikety šifruje server dlouhodobým klíčem, a když se ten nerotuje a unikne, dá se zpětně dešifrovat provoz všech obnovených relací. Vysvětlení je v [TLS handshake](TLS-handshake#obnovení-relace).

Generátor konfigurací pro všechny běžné servery: [ssl-config.mozilla.org](https://ssl-config.mozilla.org/).

## HSTS

Hlavička, kterou serveru řekneš prohlížeči „na tuhle doménu chodím vždycky přes HTTPS":

```nginx
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
```

Chrání proti útoku, kdy tě někdo při prvním requestu na `http://` přesměruje jinam.

**Pozor na `includeSubDomains`.** Platí to na rok a prohlížeč si to zapamatuje. Když máš pod doménou něco, co běží jen na HTTP, přestane to fungovat a nedá se to jednoduše vzít zpět. Nasazuj nejdřív s krátkým `max-age`, třeba 300, a zvyšuj až po ověření.

## Ukončení TLS a co dál

Reverse proxy TLS obvykle ukončí a dovnitř posílá nešifrované HTTP. Je to standardní a v důvěryhodné síti v pořádku — vnitřní provoz mezi proxy a aplikací neopouští stroj nebo důvěryhodný segment.

Alternativy:

**TLS passthrough** — proxy nerozbaluje, jen podle SNI přepošle dál. Nutné, když aplikace potřebuje vidět klientský certifikát.

**Re-encrypt** — proxy rozbalí a k aplikaci naváže nové TLS. Užitečné, když provoz jde přes nedůvěryhodnou síť, třeba mezi VM na různých hostitelích.

Pro homelab je ukončení na proxy správná volba.

## Časté chyby

**„NET::ERR_CERT_AUTHORITY_INVALID"** → chybí mezilehlý certifikát, nebo je certifikát podepsaný sám sebou. Případně ho vystavila [vlastní CA](Vlastni-certifikacni-autorita), kterou tenhle klient nezná.

**Funguje v prohlížeči, ne v `curl`** → skoro vždycky chybějící mezilehlý certifikát.

**„certificate has expired"** → neproběhla obnova. Viz [certifikáty a Let's Encrypt](Certifikaty-a-Lets-Encrypt).

**Aplikace generuje odkazy s `http://`** → aplikace neví, že běží za proxy. Chybí hlavička `X-Forwarded-Proto`, viz [reverse proxy](Reverse-proxy).

**Přesměrovací smyčka** → proxy přesměrovává na HTTPS a aplikace taky, přičemž aplikace vidí interní HTTP a myslí si, že je potřeba přesměrovat znovu. Řeší se stejnou hlavičkou.
