SSH Knowledge Base – Hardening, Keys, Jump-Hosts, sshd_config

Quellen

  • RFC 4251 – SSH Protocol Architecture
  • RFC 4252 – SSH Authentication Protocol
  • RFC 4253 – SSH Transport Layer Protocol
  • OpenSSH Manual Pages
  • CIS Benchmark SSH

Teil 1: Wie SSH funktioniert (bevor man sich aussperrt)

SSH (Secure Shell) ist ein verschlΓΌsseltes Netzwerkprotokoll.
Port 22/TCP. Zwei Phasen: Transport β†’ Authentifizierung.

Client                              Server
  β”‚                                   β”‚
  │──── TCP SYN ──────────────────►  β”‚  Port 22
  │◄─── TCP SYN-ACK ──────────────   β”‚
  β”‚                                   β”‚
  β”‚  ── TRANSPORT LAYER ─────────────────────────────
  │◄─── Server-Banner (SSH-2.0-OpenSSH_9.x) ──────  β”‚
  │──── Client-Banner ────────────►  β”‚              β”‚
  β”‚                                   β”‚              β”‚
  β”‚   Key Exchange (ECDH/DH)         β”‚   Kein       β”‚
  β”‚   Server schickt Host-Key         β”‚   Klartext   β”‚
  β”‚   Client prΓΌft Host-Key           β”‚   ab hier    β”‚
  β”‚   Session-Keys werden abgeleitet  β”‚              β”‚
  β”‚                                   β”‚              β”‚
  β”‚  ── AUTH LAYER ──────────────────────────────────
  β”‚                                   β”‚
  │──── Auth-Request ─────────────►  β”‚
  β”‚     (publickey / password)        β”‚
  │◄─── Auth-Response ────────────   β”‚
  β”‚                                   β”‚
  β”‚  ── SESSION ─────────────────────
  β”‚   Shell / SFTP / Port-Forward     β”‚

Host-Keys – das Fundament des Vertrauens

# Server hat mehrere Host-Keys (ein pro Algorithmus):
ls /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

# Client speichert bekannte Server in:
~/.ssh/known_hosts

# Warnung bei geΓ€ndertem Host-Key:
# "REMOTE HOST IDENTIFICATION HAS CHANGED"
# β†’ Server neu aufgesetzt? MITM? Immer prΓΌfen!

# Host-Key Fingerprint prΓΌfen (auf dem Server):
ssh-keygen -lf /etc/ssh/ssh_host_ed25519_key.pub
# Ausgabe: 256 SHA256:ABC123... root@server01 (ED25519)

# Known-Hosts-Eintrag entfernen (nach Server-Neuaufbau):
ssh-keygen -R hostname
ssh-keygen -R 10.0.1.50

Teil 2: SSH-Keys – Authentifizierung ohne Passwort

Key-Typen im Vergleich

RSA-4096   β†’ Alt, weit kompatibel, groß (langsam)
             Minimum: 4096 Bit! 2048 nicht mehr empfohlen.
ECDSA-256  β†’ Elliptische Kurven, schneller als RSA
             Achtung: NIST-Kurven, theoretische NSA-Bedenken
ED25519    β†’ Modern, schnell, klein, sicher
             Empfohlen fΓΌr alle neuen Keys! βœ“
             Nicht auf sehr alten Systemen (OpenSSH < 6.5)

Keys erstellen

# ED25519 – empfohlen
ssh-keygen -t ed25519 -C "admin@rz-server01" -f ~/.ssh/id_ed25519

# RSA fΓΌr alte Systeme (z.B. FreeBSD 10, alte IPMI-Konsolen)
ssh-keygen -t rsa -b 4096 -C "admin@legacy-server" -f ~/.ssh/id_rsa_legacy

# Ohne Passphrase (fΓΌr Automatisierung, Cronjobs)
ssh-keygen -t ed25519 -N "" -f /etc/ssh/deploy_key

# Passphrase Γ€ndern (ohne neuen Key!)
ssh-keygen -p -f ~/.ssh/id_ed25519

Public Key auf Server deployen

# Methode 1: ssh-copy-id (einfach)
ssh-copy-id -i ~/.ssh/id_ed25519.pub user@10.0.1.50

