markdown
SSH.md
markdown
# SSH do hloubky SSH je nejpoužívanější kryptografický protokol, který kdy budeš obsluhovat ručně. Zároveň je to jediná cesta na tvůj server, takže se vyplatí vědět, co se při připojení doopravdy děje — ne proto, aby to fungovalo, ale proto, abys věděl, co znamená, když to nefunguje nebo když se něco změní. Základní zabezpečení je na [zabezpečení serveru](Zabezpeceni-serveru). Tahle stránka jde hlouběji: protokol, ověřování, certifikáty, tunely a věci, které dělá málokdo a měl by. Předpokládá [veřejný a soukromý klíč](Verejny-a-soukromy-klic) a [Diffie-Hellmana](Diffie-Hellman). ## Co SSH doopravdy je Není to „vzdálený terminál". Terminál je jen jedno z použití. SSH je **šifrovaný multiplexovaný transport**, do kterého se dá zabalit cokoliv: - interaktivní shell- jednorázový příkaz- přenos souborů (SFTP, `rsync`, `scp`)- přesměrované TCP porty oběma směry- SOCKS proxy- unixové sokety (třeba `docker.sock`)- pomocné kanály — přeposlaný agent, X11 Všechno tohle jede **jedním TCP spojením** a rozlišuje se to čísly kanálů. Proto můžeš v jednom sezení mít shell a zároveň tunel na databázi. Protokol je popsaný v RFC 4251 až 4254 z roku 2006 a od té doby se změnily hlavně algoritmy, ne struktura. ## Tři vrstvy | Vrstva | Co dělá ||---|---|| **Transportní** | výměna klíčů, ověření serveru, šifrování, integrita, komprese || **Ověření uživatele** | dokazuje, kdo jsi — klíčem, heslem, tokenem || **Spojová** | kanály, tunely, shell, přenos souborů | Pořadí je podstatné: **server se ověřuje první**. Než pošleš jediný znak hesla, musíš už vědět, s kým mluvíš. Kdyby to bylo obráceně, útočník uprostřed by hesla sbíral bez námahy. ## Průběh připojení ```mermaidsequenceDiagram participant K as Klient participant S as Server Note over K,S: TCP spojení na port 22 K->>S: SSH-2.0-OpenSSH_9.6 S->>K: SSH-2.0-OpenSSH_9.6 Note over K,S: nešifrovaně, oba znají verzi protistrany K->>S: KEXINIT — seznam algoritmů, které umím S->>K: KEXINIT — seznam algoritmů, které umím Note over K,S: z průniku se vybere první shoda podle pořadí klienta K->>S: veřejná část dočasného klíče S->>K: veřejná část + host key + podpis K->>K: ověří host key proti known_hosts K->>K: ověří podpis exchange hashe Note over K,S: NEWKEYS — dál je vše šifrované K->>S: chci se přihlásit jako martin, metodou publickey S->>K: dobře, zkus to K->>S: podpis session ID svým soukromým klíčem S->>K: SUCCESS Note over K,S: otevírají se kanály — shell, tunely``` ### Pozdrav První dva řádky jdou **otevřeně** a jsou to jediná data, která si kdokoliv po cestě přečte: ```bashnc server 22# SSH-2.0-OpenSSH_9.6p1 Ubuntu-3ubuntu13.5``` Odtud pochází návod „skryj verzi SSH". Nemá to smysl — verze je nutná pro obcházení historických chyb v protokolu a útočník tvou verzi stejně pozná podle nabídnutých algoritmů. Naopak: ten řetězec se **započítává do podpisu**, takže ho po cestě nikdo nemůže změnit, aniž by spojení selhalo. ### Vyjednání algoritmů Obě strany pošlou seznamy toho, co umí, pro každou kategorii zvlášť: výměna klíčů, host key, šifra pro každý směr, MAC, komprese. **Vybere se první položka klientova seznamu, kterou umí i server.** Pořadí na straně klienta tedy rozhoduje. Co umí tvůj klient: ```bashssh -Q kex # výměna klíčůssh -Q key # typy host keys a klíčů uživatelessh -Q cipher # šifryssh -Q mac # ověřování integrity``` Co nabízí konkrétní server: ```bashnmap --script ssh2-enum-algos -p 22 serverssh-audit server # čitelnější, s hodnocením; pip install ssh-audit``` ### Výměna klíčů Proběhne [Diffie-Hellman](Diffie-Hellman), dnes typicky na křivce X25519 v hybridu s postkvantovým mechanismem. ```bashssh -vv server 2>&1 | grep -iE "kex:|cipher:"# kex: algorithm: mlkem768x25519-sha256# kex: host key algorithm: ssh-ed25519# kex: server->client cipher: chacha20-poly1305@openssh.com``` Historie výchozích voleb v OpenSSH, ať víš, čemu odpovídá tvoje verze: | Verze | Výchozí výměna klíčů ||---|---|| 6.5 (2014) | `curve25519-sha256` || 9.0 (2022) | `sntrup761x25519-sha512` — první postkvantová || 10.0 (2025) | `mlkem768x25519-sha256` — standardizovaná postkvantová | Vedlejším produktem výměny je **exchange hash** — otisk úplně všeho, co dosud proběhlo: obou pozdravů, obou seznamů algoritmů, host keye a veřejných částí. První takový hash se stane **session ID** a platí po celé spojení. Server ho podepíše svým **host keyem** a tím se ověří. Kdo host key nemá, nemůže se za server vydávat — může leda přeposílat pakety, ale pak podpis neodpovídá jeho vlastní výměně. Tohle je zároveň důvod, proč SSH nepotřebuje certifikáty: **důvěru neseš ty ve svém `known_hosts`.** ### Šifrování provozu Po `NEWKEYS` je vše šifrované. Z výsledku výměny se [odvodí](Sifrovani-zaklady#kdf-jak-se-z-jednoho-tajemství-udělá-víc-klíčů) šest klíčů: pro každý směr šifrovací klíč, počáteční vektor a klíč pro integritu. Moderní volba je AEAD šifra, kde se integrita řeší uvnitř: - `chacha20-poly1305@openssh.com` — výchozí, rychlá i bez hardwarové akcelerace- `aes256-gcm@openssh.com`, `aes128-gcm@openssh.com` Kde AEAD není, používá se **encrypt-then-MAC** (`hmac-sha2-256-etm@openssh.com`). Varianty bez `-etm` počítají MAC z otevřeného textu a jsou horší; CBC režimy nepoužívej vůbec. ## Host keys a known_hosts Nejzajímavější část SSH, protože tady se řeší důvěra bez jakékoliv autority. Server má vlastní dvojici klíčů, obvykle několik podle typu: ```bashls -1 /etc/ssh/ssh_host_*# ssh_host_ecdsa_key ssh_host_ecdsa_key.pub# ssh_host_ed25519_key ssh_host_ed25519_key.pub# ssh_host_rsa_key ssh_host_rsa_key.pub``` ### First use a known_hosts Při prvním připojení klient klíč nezná: ```The authenticity of host 'server (192.168.1.10)' can't be established.ED25519 key fingerprint is SHA256:5N3YbQm...Are you sure you want to continue connecting (yes/no/[fingerprint])?``` Tomu se říká **trust on first use**. Buď je to server, nebo je to útočník — protokol to nerozliší a **rozhodnutí přenáší na tebe**. Když odpovíš `yes`, klíč se uloží do `~/.ssh/known_hosts` a od té chvíle se každé připojení kontroluje. Skoro nikdo to neověřuje a je to největší reálná mezera v běžném používání SSH. Jak to udělat pořádně, seřazeno od nejlepšího: **1. Certifikáty.** Podepiš host keys vlastní CA a klient je přijme bez ptaní. Řeší to i výměnu klíčů při reinstalaci. Viz [níže](#ssh-certifikáty). **2. Otisk z jiného kanálu.** Přečti si ho na konzoli serveru přes web poskytovatele, z výstupu cloud-initu nebo z hypervizoru: ```bash# na serveru, přes konzolifor f in /etc/ssh/ssh_host_*_key.pub; do ssh-keygen -lf "$f"; done``` **3. SSHFP v DNS.** Otisk klíče jako DNS záznam: ```bashssh-keygen -r server.example.cz # vygeneruje záznamy k vložení do zónyssh -o VerifyHostKeyDNS=yes server.example.cz``` Užitečné, ale **skutečnou bezpečnost dává jen s DNSSEC** — jinak jsi jen přesunul důvěru z první odpovědi SSH na první odpověď DNS. **4. Rozdat `known_hosts` centrálně.** Ansible, `/etc/ssh/ssh_known_hosts`, správa konfigurací. Co **není** ověření: `ssh-keyscan server >> ~/.ssh/known_hosts`. Tenhle příkaz se ptá po síti, tedy přesně toho, komu nevěříš. Je to zápis „důvěřuji tomu, kdo mi zrovna odpověděl". Použitelné jen tam, kde nemáš co ztratit. ### Hashované known_hosts Debian a Ubuntu mají ve výchozím stavu `HashKnownHosts yes`, takže v souboru nejsou jména strojů, ale jejich otisky. Chrání to seznam tvých serverů, kdyby ti někdo prohledal notebook. Nedá se v tom pak hledat očima, ale nástroje ano: ```bashssh-keygen -F server # najdi záznamssh-keygen -R server # smaž záznamssh-keygen -H -f ~/.ssh/known_hosts # zahashuj existující soubor``` ### Když se otisk změní ```@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED! @``` **Neodstraňuj to reflexivně.** Legitimní důvody jsou reinstalace, obnova ze zálohy nebo výměna stroje. Nelegitimní důvod je útočník uprostřed. Rozdíl poznáš jen tak, že se zeptáš, jestli se se serverem něco dělo — a pokud ne, tak dokud to nevysvětlíš, se nepřipojuj. Když je změna očekávaná: ```bashssh-keygen -R serverssh server # a otisk zkontroluj proti konzoli``` ### Automatická rotace Od OpenSSH 8.5 je ve výchozím stavu `UpdateHostKeys yes`. Server po přihlášení pošle **všechny své host keys** a klient si je uloží. Umožňuje to bezbolestný přechod z RSA na Ed25519 nebo výměnu klíče bez varování u všech uživatelů. Funguje to jen po úspěšném ověření, takže to nic neoslabuje. ## Ověření uživatele ### Klíčem To, co se ve skutečnosti děje, je zajímavější než „server má můj veřejný klíč". 1. Klient ohlásí, kterým veřejným klíčem se chce prokázat.2. Server se podívá do `~/.ssh/authorized_keys`, jestli tam je. Pokud ne, odmítne rovnou.3. Klient **podepíše session ID** (plus jméno uživatele, služby a metody) svým soukromým klíčem.4. Server podpis ověří veřejným klíčem. Klíčový detail je ten podpis nad **session ID**. Session ID je unikátní pro každé spojení a je do něj zapleten host key serveru. Z toho plyne: - Podpis nejde přehrát na jiné spojení.- Podpis nejde přeposlat na **jiný server**, protože ten má jiný host key.- Soukromý klíč **nikdy neopustí tvůj stroj** — server dostane jen podpis. Tohle je zásadní rozdíl proti heslu. Když se přihlásíš heslem na podvržený server, útočník má tvoje heslo a přihlásí se s ním kamkoliv jinam. Když se přihlásíš klíčem, útočník má podpis, který jinde nefunguje. **Klíč tedy chrání i před špatným rozhodnutím u `known_hosts`.** ### Ostatní metody | Metoda | Poznámka ||---|---|| `publickey` | tohle chceš || `password` | posílá heslo dovnitř šifrovaného kanálu; vypni || `keyboard-interactive` | dialog otázka–odpověď, tudy jde TOTP a PAM || `hostbased` | ověřuje stroj, ne uživatele; v praxi se nepoužívá || `gssapi-with-mic` | Kerberos, firemní prostředí | ### Dva faktory pořádně Rozdíl, na kterém záleží: čárka znamená **a zároveň**, mezera znamená **nebo**. ```AuthenticationMethods publickey,keyboard-interactive``` Tohle vyžaduje klíč **a k němu** ještě kód. V PAM pak sedí `pam_google_authenticator` nebo Duo. ```AuthenticationMethods publickey keyboard-interactive``` Tohle znamená „stačí jedno z toho" a je to obvykle omyl. Pro homelab je poctivější odpověď: **hardwarový klíč s vyžadovaným dotekem je 2FA sám o sobě** a je pohodlnější než opisování číslic. ## Volby v authorized_keys Málo používaná a velmi užitečná věc. Před klíč se dá napsat seznam omezení, která platí **jen pro tento klíč**: ```restrict,from="192.168.1.0/24",command="/usr/local/bin/zaloha.sh" ssh-ed25519 AAAAC3... zaloha``` | Volba | Co dělá ||---|---|| `restrict` | zakáže **všechno** — tunely, agenta, X11, terminál. Základ, na kterém stavíš. || `pty` | povolí terminál zpátky || `port-forwarding`, `agent-forwarding` | povolí zpátky konkrétní věc || `command="..."` | při přihlášení se spustí **jen tohle**, ať uživatel žádá cokoliv || `from="1.2.3.4,10.0.0.0/8"` | klíč platí jen z těchto adres || `permitopen="db:5432"` | tunel jen sem a nikam jinam || `expiry-time="20271231"` | klíč po tomto datu přestane platit || `no-touch-required` | u FIDO klíčů zruší nutnost doteku | `command=` je nejsilnější z nich. Původní příkaz uživatele najdeš v `$SSH_ORIGINAL_COMMAND`, takže se dá postavit rozcestník, který povolí jen konkrétní operace. Typické použití: klíč pro zálohovací stroj, který smí spustit jen `rsync` v režimu čtení a odnikud jinud než z tvé sítě. Kdyby ten klíč unikl, útočník nedostane shell. ## ssh-agent Klíč chráněný heslem je bezpečný a otravný. Agent je proces, který si klíč po jednom odemknutí drží v paměti a podepisuje jím za tebe. ```bashssh-add -l # co je načtenéssh-add ~/.ssh/id_ed25519 # přidatssh-add -t 4h # přidat na omezenou dobussh-add -c ~/.ssh/id_ed25519 # ptát se na potvrzení u každého použitíssh-add -D # zapomenout všechno``` Do `~/.ssh/config` patří: ```AddKeysToAgent yes``` Agent **nikdy nevydá soukromý klíč**. Umí jen podepsat, co mu pošleš. Kdo se dostane k jeho soketu (`$SSH_AUTH_SOCK`), tvůj klíč neukradne — ale může se s ním přihlašovat, dokud agent běží. Z toho plyne následující. ### Přeposílání agenta je nebezpečné `ssh -A` zpřístupní tvého agenta na vzdáleném stroji, abys z něj mohl skočit dál. **Kdo je na tom stroji root, může tvého agenta používat.** Ne někdy, vždycky — soket je běžný soubor a root ho přečte. Dokud jsi připojený, může se tvým klíčem přihlásit kamkoliv, kam ty. A ty se to nedozvíš. Správné řešení je **ProxyJump**, které tenhle problém nemá vůbec: ```bashssh -J brana vnitrni-server``` ```Host vnitrni-* ProxyJump brana``` Rozdíl: klíč se použije **lokálně**, brána jen přeposílá zašifrovaný provoz. Nevidí do něj a nemá k čemu přistupovat. Skládá se to i do řetězu (`-J bastion1,bastion2`). Když se přeposlání agenta opravdu nedá obejít, aspoň zapni `ssh-add -c`, ať každé použití vyžaduje tvé potvrzení, a nikdy ho nedávej na stroj, kterému nevěříš. ## SSH certifikáty Tohle je funkce, kterou má OpenSSH od roku 2010, umí ji málokdo a řeší dva největší provozní problémy naráz. **Problém 1:** máš deset serverů, každý s vlastním host keyem, a deset lidí, kteří na každý potřebují `known_hosts`. **Problém 2:** máš `authorized_keys` na deseti serverech. Když někdo odejde, musíš jeho klíč odebrat na všech. Certifikát je **podepsané tvrzení o klíči** — tentýž princip jako u [TLS](TLS-a-HTTPS), jen mnohem jednodušší formát bez X.509 a bez řetězů. ### Vytvoření CA ```bashssh-keygen -t ed25519 -f user_ca -C "SSH user CA"ssh-keygen -t ed25519 -f host_ca -C "SSH host CA"``` Dvě oddělené CA, ať klíč pro podpis serverů neumí vyrábět uživatele. Soukromé klíče patří mimo běžné servery. ### Certifikát pro uživatele ```bashssh-keygen -s user_ca -I "martin@notebook" -n martin,deploy \ -V +52w -z 1 ~/.ssh/id_ed25519.pub# vznikne ~/.ssh/id_ed25519-cert.pub``` - `-I` — popisek, který se objeví v logu serveru- `-n` — **principals**, tedy jako kteří uživatelé se smí přihlásit- `-V +52w` — platnost; **tohle je ta hodnota, kvůli které to děláš**- `-z` — sériové číslo, kvůli případnému odvolání Na serveru stačí jediný řádek: ```TrustedUserCAKeys /etc/ssh/user_ca.pub``` A `authorized_keys` už nemusí existovat vůbec. Kdo má platný certifikát od tvé CA a správný principal, dostane se dovnitř. **Odchod člověka řešíš tím, že mu příště certifikát nevystavíš** — nikam se nepřihlašuješ, nic nemažeš. ### Certifikát pro server ```bashssh-keygen -s host_ca -I "web01" -h -n web01.example.cz,192.168.1.10 \ -V +52w /etc/ssh/ssh_host_ed25519_key.pub``` ```# /etc/ssh/sshd_configHostCertificate /etc/ssh/ssh_host_ed25519_key-cert.pub``` U klienta stačí jeden řádek v `known_hosts` pro celou infrastrukturu: ```@cert-authority *.example.cz ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAA...``` **A tím zmizí trust on first use.** Nový server naběhne a klienti se na něj připojí bez jediné otázky, protože jeho certifikát podepsala CA, které už věří. Reinstalace serveru přestane být událost. ### Kontrola a odvolání ```bashssh-keygen -L -f ~/.ssh/id_ed25519-cert.pub # co v certifikátu je # seznam odvolanýchssh-keygen -k -f revoked.krl id_ed25519-cert.pub# a v sshd_config: RevokedKeys /etc/ssh/revoked.krl``` V praxi ale odvolávání skoro nepotřebuješ — **stačí krátká platnost**. Certifikát na osm hodin se odvolávat nemusí, ráno prostě vyprší. To je stejná úvaha, kterou dělá `tls internal` v [Caddy](Vlastni-certifikacni-autorita#caddy-tls-internal). ## Hardwarové klíče OpenSSH od verze 8.2 umí klíče uložené v FIDO2 tokenu: ```bashssh-keygen -t ed25519-sk -O resident -O verify-required -C "yubikey"``` - **`ed25519-sk`** — soukromá část zůstane v tokenu, ven se nikdy nedostane- **`-O resident`** — klíč jde z tokenu obnovit na novém počítači (`ssh-keygen -K`), takže nepotřebuješ zálohu souboru- **`-O verify-required`** — kromě doteku ještě PIN Vznikne dvojice souborů, ale ten „soukromý" je jen odkaz do tokenu. **Bez fyzického tokenu je k ničemu.** Zkopírovaný malwarem, uniklý ze zálohy, nalezený na ztraceném notebooku — pořád k ničemu. Praktická poznámka: pořiď si **dva tokeny** a oba klíče vlož do `authorized_keys`. Ztráta jediného tokenu jinak znamená ztrátu přístupu. ## Tunely a přesměrování portů Nejužitečnější a nejméně známá schopnost SSH. Ke všem třem variantám platí, že se nemusí zabírat terminál — `-N` znamená „žádný příkaz", `-f` pošle na pozadí. ### Místní: `-L` Otevři port u sebe, provoz vylez na druhé straně. ```bashssh -L 8080:localhost:80 server# http://localhost:8080 u tebe = http://localhost:80 na serveru``` Kanonické použití: dostat se na administraci, která poslouchá jen na `127.0.0.1` a záměrně není vystavená. To je přesně ten správný způsob, jak zpřístupnit databázi nebo panel bez [reverse proxy](Reverse-proxy) a bez otevírání portu. ```bashssh -N -L 5432:db-server:5432 brana# lokální psql se připojí na localhost:5432 a mluví s databází ve vnitřní síti``` Ten prostřední údaj se rozlišuje **z pohledu serveru** — `localhost:80` je server sám, `db-server:5432` je stroj, na který server dosáhne. ### Vzdálený: `-R` Opačný směr. Otevři port **na serveru**, provoz vyleze u tebe. ```bashssh -R 8080:localhost:3000 server# kdo na serveru sáhne na localhost:8080, dostane tvou aplikaci na portu 3000``` Tohle je takzvaný **reverzní tunel** a je to plnohodnotná náhrada za port forwarding na routeru, když jsi za [CGNATem](Poskytovatel-CGNAT-a-verejna-IP). Malá VPS s veřejnou adresou stačí. Ve výchozím stavu se port na serveru otevře jen na `127.0.0.1`. Pro přístup zvenku je potřeba na serveru `GatewayPorts clientspecified` a u klienta uvést adresu: ```bashssh -R 0.0.0.0:8080:localhost:3000 server``` **Přidej `-o ExitOnForwardFailure=yes`.** Bez toho se spojení naváže, i když se tunel nepovede otevřít, a ty se pak divíš, proč nic nefunguje. Pro trvalý provoz použij `autossh` nebo systemd jednotku s `Restart=always`, `ServerAliveInterval 30` a `ServerAliveCountMax 3`. ### Dynamický: `-D` SOCKS5 proxy — všechno, co do ní pošleš, vyleze na serveru. ```bashssh -N -D 1080 servercurl --socks5-hostname localhost:1080 http://vnitrni-server/``` Nastav to v prohlížeči a máš „VPN" na jeden příkaz. Pro pohodlný trvalý přístup je [WireGuard](WireGuard) lepší, na jednorázovou potřebu je tohle rychlejší. Použij `--socks5-hostname` (a v prohlížeči „proxy DNS"), jinak se jména překládají u tebe a vnitřní jména nefungují. ### Multiplexování Druhé a další připojení na tentýž stroj může použít existující spojení a naváže se okamžitě, bez handshake: ```Host * ControlMaster auto ControlPath ~/.ssh/cm-%C ControlPersist 10m``` `%C` je hash celé kombinace, což řeší limit délky cesty u unixového soketu — s `%r@%h:%p` na to u dlouhých doménových jmen narazíš. Kromě rychlosti to znamená jedno přihlášení místo deseti, což se hodí u hardwarového klíče s dotekem. Nevýhoda: `ssh -O exit server` musíš použít, když chceš spojení opravdu zavřít. ## Konfigurace klienta `~/.ssh/config` je nejlepší investice do pohodlí. **Platí první nalezená hodnota**, takže obecné `Host *` patří na konec souboru, ne na začátek. ```Host * AddKeysToAgent yes ServerAliveInterval 30 ServerAliveCountMax 3 HashKnownHosts yes Host brana HostName 89.24.x.x User martin Port 2222 IdentityFile ~/.ssh/id_ed25519_brana IdentitiesOnly yes Host nas media pihole ProxyJump brana User martin Host github.com IdentityFile ~/.ssh/id_ed25519_github IdentitiesOnly yes``` **`IdentitiesOnly yes` je důležitější, než vypadá.** Bez něj klient nabízí serveru postupně všechny klíče z agenta. Jednak tím prozradí, které klíče máš, jednak po pár pokusech narazí na `MaxAuthTries` a dostane `Too many authentication failures` — přestože ten správný klíč má. Ověření, co se na daný stroj skutečně použije: ```bashssh -G nas # výsledná konfigurace po vyhodnocení všech pravidel``` ## Konfigurace serveru Základ je na [zabezpečení serveru](Zabezpeceni-serveru#ssh). Tady to, co jde dál. ```# /etc/ssh/sshd_config.d/99-vlastni.conf PasswordAuthentication noKbdInteractiveAuthentication noPermitRootLogin prohibit-passwordAuthenticationMethods publickey AllowGroups ssh-users LoginGraceTime 20MaxAuthTries 3MaxSessions 5MaxStartups 10:30:60 AllowAgentForwarding noAllowTcpForwarding noX11Forwarding noPermitTunnel no ClientAliveInterval 300ClientAliveCountMax 2``` `MaxStartups 10:30:60` znamená: nad 10 rozpracovaných přihlášení začni s pravděpodobností 30 % odmítat, nad 60 odmítej vše. Levná ochrana proti zahlcení. `AllowTcpForwarding no` vypne tunely. Zapni je selektivně tam, kde jsou potřeba: ```Match Group tunel AllowTcpForwarding yes PermitOpen 192.168.1.20:5432 ForceCommand /usr/sbin/nologin``` Uživatel v této skupině dostane tunel na databázi a **nic jiného** — ani shell. `Match` bloky musí být na konci souboru, protože platí až do dalšího `Match`. Kontrola: ```bashsshd -t # syntaxesshd -T | sort # výsledná konfiguracesshd -T -C user=martin,host=x,addr=10.0.0.5 # včetně vyhodnocení Matchsystemctl reload ssh``` ### Zádrhel na Ubuntu 24.04 Ubuntu 24.04 spouští SSH přes **aktivaci soketem** (`ssh.socket`). Důsledek: **`Port 2222` v `sshd_config` se ignoruje**, protože port neotevírá sshd, ale systemd. ```bashsystemctl edit ssh.socket``` ```[Socket]ListenStream=ListenStream=2222``` Ten první prázdný řádek je nutný — vynuluje předchozí hodnotu, jinak se port jen přidá k dvaadvacítce. Alternativně se dá socket vypnout a vrátit klasické chování: ```bashsystemctl disable --now ssh.socketsystemctl enable --now ssh.service``` ### Nechoď do internetu vůbec Nejlepší nastavení SSH je to, na které se zvenku nikdo nedostane. Za [WireGuardem](WireGuard) je hrubá síla mrtvá bez jediného pravidla ve [firewallu](Linux-firewall), protože bez klíče WireGuard neodpoví ani jedním bajtem. ## Přenos souborů ```bashscp soubor server:/tmp/ # jednoduchérsync -avz --progress dir/ server:/dir/ # lepší, přenáší jen rozdílysftp server # interaktivnísshfs server:/data /mnt/data # připojení jako disk (FUSE)``` Poznámka k `scp`: jeho původní protokol byl plný nepříjemných překvapení (vzdálený server mohl ovlivnit, které soubory u tebe vzniknou) a OpenSSH 9.0 ho **přepnul na SFTP**. Většina lidí si toho nevšimla, ale některé exotické možnosti zmizely. Na cokoliv většího používej `rsync` — umí pokračovat a nepřenáší, co už tam je. ## Diagnostika ```bashssh -v server # co se dějessh -vvv server # včetně vyjednávání algoritmůssh -G server # výsledná konfigurace klienta journalctl -u ssh -f # log na serveru/usr/sbin/sshd -d -p 2222 # sshd v ladicím režimu na jiném portu``` Ten poslední příkaz stojí za zapamatování. Spustí druhého démona na jiném portu s výpisem všeho, obslouží **jedno** spojení a skončí. Ideální pro ladění bez rizika, že si zavřeš dveře. | Chyba | Příčina ||---|---|| `Permission denied (publickey)` | klíč není v `authorized_keys`, nebo se nepoužil ten správný — ověř `ssh -v` || | častěji: **špatná práva**. `~` nebo `~/.ssh` zapisovatelné pro skupinu || `Too many authentication failures` | agent nabízí moc klíčů → `IdentitiesOnly yes` || `no matching host key type found` | starý server s `ssh-rsa` (SHA-1). Aktualizuj server. || `no matching key exchange method` | totéž, jen o kategorii vedle || `Connection reset by peer` hned | fail2ban, firewall, nebo špatná verze protokolu || `Connection timed out` | nedoletíš vůbec — [firewall](Firewall-a-zony) nebo směrování || `REMOTE HOST IDENTIFICATION HAS CHANGED` | host key se změnil — **zjisti proč** || `Broken pipe` po nečinnosti | NAT zahodil spojení → `ServerAliveInterval` || přihlášení trvá 10 s | server dělá zpětný DNS překlad → `UseDNS no` | Nouzový přístup, když si zavřeš dveře: konzole u poskytovatele nebo hypervizoru, nebo připojení disku k jinému stroji a oprava `sshd_config`. Proto se při změnách vždycky drží **druhé otevřené spojení**, dokud si nové neověříš. ## Chyby, o kterých je dobré vědět **Terrapin, CVE-2023-48795 (2023).** Útočník uprostřed dokázal na začátku spojení nepozorovaně **umazat pakety**, protože se pořadová čísla nezapočítávala do ověření. Šlo tak potlačit vyjednání rozšíření a snížit bezpečnost sezení. Týkalo se to `chacha20-poly1305` a CBC s EtM. Opravou je **strict KEX** od OpenSSH 9.6 — obě strany si zavedou pořadová čísla do výpočtu. `ssh -v` ti to potvrdí řádkem o `kex-strict`. **regreSSHion, CVE-2024-6387 (2024).** Souběh v obsluze signálu při vypršení `LoginGraceTime` umožňoval **vzdálené spuštění kódu jako root, bez přihlášení**. Netriviálně zneužitelné, ale reálné. Ukázka toho, že drtivá většina zranitelností je v okolí, ne v kryptografii. **xz / liblzma, CVE-2024-3094 (2024).** Do komprimační knihovny byl zaveden zadní vrátka, která přes distribuční záplatu pro systemd zasáhla `sshd`. Chytilo se to náhodou, protože si někdo všiml, že přihlášení trvá o půl vteřiny déle. Samotný OpenSSH `liblzma` nepoužívá — problém přinesla úprava distribuce. Praktický závěr je nudný a pravdivý: **aktualizuj**. Všechny tři případy byly opravené dřív, než se objevily plošné útoky. ## Tahák ```bash# klíčessh-keygen -t ed25519 -a 100 -C "martin@notebook"ssh-keygen -t ed25519-sk -O resident -O verify-requiredssh-copy-id -i ~/.ssh/id_ed25519.pub serverssh-keygen -lf klic.pub # otiskssh-keygen -y -f klic > klic.pub # veřejný z privátního # známé hostitelessh-keygen -F server / -R serverssh -o VerifyHostKeyDNS=yes server # agentssh-add -l / -t 4h / -c / -D # tunelyssh -N -L 8080:localhost:80 server # k soběssh -N -R 8080:localhost:3000 server # od sebessh -N -D 1080 server # SOCKSssh -J brana cil # skok přes bránu # certifikátyssh-keygen -s user_ca -I martin -n martin -V +8h klic.pubssh-keygen -L -f klic-cert.pub # diagnostikassh -vvv server ; ssh -G server ; ssh -Q kexsshd -t ; sshd -T ; ssh-audit server``` ## Kam dál - **[Zabezpečení serveru](Zabezpeceni-serveru)** — základní nastavení a co nepomáhá- **[WireGuard](WireGuard)** — jak SSH nevystavovat vůbec- **[Veřejný a soukromý klíč](Verejny-a-soukromy-klic)** — co jsou ty klíče zač- **[Diffie-Hellman](Diffie-Hellman)** — jak se domluví klíče spojení- **[Fail2ban a CrowdSec](Fail2ban-a-CrowdSec)** — když SSH vystavené být musí- **[Vzdálený přístup](Vzdaleny-pristup)** — kdy tunel, kdy VPN, kdy proxy