Wir haben in der Infrastrukturgruppe über den Umgang mit SSH-Keys mit Zugang zu administrativen Accounts gesprochen. Genauer darüber, wie wir vermeiden, dass ohne weiteren Aufwand in administrative Accounts auf anderen Servern gewechselt werden könnte, wenn einer unserer Server (z.B. Hopper) gehackt würde.

Wohin mit den Keys? Link to heading

Private Key mit Passphrase (Tipps zur Wahl hier) geschützt nur auf (verschlüsselten) privaten Geräten (Laptop/PC) ablegen, nicht auf Servern wie Hopper.

# SSH-Key erzeugen und sichere Passphrase wählen
# Am besten ED25519 verwenden. Eine Begründung würde den Rahmen sprengen.
ssh-keygen -t ed25519

Ich möchte nicht jedes Mal meine Passphrase eingeben Link to heading

Normalerweise müsste bei jedem Login auf einem Server der SSH-Key entsperrt werden, damit sich damit angemeldet werden kann. Um das zu umgehen, kann der SSH-Key nach einmaliger Eingabe der Passphrase in den SSH-Agent geladen werden.

# Wenn nötig, SSH-Agent starten. Manche Systeme sorgen selbst für einen
# laufenden Agent
# Beachten: `eval` kann gefährlich sein. Nur bei Programmen verwenden, denen
# vertraut wird. Zur Prüfung der Ausgabe `ssh-agent` ohne `eval` verwenden.
[[ -z "$SSH_AUTH_SOCK" ]] && eval "$(ssh-agent)"

# SSH-Key(s) in SSH-Agent laden, um ständige Passphraseneingabe zu umgehen
ssh-add # optional mit Pfad zu private key, wenn nicht Standardpfad (~/.ssh/id_type)

# Wenn nur ein einziger Key verwendet wird, kann folgendes am Anfang von
# ~/.ssh/config eingetragen werden, um diesen automatisch bei erster Benutzung
# zum Agent hinzuzufügen. Wenn Bestätigung, siehe unten SSH-Agent-Forwarding,
# funktioniert, "AddKeysToAgent confirm" statt yes.
# AddKeysToAgent yes
# IdentityFile ~/.ssh/id_ed25519

# Fingerprints von SSH-Keys in SSH-Agent auflisten
ssh-add -l

# PublicKeys aus SSH-Agent ausgeben
ssh-add -L

Wie soll ich mich mit Servern verbinden? Link to heading

Bisher melden sich einige von uns meist auf Hopper an – starten dort eventuell tmux – und loggen sich von da aus auf andere Server ein, um als normale Benutzys auf den Servern zu arbeiten. Das ist nach wie vor kein Problem. Dass sich einige über diesen Weg auch mit Admin-Accounts anmelden, entweder mit Passwort oder einem SSH-Key, dessen privater Teil auf Hopper liegt, muss sich ändern (warum siehe unten):

Es soll sich immer vom eigenen Gerät auf die jeweiligen Server mit administrativen Privilegien angemeldet werden.

Um sich trotzdem bei Servern anmelden zu können, die nur intern zu erreichen sind, sollte die SSH-Funktion ProxyJump1 verwendet werden. Dadurch wird erst eine Verbindung zu einem Jump-Host (z.B. Hopper) hergestellt, ohne eine Shell zu starten, und darin getunnelt eine Verbindung zu dem eigentlichen Ziel, den internen Server.

Es darf derselbe Key zum Login bei normalen Accounts und administrativen Accounts verwendet werden – wer möchte, kann das aber gerne trennen und verschiedene Keys verwenden. Außerdem darf sich weiterhin mit Keys, die auf Hopper und Co. liegen, bei normalen Accounts angemeldet werden, z.B. zum Klonen von Repos bei GitLab. Das Risiko einer Übernahme der normalen Accounts kann jede für sich selbst einschätzen.

Warum das ganze? Link to heading

Wir wollen die Sicherheit unserer Server erhöhen und die Auswirkungen eines Hacks minimieren. Nehmen wir ein mal an, dass ein Account auf Hopper gehackt wird:

  1. Würden wir Passwort-Logins bei Admin-Accounts verwenden, könnte über eine gefälschte SSH-Binary auf Hopper das Passwort abgegriffen werden.
  2. Liegen private SSH-Keys auf Hopper, könnten diese verwendet werden, um ohne Passwort weitere Server zu kompromittieren. Sind die SSH-Keys mit einer Passphrase gesichert, müsste die zumindest erst geknackt werden.

Was ist mit SSH-Agent-Forwarding? Link to heading

Über die Option ForwardAgent (oder den Schalter -A beim SSH-Aufruf), kann der Agent an den Host weitergeleitet werden, sodass dieser den Agent verwenden kann, als liefe er auf dem Host – der Agent läuft aber immer noch auf dem lokalen Gerät. Dadurch kann sich beispielsweise von Hopper aus auf anderen Servern angemeldet werden, obwohl die privaten SSH-Keys auf dem lokalen Gerät liegen.