# Methode 2: manuell (wenn ssh-copy-id nicht verfΓΌgbar)
cat ~/.ssh/id_ed25519.pub | ssh user@10.0.1.50 \
  "mkdir -p ~/.ssh && chmod 700 ~/.ssh && \
   cat >> ~/.ssh/authorized_keys && \
   chmod 600 ~/.ssh/authorized_keys"

# Methode 3: direkt auf Server
echo "ssh-ed25519 AAAA...KEY... admin@client" \
  >> /home/adminuser/.ssh/authorized_keys
chmod 600 /home/adminuser/.ssh/authorized_keys
chmod 700 /home/adminuser/.ssh

Berechtigungen – die hΓ€ufigste Fehlerquelle

# KRITISCH: falsche Berechtigungen = Key funktioniert nicht!
# SSH ist absichtlich paranoid bei Dateiberechtigungen.

chmod 700 ~/.ssh                        # nur Owner darf rein
chmod 600 ~/.ssh/authorized_keys        # nur Owner lesen/schreiben
chmod 600 ~/.ssh/id_ed25519             # Private Key: KEIN Gruppenlesezugriff!
chmod 644 ~/.ssh/id_ed25519.pub         # Public Key: darf gelesen werden
chmod 600 ~/.ssh/config                 # Client-Config

# Diagnose wenn Key-Auth nicht funktioniert:
ssh -vvv user@server 2>&1 | grep -E "permission|auth|key"

# Auf dem Server prΓΌfen:
ls -la ~/.ssh/
ls -la ~/.ssh/authorized_keys
# Auch Elternverzeichnis prΓΌfen!
ls -la ~/         # Home-Dir darf NICHT world-writable sein!
namei -l ~/.ssh/authorized_keys  # komplette Pfad-Berechtigungen

Teil 3: sshd_config – Hardening

Datei-Pfade

