2-Faktor-Authentifizierung unter Linux mit YubiKeys

Für etliche Dienste nutzen wir bereits unternehmensintern die 2-Faktor-Authentifizierung mit YubiKeys. Daher hat sich Philipp die Frage gestellt, ob man diesen Ansatz nicht auch für die Anmeldung am Linux-Desktop bzw. für die Autorisierung zur Rechteerweiterung verwenden kann. Wie das im Detail funktioniert, berichtet er hier:

Ein Laptop-Computer in einem dunklen Raum
How to implement Two-Factor Authentication on Linux with YubiKeys (Bild: Markus Spiske/Unsplash)

Da mein Desktop mittels LUKS verschlüsselt ist, habe ich recherchiert, inwieweit eine 2-Faktor-Authentifizierung zum Entsperren der Festplattenverschlüsselung genutzt werden kann. Der erste Teil dieses Artikels befasst sich mit der Einrichtung von YubiKeys als zweiten Faktor für die Desktop-Anmeldung und Rechteerweiterung. Das Entsperren der Festplatte mittels Passwort und YubiKey soll im zweiten Teil erläutert werden. Im Anschluss folgt ein kleiner Ausblick auf weitere mögliche Maßnahmen zur Erhöhung der Sicherheit, welche in nachfolgenden Blog-Artikeln ausführlicher erläutert werden sollen. Ich selbst verwende Debian als Distribution, daher orientiert sich dieser Artikel auch primär an dem Debian-Ökosystem.

2-FA im Linux-Desktop

Grundsätzlich kann man auch den Anleitungen von Ubuntu-Wiki und Yubico folgen.

PAM Modul installieren

Ich verwende als Distribution ein Debian, daher ist der Paketname zu Ubuntu gleich.

sudo apt install libpam-u2f

Für andere Distributionen:

Yubikey bekannt machen

Zuerst muss der Yubikey dem System bekannt gemacht werden über:

pamu2fcfg > u2f_keys

Der Yubikey blinkt auf und muss einmal bestätigt werden. Sollte eine PIN hinterlegt sein, so muss diese zuerst eingegeben werden.

Soll ein Backup-Key hinterlegt werden, so kann dieser an die Datei angehängt werden:

pamu2fcfg -n >> u2f_keys

Nun die Datei in das root Home Verzeichnis verschieben (alternativ kann auch /etc gewählt werden):

sudo mkdir -p /root/.config/Yubico
sudo mv /home/MYUSER/u2f_keys /root/.config/Yubico

PAM Module registrieren

Unter /etc/pam.d finden sich verschiedene Dateien, in denen die 2-Faktor-Authentifizierung eingeschaltet werden kann.

Je nach Wunsch kann eine 2-Faktor-Authentifizierung für folgende Dienste eingerichtet werden:

  • /etc/pam.d/runuser
  • /etc/pam.d/runuser-l
  • /etc/pam.d/su
  • /etc/pam.d/sudo-i
  • /etc/pam.d/su-

Dafür nach @include common-auth folgendes eintragen:

auth    required            pam_u2f.so authfile=/root/.config/Yubico/u2f_keys

Möchte man für jede Authentifizierung eine 2-Faktor-Authentifizierung benutzen, so muss in /etc/pam.d/common-auth im “Additional” Block das pam_u2f.so Module eingetragen werden:

# here are the per-package modules (the "Primary" block)
auth [success=2 default=ignore] pam_fprintd.so max-tries=1 timeout=10 # debug
auth [success=1 default=ignore] pam_unix.so nullok try_first_pass
# here's the fallback if no module succeeds
auth requisite pam_deny.so
# prime the stack with a positive return value if there isn't one already;
# this avoids us returning an error just because nothing sets a success code
# since the modules above will each just jump around
auth required pam_permit.so
# and here are more per-package modules (the "Additional" block)
auth required pam_u2f.so authfile=/root/.config/Yubico/u2f_keys
# end of pam-auth-update config

