Wstęp

Klucze RSA pełnią ważną rolę w uwierzytelnianiu ssh - są:

  • długie, więc bardzo trudno je złamać,
  • łatwe w użyciu
  • powszechne

jednak czy masz ochotę, Drogi Użytkowniku, każdorazowo wpisywać passphrase przed użyciem klucza? Krótkie frazy wcale nie są rozwiązaniem? To co w takim razie?!

FIDO2

Opis standardu, za którym stoi organizacja FidoAlliance, znajdziesz tutaj. Spotkasz się także z nazwami CTAP, CTAP2 i Webauthn, jednak wszystkie oznaczają trochę co innego. Tutaj za to znajdziesz krótki opis każdego skrótu.

Przygotowania

Potrzebujesz:

  • klucza U2F ze wsparciem FIDO2,
  • innego urządzenia, do którego możesz zalogować się po ssh,
  • OpenSSH w wersji 8.2+ - release note,

wersję ssh sprawdzisz przez ssh -V

Generowanie

Artykuł pokazuje pracę na Ubuntu. Zobacz drugą część, by łatwo i przyjemnie odtworzyć to na Windowsie - w WSL2.

Sprawdź wersję oprogramowania klucza Yubico

lsusb -v 2>/dev/null | grep -A2 Yubico | grep "bcdDevice" | awk '{print $2}'

wydrukowało 5.26 .

To wyższa wersja niż 5.23, która wniosła dużo nowego.

Wygeneruj klucz ed25519-sk

O ed25519-sk słów kilka

  1. -sk oznacza security key
  2. ed25519-sk jest wspierane przez firmware Yubico od wersji 5.2.3
  3. Niższe wersje firmware’u obsługują tylko ecdsa-sk
  4. Za tym źródłem wolę ed25519 zamiast ecdsa
sudo ssh-keygen -t ed25519-sk

Prawdopodobnie wygenerowanie klucza będzie wymagało praw root a, więc wykonaj to z sudo . System poprosi o podanie PINu - podaj go, a następnie dotknij złotej płytki (ringu) na kluczu. Później zatwierdź enterem - to tyle!

Server-side

Wygenerowany klucz publiczny dodaj do ~/.ssh/authorized_keys na serwerze, do którego chcesz się łączyć przez ssh. Możesz użyć ssh-copy-id albo zrobić to ręcznie. Pamiętaj o odpowiednich uprawnieniach obu plików i katalogu .ssh !

Sprawdź

Użyj ssh do zalogowania się

ssh onyxcherry@localhostowykomputer -i klucz_ed25519-sk

po czym złoty ring klucza czeka już na wciśnięcie. Wciśnij go - zalogowałeś się!

W razie problemów użyj ssh ... -vvv , co pokaże bardzo dużo informacji poziomu DEBUG.

Tutaj widać zwięzłość tych poleceń.

Klucz poprosił o PIN, gdyż jest to zabezpieczenie przed nieuprawnionym użyciem FIDO2, gdybyśmy ten klucz zgubili etc.

Resident Key

Możesz też użyć opcji resident key1 - przechowywania kluczy w pamięci urządzenia. Po szczegóły pójdź tutaj lub do changeloga openssh 8.2 (sekcja FIDO2 resident keys).

W tym celu dodaj opcję -O resident, więc

ssh-keygen -t ed25519-sk -O resident -O user=onyxcherry -f ~/.ssh/id_mykey_sk

poprosi - jak poprzednio - o PIN oraz dotknięcie płytki.

Na komputerze, na którym chcesz użyć takiego klucza:

  1. Uruchom ssh-agent poprzez eval `ssh-agent`
  2. Dodaj informację o kluczu prywatnym do ssh-agent poprzez ssh-add -K
  3. Dodaj klucz publiczny do ~/.ssh/authorized_keys na serwerze.

Listę kluczy publicznych (w ssh_agent) zobaczysz przez ssh-add -L , a ich skróty SHA-256 przez małe el - ssh-add -l .

Szczegółowe użycie ssh-add znajdziesz za to pod tym adresem.

Yubikey-Manager

Wersję graficzną możesz znaleźć tutaj. Pod Windowsem ykman instaluje się wraz z wersją graficzną. Plik .exe znajdziesz w tej samej lokalizacji (u mnie C:\Program Files\Yubico\YubiKey Manager\ykman.exe ).

Zwróć koniecznie uwagę na wersję, którą posiadasz. Graficzna wersja yubikey-manager-qt-1.2.1 bazuje już na yubikey-manager 4.0.1, czyli uwzględniono już tę poprawkę, co pozwala zobaczyć reprezentację hex kluczy resident. Jest to ważne przy usuwaniu - o szczegółach niżej.

Będziemy zatem odwoływać się do poleceń z wersji 4.0 - więc ykman fido credentials list zastępuje polecenie ykman fido list .