/etc/ssh/sshd_config              ← Haupt-Config (alle Distros)
/etc/ssh/sshd_config.d/*.conf     ← Drop-in Configs (neuere Systeme)
~/.ssh/config                     ← Client-Config (pro User)
/etc/ssh/ssh_config               ← Globale Client-Config

GehΓ€rtete sshd_config fΓΌr RZ

# /etc/ssh/sshd_config – RZ-HΓ€rtung
# Nach Γ„nderungen: sshd neu laden (NICHT neustarten solange Verbindung offen!)

# ── Protokoll und Port ────────────────────────────────────────
Port 22                          # Im RZ oft belassen – Security by Obscurity hilft wenig
# Port 2222                      # Alternative – reduziert Brute-Force-Rauschen

# ── Authentifizierung ─────────────────────────────────────────
PasswordAuthentication no        # ← WICHTIGSTE EINSTELLUNG! Keys only!
PubkeyAuthentication yes
AuthorizedKeysFile .ssh/authorized_keys

PermitRootLogin no               # Root-Login verbieten!
# PermitRootLogin prohibit-password  # Kompromiss: Root nur mit Key

# Kein leeres Passwort
PermitEmptyPasswords no

# Challenge-Response deaktivieren (PAM-Bypass-Vektor)
ChallengeResponseAuthentication no
KbdInteractiveAuthentication no  # Neuerer Name fΓΌr dasselbe

# ── Verbindungs-Timeouts ──────────────────────────────────────
LoginGraceTime 30                # 30s fΓΌr Login, dann Disconnect
MaxAuthTries 3                   # Max 3 Versuche pro Verbindung
MaxSessions 10                   # Max parallele Sessions pro Verbindung

ClientAliveInterval 300          # Alle 300s keepalive senden
ClientAliveCountMax 2            # Nach 2 Misses: Disconnect
# β†’ hΓ€ngende Verbindungen werden nach 10min getrennt

# ── Verbindungs-EinschrΓ€nkungen ───────────────────────────────
MaxStartups 10:30:60             # Brute-Force-Schutz:
                                 # 10 gleichzeitig, ab 30% random drop,
                                 # ab 60 komplett blockieren

# Nur bestimmte User erlauben (whitelist)
# AllowUsers adminuser deployuser
# AllowGroups sshusers admins

# IP-basiert einschrΓ€nken (Alternative zu iptables)
# ListenAddress 10.0.0.53        # Nur auf diesem Interface lauschen

# ── Crypto-Algorithmen (moderne Auswahl) ─────────────────────
# Schwache Algorithmen deaktivieren:
KexAlgorithms curve25519-sha256,curve25519-sha256@libssh.org,diffie-hellman-group16-sha512,diffie-hellman-group18-sha512
HostKeyAlgorithms ssh-ed25519,ecdsa-sha2-nistp256,rsa-sha2-512,rsa-sha2-256
Ciphers chacha20-poly1305@openssh.com,aes256-gcm@openssh.com,aes128-gcm@openssh.com
MACs hmac-sha2-512-etm@openssh.com,hmac-sha2-256-etm@openssh.com

# ── Features deaktivieren die man nicht braucht ───────────────
X11Forwarding no                 # GUI-Forwarding aus
AllowTcpForwarding no            # Port-Forwarding aus (wenn nicht gebraucht)
AllowAgentForwarding no          # SSH-Agent-Forwarding aus
PermitTunnel no                  # VPN-Tunnel aus

# ── Banner und MOTD ───────────────────────────────────────────
Banner /etc/ssh/banner           # Rechtlicher Hinweis vor Login
PrintMotd no                     # MOTD ΓΌber PAM steuern

# ── Logging ───────────────────────────────────────────────────
LogLevel VERBOSE                 # Mehr als INFO, aber kein DEBUG
# LogLevel DEBUG3                # Nur fΓΌr ProblemlΓΆsung!
SyslogFacility AUTH

sshd nach Config-Γ„nderung neu laden

# ⚠️ NIEMALS sshd neustarten wÀhrend man verbunden ist!
# Bei Fehler in Config β†’ neue Verbindung schlΓ€gt fehl β†’ ausgesperrt!

# SICHERE Vorgehensweise:
# 1. Config prΓΌfen BEVOR reload:
sshd -t                          # Test-Modus, prΓΌft Syntax
sshd -T | grep -i password       # Effektive Config anzeigen

# 2. In ZWEITER SSH-Session testen (alte Session OFFEN lassen!)
ssh -o "StrictHostKeyChecking=no" adminuser@10.0.1.50 "echo OK"

# 3. Wenn neue Session funktioniert: reload
systemctl reload sshd            # Debian/Ubuntu/RHEL
# oder
kill -HUP $(cat /var/run/sshd.pid)

# ⚠️ NICHT: systemctl restart sshd  (trennt bestehende Verbindungen)

Teil 4: Das Aussperr-Kapitel

Die hΓ€ufigsten Wege sich selbst auszusperren – und wie man rauskommt.

Szenario 1: PasswordAuthentication no – und kein Key hinterlegt

# PrΓ€vention:
# IMMER Key hinterlegen BEVOR PasswordAuthentication deaktiviert wird!

# Reparatur via Konsole (IPMI, iDRAC, iLO, KVM):
# 1. Konsole ΓΆffnen
# 2. Datei editieren:
vi /etc/ssh/sshd_config
# PasswordAuthentication yes  ← temporΓ€r
systemctl reload sshd
# 3. Key hinterlegen
# 4. Wieder auf no setzen

Szenario 2: Falsche Berechtigungen auf ~/.ssh

# Symptom: Key-Auth schlΓ€gt fehl obwohl Key stimmt
# Server-Log:
journalctl -u sshd | tail -20
# Authentication refused: bad ownership or modes for directory /home/user/.ssh

# Reparatur:
chmod 700 ~/.ssh
chmod 600 ~/.ssh/authorized_keys
chown -R $USER:$USER ~/.ssh

Szenario 3: sshd startet nicht nach Config-Fehler

# Symptom: systemctl reload sshd schlΓ€gt fehl
# Diagnose:
sshd -t                          # zeigt den genauen Fehler
journalctl -u sshd -n 20

# HΓ€ufige Fehler:
# "Bad configuration option" β†’ Tippfehler in sshd_config
# "Permissions 0644 for '/etc/ssh/ssh_host_ed25519_key' are too open"
chmod 600 /etc/ssh/ssh_host_*_key

# Reparatur: alte Config aus Backup wiederherstellen
cp /etc/ssh/sshd_config.bak /etc/ssh/sshd_config
systemctl start sshd

Szenario 4: fail2ban hat eigene IP gesperrt

# Diagnose:
fail2ban-client status sshd
iptables -L f2b-sshd -n

# Entsperren:
fail2ban-client set sshd unbanip 10.0.1.100

# Eigene Management-IPs fΓΌr immer whitelisten:
# /etc/fail2ban/jail.local:
[DEFAULT]
ignoreip = 127.0.0.1/8 10.0.0.0/19

Szenario 5: AllowUsers/AllowGroups zu restriktiv

# Symptom: "Permission denied" obwohl Key stimmt
# Server-Log:
journalctl -u sshd | grep "User .* not allowed"

# PrΓΌfen wer erlaubt ist:
sshd -T | grep -i "allowusers\|allowgroups\|denyusers"

# User zur Gruppe hinzufΓΌgen:
usermod -aG sshusers adminuser
# Dann: NEUE Session testen, BEVOR alte geschlossen wird!

Notfallzugang: IPMI / Serial Console

# Wenn SSH komplett versperrt: IPMI nutzen
# IPMI ist der Zugang zum Server unabhΓ€ngig vom OS

# iDRAC (Dell):
racadm -r 10.0.0.100 -u root -p password getconfig

# iLO (HP):
# Browser: https://10.0.0.100

# IPMI Serial-over-LAN:
ipmitool -I lanplus -H 10.0.0.100 -U admin -P password sol activate

# Nach Reparatur:
ipmitool -I lanplus -H 10.0.0.100 -U admin -P password sol deactivate

Teil 5: SSH Client-Config (~/.ssh/config)

# ~/.ssh/config – spart tΓ€glich viele Tippfehler

# ── Globale Defaults ──────────────────────────────────────────
Host *
    ServerAliveInterval 60
    ServerAliveCountMax 3
    AddKeysToAgent yes
    IdentityFile ~/.ssh/id_ed25519
    StrictHostKeyChecking ask      # ask | yes | no | accept-new

# ── RZ-Server ─────────────────────────────────────────────────
Host rz-server01
    HostName 10.0.1.50
    User adminuser
    Port 22
    IdentityFile ~/.ssh/id_ed25519_rz

Host rz-*
    User adminuser
    IdentityFile ~/.ssh/id_ed25519_rz
    StrictHostKeyChecking yes      # Im RZ: kein auto-accept!

# ── Jump-Host (Bastion) ───────────────────────────────────────
Host bastion
    HostName 203.0.113.10
    User jumpuser
    Port 22
    IdentityFile ~/.ssh/id_ed25519_bastion

Host internal-*
    ProxyJump bastion              # automatisch ΓΌber bastion
    User adminuser
    IdentityFile ~/.ssh/id_ed25519_internal

# ── IPMI-Management direkt ────────────────────────────────────
Host ipmi-*
    User admin
    IdentityFile ~/.ssh/id_rsa_ipmi  # IPMI oft noch RSA
    KexAlgorithms +diffie-hellman-group14-sha1  # alte IPMI-Firmware
    HostKeyAlgorithms +ssh-rsa
    PubkeyAcceptedAlgorithms +ssh-rsa
# Mit Config: kurze Befehle
ssh rz-server01               # statt: ssh -i ~/.ssh/id_ed25519_rz adminuser@10.0.1.50
ssh internal-server02         # automatisch ΓΌber bastion

Teil 6: Jump-Hosts und Port-Forwarding

Jump-Host / Bastion

# Direkt via ProxyJump (OpenSSH >= 7.3)
ssh -J jumpuser@bastion.example.com adminuser@10.0.1.50

# Mehrere Hops:
ssh -J user@hop1,user@hop2 user@final-target

# In ~/.ssh/config:
Host internal-server
    HostName 10.0.1.50
    ProxyJump bastion

# Γ„ltere Methode (ProxyCommand, immer noch nΓΌtzlich):
Host internal-server
    HostName 10.0.1.50
    ProxyCommand ssh -W %h:%p bastion

Port-Forwarding

# Local Port Forward: lokaler Port β†’ remote
# "Ich will auf 10.0.1.50:5432 (PostgreSQL) zugreifen"
ssh -L 15432:10.0.1.50:5432 adminuser@bastion
# β†’ localhost:15432 verbindet zu 10.0.1.50:5432

# Remote Port Forward: remote Port β†’ lokal
# "Server soll auf meinen lokalen Dienst zugreifen"
ssh -R 8080:localhost:80 adminuser@server
# β†’ server:8080 verbindet zu meinem localhost:80

# Dynamic Port Forward (SOCKS Proxy)
ssh -D 1080 adminuser@bastion
# β†’ localhost:1080 als SOCKS5-Proxy fΓΌr das remote Netz

# Persistent (kein Terminal, nur Tunnel):
ssh -fNT -L 15432:10.0.1.50:5432 adminuser@bastion
# -f = in Hintergrund
# -N = kein Befehl ausfΓΌhren
# -T = kein PTY

SSH-Agent und Agent-Forwarding

# SSH-Agent starten und Key hinzufΓΌgen
eval $(ssh-agent)
ssh-add ~/.ssh/id_ed25519
ssh-add -l                       # geladene Keys anzeigen
ssh-add -t 3600 ~/.ssh/id_ed25519  # Key mit Timeout (1h)

# Agent-Forwarding (mit Vorsicht verwenden!)
ssh -A adminuser@bastion         # Agent wird weitergeleitet
# ⚠️ Auf Bastion kann Root den Agent-Socket nutzen!
# Nur auf vertrauenswΓΌrdigen Servern!

# Sicherer: ProxyJump statt Agent-Forwarding
# ProxyJump nutzt Client-Keys direkt, kein Forwarding nΓΆtig

Teil 7: fail2ban – SSH Brute-Force-Schutz

# Installation
apt install fail2ban   # Debian/Ubuntu
dnf install fail2ban   # RHEL/Rocky

# /etc/fail2ban/jail.local – lokale Anpassungen:
[DEFAULT]
bantime  = 1h           # Sperrzeit
findtime = 10m          # Zeitfenster fΓΌr maxretry
maxretry = 5            # Versuche bis Sperre
ignoreip = 127.0.0.1/8 10.0.0.0/19  # Eigenes Netz nie sperren!

[sshd]
enabled  = true
port     = ssh
logpath  = %(sshd_log)s
backend  = %(syslog_backend)s
maxretry = 3

# Service
systemctl enable fail2ban
systemctl start fail2ban

# Status
fail2ban-client status
fail2ban-client status sshd

# Gesperrte IPs anzeigen
fail2ban-client status sshd | grep "Banned IP"

# IP entsperren
fail2ban-client set sshd unbanip 1.2.3.4

# Log beobachten
tail -f /var/log/fail2ban.log
journalctl -u fail2ban -f

Teil 8: Distro-Unterschiede

sshd_config Standardwerte

Einstellung Debian 12 Ubuntu 24 RHEL 9 FreeBSD
PasswordAuthentication yes yes yes yes
PermitRootLogin no prohibit-password no (RHEL9) no
Default-Port 22 22 22 22
Config-Pfad /etc/ssh/sshd_config /etc/ssh/sshd_config /etc/ssh/sshd_config /etc/ssh/sshd_config
Drop-in-Dir /etc/ssh/sshd_config.d/ /etc/ssh/sshd_config.d/ /etc/ssh/sshd_config.d/ –
Service-Reload systemctl reload sshd systemctl reload ssh systemctl reload sshd service sshd reload

Ubuntu: Service heißt ssh nicht sshd!

# Ubuntu:
systemctl status ssh       # ← ssh, nicht sshd!
systemctl reload ssh
journalctl -u ssh

# Alle anderen (Debian, RHEL, Arch, FreeBSD):
systemctl status sshd
systemctl reload sshd
journalctl -u sshd

RHEL 9: sshd_config.d Drop-ins

# RHEL 9 hat crypto-policies die sshd_config ΓΌberschreiben kΓΆnnen!
# Effektive Config anzeigen:
sshd -T | head -40

# Crypto-Policy setzen:
update-crypto-policies --set DEFAULT
update-crypto-policies --set FUTURE    # strenger
update-crypto-policies --show

# Drop-in fΓΌr eigene Regeln:
# /etc/ssh/sshd_config.d/99-rz-hardening.conf

FreeBSD: rc.conf und sshd

# /etc/rc.conf:
sshd_enable="YES"

# Service:
service sshd start
service sshd reload
service sshd status

# Kein systemd! Kein journalctl!
# Logs:
tail -f /var/log/auth.log
grep sshd /var/log/auth.log

# authorized_keys fΓΌr root:
/root/.ssh/authorized_keys

# Alte IPMI-Firmware auf FreeBSD-Servern:
# oft braucht man legacy KexAlgorithms in Client-Config:
# KexAlgorithms +diffie-hellman-group1-sha1

Teil 9: SSH-Diagnose – Einzeiler

# Verbindung mit maximalem Debug-Output
ssh -vvv user@server 2>&1 | less

# Welche Authentifizierungsmethoden bietet der Server?
ssh -v user@server 2>&1 | grep "Authentications that can continue"

# Welchen Key nutzt der Client?
ssh -v user@server 2>&1 | grep "Trying\|Offering\|Server accepts"

# Server-Config live anzeigen (ohne Restart)
sshd -T
sshd -T | grep -i "passwordauth\|pubkeyauth\|permitroot"

# Alle fehlgeschlagenen Logins heute
journalctl -u sshd --since today | grep "Failed\|Invalid"

# Bruteforce-Versuch erkennen
journalctl -u sshd | grep "Failed password" | \
  awk '{print $11}' | sort | uniq -c | sort -rn | head -20

# Wer ist gerade eingeloggt?
who
w
ss -tlnp | grep :22
last | head -20                  # letzte Logins

# SSH-Verbindungen im System
ss -tnp | grep :22
netstat -tnp | grep :22

# Host-Key Fingerprint des Servers anzeigen
ssh-keyscan -t ed25519 10.0.1.50 2>/dev/null | ssh-keygen -lf -

# Known-Hosts aufrΓ€umen
ssh-keygen -R 10.0.1.50          # einzelnen Eintrag entfernen

Teil 10: SSH-HΓ€rtung Checkliste RZ

[ ] PasswordAuthentication no        ← Keys only
[ ] PermitRootLogin no               ← kein direkter Root-Login
[ ] MaxAuthTries 3                   ← Brute-Force-Erschwernis
[ ] LoginGraceTime 30                ← Timeout fΓΌr Login
[ ] ClientAliveInterval 300          ← hΓ€ngende Sessions aufrΓ€umen
[ ] X11Forwarding no                 ← GUI aus
[ ] AllowTcpForwarding no            ← Port-Forward aus (wenn nicht gebraucht)
[ ] Banner gesetzt                   ← rechtlicher Hinweis
[ ] fail2ban aktiv                   ← automatische IP-Sperre
[ ] eigene IP in fail2ban ignoreip   ← eigenes Netz nie sperren!
[ ] sshd -t vor jedem reload         ← Syntax immer prΓΌfen
[ ] zweite Session testen            ← vor Schließen der alten
[ ] IPMI-Zugang bekannt              ← Notfallzugang dokumentiert
[ ] Host-Keys gesichert              ← /etc/ssh/ssh_host_* im Backup
[ ] authorized_keys gepflegt         ← alte Keys regelmÀßig entfernen

Wichtige Dateipfade

Datei Inhalt
/etc/ssh/sshd_config Server-Config
/etc/ssh/sshd_config.d/ Drop-in Configs
/etc/ssh/ssh_host_* Server Host-Keys (privat + ΓΆffentlich)
~/.ssh/config Client-Config (pro User)
~/.ssh/authorized_keys Erlaubte Public Keys
~/.ssh/known_hosts Bekannte Server-Keys
~/.ssh/id_ed25519 Privater Key (chmod 600!)
~/.ssh/id_ed25519.pub Γ–ffentlicher Key
/var/log/auth.log SSH-Logs (Debian/Ubuntu)
/var/log/secure SSH-Logs (RHEL/Rocky)
/var/log/auth.log SSH-Logs (FreeBSD)
journalctl -u sshd SSH-Logs (systemd)
journalctl -u ssh SSH-Logs Ubuntu (!)

Quellen / WeiterfΓΌhrend

  • man sshd_config, man ssh_config, man ssh-keygen
  • RFC 4251/4252/4253 – SSH Protokoll
  • OpenSSH: https://www.openssh.com
  • CIS Benchmark: https://www.cisecurity.org
  • Arch Wiki: https://wiki.archlinux.org/title/OpenSSH
  • Mozilla SSH Guidelines: https://infosec.mozilla.org/guidelines/openssh