Nun kann in einem neuen Fenster zum Beispiel über sudo su - die 2FA getestet werden. Nachdem man das Passwort eingegeben hat, muss der YubiKey bestätigt werden. Selbiges funktioniert auch bei der Anmeldung am Desktop oder wenn über eine Desktop-Applikation höhere Rechte angefordert werden.

2-FA unter LUKS

Grundsätzlich lassen sich mit LUKS verschlüsselte Partitionen sehr einfach mittels YubiKeys öffnen. Dies kann entweder über cryptsetup selbst oder dem etwas moderneren und einfacher zu bedienenden systemd-cryptenroll erfolgen. systemd-cryptenroll verfügt neben der Registrierung von Keys auch über andere nützliche Funktionalitäten, wie diekryptographische Generierung von Recovery-Keys oder das Auslesen des LUKS-Keys aus einem TPM-Chip. In diesem Abschnitt möchte ich erst einmal nur die Verwendung eines YubiKeys als zweitem Faktor mit systemd-cryptenroll beschreiben.

Voraussetzungen

Damit YubiKeys zum Entsperren des LUKS-Containers genutzt werden können, müssen einige Voraussetzungen erfüllt sein.

  • Der YubiKey muss den sogenannten Challenge-Response-Modus unterstützen, dies ist bei YubiKeys der Version 5 der Fall: https://www.yubico.com/products/identifying-your-yubikey/
  • Für systemd-cryptenroll muss LUKS2 verwendet werden, da die neuere Version externe Authentifizierungsmethoden unterstützt und systemd-cryptenroll den LUKS-Header im JSON-Format bearbeitet.

YubiKey vorbereiten

Der Challenge-Response-Modus muss im YubiKey erst noch aktiviert werden. Dafür gibt es ein grafisches Tool. Der Challenge-Reponse-Modus verwendet einen Passkey zum Entsperren der LUKS Partition. Dieses Verfahren bringt eine 2-Faktor-Authentifizierung quasi mit sich. Im LUKS-Header wird der öffentliche Schlüssel des Passkeys hinterlegt, welcher verwendet wird, um beim Entsperren eine Challenge zu erzeugen. Diese kann nur mit dem privaten Key gelöst werden, welcher auf dem YubiKey gespeichert ist. Damit dieser private Key entsperrt werden kann, ist es notwendig, eine PIN zu setzen. Hierdurch ergibt sich die 2-Faktor-Authentifizierung: PIN und Challenge-Response.

1. YubiKey Manager installieren
2. YubiKey einstecken und YubiKey Manager als root ausführen

yubikey
yubikey

3. PIN setzen Applications > FIDO2

yubikey
yubikey

4. Challenge-Response Mode aktivieren Applications > OTP (Slot 2)

yubikey
yubikey

5. Challenge-Response Auswählen

yubikey
yubikey

6. Über “Generate” neuen Secret Key generieren:

yubikey
yubikey

Verschlüsselung vorbereiten

Ein verschlüsselter LUKS Container kann recht einfach von Version 1 zu Version 2 konvertiert werden. Da nur der Header bearbeitet wird, funktioniert das sogar im laufenden System.

Zuerst geben wir eine Liste der Partitionen aus:

root in ~
❯ lsblk
NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINTS
loop0 7:0 0 4K 1 loop /snap/bare/5
loop1 7:1 0 85,4M 1 loop /snap/bitwarden/90
loop2 7:2 0 85,4M 1 loop /snap/bitwarden/91
loop3 7:3 0 118,2M 1 loop /snap/core/15511
loop4 7:4 0 105,8M 1 loop /snap/core/15925
loop5 7:5 0 55,7M 1 loop /snap/core18/2785
loop6 7:6 0 55,7M 1 loop /snap/core18/2790
loop7 7:7 0 164,8M 1 loop /snap/gnome-3-28-1804/198
loop8 7:8 0 91,7M 1 loop /snap/gtk-common-themes/1535
loop9 7:9 0 4,7M 1 loop /snap/spt/289
nvme0n1 259:0 0 476,9G 0 disk
├─nvme0n1p1 259:1 0 300M 0 part /boot/efi
├─nvme0n1p2 259:2 0 476,2G 0 part
│ └─luks-60d3a662-dfa6-4b5b-b77f-84a987d38dee 254:0 0 476,2G 0 crypt /

