Koniecznie przeczytaj poprzednią część

Windows 11

Sprawdzając wersję ssh -V w command prompt w Windows 10/11 otrzymamy OpenSSH_for_Windows_8.1p1, LibreSSL 3.0.2, a więc jest to za niska wersja, by użyć dobrodziejstw FIDO2.

Git Bash (for Windows)

Zainstalowany z git 2.37.3.windows.1 shell posiada wersję ssh OpenSSH_9.0p1, OpenSSL 1.1.1q 5 Jul 2022 – obecnie najnowszą, wspierającą FIDO2.

Ten poradnik dotyczył konfiguracji i używania kluczy sprzętowych właśnie na Git Bash, jednak przy codziennej pracy na koncie innym niż administratora Windows, uruchomienie Git Bash na Windows Terminal z prawami administratora (lecz na koncie użytkownika) przyprawiało o zawrót głowy.
Jednakże do Windows Terminal niedawno dodano opcję uruchamiania karty z prawami administratora, więc jest trochę łatwiej.

WSL2

Nie ma natomiast powodu, dla którego mielibyśmy być przywiązani do Git Bash, skoro na WSL2 można używać kluczy sprzętowych w ramach FIDO2.

Trochę historii

Założyłem dyskusję na GitHubie o próbie użycia usbip (właściwie - usbipd dla Windowsa), by z pomocą USB/IP podłączyć Yubikey do WSL2.

Oto wniosek – należy skompilować kernel samodzielnie, by ustawić między innymi CONFIG_USB_HIDDEV=y.

Być może kiedyś będzie domyślne wsparcie.

Przygotowanie

  1. sudo apt install libfido2-1 libfido2-dev libfido2-doc fido2-tools, przy czym libfido2 musi być w wersji przynajmniej 1.5.0
  2. Należy przeczytać całą treść poradnika1, by aktualizować i instalować wymagane paczki dopiero na docelowej dystrybucji
  3. Wszystkie narzędzia związane z openssh muszą być w odpowiednio wysokich wersjach.
    Poniżej (przeczytawszy changelogi wszystkich wersji) wypunktowałem najważniejsze zmiany dotyczące kluczy sprzętowych.

Zmiany w wersjach openssh

O ile Ubuntu 20.04 (Focal) zawiera paczkę openssh-client w wersji 8.2, czyli pierwszą wersję wspierającą FIDO2, o wiele lepiej użyć Ubuntu 22.04 (Jamny), gdyż zaszły następujące zmiany (między innymi) w openssh w wersjach:

8.3
  • ssh(1), ssh-keygen(1): when signing a challenge using a FIDO token, perform hashing of the message to be signed in the middleware layer rather than in OpenSSH code. This permits the use of security key middlewares that perform the hashing implicitly, such as Windows Hello.

  • ssh-keygen(1), ssh-add(1): when downloading FIDO2 resident keys from a token, don’t prompt for a PIN until the token has told us that it needs one. Avoids double-prompting on devices that implement on-device authentication.

8.4
  • ssh(1), ssh-keygen(1): better support for multiple attached FIDO tokens. In cases where OpenSSH cannot unambiguously determine which token to direct a request to, the user is now required to select a token by touching it. In cases of operations that require a PIN to be verified, this avoids sending the wrong PIN to the wrong token and incrementing the token’s PIN failure counter (tokens effectively erase their keys after too many PIN failures).

  • ssh(1), ssh-keygen(1): support for FIDO keys that require a PIN for each use. These keys may be generated using ssh-keygen using a new “verify-required” option. When a PIN-required key is used, the user will be prompted for a PIN to complete the signature operation.

8.5
  • ssh(1): for FIDO keys, if a signature operation fails with a “incorrect PIN” reason and no PIN was initially requested from the user, then request a PIN and retry the operation. This supports some biometric devices that fall back to requiring PIN when reading of the biometric failed, and devices that require PINs for all hosted credentials.
8.7
  • ssh-keygen(1): when generating a FIDO key and specifying an explicit attestation challenge (using -Ochallenge), the challenge will now be hashed by the builtin security key middleware. This removes the (undocumented) requirement that challenges be exactly 32 bytes in length and matches the expectations of libfido2.
8.9
  • ssh-add(1), ssh-agent(1): allow pin-required FIDO keys to be added to ssh-agent(1). $SSH_ASKPASS will be used to request the PIN at authentication time

  • ssh-keygen(1), ssh(1), ssh-agent(1): better handling for FIDO keys on tokens that provide user verification (UV) on the device itself, including biometric keys, avoiding unnecessary PIN prompts.

  • ssh-keygen(1): when downloading resident keys from a FIDO token, pass back the user ID that was used when the key was created and append it to the filename the key is written to (if it is not the default). Avoids keys being clobbered if the user created multiple resident keys with the same application string but different user IDs.

  • ssh(1), sshd(8), ssh-add(1), ssh-agent(1): add a system for restricting forwarding and use of keys added to ssh-agent(1) A detailed description of the feature is available at https://www.openssh.com/agent-restrict.html and the protocol extensions are documented in the PROTOCOL and PROTOCOL.agent files in the source release.

