FIDO2 i ssh - część 1
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⌗
-sk
oznacza security keyed25519-sk
jest wspierane przez firmware Yubico od wersji 5.2.3- Niższe wersje firmware’u obsługują tylko
ecdsa-sk
- Za tym źródłem wolę
ed25519
zamiastecdsa
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:
- Uruchom ssh-agent poprzez
eval `ssh-agent`
- Dodaj informację o kluczu prywatnym do ssh-agent poprzez
ssh-add -K
- 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ęcykman fido credentials list
zastępuje polecenieykman 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 tutajrp_id
- w skrócie3 jest to efektywna domena - najczęściejhost
. Więcej informacji tutaj.user_id
- miejsce na nazwę użytkownika klucza sshuser_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
tossh
- 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⌗
-
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.
-
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
-
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
-
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.
-
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 deviceTakie klucze z prefiksem
ssh:
(zwróć uwagę na:
) faktycznie miałem. Po zdekodowaniu ciągów base64 zobaczęthomasi
orazonyxcherry
- dokładnie te nazwy, które podałem przy generowaniu kluczy. -
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 tylko00: 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.
-
W terminologii Webauthn resident keys zostało przemianowane na discoverable credentials ↩︎
-
ykman
musi być w wersji powyżej 3.11. Wersja 4.0.0+ będzie odpowiednia. ↩︎ -
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. ↩︎