└─nvme0n1p3 259:3 0 500M 0 part

Nun prüfen wir welche LUKS Version auf der verschlüsselten Partition verwendet wird:

root in ~
❯ cryptsetup luksDump /dev/nvme0n1p2
LUKS header information for /dev/nvme0n1p2

Version: 1
Cipher name: aes
Cipher mode: xts-plain64
Hash spec: sha256
Payload offset: 4096

Version: 1 steht für LUKS1. Mit folgendem Befehl kann diese nach LUKS2 konvertiert werden:

root in ~
❯ cryptsetup convert /dev/nvme0n1p2 --type luks2

Anschließend bestätigen und mit einer Abfrage die LUKS Version überprüfen:

root in ~
❯ cryptsetup luksDump /dev/nvme0n1p2
LUKS header information
Version: 2
Epoch: 32
Metadata area: 16384 [bytes]
Keyslots area: 2064384 [bytes]
UUID: 60d3a662-dfa6-4b5b-b77f-84a987d38dee
Label: (no label)
Subsystem: (no subsystem)
Flags: (no flags)

YubiKey als zusätzlichen Schlüssel hinterlegen

Damit der YubIkey als LUKS-Key hinterlegt werden kann, wird zum einen systemd-cryptenroll benötigt. Dies ist mit systemd standardmäßig installiert. dracut wird zum Bauen des initramfs mit FIDO2-Support verwendet. Ubuntu und Debian verwenden standardmäßig initramfs-tool zum Bauen des initramfs, dies unterstützt jedoch kein FIDO2.

Installieren und Einrichten von dracut

Installation:
sudo apt install dracut

Fido2 Konfiguration:
root in ~
❯ cat /etc/dracut.conf.d/fido2.conf
add_dracutmodules+=" crypt systemd fido2 "


Kernel-CmdLine Konfiguration (sollte identisch zu GRUB_CMDLINE_LINUX_DEFAULT in /etc/default/grub sein):

root in ~
❯ cat /etc/dracut.conf.d/cmdline.conf
kernel_cmdline="rd.luks.uuid=luks-60d3a662-dfa6-4b5b-b77f-84a987d38dee root=/dev/mapper/luks-60d3a662-dfa6-4b5b-b77f-84a987d38dee rootfstype=ext4 rootflags=rw,relatime quiet splash"

Schlüssel hinterlegen

Neuen Schlüssel hinterlegen:
root in ~
❯ systemd-cryptenroll /dev/nvme0n1p2 --fido2-device=auto


Nun muss das Passwort zum Öffnen der LUKS-Partition eingegeben werden. Anschließend der PIN, welcher oben für den Key hinterlegt wurde. Mit diesem Befehl können auch weitere Keys hinterlegt werden, es kann jedoch immer nur ein Key eingesteckt sein.

In der /etc/crypttab muss noch bekannt gemacht werden, dass die Partition mit einem YubiKey entsperrt werden soll. Dazu hinter der Partition fido2-device=auto eintragen:

root in ~
❯ cat /etc/crypttab -p
# /etc/crypttab: mappings for encrypted partitions.
#
# Each mapped device will be created in /dev/mapper, so your /etc/fstab
# should use the /dev/mapper/<name> paths for encrypted devices.
#
# See crypttab(5) for the supported syntax.
#
# NOTE: You need not list your root (/) partition here, but it must be set up
# beforehand by the initramfs (/etc/mkinitcpio.conf). The same applies
# to encrypted swap, which should be set up with mkinitcpio-openswap
# for resume support.
#
# <name> <device> <password> <options>

luks-60d3a662-dfa6-4b5b-b77f-84a987d38dee UUID=60d3a662-dfa6-4b5b-b77f-84a987d38dee none luks,discard,fido2-device=auto


Abschließend muss das aktuelle initramfs noch neu erstellt werden:

root in ~
❯ dracut -f


Sind Kernel in anderen Versionen installiert, so kann über den Parameter --kver [VERSION] ein initramfs für diese Version erstellt werden. Verfügbare Versionen sind unter /usr/lib/modules zu finden.

Die neuen Schlüssel können auch mittels cryptsetup überprüft werden:

root in ~
❯ cryptsetup luksDump /dev/nvme0n1p2
LUKS header information
Version: 2
Epoch: 30
Metadata area: 16384 [bytes]
Keyslots area: 2064384 [bytes]
UUID: 60d3a662-dfa6-4b5b-b77f-84a987d38dee
Label: (no label)
Subsystem: (no subsystem)
Flags: (no flags)

Data segments:
0: crypt
offset: 2097152 [bytes]
length: (whole device)
cipher: aes-xts-plain64
sector: 512 [bytes]

Keyslots:
0: luks2
1: luks2
2: luks2
Tokens:
0: systemd-fido2
fido2-rp: io.systemd.cryptsetup
fido2-clientPin-required:
true
fido2-up-required:
true
fido2-uv-required:
false
Keyslot: 1
1: systemd-fido2
fido2-rp: io.systemd.cryptsetup
fido2-clientPin-required:
true
fido2-up-required:
true
fido2-uv-required:
false
Keyslot: 2
3: systemd-recovery
Keyslot: 0
Digests:
0: pbkdf2
Hash: sha256

Iterations: 241830

Gegebenenfalls sind hier noch weitere Keys gelistet, wie der bisherige Key, den man zum Entschlüsseln der Partition genutzt hat, sowie Keyfiles zum automatischen Entschlüsseln weiterer Partitionen.

Wer möchte, kann auch noch einen Recovery-Key hinterlegen:

root in ~
❯ systemd-cryptenroll /dev/nvme0n1p2 --recovery-key


Dieser kann anstelle eines Passwortes verwendet werden.
Nun kann das System neu gestartet werden. Handelt es sich um ein komplett verschlüsseltes System (Boot- und Root-Partition oder Boot auf einer verschlüsselten Root-Parition), so wird man beim Laden von Grub um die Eingabe eines bisher genutzten Passworts gebeten. Nachdem Grub geladen wurde und man den entsprechenden Booteintrag ausgewählt hat, erscheint eine Aufforderung zum Entsperren der Root-Partition. Hier muss die PIN des YubiKeys eingeben werden und im Anschluss der YubiKey einmal bestätigt werden (lange drücken). Ist nur die Root-Partition verschlüsselt, so ist die erste Passworteingabe nicht notwendig. Leider kann Grub die Boot-Partition nicht mittels YubiKey entschlüsseln, daher muss zweimal ein Passwort eingegeben werden.

Wie geht es weiter?

Das System verfügt nun über eine 2-Faktor-Authentifizierung beim Entsperren der LUKS-Partition und der Anmeldung des Benutzers oder dem Zugriff auf höhere Rechte. Leider bringt das ein paar Unannehmlichkeiten mit sich:

Beim Starten ist zweimal eine Passworteingabe notwendig.
Nutzt man eine unverschlüsselte Boot-Partition, um nur einmal beim Starten des Systems ein Passwort einzugeben, so ist der Kernel für Angriffe verwundbar.

Abhilfe kann hier ein Secure-Boot schaffen. Die Idee ist, den Kernel samt initramfs zu signieren, damit gewährleistet ist, dass niemand diesen manipuliert hat. Initramfs und Kernel können dann unverschlüsselt auf einer separaten Partition liegen. Sollten diese dennoch manipuliert werden, stimmt die Signatur nicht mehr überein und das Starten des Systems wird verweigert. Im nächsten Blog-Artikel soll genau dies gezeigt werden. Statt initramfs und Kernel jedoch separat zu signieren, wird ein EFI-Image erstellt, welches Kernel, initramfs und Kernel-Cmdline enthält und somit nur einmal eine Signatur erfordert.

Philipp Wiesner
Philipp Wiesner
08.11.23
Icon