Ykman

ykman fido credentials list

wyświetli wszystkie resident keys.

Natomiast

ykman fido credentials delete <uzytkownik>

usunie daną pozycję.

Usuwanie kluczy ssh resident

Tutaj ujawniają się potencjalne błędy popełnione przy generowaniu (i zapisywaniu do Yubikeya) kluczy w trybie resident.

Otóż ykman fido credentials delete może sprawiać problemy przy usuwaniu kluczy ssh.

Dzieje się tak dlatego, że ykman nie będzie wiedział, który klucz chcemy usunąć, gdy:

  • napotka więcej niż jeden klucz ssh (oczywiście ten resident)
  • nie podano w obu/wielu nazwy użytkownika (-O user=uzytkownik)

Program mówi to jasno w komunikacie Multiple matches, make the query more specific.. Alternatywnie zobaczysz na ekranie Failed to delete resident credential, ale umówmy się - nie do tego zmierzałeś.

Jak zapobiec takim problemom? Rozwiązanie jest proste - zawsze dodawaj nazwę użytkownika do generowanego klucza.

Dodaj do polecenia -O user=uzytkownik , tak jak tutaj:

ssh-keygen -t ed25519-sk -O resident -O user=onyxcherry

Wtedy w wyniku ykman fido credentials list 2 otrzymasz mniej więcej coś takiego:

ssh: 6f6e797863686572727900000000000000000000000000000000000000000000 openssh

Ciąg ten jest wynikiem binascii.b2a_hex() z pola user_id (sic!) - czyli, w moim przypadku, onyxcherry.

Możesz to sprawdzić przez funkcję a2b_hex (mówimy oczywiście o Python3):

>>> import binascii
>>> binascii.a2b_hex('6f6e7978636865727279')
b'onyxcherry'

Polecenie usunięcia klucza to

ykman fido credentials delete [OPTIONS] QUERY

Pole QUERY jest opisywane jako

A unique substring match of a credentials RP ID, user ID (hex) or name, or credential ID.

Jeśli podałeś nazwę użytkownika, to otrzymasz heksadecymalny ciąg, który zaczyna się od znaków różnych od 0 .

Aby usunąć taki klucz, podaj jako argument (do ykman fido credentials delete ... ) właśnie ten heksadecymalny ciąg.

Jeśli nie podałeś nazwy użytkownika, Twoje pole user_id będzie samymi 0 .

Możesz usunąć taki klucz (jeśli nie ma więcej niż jednego takiego wpisu) przez podanie wszystkich 0 jako argument.

Wtedy ykman znajdzie tylko jeden taki klucz.

Szczegółowy opis techniczny

Kod źródłowy

Interesują nas polecenia (ykman fido) list i delete, zatem przeanalizujmy ich kod źródłowy.

Użyty obiekt cred posiada pola (przeznaczone do szerokiego użytku; pomijamy zmienne rozpoczynające się od _):

  • credential_id - odsyłam tutaj
  • rp_id - w skrócie3 jest to efektywna domena - najczęściej host. Więcej informacji tutaj.
  • user_id - miejsce na nazwę użytkownika klucza ssh
  • user_name - nazwa użytkownika w rozumieniu takiej używanej w przeglądarce

Przykład:

www.passwordless.dev 546f6d656b2028557365726e616d656c657373207573657220637265617465642061742031322... Tomek
ssh: 74686f6d61736900000000000000000000000000000000000000000000000000 openssh

W kodzie źródłowym widzimy, że jest to kolejno


       rp_id                       user_id (hex)                  user_name
www.passwordless.dev  546f6d656b2028557365726e616d656c6573732...     Tomek
        ↑                              ↑                               ↑
      origin              heksadecymalna postać user_id         nazwa użytkownika

W przypadku kluczy ssh:

  • rp_id to ssh
  • user_name to openssh

Zatem jedynym rozróżnieniem (używalnym przez użytkownika) jest pole user_id - w to miejsce zapisują się dane z ssh-keygen -O user=... .

Usuwanie nierozróżnialnych kluczy

Może się zdarzyć, że - przez nieuwagę lub niewiedzę - wygenerowałeś i zapisałeś do Yubikeya więcej niż jeden klucz ssh bez nazwy użytkownika (flaga -O ). Wtedy ykman fido delete nie pozwoli na usunięcie takich kluczy, gdyż nie będzie wiedział, który ma usunąć.

Z pomocą przychodzi to narzędzie.

