
Linux commando: ssh
Elke server waar je niet voor staat, bereik je met ssh. Het is de voordeur van Linux-beheer: het commando dat je een shell geeft op een machine in een datacenter drie landen verderop, je bestanden verplaatst, je deployments draagt en stilletjes onder git, rsync en ansible zit zonder ooit om erkenning te vragen. De meeste mensen leren er net genoeg van om in te loggen en stoppen daar, en dat is jammer, want juist de delen die ze overslaan maken dagelijks serverwerk snel en veilig.
1. De basis
De taak van ssh is je te verbinden met een andere computer over een onbetrouwbaar netwerk, en die verbinding veilig te maken. Je typt een regel, en een moment later hangt je toetsenbord aan een machine ergens anders.
$ ssh Dit e-mailadres wordt beveiligd tegen spambots. JavaScript dient ingeschakeld te zijn om het te bekijken.
Er zijn twee programma's bij betrokken. Aan jouw kant is ssh de client. Aan de andere kant is een achtergronddienst genaamd sshd (de SSH-daemon) de server. De client verbindt standaard met de server op TCP-poort 22, en de twee spreken af hoe ze praten voordat er ook maar een teken van je sessie over de lijn gaat.
In die openingshanddruk doet ssh drie losse dingen, en het helpt om ze in je hoofd uit elkaar te houden:
- Het controleert de server. De server bewijst dat hij een prive hostsleutel bezit, zodat je weet dat je de echte machine bereikt hebt en niet een bedrieger die ertussen zit.
- Het controleert jou. Je bewijst wie je bent met een sleutel of een wachtwoord, zodat de server weet welk account hij moet openen.
- Het versleutelt alles. Vanaf de handdruk is elke toetsaanslag en elke byte uitvoer versleuteld en op integriteit gecontroleerd.
Wat ssh bijzonder maakt, is dat het geen inlogprogramma bleef. Omdat het elke stroom bytes veilig kan dragen, zijn er andere programma's bovenop gebouwd. scp en sftp kopieren er bestanden mee. rsync gebruikt het als standaardtransport. git push over een ssh://-URL is een SSH-sessie. Ansible configureert er hele vloten servers mee. Leer ssh goed en je verbetert stilletjes al die gereedschappen tegelijk.
Het commando dat een machine op afstand in een lokale verandert. Achter een simpele inlogprompt zitten hostsleutels, sleutelparen, een agent, een configuratiebestand en een tunnelmachine waar de meeste gebruikers nooit aankomen.
Dit artikel begint bij je allereerste verbinding en bouwt op naar sleutels, het configuratiebestand, port forwarding, jump hosts, verbindingen delen en het dichttimmeren van de server. Aan het einde kun je een ssh-commando lezen dat iemand anders schreef en precies weten wat het doet.
Naar bovenHet juiste denkmodel:
sshis geen "terminalprogramma op afstand". Het is een versleutelde tunnel tussen twee machines, en een terminalsessie is alleen het meest voorkomende dat je erdoorheen duwt. Zodra je de tunnel ziet, houden port forwarding en jump hosts op trucjes te lijken en worden ze vanzelfsprekend.
2. Waar komt de naam vandaan?
De naam ssh is kort voor Secure SHell, oftewel veilige shell.
ssh = Secure SHell
De helft "secure" is het hele punt van het programma. De helft "shell" is geerfd. Voordat SSH bestond, logden mensen in op Unix-machines op afstand met telnet, rlogin en rsh (kort voor remote shell), en kopieerden ze bestanden met rcp (remote copy). Die gereedschappen werkten, maar ze stuurden alles in leesbare tekst over het netwerk, inclusief je wachtwoord. Iedereen die het verkeer kon meekijken, kon het gewoon lezen.
SSH is geschreven als directe vervanger van die familie, dus nam het het naampatroon over: rsh werd ssh, en rcp werd scp. De letter die veranderde is de letter die ertoe doet.
Een kleine correctie op een veelvoorkomende aanname: ssh bevat geen shell. Het draait er zelf nooit een. Het vraagt de server om jouw normale login-shell te starten voor je account, en fungeert daarna als veilige pijp tussen die shell en je terminal. De naam beschrijft het resultaat dat je krijgt, niet de code in het programma.
Het poortnummer heeft ook een verhaal. SSH luistert op poort 22 omdat Tatu Ylönen, die het schreef, vroeg om een nummer dat tussen de twee diensten lag die hij verving: ftp op poort 21 en telnet op poort 23. IANA kende het toe, en het is sindsdien poort 22 gebleven.
3. Een korte geschiedenis
Anders dan ls of cat, die teruggaan tot de eerste dagen van Unix, is SSH een antwoord op een specifiek probleem op een specifiek moment. In 1995 ontdekte Tatu Ylönen, onderzoeker aan de Technische Universiteit Helsinki in Finland, een wachtwoord-afluisteraanval op zijn universiteitsnetwerk. Iemand ving de leesbare wachtwoorden op die telnet en rlogin zo gul weggaven. Hij schreef SSH als oplossing en bracht het in juli van dat jaar uit als freeware. Eind 1995 had het ongeveer 20.000 gebruikers in vijftig landen.
Succes bracht een bedrijf, en het bedrijf bracht licenties. Latere versies van het oorspronkelijke SSH werden commercieel en steeds beperkender. In 1999 nam het OpenBSD-team de laatste vrij gelicentieerde code, versie 1.2.12, maakte die schoon en bracht OpenSSH uit. Het werd meegeleverd met OpenBSD 2.6 op 1 december 1999. Vrijwel meteen begon een aparte portable-tak zodat dezelfde code op Linux en andere Unix-systemen kon draaien, en dat is de versie die vrijwel iedereen vandaag draait.
Je ziet beide feiten terug in de versieregel op elke Linux-machine:
$ ssh -V
OpenSSH_9.6p1 Ubuntu-3ubuntu13.18, OpenSSL 3.0.13 30 Jan 2024
De p1 staat voor portable, release 1. Alles na de komma is wat je distributie er bovenop heeft gezet.
Ondertussen werd het protocol zelf herschreven. Het oorspronkelijke SSH-1-protocol had ontwerpzwaktes, en een herontworpen SSH-2 verving het. De IETF publiceerde SSH-2 in januari 2006 als standaard, verspreid over RFC 4251 tot en met RFC 4254. SSH-1 is al lang dood en modern OpenSSH kan het helemaal niet meer spreken.
| Periode | Mijlpaal |
|---|---|
| 1995 | Tatu Ylönen schrijft SSH na een wachtwoord-afluisteraanval en brengt het in juli uit als freeware |
| eind 1995 | Ongeveer 20.000 gebruikers in vijftig landen; de ontwikkeling gaat commercieel verder |
| 1999 | Het OpenBSD-team forkt de laatste vrije code en levert OpenSSH mee met OpenBSD 2.6 op 1 december |
| 2006 | De IETF publiceert SSH-2 als RFC 4251 tot en met RFC 4254 (januari) |
| 2014 | OpenSSH 6.5 voegt Ed25519-sleutels toe: klein, snel en modern (januari) |
| 2020 | OpenSSH 8.2 voegt FIDO/U2F-hardwaresleutels toe, ecdsa-sk en ed25519-sk (februari) |
| 2021 | OpenSSH 8.8 schakelt RSA-handtekeningen met SHA-1 uit, omdat SHA-1 gebroken is (september) |
| 2022 | OpenSSH 9.0 zet scp op het SFTP-protocol en schakelt standaard hybride post-quantum sleuteluitwisseling in (april) |
| 2023 | OpenSSH 9.5 maakt Ed25519 het standaard sleuteltype voor ssh-keygen (oktober) |
| 2024-2025 | OpenSSH 9.8 schakelt DSA uit, en 10.0 verwijdert het helemaal en maakt de post-quantum sleutelovereenkomst mlkem768x25519-sha256 de standaard |
Die laatste rij verdient een tweede blik. De mensen die vandaag versleuteld verkeer opvangen in de hoop het te ontsleutelen zodra er quantumcomputers zijn, vormen al een echt dreigingsmodel. SSH is stilletjes overgestapt op sleuteluitwisseling die daartegen bestand is, en op OpenSSH 9.6 kun je het zien gebeuren:
$ ssh -v server.example
...
debug1: kex: algorithm: Dit e-mailadres wordt beveiligd tegen spambots. JavaScript dient ingeschakeld te zijn om het te bekijken.
debug1: kex: host key algorithm: ssh-ed25519
debug1: kex: server->client cipher: Dit e-mailadres wordt beveiligd tegen spambots. JavaScript dient ingeschakeld te zijn om het te bekijken. MAC: <implicit> compression: none
Je hebt er niets voor hoeven vragen, en dat hoefde ook niet. ssh bijgewerkt houden is het grootste deel van het beveiligingswerk.
4. Eenvoudige toepassingen
4.1 De eenvoudigst mogelijke verbinding
Geef ssh een hostnaam en het verbindt, met je lokale gebruikersnaam aan de andere kant. In de voorbeelden hieronder is de regel die begint met $ wat jij typt; de regels eronder zijn wat je terugkrijgt.
$ ssh server.example
Meestal wil je een specifiek account, dat je er met een @ voor zet:
$ ssh Dit e-mailadres wordt beveiligd tegen spambots. JavaScript dient ingeschakeld te zijn om het te bekijken.
Als de server op een andere poort luistert, voeg je -p toe (kort voor port, poort). Let op de kleine letter; in scp gebruikt dezelfde taak een hoofdletter -P, waar iedereen minstens een keer over struikelt.
$ ssh -p 2222 Dit e-mailadres wordt beveiligd tegen spambots. JavaScript dient ingeschakeld te zijn om het te bekijken.
Er is ook -l (kort voor login name, inlognaam), een oudere manier om hetzelfde te zeggen als de @-vorm:
$ ssh -l peter server.example # same as Dit e-mailadres wordt beveiligd tegen spambots. JavaScript dient ingeschakeld te zijn om het te bekijken.
4.2 De eerste verbinding: die vraag over authenticiteit
De eerste keer dat je met een machine verbindt, stopt ssh en vraagt het je iets:
$ ssh Dit e-mailadres wordt beveiligd tegen spambots. JavaScript dient ingeschakeld te zijn om het te bekijken.
The authenticity of host 'server.example (203.0.113.10)' can't be established.
ED25519 key fingerprint is SHA256:Cbs69zP2QMpFcfrRMSlg3fsDbEpOD+MH2FANbuWy5J8.
This key is not known by any other names.
Are you sure you want to continue connecting (yes/no/[fingerprint])?
Bijna iedereen typt yes zonder het te lezen. Het is de moeite waard om het te begrijpen, want dit is het moment dat bepaalt of je verbinding echt prive is.
Elke SSH-server heeft zijn eigen hostsleutel, aangemaakt bij de installatie en opgeslagen in /etc/ssh/. Tijdens de handdruk bewijst de server dat hij de bijbehorende privesleutel heeft. De vingerafdruk in de vraag is een korte hash van de publieke hostsleutel van de server. Je client vertelt je eerlijk dat hij deze machine nog nooit gezien heeft en niet voor hem kan instaan.
Als je yes antwoordt, wordt de sleutel naar ~/.ssh/known_hosts geschreven, en vanaf dat moment controleert ssh hem stilletjes bij elke verbinding. Deze aanpak heet vertrouwen bij eerste gebruik: de eerste verbinding is een sprong in het diepe, elke latere wordt geverifieerd.
Om die sprong eruit te halen, vraag je de serverbeheerder vooraf om de vingerafdruk en vergelijk je, of je maakt hem zelf aan vanaf een machine die je al vertrouwt:
$ ssh-keygen -lf <(ssh-keyscan -t ed25519 server.example 2>/dev/null)
256 SHA256:Cbs69zP2QMpFcfrRMSlg3fsDbEpOD+MH2FANbuWy5J8 server.example (ED25519)
Merk op dat de prompt drie antwoorden accepteert, niet twee: yes, no, of de vingerafdruk zelf, geplakt in het antwoord. Die derde optie is de zorgvuldige, want daarmee laat je ssh de tekst vergelijken in plaats van je ogen.
4.3 Een commando draaien in plaats van een sessie
Je hoeft niet interactief in te loggen. Zet een commando achter de bestemming en ssh draait het, drukt de uitvoer af op je eigen terminal en stopt.
$ ssh Dit e-mailadres wordt beveiligd tegen spambots. JavaScript dient ingeschakeld te zijn om het te bekijken. 'uptime'
13:53:17 up 4:27, 1 user, load average: 1.73, 2.06, 2.03
Dit is de vorm die ssh bruikbaar maakt in scripts. Omdat de uitvoer op standaarduitvoer binnenkomt, kun je hem in lokale gereedschappen pipen alsof het commando thuis gedraaid had:
$ ssh Dit e-mailadres wordt beveiligd tegen spambots. JavaScript dient ingeschakeld te zijn om het te bekijken. 'ls /etc' | wc -l
231
Het werkt ook andersom. Wat je in ssh pipet, komt binnen op de standaardinvoer van het commando op afstand, wat een nette manier is om een bestand of een databasedump te versturen zonder tweede gereedschap:
$ cat backup.sql | ssh Dit e-mailadres wordt beveiligd tegen spambots. JavaScript dient ingeschakeld te zijn om het te bekijken. 'mysql mydb'
$ mysqldump mydb | ssh Dit e-mailadres wordt beveiligd tegen spambots. JavaScript dient ingeschakeld te zijn om het te bekijken. 'cat > /backups/mydb.sql'
Zet dat in een lus en je hebt een gezondheidscheck voor je hele vloot in drie regels, en zo schrijven de meeste beheerders hun eerste automatisering zonder het door te hebben:
$ for server in web01 web02 web03; do
echo -n "$server: "
ssh "$server" 'uptime'
done
web01: 13:53:17 up 4:27, 1 user, load average: 1.73, 2.06, 2.03
web02: 13:53:18 up 22 days, 3:11, 0 users, load average: 0.08, 0.12, 0.09
web03: 13:53:19 up 6 days, 18:44, 1 user, load average: 0.44, 0.39, 0.35
Die korte namen komen uit het configuratiebestand in paragraaf 5.5. Zodra zo'n lus verder groeit dan een handvol commando's, is een configuratiebeheergereedschap het betere antwoord, maar het onderliggende mechanisme blijft precies dit.
4.4 De valkuil van aanhalingstekens
Het commando op afstand wordt aan een shell op de server gegeven, maar het passeert eerst ook je lokale shell. Dat betekent dat de aanhalingstekens bepalen welke shell je variabelen uitwerkt, en het verschil is makkelijk te laten zien:
$ GREETING=hello-from-my-laptop
$ ssh Dit e-mailadres wordt beveiligd tegen spambots. JavaScript dient ingeschakeld te zijn om het te bekijken. 'echo [$GREETING]'
[] # single quotes: expanded on the SERVER, where it is not set
$ ssh Dit e-mailadres wordt beveiligd tegen spambots. JavaScript dient ingeschakeld te zijn om het te bekijken. "echo [$GREETING]"
[hello-from-my-laptop] # double quotes: your local shell expanded it first
Gebruik enkele aanhalingstekens als je bedoelt "draai dit precies zoals het er staat op de server". Gebruik dubbele alleen als je bewust een lokale waarde in het commando wilt bakken voordat het vertrekt. Ze door elkaar halen levert commando's op die op je laptop werken en op de server stilletjes niets nuttigs doen.
4.5 Eruit komen, en zien waarom het misging
Om een interactieve sessie te verlaten typ je exit of druk je op Ctrl-D. Als de sessie vastloopt en geen van beide werkt, staat in paragraaf 8.1 de truc die bijna niemand kent.
Als een verbinding weigert te werken, is de nuttigste vlag -v (kort voor verbose, uitgebreid). Voeg meer v's toe voor meer detail, tot -vvv. Het drukt het hele gesprek af en vertelt je precies waar het misging:
$ ssh -v Dit e-mailadres wordt beveiligd tegen spambots. JavaScript dient ingeschakeld te zijn om het te bekijken.
OpenSSH_9.6p1 Ubuntu-3ubuntu13.18, OpenSSL 3.0.13 30 Jan 2024
debug1: Reading configuration data /home/peter/.ssh/config
debug1: Connecting to server.example [203.0.113.10] port 22.
debug1: Connection established.
debug1: Authenticating to server.example:22 as 'peter'
debug1: Host 'server.example' is known and matches the ED25519 host key.
debug1: Will attempt key: /home/peter/.ssh/id_ed25519 ED25519 SHA256:nDjQm9Dso... explicit
debug1: Offering public key: /home/peter/.ssh/id_ed25519 ED25519 SHA256:nDjQm9Dso... explicit
debug1: Server accepts key: /home/peter/.ssh/id_ed25519
debug1: Authentication succeeded (publickey).
Lees het als een verhaal: config lezen, verbinden, de host verifieren, sleutels aanbieden, geaccepteerd worden. Als er iets misgaat, vertellen de laatste regels voor de fout welke stap brak, en dat is meestal genoeg om het op te lossen zonder gokken.
Naar boven5. Gemiddelde toepassingen
5.1 Sleutels in plaats van wachtwoorden
Bij elke verbinding een wachtwoord typen is traag, en erger nog, een wachtwoord kan geraden, gephisht of bruut geforceerd worden door de eindeloze robots die de hele dag poort 22 scannen. Authenticatie met een publieke sleutel lost beide problemen tegelijk op.
Je maakt een sleutelpaar aan: een privesleutel die je machine nooit verlaat, en een publieke sleutel die je vrij mag uitdelen. De server bewaart een kopie van de publieke sleutel. Als je verbindt, stuurt de server een uitdaging, je client ondertekent die met de privesleutel, en de server verifieert de handtekening met de publieke sleutel. Je geheim gaat nooit over de lijn, geen enkele keer.
$ ssh-keygen -t ed25519 -C "peter@laptop"
Generating public/private ed25519 key pair.
Enter file in which to save the key (/home/peter/.ssh/id_ed25519):
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in /home/peter/.ssh/id_ed25519
Your public key has been saved in /home/peter/.ssh/id_ed25519.pub
The key fingerprint is:
SHA256:nDjQm9DsoviIQK4am/qOpuskiuvJ1RY4lRpJgs+iyxY peter@laptop
The key's randomart image is:
+--[ED25519 256]--+
|.. . |
|. o .+. |
| o ooo+ |
|. o =+ = . |
|.o +..* S |
|+E .o... |
|*+o. o |
|@@o . |
|^X+ |
+----[SHA256]-----+
Dat plaatje aan het einde is de randomart: een visuele tekening van dezelfde vingerafdruk. Het idee is dat mensen een veranderd plaatje sneller zien dan een veranderde reeks base64. Bijna niemand gebruikt het, en je kunt het rustig negeren.
Drie vlaggen doen er hier toe. -t (kort voor type) kiest het algoritme, -C (kort voor comment, opmerking) voegt een label toe zodat je je sleutels later uit elkaar houdt, en -f (kort voor file, bestand) zet de bestandsnaam als je de standaard niet wilt.
Op OpenSSH 9.5 en nieuwer kun je -t helemaal weglaten, omdat Ed25519 nu de standaard is. Het is voor bijna iedereen de juiste keuze: modern, snel en kort genoeg om op een regel te passen.
| Type | Oordeel | Toelichting |
|---|---|---|
ed25519 |
Gebruik deze | Vaste lengte, snel, kleine publieke sleutel. Ondersteund sinds OpenSSH 6.5 (2014) |
ed25519-sk / ecdsa-sk |
Voor waardevolle toegang | Ondersteund door een FIDO2-hardwaretoken; de sleutel kan niet van het apparaat gekopieerd worden, en je raakt het aan om te authenticeren |
rsa |
Alleen voor oude servers | Nog prima op 3072 bits (de standaard) of 4096, maar de sleutel is veel groter en trager |
ecdsa |
Vermijden | Werkt, maar Ed25519 is beter op elk punt dat ertoe doet |
dsa |
Nooit | Verouderd en zwak; uitgeschakeld in OpenSSH 9.8 en verwijderd in 10.0 |
Kijk eens naar wat je daadwerkelijk krijgt. De publieke sleutel is een leesbare regel, en de privesleutel is een versleuteld blok dat je nooit opent:
$ cat ~/.ssh/id_ed25519.pub
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIDA3N37toeUWk+bfwHUdgC64txzH0aqdogl9qkha0mme peter@laptop
$ head -2 ~/.ssh/id_ed25519
-----BEGIN OPENSSH PRIVATE KEY-----
b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW
De publieke regel heeft drie velden: het sleuteltype, de sleutel zelf in base64, en jouw opmerking. Die regel delen is veilig. Het bestand zonder .pub is het bestand dat je machine nooit mag verlaten, nooit in een chat geplakt mag worden en nooit in git terecht mag komen.
Zet altijd een wachtwoordzin. Een privesleutelbestand zonder wachtwoordzin is een inloggegeven in leesbare vorm: wie dat ene bestand kopieert, bezit elke server die het opent. Met een wachtwoordzin is het bestand op schijf versleuteld, en is een gestolen laptop een ongemak in plaats van een inbraak. Paragraaf 5.4 haalt de dagelijkse kosten van het typen weg.
Heb je al een sleutel zonder wachtwoordzin, dan hoef je niet opnieuw te beginnen. De vlag -p (kort voor passphrase, wachtwoordzin) wijzigt de wachtwoordzin van een bestaande sleutel en laat de sleutel zelf ongemoeid, dus er hoeft op geen enkele server iets bijgewerkt te worden:
$ ssh-keygen -p -f ~/.ssh/id_ed25519
Key has comment 'peter@laptop'
Enter new passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved with the new passphrase.
5.2 Je sleutel op de server krijgen
De server bewaart je publieke sleutels in ~/.ssh/authorized_keys, een per regel. Je kunt de regel met de hand toevoegen, maar er is gereedschap dat het correct doet, inclusief het aanmaken van de map met de juiste rechten:
$ ssh-copy-id Dit e-mailadres wordt beveiligd tegen spambots. JavaScript dient ingeschakeld te zijn om het te bekijken.
/usr/bin/ssh-copy-id: INFO: Source of key(s) to be installed: "/home/peter/.ssh/id_ed25519.pub"
/usr/bin/ssh-copy-id: INFO: attempting to log in with the new key(s), to filter out any that are already installed
/usr/bin/ssh-copy-id: INFO: 1 key(s) remain to be installed -- if you are prompted now it is to install the new keys
Number of key(s) added: 1
Now try logging into the machine, with: "ssh Dit e-mailadres wordt beveiligd tegen spambots. JavaScript dient ingeschakeld te zijn om het te bekijken. '"
and check to make sure that only the key(s) you wanted were added.
Het logt een laatste keer in met je wachtwoord en voegt de sleutel toe. Gebruik -i om een specifieke sleutel te kiezen en -n voor een proefdraai die laat zien wat het zou doen:
$ ssh-copy-id -n -i ~/.ssh/id_ed25519.pub Dit e-mailadres wordt beveiligd tegen spambots. JavaScript dient ingeschakeld te zijn om het te bekijken.
/usr/bin/ssh-copy-id: WARNING: All keys were skipped because they already exist on the remote system.
Rechten doen er meer toe dan beginners verwachten, en ssh weigert bewust te werken als ze verkeerd staan. Beide kanten controleren:
~/.ssh 700 (drwx------)
~/.ssh/id_ed25519 600 (-rw-------) private key
~/.ssh/id_ed25519.pub 644 (-rw-r--r--) public key
~/.ssh/authorized_keys 600 (-rw-------) on the server
Zet de privesleutel verkeerd en je krijgt een van de luidste foutmeldingen in Unix:
$ ssh -i ~/.ssh/id_ed25519 Dit e-mailadres wordt beveiligd tegen spambots. JavaScript dient ingeschakeld te zijn om het te bekijken.
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@ WARNING: UNPROTECTED PRIVATE KEY FILE! @
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
Permissions 0644 for '/home/peter/.ssh/id_ed25519' are too open.
It is required that your private key files are NOT accessible by others.
This private key will be ignored.
De oplossing is een commando: chmod 600 ~/.ssh/id_ed25519. Dit is ook de meest voorkomende reden dat een sleutel die "gisteren nog werkte" ermee ophoudt nadat bestanden met de verkeerde vlaggen zijn rondgekopieerd.
5.3 Controleren of het gewerkt heeft
Voordat je ergens wachtwoorden uitzet, bevestig je in de logs van de server zelf dat de sleutel gebruikt is. Op de server:
$ sudo journalctl -u ssh --since "10 min ago" | tail -3
Accepted publickey for peter from 203.0.113.55 port 50962 ssh2: ED25519 SHA256:nDjQm9DsoviIQK4am/qOpuskiuvJ1RY4lRpJgs+iyxY
Het woord waar je op let is publickey. Staat er password, dan wordt je sleutel nog niet gebruikt en sluit je jezelf buiten als je wachtwoorden uitzet.
De naam van de dienst verschilt per distributiefamilie, wat mensen betrapt die met beide werken. Debian en Ubuntu noemen de unit ssh; Red Hat, Fedora en SUSE noemen hem sshd. Levert de ene naam niets op, probeer dan de andere:
$ systemctl status ssh # Debian, Ubuntu
$ systemctl status sshd # RHEL, Fedora, SUSE
Deze logs zijn meer waard dan een eenmalige controle. Ze leggen mislukte pogingen, onbekende gebruikersnamen en bron-IP-adressen vast, dus het is de eerste plek om te kijken als je wilt weten of iemand anders aan de deur heeft gerammeld.
5.4 De agent: je wachtwoordzin een keer typen
Een wachtwoordzin op je sleutel is alleen te doen als je hem een keer per dag typt in plaats van bij elke verbinding. Dat is het werk van ssh-agent, een klein achtergrondprogramma dat je ontsleutelde privesleutels in het geheugen houdt en op verzoek uitdagingen ondertekent. Het geeft de sleutel zelf nooit aan iets anders.
$ ssh-add ~/.ssh/id_ed25519
Enter passphrase for /home/peter/.ssh/id_ed25519:
Identity added: /home/peter/.ssh/id_ed25519 (peter@laptop)
$ ssh-add -l
256 SHA256:nDjQm9DsoviIQK4am/qOpuskiuvJ1RY4lRpJgs+iyxY peter@laptop (ED25519)
De vlag -l (kort voor list, lijst) laat zien wat de agent op dit moment heeft. Op een Linux-desktop wordt er meestal bij het inloggen al een agent voor je gestart, en je desktop-sleutelring ontgrendelt de sleutel misschien automatisch. Op een server of in een kale shell moet je er soms zelf een starten:
$ eval "$(ssh-agent -s)"
Agent pid 74960
Twee opties maken de agent prettiger in het gebruik. ssh-add -t (kort voor time, tijd) stelt een levensduur in, zodat de sleutel na een tijdje weer vergeten wordt, en AddKeysToAgent yes in je config laadt een sleutel in de agent zodra je hem voor het eerst gebruikt, zodat je ssh-add nooit meer met de hand draait:
$ ssh-add -t 8h ~/.ssh/id_ed25519 # forget it after eight hours
5.5 Het configuratiebestand dat alles verandert
Als je maar een ding uit dit artikel meeneemt naast sleutels, laat het dan ~/.ssh/config zijn. Het verandert lange, onthoudbare commando's in korte namen, en het is waar ervaren beheerders hun hele serverlijst bewaren.
Host web
HostName web01.example.com
User peter
Port 2222
IdentityFile ~/.ssh/id_ed25519
IdentitiesOnly yes
Host *.internal
User admin
ServerAliveInterval 60
Host *
AddKeysToAgent yes
HashKnownHosts yes
Daarmee wordt dit:
$ ssh -p 2222 -i ~/.ssh/id_ed25519 Dit e-mailadres wordt beveiligd tegen spambots. JavaScript dient ingeschakeld te zijn om het te bekijken.
dit:
$ ssh web
De alias werkt overal, niet alleen in ssh: scp file web:/tmp/ en rsync -a site/ web:/var/www/ lezen hetzelfde bestand.
Een regel beheerst het hele bestand, en die verrast mensen: de eerste gevonden waarde voor elke instelling wint. Latere blokken kunnen een eerdere niet overschrijven. Dat is het tegenovergestelde van de meeste configuratieformaten, en het betekent dat specifieke Host-blokken bovenaan horen en het vangnet Host * onderaan.
Als je niet zeker weet wat ssh echt gaat doen, vraag het dan. De vlag -G drukt de volledig uitgewerkte configuratie voor een bestemming af zonder te verbinden:
$ ssh -G web | grep -E '^(hostname|user|port|identityfile)'
user peter
hostname web01.example.com
port 2222
identityfile ~/.ssh/id_ed25519
Dit ene commando beantwoordt de meeste "waarom verbindt hij als de verkeerde gebruiker?"-vragen in seconden.
5.6 Leven met known_hosts
Elke host die je accepteert belandt in ~/.ssh/known_hosts. Op Debian en Ubuntu worden de regels standaard gehasht, omdat /etc/ssh/ssh_config met HashKnownHosts yes wordt geleverd, dus het bestand ziet er zo uit in plaats van je servers leesbaar op te sommen:
$ head -1 ~/.ssh/known_hosts
|1|2UYDkyiaFpx+aPALlarpS9sQdVI=|KpOmbFYFEeDy0di64KcmzAaRymA= ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAA...
Dat hashen is een bewuste verdediging: als iemand je laptop of je backups steelt, krijgt hij er niet ook een keurige lijst van elke server die je beheert bij. De keerzijde is dat je het bestand niet kunt lezen, dus je hebt ssh-keygen nodig om ermee te werken. Gebruik -F (kort voor find, zoeken) om een host op te zoeken en -R (kort voor remove, verwijderen) om hem weg te halen:
$ ssh-keygen -F server.example # is this host known?
$ ssh-keygen -R server.example # forget it
Vroeg of laat kom je de alarmerende versie hiervan tegen. Installeer een server opnieuw, of verhuis een domein naar een andere machine, en de hostsleutel verandert:
$ ssh Dit e-mailadres wordt beveiligd tegen spambots. JavaScript dient ingeschakeld te zijn om het te bekijken.
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@ WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED! @
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
IT IS POSSIBLE THAT SOMEONE IS DOING SOMETHING NASTY!
Someone could be eavesdropping on you right now (man-in-the-middle attack)!
It is also possible that a host key has just been changed.
The fingerprint for the ED25519 key sent by the remote host is
SHA256:pWQKB1gfFxQ8fTJ9KiwFIFmT4g71fILH0jZEHn3mOII.
Offending ED25519 key in /home/peter/.ssh/known_hosts:2
remove with:
ssh-keygen -f '/home/peter/.ssh/known_hosts' -R 'server.example'
Host key verification failed.
ssh geeft je zelfs het exacte commando om de waarschuwing te laten verdwijnen, en precies daarom wordt de waarschuwing genegeerd. Sta eerst even stil. Als je die server een uur geleden opnieuw hebt geinstalleerd, is de verandering verwacht en mag je de regel opruimen. Als er aan jouw kant niets veranderd is, is dit de ene waarschuwing in heel Linux die echt betekent "misschien zit er iemand tussen", en dan is de juiste reactie om uit te zoeken waarom, voordat je nog iets typt.
6. Gevorderde toepassingen
Alles tot nu toe behandelt ssh als een manier om een shell te krijgen. Deze paragraaf gebruikt het als wat het echt is: een tunnel die alles kan dragen.
6.1 Lokaal doorsturen: een poort op afstand naar je toe halen
De vlag -L (kort voor local, lokaal) opent een poort op jouw machine en stuurt alles wat daar binnenkomt door de versleutelde verbinding naar een adres dat de server kan bereiken.
$ ssh -L 9099:127.0.0.1:8099 Dit e-mailadres wordt beveiligd tegen spambots. JavaScript dient ingeschakeld te zijn om het te bekijken.
Lees de drie delen van links naar rechts als "luister hier, verbind daarheen": luister op lokale poort 9099 en stuur door naar 127.0.0.1:8099 zoals gezien vanaf de server. Dat laatste detail is wat mensen missen. Het adres in het midden wordt aan de andere kant opgelost, dus 127.0.0.1 betekent de server zelf.
Zolang die sessie open is, bereikt een browser op je laptop die naar http://127.0.0.1:9099/ wijst de dienst op de server:
$ curl -s -o /dev/null -w "%{http_code}\n" http://127.0.0.1:9099/
200
Zo bereik je dingen die bewust niet aan het internet hangen: een database op localhost:3306, een beheerpaneel dat aan de loopback-interface gebonden is, een monitoringdashboard achter een firewall. Je krijgt toegang op afstand zonder ook maar een extra poort voor de wereld open te zetten.
$ ssh -L 3307:127.0.0.1:3306 Dit e-mailadres wordt beveiligd tegen spambots. JavaScript dient ingeschakeld te zijn om het te bekijken.
# now connect a local client to 127.0.0.1:3307 as if MySQL ran on your laptop
Twee vlaggen komen er bijna altijd bij. -N (kort voor no command, geen commando) zegt "geef me geen shell, alleen de tunnel", en -f (kort voor fork) stuurt ssh naar de achtergrond:
$ ssh -f -N -L 3307:127.0.0.1:3306 Dit e-mailadres wordt beveiligd tegen spambots. JavaScript dient ingeschakeld te zijn om het te bekijken.
6.2 Doorsturen op afstand: een lokale poort uitdelen
De vlag -R (kort voor remote, op afstand) is het spiegelbeeld. Hij opent een poort op de server en stuurt verbindingen terug naar een adres dat jij kunt bereiken.
$ ssh -R 9097:127.0.0.1:3000 Dit e-mailadres wordt beveiligd tegen spambots. JavaScript dient ingeschakeld te zijn om het te bekijken.
Nu komt iedereen op de server die met poort 9097 verbindt uit bij de applicatie die op poort 3000 op jouw laptop draait. Zo laat je een collega een site zien die alleen op je ontwikkelmachine bestaat, of laat je een server een dienst bereiken waar hij geen route naartoe heeft.
Standaard bindt de doorgestuurde poort op de server alleen aan loopback, dus hij is bereikbaar vanaf de server zelf en niet vanaf het hele netwerk. Verder openzetten vereist GatewayPorts in de serverconfiguratie, en dat is een bewuste beslissing en niets om achteloos aan te zetten.
6.3 Dynamisch doorsturen: een SOCKS-proxy in een vlag
De vlag -D (kort voor dynamic, dynamisch) is de krachtigste van de drie en de minst bekende. In plaats van een vaste poort door te sturen, verandert hij ssh in een SOCKS-proxy op je machine. Elk programma dat SOCKS spreekt bereikt daarna het netwerk via de server, en kiest per verbinding zijn eigen bestemming.
$ ssh -f -N -D 9098 Dit e-mailadres wordt beveiligd tegen spambots. JavaScript dient ingeschakeld te zijn om het te bekijken.
$ curl -s --socks5-hostname 127.0.0.1:9098 -o /dev/null -w "%{http_code}\n" http://internal.example/
200
Zet de SOCKS-proxyinstelling van een browser op 127.0.0.1:9098 en elke pagina die hij laadt wordt door de server opgehaald. Voor een beheerder is dit de snelle weg een prive netwerk in: een vlag, geen VPN-client, geen extra software aan beide kanten. Let op de vorm --socks5-hostname in plaats van gewoon --socks5, want die laat ook de DNS-opzoekingen op de server gebeuren, zodat interne hostnamen goed opgelost worden.
6.4 Jump hosts: machines bereiken die je niet kunt bereiken
Productieservers weigeren vaak verbindingen vanaf het internet en accepteren ze alleen vanaf een bastion- of gatewaymachine. De oude manier was inloggen op de gateway en daar opnieuw ssh draaien. De moderne manier is -J (kort voor jump, springen):
$ ssh -J Dit e-mailadres wordt beveiligd tegen spambots. JavaScript dient ingeschakeld te zijn om het te bekijken. Dit e-mailadres wordt beveiligd tegen spambots. JavaScript dient ingeschakeld te zijn om het te bekijken.
De verbinding naar de uiteindelijke host loopt door de gateway, maar het blijft een end-to-end versleutelde sessie tussen jouw machine en db01. De gateway stuurt bytes door die hij niet kan lezen. Je kunt meerdere sprongen aan elkaar rijgen met komma's.
Een valkuil staat gedocumenteerd maar wordt makkelijk gemist: opties die je op de commandoregel meegeeft gelden voor de bestemming, niet voor de jump host. Heeft de gateway een eigen poort, gebruiker of sleutel nodig, zet dat dan in het configuratiebestand, waar deze functie eigenlijk toch thuishoort:
Host gateway
HostName gateway.example.com
User peter
Port 2222
IdentityFile ~/.ssh/id_ed25519
Host db01
HostName 10.0.0.5
User peter
ProxyJump gateway
Daarna werkt ssh db01 gewoon, en rsync -a backup/ db01:/srv/ ook. Niemand hoeft de topologie te onthouden.
6.5 Verbindingen delen: ssh bijna instant maken
Elke nieuwe SSH-verbinding kost een TCP-handdruk, een sleuteluitwisseling en een authenticatieronde. Over het internet is dat een merkbare pauze, en je betaalt hem opnieuw voor elke git push, elke rsync en elk commando in een deployment-script.
Verbindingen multiplexen haalt die kosten weg. De eerste verbinding opent een control socket, en elke latere verbinding met dezelfde host hergebruikt het bestaande versleutelde kanaal in plaats van een nieuw op te bouwen.
Host *
ControlMaster auto
ControlPath ~/.ssh/control-%C
ControlPersist 10m
ControlMaster auto betekent "hergebruik een master als die er is, word anders zelf de master". ControlPath is waar de socket staat. ControlPersist 10m houdt de master tien minuten na je uitloggen op de achtergrond in leven, zodat het volgende commando ook meteen gaat.
Die %C verdient een opmerking, want het voor de hand liggende alternatief geeft problemen. Je kunt het pad uitschrijven met %r, %h en %p (gebruiker, host en poort op afstand), maar een control socket is een Unix-domeinsocket en het pad mag niet langer zijn dan ongeveer 100 tekens. Lange hostnamen of een diepe home-map duwen het eroverheen, en dan krijg je:
ControlPath too long ('/home/peter/.ssh/Dit e-mailadres wordt beveiligd tegen spambots. JavaScript dient ingeschakeld te zijn om het te bekijken. :22' >= 108 bytes)
%C is een hash van al die waarden tegelijk, dus hij heeft altijd dezelfde korte lengte en het probleem kan niet optreden:
$ ls -l ~/.ssh/control-*
srw------- 1 peter peter 0 Aug 11 14:06 /home/peter/.ssh/control-7837652c3f95189960cc3a12f3cfe47d
Let op de rechten van die socket: srw-------, alleen de eigenaar. Dat is geen versiering. Wie de control socket kan openen, kan meeliften op jouw geauthenticeerde verbinding met de server zonder zelf te authenticeren, dus de socket hoort in je eigen home-map en nooit op een gedeelde plek zoals /tmp.
Het effect is makkelijk te meten. Hetzelfde triviale commando twee keer gedraaid, een keer zonder delen en een keer via een bestaande master:
$ time ssh -o ControlPath=none server true
real 0m0.182s
$ time ssh server true
real 0m0.005s
Die test liep tegen een server op dezelfde machine, dus vrijwel alle 182 ms is cryptografie en geen netwerk. Over het internet is de besparing veel groter, want dan vallen de heen-en-weerrondes er ook nog af. Een deployment-script dat vijftig verbindingen opent, voelt als een ander gereedschap.
Je kunt de master bekijken en besturen met -O (kort voor control command, besturingscommando):
$ ssh -O check server
Master running (pid=74960)
$ ssh -O exit server
Exit request sent.
6.6 Sessies in leven houden
Een inactieve SSH-sessie sterft vaak omdat een router of firewall ertussen verbindingen weggooit die hij als afgelopen beschouwt. De remedie is een keepalive, en standaard staat die aan beide kanten uit: ServerAliveInterval in de client staat standaard op 0, en ClientAliveInterval in de server ook.
Host *
ServerAliveInterval 60
ServerAliveCountMax 3
Dat stuurt elke 60 seconden een klein versleuteld bericht als er verder niets gebeurt, en geeft het op na drie onbeantwoorde. Het lost het klassieke "mijn sessie bevriest als ik koffie ga halen" op, en het doet dat vanuit je eigen config, zonder dat er iets op de server hoeft te veranderen.
6.7 De serverkant: sshd dichttimmeren
Alles hierboven is de client. De server wordt geconfigureerd in /etc/ssh/sshd_config, en een handvol instellingen doet het meeste beveiligingswerk. De standaardwaarden zijn redelijk maar niet streng:
| Instelling | Standaard | Aanbevolen | Waarom |
|---|---|---|---|
PasswordAuthentication |
yes |
no |
Haalt brute-force- en gokaanvallen volledig weg, zodra sleutels werken |
PermitRootLogin |
prohibit-password |
no |
Dwingt beheerders als zichzelf in te loggen en sudo te gebruiken, wat meteen een auditspoor oplevert |
PubkeyAuthentication |
yes |
yes |
Laat aanstaan; dit is waar je naartoe overstapt |
MaxAuthTries |
6 |
3 |
Kapt gokken eerder af en houdt de logs leesbaar |
LoginGraceTime |
120 |
30 |
Minder halfopen verbindingen die staan te wachten |
AllowUsers / AllowGroups |
niet gezet | zet het | Alleen genoemde accounts kunnen uberhaupt inloggen, wat er verder ook verkeerd staat |
AllowTcpForwarding |
yes |
no waar niet nodig |
Voorkomt dat shell-accounts je interne netwerk in tunnelen (zie hieronder) |
PermitEmptyPasswords |
no |
no |
Al veilig; controleer of het niet gewijzigd is |
Die rij over doorsturen is degene die beheerders het vaakst over het hoofd zien, en paragraaf 6.1 tot en met 6.3 legde uit waarom hij ertoe doet. Geef iemand een SSH-account en je hebt hem niet alleen een shell gegeven. Je hebt hem de mogelijkheid gegeven alles te bereiken wat de server kan bereiken: de database op de prive interface, het beheerpaneel op loopback, het hele interne subnet via -D. Je firewall ziet het niet, want van buitenaf is het een gewone SSH-verbinding.
SSH-toegang betekent netwerktoegang, niet alleen shell-toegang. Vraag je voordat je een account uitdeelt af wat dat account door een tunnel kan bereiken, en niet alleen wat het kan typen.
Voor accounts die alleen een shell nodig hebben, doet AllowTcpForwarding no de deur dicht. Wil je fijner sturen, beperk dan losse sleutels (paragraaf 8.2), of som met permitopen in authorized_keys precies op welke bestemmingen een sleutel mag bereiken.
Meer dan een factor eisen. Voor waardevolle systemen is een sleutel alleen misschien niet genoeg. AuthenticationMethods eist dat meerdere methodes allemaal slagen, in plaats van te accepteren welke er als eerste werkt:
AuthenticationMethods publickey,keyboard-interactive
Lees de komma als "en". Die regel eist een geldige sleutel en een tweede uitdaging, meestal een eenmalige code via PAM. Je kunt alternatieven aanbieden door hele lijsten met spaties te scheiden, dus publickey,keyboard-interactive publickey,password accepteert beide combinaties. Test dit met extra zorg vanuit een tweede terminal, want een half geconfigureerde PAM-stack sluit sleutels buiten die een minuut eerder nog werkten.
Een hardwaregebonden sleuteltype zoals ed25519-sk is vaak de eenvoudiger route naar hetzelfde doel: het inloggegeven kan niet van de token gekopieerd worden, en je raakt het apparaat bij elke login fysiek aan.
Moderne distributies leveren een map /etc/ssh/sshd_config.d/ die vanuit het hoofdbestand wordt ingelezen. Zet je wijzigingen in een bestand daar, bijvoorbeeld /etc/ssh/sshd_config.d/99-hardening.conf, zodat een pakketupdate ze nooit overschrijft.
Test de configuratie altijd voordat je herstart, en houd altijd een tweede sessie open terwijl je het doet:
$ sudo sshd -t # syntax check; silence means OK
$ sudo systemctl reload ssh # apply without dropping existing sessions
De gewoonte om een tweede sessie open te houden is niet optioneel. Sluit je jezelf met een verkeerde SSH-config buiten op een server op afstand, dan is de enige weg terug misschien een console via je hostingprovider. Test de nieuwe instelling vanuit een verse terminal voordat je de sessie sluit die nog werkt.
Een populair advies verdient een kanttekening. SSH naar een niet-standaard poort verplaatsen vermindert je logruis enorm, want de meeste scannende robots proberen alleen poort 22. Het maakt de server niet noemenswaardig moeilijker aan te vallen, want een echte aanvaller scant toch alle poorten. Verander de poort gerust voor rustiger logs, maar reken het niet als beveiliging. Sleutels en PasswordAuthentication no zijn de verandering die ertoe doet.
7. Onder de motorkap: wat de versleuteling echt doet
Je kunt SSH jarenlang gebruiken zonder deze doos te openen, en alles hierboven werkt of je deze paragraaf nu leest of niet. Maar een verrassend aantal misverstanden verdwijnt zodra je weet wat er echt gebeurt, dus twintig minuten zijn het waard.
7.1 Drie soorten sleutels, en waarom mensen ze door elkaar halen
Het woord sleutel wordt in SSH voor drie totaal verschillende dingen gebruikt. Bijna elk verward gesprek over SSH-beveiliging komt voort uit het door elkaar halen daarvan.
| Soort | Van wie | Staat in | Beantwoordt de vraag |
|---|---|---|---|
| Hostsleutel | De server | /etc/ssh/ssh_host_ed25519_key op de server, vingerafdruk bewaard in jouw known_hosts |
"Praat ik echt met de machine die ik vroeg?" |
| Gebruikerssleutel | Jij | ~/.ssh/id_ed25519 op jouw machine, publieke helft in authorized_keys op de server |
"Mag deze persoon naar binnen?" |
| Sessiesleutels | Deze ene verbinding | Alleen geheugen, nooit naar schijf geschreven, weggegooid als je uitlogt | "Wat versleutelt de bytes zelf?" |
De eerste twee leven lang en jij beheert ze. De derde soort wordt voor elke verbinding opnieuw gemaakt en je ziet hem nooit. Als iemand "SSH-sleutels" zegt, bedoelt hij bijna altijd de tweede soort, maar de waarschuwing in paragraaf 5.6 gaat over de eerste soort, en precies daarom verwart die waarschuwing mensen.
7.2 De handdruk, stap voor stap
Dit is de volgorde van gebeurtenissen als je ssh typt:
- De client zoekt de hostnaam op en opent een gewone TCP-verbinding naar poort 22.
- Beide kanten kondigen hun versieregels en hun lijsten met ondersteunde algoritmes aan.
- Ze spreken af welke algoritmes ze gebruiken: een voor de sleuteluitwisseling, een voor de hostsleutel, een cipher en een integriteitscontrole. De winnaar is de eerste regel op de voorkeurslijst van de client die de server ook ondersteunt, en daarom verbetert een bijgewerkte client de verbinding zelfs naar een oudere server.
- Ze draaien de sleuteluitwisseling, die gedeelde sessiesleutels oplevert zonder dat een van beide kanten ze ooit over de lijn stuurt.
- De server ondertekent een deel van die uitwisseling met zijn hostsleutel en bewijst daarmee zijn identiteit. Je client controleert de handtekening tegen
known_hosts. - De versleuteling gaat aan. Alles vanaf hier is beschermd.
- Pas nu authenticeer jij, binnen het versleutelde kanaal.
- De sessie start en de server draait je shell of je commando.
Stap 7 is degene om te onthouden. Je inloggegevens gaan pas nadat de versleuteling draait over de lijn, en daarom ligt zelfs een gewoon wachtwoord over SSH niet open op het netwerk, en daarom heeft SSH telnet zo volledig vervangen. Het verklaart ook de volgorde van de -v-uitvoer: eerst het gepraat over de sleuteluitwisseling, daarna de authenticatiepogingen.
Je ziet het onderhandelde resultaat bij elke verbinding:
$ ssh -v server.example 2>&1 | grep -E 'kex:|Server host key'
debug1: kex: algorithm: Dit e-mailadres wordt beveiligd tegen spambots. JavaScript dient ingeschakeld te zijn om het te bekijken.
debug1: kex: host key algorithm: ssh-ed25519
debug1: kex: server->client cipher: Dit e-mailadres wordt beveiligd tegen spambots. JavaScript dient ingeschakeld te zijn om het te bekijken. MAC: <implicit> compression: none
debug1: Server host key: ssh-ed25519 SHA256:Cbs69zP2QMpFcfrRMSlg3fsDbEpOD+MH2FANbuWy5J8
En je kunt je client vragen wat hij uberhaupt wil onderhandelen. De vlag -Q (kort voor query, opvragen) somt de algoritmes op die jouw build ondersteunt:
$ ssh -Q kex # key exchange methods
$ ssh -Q key # key and certificate types
$ ssh -Q cipher # symmetric ciphers
Zo controleer je een compliance-eis of bevestig je dat een oud algoritme echt weg is, in plaats van het te gokken uit een blogbericht.
7.3 Forward secrecy: waarom een gestolen hostsleutel het verleden niet opent
Merk op dat de sessiesleutels in stap 4 vers gemaakt worden en nooit opgeslagen. Dat geeft SSH een eigenschap die forward secrecy heet, en die is sterker dan de meeste mensen aannemen.
Stel dat een aanvaller je versleutelde SSH-verkeer vandaag opneemt en bewaart. Jaren later breekt hij in op de server en steelt hij de prive hostsleutel. Hij kan de opgenomen sessies dan nog steeds niet lezen. De hostsleutel ondertekende alleen de handdruk, hij versleutelde de gegevens nooit, en de sleutels die dat wel deden zijn weggegooid op het moment dat je uitlogde.
De hostsleutel bewijst met wie je gepraat hebt. Hij beschermt niet wat je gezegd hebt. Dat werk hoort bij sessiesleutels die niet meer bestaan, en daarom is SSH-verkeer opvangen om later te ontsleutelen een slechte investering.
Dit is ook de reden dat de post-quantum sleuteluitwisseling uit paragraaf 3 zo belangrijk is. Forward secrecy houdt stand zolang de sleuteluitwisseling zelf later niet gebroken kan worden, dus die stap naar een quantumbestendig algoritme verhuizen beschermt gesprekken die je vandaag voert.
7.4 Certificaten: sleutels die vanzelf verlopen
Alles tot nu toe kopieert publieke sleutels naar authorized_keys, een bestand per gebruiker per server. Met vijf servers gaat dat prima. Met tweehonderd servers en dertig engineers wordt het een archiefprobleem, en het lastige is niet toegang geven maar toegang weghalen als iemand vertrekt.
OpenSSH lost dit op met certificaten. Je maakt een sleutel voor een certificaatautoriteit aan, en servers krijgen te horen dat ze die moeten vertrouwen. De CA ondertekent daarna gebruikerssleutels, en een ondertekende sleutel draagt een identiteit, een lijst met principals en een vervaldatum.
$ ssh-keygen -t ed25519 -f user_ca -C "user CA" # once, kept very safe
$ ssh-keygen -s user_ca -I "peter@laptop" -n peter -V +1h ~/.ssh/id_ed25519.pub
Signed user key id_ed25519-cert.pub: id "peter@laptop" serial 0 for peter valid from 2026-08-11T14:04:00 to 2026-08-11T15:05:38
De vlaggen lezen als een zin: -s (kort voor sign, ondertekenen) kiest de CA-sleutel, -I (kort voor identity, identiteit) labelt voor wie dit is in de logs, -n somt de gebruikersnamen op waarmee het certificaat mag inloggen, en -V (kort voor validity, geldigheid) zet de levensduur. Je kunt het resultaat altijd bekijken:
$ ssh-keygen -Lf ~/.ssh/id_ed25519-cert.pub
Type: Dit e-mailadres wordt beveiligd tegen spambots. JavaScript dient ingeschakeld te zijn om het te bekijken. user certificate
Signing CA: ED25519 SHA256:f0JisVLECAQaZ8Zvusfs/hcR2RSfhKN3P0sHDRiCmFc
Key ID: "peter@laptop"
Valid: from 2026-08-11T14:04:00 to 2026-08-11T15:05:38
Principals:
peter
Extensions:
permit-pty
permit-port-forwarding
...
Op de server vervangt een regel in sshd_config het hele archiefsysteem:
TrustedUserCAKeys /etc/ssh/user_ca.pub
Dat is de hele opzet. Daarmee slaagt een login zelfs als het bestand authorized_keys van de gebruiker helemaal leeg is, en de serverlog legt vast welk certificaat gebruikt is en welke CA ervoor instond:
Accepted publickey for peter from 203.0.113.55 port 52318 ssh2: ED25519-CERT SHA256:cSoLJ2nLw74r... ID peter@laptop (serial 0) CA ED25519 SHA256:f0JisVLECAQaZ8Zvusfs/hcR2RSfhKN3P0sHDRiCmFc
En als het uur om is, stopt de sleutel overal tegelijk met werken, zonder opruimwerk om te vergeten:
$ ssh Dit e-mailadres wordt beveiligd tegen spambots. JavaScript dient ingeschakeld te zijn om het te bekijken.
Dit e-mailadres wordt beveiligd tegen spambots. JavaScript dient ingeschakeld te zijn om het te bekijken. : Permission denied (publickey).
# in the server log:
Certificate invalid: expired
Hetzelfde idee werkt de andere kant op. Een hostcertificaat dat ondertekend is door een CA die je clients vertrouwen, haalt de vraag over vertrouwen bij eerste gebruik uit paragraaf 4.2 helemaal weg: nieuwe servers worden meteen herkend, en niemand hoeft vingerafdrukken met de hand te vergelijken. De keerzijde is echt. De prive CA-sleutel wordt het waardevolste geheim dat je hebt, want hij kan voor iedereen instaan, dus hij hoort offline of in hardware en niet op een buildserver.
Naar boven8. Iets wat de meeste gebruikers niet weten
8.1 Het escape-teken: een verborgen commandoregel in je sessie
Dit is het detail dat bijna iedereen verrast, ook mensen die SSH al jaren gebruiken. Binnen een interactieve sessie let ssh op een geheim escape-teken: een tilde (~) getypt aan het begin van een regel. Het bereikt de server nooit. Het praat met je lokale ssh-client.
De nuttigste lost een probleem op dat iedereen gehad heeft. Je verbinding bevriest, de server is weg, en Ctrl-C doet niets omdat Ctrl-C een tunnel in gestuurd wordt die niet meer werkt. Druk op Enter, typ dan ~. en de client hangt meteen op, in plaats van te wachten op een server die nooit meer antwoordt.
Er zijn er meer, en de client somt ze voor je op. Druk op Enter en typ dan ~?:
Supported escape sequences:
~. - terminate connection (and any multiplexed sessions)
~B - send a BREAK to the remote system
~R - request rekey
~V/v - decrease/increase verbosity (LogLevel)
~^Z - suspend ssh
~# - list forwarded connections
~& - background ssh (when waiting for connections to terminate)
~? - this message
~~ - send the escape character by typing it twice
(Note that escapes are only recognized immediately after newline.)
commandline disabled
Die laatste regel wijst op het interessantste escape van allemaal, en verklaart waarom je het misschien nooit hebt zien werken. ~C opent een kleine commandoregel binnen je sessie waar je een port forward kunt toevoegen zonder uit te loggen en opnieuw te verbinden met -L. Vroeger stond het standaard aan, maar OpenSSH 9.2 heeft het uitgezet zodat de client in een strengere sandbox kan draaien, dus vandaag moet je erom vragen:
Host *
EnableEscapeCommandline yes
Met dat in je config zit je halverwege een sessie, besef je dat je een tunnel naar de database nodig hebt, en voeg je er gewoon een toe. Druk op Enter, typ ~C, en je krijgt een prompt:
ssh> -L 3307:127.0.0.1:3306
Forwarding port.
Je kunt het resultaat controleren zonder ergens weg te gaan, met ~#:
The following connections are open:
#0 client-session (t4 [session] r0 i0/1 o0/0 e[write]/4 fd 4/5/6 sock -1 cc -1 io 0x01/0x01)
De regel waar mensen over struikelen staat in de opmerking onderaan die hulptekst: de tilde telt alleen direct na een nieuwe regel. Gebeurt er niets, druk dan eerst op Enter en probeer opnieuw. En zit je twee niveaus diep (een ssh vanuit een ssh), verdubbel dan de tilde: ~~. bereikt de tweede client in plaats van de eerste.
8.2 Sleutels die maar een ding kunnen
Een regel in authorized_keys hoeft geen volledige shell te geven. Je kunt er opties voor zetten die precies beperken wat die sleutel mag doen, en zo zetten professionals geautomatiseerde toegang op.
command="/usr/local/bin/backup.sh",no-port-forwarding,no-agent-forwarding,no-pty ssh-ed25519 AAAAC3Nza... backup@ci
De optie command= is een afgedwongen commando: wat de client ook vraagt, het wordt weggegooid en dit script draait in plaats daarvan. Kijk wat er gebeurt als een zo beperkte sleutel iets anders probeert:
$ ssh -i backup_key Dit e-mailadres wordt beveiligd tegen spambots. JavaScript dient ingeschakeld te zijn om het te bekijken. 'rm -rf /'
RESTRICTED: only the backup script runs
Het gevaarlijke commando draait nooit. Er is niet eens over nagedacht. En de beperkingen op doorsturen worden door de server afgedwongen, niet beleefd aan de client gevraagd:
$ ssh -i backup_key -N -L 9096:127.0.0.1:8099 Dit e-mailadres wordt beveiligd tegen spambots. JavaScript dient ingeschakeld te zijn om het te bekijken.
channel 2: open failed: administratively prohibited: open failed
Twee opties zijn nog het kennen waard. from="203.0.113.0/24" beperkt een sleutel tot verbindingen vanaf bepaalde adressen, en restrict zet elke optionele functie in een keer uit, zodat je alleen terugzet wat je nodig hebt:
restrict,command="/usr/local/bin/backup.sh",from="203.0.113.10" ssh-ed25519 AAAAC3Nza... backup@ci
Zo krijgt een backupserver, een deployment-pijplijn of een monitoringtaak precies de toegang die hij nodig heeft en niets meer. Lekt die sleutel ooit, dan erft de aanvaller de mogelijkheid een script vanaf een adres te draaien.
8.3 Waarom agent forwarding een valkuil is, en wat je in plaats daarvan gebruikt
Vroeg of laat stelt iemand -A voor, agent forwarding, zodat je sleutels je "volgen" van de ene server naar de volgende. Het werkt, en de handleiding zelf waarschuwt ertegen.
Als je je agent doorstuurt, verschijnt er op de machine op afstand een socket die je lokale agent kan vragen dingen te ondertekenen. Iedereen op die server die de socket kan bereiken, inclusief root en iedereen die de machine gekraakt heeft, kan je sleutels gebruiken zolang je verbonden blijft. Ze kunnen het sleutelmateriaal niet stelen, maar ze kunnen zich wel als jou authenticeren bij elke server die je sleutels openen, en jij ziet het niet gebeuren.
Agent forwarding geeft een machine op afstand de mogelijkheid om als jou op te treden. Een jump host doet dat niet: met
-Jstuurt de machine in het midden alleen versleutelde bytes door die hij niet kan lezen, en je sleutel ondertekent nooit iets voor hem.
Gebruik ProxyJump om machines via een gateway te bereiken, en ssh-add -c (kort voor confirm, bevestigen) als je echt moet doorsturen, zodat elk gebruik van de sleutel je goedkeuring op je eigen scherm vereist.
8.4 Weten waar ssh ophoudt
Een deel van vakmanschap is weten welk gereedschap het overneemt. ssh is het fundament, niet het antwoord op alles wat erop gebouwd is.
| Behoefte | Gebruik | Waarom |
|---|---|---|
| Een bestand een keer kopieren | scp of sftp |
Beide draaien over SSH; sinds OpenSSH 9.0 gebruikt scp het SFTP-protocol eronder |
| Dezelfde boom herhaaldelijk kopieren | rsync |
Verstuurt alleen wat veranderd is, over dezelfde SSH-verbinding |
| Een weggevallen verbinding overleven | tmux of screen |
Draait op de server, dus je werk gaat door als de sessie sneuvelt |
| Werken over een slechte mobiele verbinding | mosh |
Gebruikt SSH om in te loggen en daarna een zwervend UDP-protocol dat IP-wisselingen overleeft |
| Veel servers tegelijk configureren | ansible |
Stuurt SSH voor je aan, parallel, met een verslag van wat het veranderd heeft |
| Volledige netwerktoegang, permanent | WireGuard of een andere VPN | -D is een prima ad-hocproxy, maar een VPN is het echte antwoord voor dagelijks gebruik |
| Sleutels beheren voor een groot team | SSH-certificaten | Onderteken kortlopende sleutels met een CA in plaats van authorized_keys naar elke host te kopieren |
Die laatste rij is degene waar groeiende teams uiteindelijk bij uitkomen. Zodra je twintig servers en tien engineers hebt, houdt publieke sleutels met de hand verdelen op te schalen, en met de ingebouwde certificaatondersteuning van OpenSSH kan een vertrouwde CA-sleutel voor iedereen instaan, met een vervaldatum eraan vast.
Naar boven9. Best practices
- Gebruik Ed25519-sleutels met een wachtwoordzin, en een agent. Een keer
ssh-keygen -t ed25519, een keer per dagssh-add, en je krijgt betere beveiliging en minder typwerk tegelijk. - Zet wachtwoordauthenticatie uit zodra sleutels werken. Controleer eerst de serverlog op
Accepted publickey, houd een tweede sessie open, en zet dan pasPasswordAuthentication no. - Zet alles in
~/.ssh/config. Hosts, gebruikers, poorten, sleutels, jump hosts, keepalives. Onthoud dat de eerste passende waarde wint, dus specifieke blokken staan bovenaan. - Deel of kopieer nooit een privesleutel. Een sleutelpaar per machine waarop je typt. Raak je een laptop kwijt, dan haal je een publieke sleutel uit
authorized_keysin plaats van alles te vervangen. - Lees de waarschuwing over de hostsleutel voordat je hem opruimt. Verwacht na een herinstallatie, alarmerend op elk ander moment. Dit is de ene waarschuwing die tien minuten van je aandacht waard is.
- Beperk geautomatiseerde sleutels. Elke sleutel die door een script of CI-systeem gebruikt wordt, verdient
restrict, een afgedwongencommand=en eenfrom=-adreslijst. - Kies
ProxyJumpboven agent forwarding. Hetzelfde gemak, geen van de risico's. - Vraag wat een account kan bereiken, niet alleen wat het kan typen. SSH-toegang is netwerktoegang. Zet
AllowTcpForwarding novoor accounts die geen tunnels nodig hebben. - Wordt het beheer van sleutelbestanden een archiefprobleem, stap dan over op certificaten. Een vertrouwde CA, korte geldigheidsduur, en toegang die vanzelf eindigt in plaats van te wachten tot iemand eraan denkt.
- Zet verbindingen delen aan.
ControlMaster autometControlPersistlaat elk SSH-gereedschap dat je hebt sneller aanvoelen, voor vier regels config. - Houd OpenSSH bijgewerkt. De standaardwaarden voor ciphers en sleuteluitwisseling worden bij elke release beter, en je krijgt die verbeteringen gratis.
- Grijp naar de documentatie. De handleidingen zijn uitstekend en behandelen veel meer dan welk artikel dan ook kan.
$ man ssh # the client
$ man ssh_config # every client option, including the config file
$ man sshd_config # every server option
$ man ssh-keygen # keys, fingerprints, known_hosts, certificates
$ ssh -G server # what ssh will actually do for this host
Naar boven10. Veelgemaakte fouten
10.1 Veelvoorkomende mythes
| Mythe | Werkelijkheid |
|---|---|
| "SSH naar poort 2222 verplaatsen beveiligt de server." | Het vermindert logruis van automatische scanners. Een echte aanvaller scant elke poort. Sleutels en uitgeschakelde wachtwoorden zijn de echte verdediging. |
| "Een sleutel zonder wachtwoordzin is prima, het bestand staat op mijn laptop." | Dat bestand is het inloggegeven. Wie het kopieert, bezit elke server die het opent, zonder enige tweede factor. |
| "Ik moet mijn privesleutel op de server plakken." | Alleen het .pub-bestand verlaat ooit je machine. Vraagt een formulier of een collega om het andere bestand, dan klopt er iets niet. |
"Agent forwarding (-A) is gewoon gemak." |
Het laat iedereen met root op die server zich elders als jou authenticeren zolang je verbonden bent. Gebruik -J. |
| "De waarschuwing over de hostsleutel is een bug in ssh." | Het is ssh dat zijn allerbelangrijkste werk doet. Ruim hem alleen op als je weet waarom de sleutel veranderd is. |
"ssh host 'echo $HOME' toont mijn lokale home-map." |
Enkele aanhalingstekens sturen het commando onuitgewerkt door, dus de shell van de server werkt het uit. Dubbele werken het eerst lokaal uit. |
10.2 Valkuilen om te vermijden
- Te veel sleutels in de agent.
sshbiedt agent-sleutels aan voordat het de sleutel gebruikt die je met-inoemde, en de server weigert de verbinding zodraMaxAuthTriesbereikt is:
De oplossing isReceived disconnect from 203.0.113.10 port 22:2: Too many authentication failuresIdentitiesOnly yesin het configuratieblok van die host, waarmeesshalleen de sleutel gebruikt die jij opgaf. - Verkeerde rechten.
chmod 600op de privesleutel enauthorized_keys,chmod 700op de map~/.ssh. Een privesleutel die voor iedereen leesbaar is wordt stilletjes genegeerd, en een home-map op de server waar de groep in mag schrijven kan sleutelauthenticatie laten mislukken zonder bruikbare melding. - sshd_config bewerken zonder weg terug. Draai
sshd -tom de syntax te controleren, herlaad in plaats van te herstarten, en sluit je werkende sessie nooit voordat een nieuwe slaagt. -pen-Pverwarren. Kleine letter voorssh, hoofdletter voorscp. Iedereen is erin getrapt.- Vergeten welke kant
-Loplost. In-L 9099:127.0.0.1:8099wordt het middelste adres op de server opgelost.localhostbetekent daar de server, niet jij. - Een sleutelpaar naar elke machine kopieren. Een sleutel per apparaat. Een gelekte sleutel vervangen die op zes plekken staat, is een vervelende middag.
- Wachtwoorden "voor de zekerheid" aan laten staan. Op die zekerheid rekenen de scanners nu juist. Regel in plaats daarvan een console-terugvaloptie bij je hostingprovider.
- Aannemen dat een inactieve sessie blijft leven. Zonder
ServerAliveIntervalgooit een firewall ertussen hem uiteindelijk weg. Lange klussen horen sowieso intmux.
11. Samenvatting
Het commando ssh lijkt een manier om een terminal op een andere machine te krijgen, en dat is het ook. Het is daarnaast de versleutelde tunnel waar het meeste moderne serverbeheer doorheen loopt.
sshbetekent Secure SHell. Het vervingtelnet,rloginenrsh, die wachtwoorden in leesbare tekst stuurden, en het gebruikt poort 22 omdat die tussenftpentelnetlag.- Tatu Ylönen schreef het in 1995 na een wachtwoord-afluisteraanval in Finland. Het OpenBSD-team forkte de laatste vrije versie in 1999 tot OpenSSH, en het SSH-2-protocol werd in 2006 een IETF-standaard.
- Elke verbinding doet drie dingen: de hostsleutel van de server verifieren, jou authenticeren, en alles versleutelen. De vraag bij de eerste verbinding en
known_hostszijn hoe het eerste werkt. - Authenticatie met een publieke sleutel verslaat wachtwoorden:
ssh-keygen -t ed25519,ssh-copy-id, daarnaPasswordAuthentication noop de server. Zet altijd een wachtwoordzin en laatssh-agentde sleutel vasthouden. ~/.ssh/configverandert lange commando's in korte aliassen en wordt ook doorscp,rsyncengitgelezen. De eerste passende waarde wint, dus specifieke hosts staan bovenaan.-Lhaalt een poort op afstand naar je toe,-Rdeelt een lokale poort uit, en-Dverandertsshin een SOCKS-proxy voor een heel prive netwerk.-J(ProxyJump) bereikt machines achter een bastion, end-to-end versleuteld, en is de veilige vervanger van agent forwarding.ControlMastermetControlPersisthergebruikt een verbinding voor veel sessies en maakt elk SSH-gereedschap merkbaar sneller.- Een tilde aan het begin van een regel praat met je lokale client, niet met de server:
~.doodt een bevroren sessie,~?somt de rest op, en~Cvoegt een port forward toe zonder opnieuw te verbinden zodra jeEnableEscapeCommandline yeszet. - In
authorized_keysbeperkenrestrict,command=enfrom=een sleutel tot precies een taak vanaf precies een plek. - Drie verschillende dingen heten een sleutel: de hostsleutel van de server, jouw gebruikerssleutel, en de wegwerp-sessiesleutels die het echte versleutelen doen. Omdat de sessiesleutels weggegooid worden, kan een later gestolen hostsleutel het verkeer van vandaag niet ontsleutelen.
- SSH-certificaten vervangen het overal kopieren van publieke sleutels: een CA ondertekent een sleutel met een identiteit en een vervaldatum, servers vertrouwen de CA via
TrustedUserCAKeys, en toegang eindigt vanzelf. - Iemand SSH-toegang geven geeft hem netwerktoegang tot alles wat de server kan bereiken.
AllowTcpForwarding noenpermitopenzetten daar grenzen aan. - Op de server doen
PasswordAuthentication no,PermitRootLogin noenAllowUsershet meeste werk. Test metsshd -ten houd een tweede sessie open. - Twijfel je, draai dan
ssh -vom te zien wat er gebeurt, ofssh -G hostom te zien wat er zou gebeuren.
Dit is het overzicht dat je het beste kunt bewaren:
ssh user@host log in
ssh -p 2222 user@host non-standard port
ssh user@host 'uptime' run one command and exit
ssh -v user@host show what is going wrong
ssh -G host show the resolved config, do not connect
ssh-keygen -t ed25519 -C "me@laptop" create a key pair
ssh-copy-id user@host install the public key
ssh-add -t 8h ~/.ssh/id_ed25519 load it into the agent for 8 hours
ssh-keygen -lf ~/.ssh/id_ed25519.pub show a key fingerprint
ssh-keygen -p -f ~/.ssh/id_ed25519 add or change the passphrase
ssh-keygen -R host forget a changed host key
ssh-keygen -s ca -I who -n user -V +8h key.pub sign a short-lived certificate
ssh-keygen -Lf key-cert.pub inspect a certificate
ssh -f -N -L 3307:127.0.0.1:3306 host remote database on your local port 3307
ssh -f -N -R 9097:127.0.0.1:3000 host your local app on the server's port 9097
ssh -f -N -D 9098 host SOCKS proxy through the server
ssh -J user@gateway user@internal reach a host behind a bastion
ssh -O check host is a shared master connection running?
~. disconnect a frozen session (press Enter first)
~# list the forwards on this connection
~? list the escape sequences
~C add a port forward mid-session (needs EnableEscapeCommandline yes)
SSH is het ene commando waarbij een beetje extra kennis zich elke dag terugbetaalt: minder wachtwoorden, snellere deployments, en een server die ophoudt interessant te zijn voor de robots die hem scannen.
Naar boven

Peter is Joomla specialist en Linux admin voor snelle, veilige en schaalbare websites.