O wersjach

Jest to trochę nudne, niemniej lepiej od razu wybrać najwyższą możliwą wersję openssh, niż później zastanawiać się godzinami, dlaczego poszczególne funkcje nie działają.

Przy okazji nowe restrykcje dla udostępniania kluczy z ssh-agent zdalnym hostom są ciekawe i przydatne!

Testy na Ubuntu 20.04

By potwierdzić sens użycia openssh w wersji przynajmniej 8.4, spróbowałem użyć klucza ed25519-sk bez trybu resident (a więc klucz sprzętowy jest używany wraz z kluczem cyfrowym prywatnym znajdującym się na dysku) na Ubuntu 20.04 i otrzymałem błąd.

Oto część logu (ssh -vvv ...):

debug1: Will attempt key: /home/tomasz/.ssh/root-sk ED25519-SK SHA256:d2TO... explicit authenticator
debug2: pubkey_prepare: done
debug3: send packet: type 5
debug3: receive packet: type 7
debug1: SSH2_MSG_EXT_INFO received
debug1: kex_input_ext_info: server-sig-algs=<ssh-ed25519,[email protected],...>
debug3: receive packet: type 6
debug2: service_accept: ssh-userauth
debug1: SSH2_MSG_SERVICE_ACCEPT received
debug3: send packet: type 50
debug3: receive packet: type 51
debug1: Authentications that can continue: publickey,password,keyboard-interactive
debug3: start over, passed a different list publickey,password,keyboard-interactive
debug3: preferred gssapi-with-mic,publickey,keyboard-interactive,password
debug3: authmethod_lookup publickey
debug3: remaining preferred: keyboard-interactive,password
debug3: authmethod_is_enabled publickey
debug1: Next authentication method: publickey
debug1: Offering public key: /home/tomasz/.ssh/root-sk ED25519-SK SHA256:d2TO... explicit authenticator
debug3: send packet: type 50
debug2: we sent a publickey packet, wait for reply
debug3: receive packet: type 60
debug1: Server accepts key: /home/tomasz/.ssh/root-sk ED25519-SK SHA256:d2TO... explicit authenticator
debug3: sign_and_send_pubkey: ED25519-SK SHA256:d2TO...
debug3: sign_and_send_pubkey: signing using [email protected] SHA256:d2TO...
Confirm user presence for key ED25519-SK SHA256:d2TOe...
debug3: start_helper: started pid=1589
debug3: ssh_msg_send: type 5
debug3: ssh_msg_recv entering
debug1: start_helper: starting /usr/lib/openssh/ssh-sk-helper
debug1: ssh-sk-helper: ready to sign with key ED25519-SK, provider internal: msg len 185, compat 0x4000000
debug1: sshsk_sign: provider "internal", key ED25519-SK, flags 0x01
debug1: find_device: found 0 device(s)
debug1: ssh_sk_sign: couldn't find device for key handle
debug1: sshsk_sign: sk_sign failed with code -1
debug1: ssh-sk-helper: Signing failed: invalid format
debug1: ssh-sk-helper: reply len 8
debug3: ssh_msg_send: type 5
debug1: client_converse: helper returned error -4
debug3: reap_helper: pid=1589
debug1: identity_sign: sshkey_sign: invalid format
sign_and_send_pubkey: signing failed for ED25519-SK "/home/tomasz/.ssh/root-sk": invalid format
debug2: we did not send a packet, disable method
debug3: authmethod_lookup keyboard-interactive
debug3: remaining preferred: password
debug3: authmethod_is_enabled keyboard-interactive
debug1: Next authentication method: keyboard-interactive

Użyj więc openssh z Ubuntu 22.04!

Aktualizacja openssh-client

O ile jest możliwe pobranie skompilowanej najnowszej wersji openssh, to trudno będzie spełnić wymogi tej paczki, w szczególności libc6 (>= 2.34) (dpkg -l | grep libc6 na 20.04 reportuje 2.31), libselinux1 czy libssl3, której w ogóle nie ma.

Kompilacja kernela

Należy postępować dokładnie według tych instrukcji.

Wykonując git checkout, wybierz oczywiście najnowszą wersję (w poradniku jeszcze jest wskazana konkretna wersja PATCH).
Czyli git checkout linux-msft-wsl-5.10.y, chociaż domyślna jest już linux-msft-wsl-5.15.y.

Uruchomienie

Następnie skopiuj istniejący profil z Windows Terminal (niewymagane, niemniej Windows Terminal jest świetny) i zmień jego ścieżkę uruchomieniową na stworzoną właśnie dystrybucję.

Następnie zainstaluj wymagane paczki na nowo stworzonej dystrybucji (bądź dotychczasowo używanej, o ile posiada openssh >8.9).