fido2-token

  1. fido2-token -L pokaże dostępne urządzenia (klucze sprzętowe):

    /dev/hidraw0: vendor=0x1050, product=0x0120 (Yubico Security Key by Yubico)
    

    Listę vendorów USB oraz listę ich produktów z przypisanymi numerami zobaczysz tutaj - dla przykładu Yubico.

  2. fido2-token -I /dev/hidraw0 udostępnia jeszcze więcej informacji:

    proto: 0x02
    major: 0x05
    minor: 0x02
    build: 0x06
    caps: 0x05 (wink, cbor, msg)
    version strings: U2F_V2, FIDO_2_0, FIDO_2_1_PRE
    extension strings: credProtect, hmac-secret
    aaguid: 149a20218ef6413396b881f8d5b7f1f5
    options: rk, up, noplat, clientPin, credentialMgmtPreview
    maxmsgsiz: 1200
    pin protocols: 1
    pin retries: 8
    

    aaguid: An identifier chosen by the authenticator manufacturer, indicating the make and model of the authenticator. ~źródło

  3. fido2-token -I -c /dev/hidraw0 pokazuje informacje o liczbie zajętych i dostępnych miejsc dla resident keys (wymaga PINu):

    Enter PIN for /dev/hidraw0:
    existing rk(s): 3
    possible rk(s): 22
    
  4. fido2-token -L -r /dev/hidraw0 wypisze klucze dla różnych RP:

    Enter PIN for /dev/hidraw0:
    00: 5ijj1Xp14KIhExvoQMLb1cbXecEvrT5jH7qHFJRhLW4= www.passwordless.dev
    01: 4wYQ6KFiEVlg/h7CI+ZSnJ9LboAgDcteXDIcivHisb8= ssh:
    

    Zdziwiło mnie to, gdyż w momencie wydania tego polecenia, miałem dwa klucze ssh resident key w Yubikeyu. Potwierdza to też wynik poprzedniego polecenia - 3 klucze, a nie 2 jak widać.

    Wynik polecenia nie zmienił się, gdy usunąłem jeden z kluczy ssh.

  5. fido2-token -L -k ssh: /dev/hidraw0 pokazuje dostępne klucze ssh:

    Enter PIN for /dev/hidraw0:
    00: NSmRQvC+53fXH7BfR7PfjA== openssh (dGhvbWFzaQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=) eddsa
    01: tz2i6N2OrZB9tuM6CyivnQ== openssh (b255eGNoZXJyeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=) eddsa
    

    [fido2-token] -L -k rp_id device Produces a list of resident credentials corresponding to relying party rp_id on device

    Takie klucze z prefiksem ssh: (zwróć uwagę na :) faktycznie miałem. Po zdekodowaniu ciągów base64 zobaczę thomasi oraz onyxcherry - dokładnie te nazwy, które podałem przy generowaniu kluczy.

  6. Wreszcie usuwanie - fido2-token -D -i tz2i6N2OrZB9tuM6CyivnQ== /dev/hidraw0

    Program oczywiście prosi o PIN. Nie wypisuje nic dodatkowego.

    Po sprawdzeniu dostępnych kluczy ponownie (fido2-token -L -k ssh: /dev/hidraw0) otrzymałem tylko

    00: NSmRQvC+53fXH7BfR7PfjA== openssh (dGhvbWFzaQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=) eddsa
    

    czyli usuwanie przebiegło pomyślnie.

    W związku z tym, że podajemy ciąg base64 (na przykład NSmRQvC+53fXH7BfR7PfjA== ) każdorazowo różny, możliwe jest usunięcie powtórzonych kluczy bez nazwy użytkownika.

    Co ciekawe - nie zmieniła się lista kluczy wyświetlana przez fido2-token -L -r /dev/hidraw0!

Podsumowanie

Moja droga - od podstawowego użycia Yubikeya do generowania kluczy, aż do usuwania powielonych resident keys z pamięci Yubikeya - biegła dosyć wyboiście. Nie spowodowała jednak wypadku, gdyż nie posiadałem wielu ważnych kluczy.

Mogłem eksperymentować do woli - w najgorszym przypadku zresetowałbym cały interfejs FIDO2 klucza.

Teraz zapraszam Cię do drugiej części artykułu - opowiem o prostym korzystaniu z dobrodziejstw resident keys na Windowsie.


  1. W terminologii Webauthn resident keys zostało przemianowane na discoverable credentials ↩︎

  2. ykman musi być w wersji powyżej 3.11. Wersja 4.0.0+ będzie odpowiednia. ↩︎

  3. In the context of the WebAuthn API, a relying party identifier is a valid domain string identifying the WebAuthn Relying Party on whose behalf a given registration or authentication ceremony is being performed. A public key credential can only be used for authentication with the same entity (as identified by RP ID) it was registered with. By default, the RP ID for a WebAuthn operation is set to the caller’s origin’s effective domain. This default MAY be overridden by the caller, as long as the caller-specified RP ID value is a registrable domain suffix of or is equal to the caller’s origin’s effective domain. ↩︎