Wenn diese Funktion verwendet wird, sollte jede Benutzung des SSH-Keys bestätigt werden müssen, da ein kompromittierter Server beliebig Anfragen an den Agent schicken könnte. Für die Bestätigung muss ein Programm angegeben werden, das verwendet werden soll. Dieses Programm wird über die Umgebungsvariable SSH_ASKPASS angegeben und muss über den Exit-Code mitteilen, ob bestätigt (0) oder abgelehnt (!= 0) wurde.

Standardmäßig wird SSH_ASKPASS mit einem GUI-Programm belegt, das ein Pop-up öffnet. Auf KDE-Systemen kann bspw. SSH_ASKPASS=ksshaskpass verwendet werden. Eine Alternative wäre lxqt-openssh-askpass mit der Variablenbelegung SSH_ASKPASS=lxqt-openssh-askpass. Unter macOS könnte theseal/ssh-askpass eine Alternative sein, die ich aber mangels Mac nicht testen kann.

Um ein TUI-Programm zu benutzen, bedarf es eines kleinen Hacks, da das Bestätigungsprogramm vom SSH-Agent aufgerufen wird, der im Hintergrund läuft und daher nicht mit einem Terminal verbunden ist. Ich habe dafür ein Skript geschrieben, das das entsprechende Terminal basierend auf der Laufzeit der SSH-Prozesse sucht. Es sollten also nicht zu schnell mehrere SSH-Verbindungen geöffnet werden, da sonst das falsche Terminal ausgewählt werden könnte. Das Skript verwendet pinentry-curses.

#!/usr/bin/env bash

# This script is not associated with a TTY
# $1 is the message from the SSH-Agent wether to
#     allow the use of key ... with fingerprint ...

# The script fails, if anything is written to stdout
exec &>>/dev/null
# DEBUG
#exec &>>"/tmp/ssh-ask.$USER.log"

msg=$1

# shellcheck disable=2009 # I need ps output
proc=$(ps -u "$USER" -o etimes:1,pid:1,tty:1,comm:1 | \
  grep -E '[^?] ssh$' | \
  sort -n | \
  head -n1)
tty=${proc% *}
tty=/dev/${tty##* }

lc=${LANG:-de_DE.UTF-8}
msg=$(tr '\n' ' ' <<<"$msg")
res=$(printf "%s\n" \
  "SETDESC ${msg}" \
  "OPTION ttyname=${tty}" \
  "OPTION lc-ctype=${lc}" \
  "CONFIRM" | \
  pinentry-curses | \
  tail -n1)

if [[ "${res%% *}" == OK ]]; then
  exit 0
else
  exit 1
fi

Um die Bestätigung für Keys zu erfordern, kann beim Hinzufügen von Keys zum Agent die Variante ssh-add -c verwendet werden. Automatisches Hinzufügen von Keys bei erster Benutzung kann über die Konfigurationsoptionen IdentityFile ~/.ssh/id_ed25519 und AddKeysToAgent confirm (siehe man ssh_config) in der SSH-Config erreicht werden.

Bei mir läuft standardmäßig kein SSH-Agent Link to heading

In diesem Fall kannst du, wie oben gezeigt, selbst einen Agent starten. Damit in verschiedenen Terminals der gleiche Agent verwendet wird und du nicht manuell die Variable SSH_AUTH_SOCK übertragen musst, kannst du folgendes Snippet zu deiner Shell-RC hinzufügen:

SSH_ENV="/tmp/.$USER-ssh-agent-env"
start_agent() {
  touch "${SSH_ENV}"
  chmod 600 "${SSH_ENV}"
  ssh-agent > "${SSH_ENV}"
  source "${SSH_ENV}" > /dev/null
}

load-ssh-agent() {
  if [ -f "${SSH_ENV}" ]; then
    source "${SSH_ENV}" > /dev/null
    # start agent if not running
    ps -p "${SSH_AGENT_PID}" | grep -q 'ssh-agent' || start_agent
  else
    start_agent
  fi
}

load-ssh-agent

Choose A Passphrase Link to heading

Eine Passphrase sollte lang genug sein, sodass sie nicht durch Brute-Force-Angriffe erraten werden kann (z.B. mehr als 20 Zeichen). Sie sollte aus zufälligen, nicht zusammenhängenden Worten, die nicht direkt der eigenen Person zuzuordnen sind, bestehen: Palabras Wimpern Hinterhalt Sirene. Optional können noch Buchstaben durch Ziffern oder Sonderzeichen ersetzt werden, um Komplexitätsanforderungen zu erfüllen.

Passphrasen können gut generiert werden, z.B. mit dem Bitwarden Passphrase Generator

Edits Link to heading

2023-06-08: Die Funktion load-ssh-agent aus dem Snippet prüft nun, ob die Prozess-ID aus der Variable auch wirklich zu einem SSH-Agent gehört und nicht zu einem anderen Prozess mit derselben ID. Das Problem kann auftreten, wenn die Datei mit den Variablen beim Beenden des Agent nicht gelöscht wird, aber die ID neu vergeben wurde.

2023-06-09: Skript für TUI-Bestätigung in Was ist mit SSH-Agent-Forwarding? hinzugefügt und Absätze zu SSH_ASKPASS überarbeitet.


  1. Siehe Schalter -J in man ssh und Option ProxyJump in man ssh_config ↩︎