Konfiguracja udev nie była (w moim przypadku) wymagana.

Zainstaluj też usbipd na Windowsie (hoście).

Udostępnienie Yubikeya z Windowsa do WSL2

  1. Pokaż listę urządzeń

    C:\Users\Tomasz> usbipd list
    Present:
    BUSID  DEVICE                                                        STATE
    1-1    Urządzenie audio USB, Urządzenie wejściowe USB                Not shared
    1-2    Urządzenie wejściowe USB                                      Not shared
    1-3    Bluetooth USB Module                                          Not shared
    2-2    Urządzenie wejściowe USB                                      Not shared
    2-3    Urządzenie wejściowe USB                                      Not shared
    2-4    Urządzenie wideo USB                                          Not shared
    
    Persisted:
    GUID                                    BUSID  DEVICE
    

    po czym sprawdź busid Yubikeya, który chcesz udostępnić.
    Jak? Wyjmij klucz, sprawdź listę; włóż klucz, sprawdź listę i zaobserwuj różnicę.

  2. Udostępnij klucz (cmd w trybie podniesionych uprawnień – administratora)

    C:\WINDOWS\system32> usbipd bind --busid 1-2
    
  3. Podłącz klucz do konkretnej dystrybucji WSL2 (wykonaj jako root)

    tomasz@TomekAspire5:~$ sudo usbip attach --remote=<HOST> --busid=<BUSID>
    

Niedziałające komendy

O ile usbipd w teorii pozwala na podpięcie klucza do konkretnej dystrybucji z poziomu Windowsa (hosta), co widać w pomocy:

C:\Users\Tomasz> usbipd wsl
usbipd-win 1.2.0

Usage: usbipd wsl [options] [command]

Options:
  -h|--help  Show help information.

Commands:
  attach  Attach a compatible USB device to a WSL instance.
  detach  Detaches a USB device from a WSL instance.
  list    List all compatible USB devices.

Use "wsl [command] --help" for more information about a command.

Convenience commands for attaching devices to Windows Subsystem for Linux.

to wymagane są uprawnienia administratora.
A ten za to ma inne profile dystrybucji niż konto użytkownika Windowsa.

Można jednak posłużyć się takim poleceniem:

for /f "tokens=3" %i in ('netsh interface ip show address "vEthernet (WSL)" ^| ^
findstr "IP Address"') do set "WSL_HOST_ADDRESS=%i" && ^
wsl -d wsl2-usbip -- usbip attach -b 1-2 -r %WSL_HOST_ADDRESS%

które:

  1. Znajdzie adres hosta (Windowsa) w sieci współdzielonej z WSL2
  2. Wykona polecenie podane po -- w dystrybucji wsl2-usbip (zmień tę nazwę na swoją), przy czym command prompt zastąpi %WSL_HOST_ADDRESS% cyferkami z kropkami, czyli shell WSL2 otrzyma gotowe polecenie

Jednolinijkowiec jest mojego autorstwa!
Adres tak hosta jak WSL2 zmienia się po każdym uruchomieniu, więc nie wpiszesz go na stałe.

Odpinanie klucza

Jeśli po podpięciu klucza Yubico do WSL2 zdarzy się sytuacja, gdy będziesz potrzebować użyć klucza na hoście (Windowsie), na WSL2 odepnij klucz.

Jest to wymagane, gdyż po samym wyjęciu klucza z portu USB oraz włożeniu ponownie ten nadal będzie przekazywany bezpośrednio do WSL2 i inaczej nie będzie widoczny na Windowsie.

  1. Zobacz port klucza

    tomasz@TomekAspire5:~$ usbip port
    Imported USB devices
    ====================
    Port 00: <Port in Use> at Full Speed(12Mbps)
        Yubico.com : Yubikey Touch U2F Security Key (1050:0120)
        1-1 -> usbip://172.29.240.1:3240/1-2
            -> remote bus/dev 001/002
    

    a więc port to 00

  2. Odepnij klucz

    usbip detach -p 00
    

VMWare Workstation Player

Można także podpiąć Yubikeya pod maszynę wirtualną typu VMWare Workstation (Player). Musisz jednak wcześniej włączyć taką funkcjonalność.

Podsumowanie

Pomimo kilku niedogodności – jak kompilacja modułów kernela czy korzystanie z USB/IP – logowanie się kluczem sprzętowym po ssh jest bardzo szybkie i przyjemne.
Zwłaszcza w trybie resident, który umożliwia skorzystanie z komputera kolegi (przy posiadaniu zaufania 😉) oraz pozwala śmiało pominąć passphrase, jako że przed nieautoryzowanym skorzystaniem z Yubikeya chroni PIN.

Użytkownicy dekstopowego Linuxa mają znacznie prościej. Wkładają klucz i już - działa od razu, o ile oczywiście zainstalowali wymagane biblioteki.


  1. Ten poradnik jest cyklicznie aktualizowany. Wersja, do której się odnosiłem, ma hash 6befeed ↩︎