0x00. Інтро

Про YubiKey + SSH вже було дві частини: під Windows (0x00) і під Linux (0x01). Тепер у хаті з’явився Mac, і треба було підключити до нього той самий YubiKey, який уже працює для SSH на Linux і Windows. Головна умова — на самому ключі нічого не чіпати: жодних нових ключів, жодних ресетів, жодних змін PIN.

Попередні частини: 0x00. YubiKey + SSH/Git під Windows та 0x01. YubiKey + SSH/Git під Linux.

Спойлер: вийшло, і сам сетап займає хвилин п’ять. Але дорогою я наступив на граблі, які варто описати окремо, бо саме на них я витратив більше часу, ніж на всю решту.

0x01. Граблі: FIDO2 ≠ PIV

Перший гугл по «yubikey ssh mac» приводить на офіційний гайд Yubico «Securing SSH with FIDO2». Там пропонують ssh-keygen -t ed25519-sk, тобто згенерувати новий ключ. Мені новий не потрібен — у мене вже є. Тому я вирішив «стягнути» існуючий ключ з YubiKey:

└─$ ssh-keygen -K
Enter PIN for authenticator:
You may need to touch your authenticator to authorize key download.
Provider "internal" returned failure -1
Unable to load resident keys: invalid format

Тут треба зупинитись і згадати, що YubiKey — це кілька незалежних застосунків в одному корпусі:

  • FIDO2 — те, чим ви логінитесь у Google чи GitHub через браузер. OpenSSH з версії 8.2 теж уміє ним користуватись (типи ключів ed25519-sk / ecdsa-sk). Такий ключ можна зробити «резидентним», і тоді ssh-keygen -K витягує його з токена на нову машину.
  • PIV — смарт-карта. Приватний ключ лежить у слоті (9a, 9c, …), а SSH спілкується з ним через PKCS#11-бібліотеку.
  • OpenPGP — ще один варіант, через gpg-agent. Не наш випадок.

У частинах 0x00 і 0x01 ми робили саме PIV (слот 9c, libykcs11). Тому FIDO2-гайд і ssh-keygen -K до нашого ключа не мають жодного стосунку, а «invalid format» — це просто кривий спосіб OpenSSH сказати «резидентних FIDO2-ключів тут нема». На ключі ця команда нічого не змінює, тільки читає. Але не варто перебирати PIN навмання: після восьми помилок FIDO2-застосунок блокується.

Друга важлива річ: при PIV копіювати з Linux нічого не треба. Там немає файлу приватного ключа — він існує лише всередині YubiKey і звідти не експортується. Кожній машині потрібна тільки PKCS#11-бібліотека, щоб до нього достукатись.

0x02. Інструменти

На Linux ми ставили ykcs11, yubico-piv-tool, yubikey-manager і pcscd. На macOS простіше:

  • pcscd не потрібен — підтримка смарт-карт (CryptoTokenKit) є в системі з коробки.
  • Все інше є в Homebrew.
└─$ brew install ykman yubico-piv-tool

yubico-piv-tool приносить із собою libykcs11.dylib — це той самий libykcs11.so з Linux-частини, тільки для macOS:

└─$ ls -la /opt/homebrew/lib/libykcs11.dylib
lrwxr-xr-x  1 user  admin  51 Oct 14 09:35 /opt/homebrew/lib/libykcs11.dylib -> ../Cellar/yubico-piv-tool/2.7.3/lib/libykcs11.dylib

На Intel-маках Homebrew живе в /usr/local, тож шлях буде /usr/local/lib/libykcs11.dylib. Далі всюди підставляйте свій.

Перевіряємо, що Mac бачить ключ:

└─$ ykman list
YubiKey 5C NFC (5.4.3) [OTP+FIDO+CCID] Serial: XXXXXXXX
└─$ ykman piv info

0x03. OpenSSH

Публічний ключ читаємо так само, як на Linux, лише з іншим шляхом до бібліотеки. PIN для цього не потрібен:

└─$ ssh-keygen -D /opt/homebrew/lib/libykcs11.dylib -e
ecdsa-sha2-nistp256 AAAA... Public key for PIV Authentication
ssh-rsa AAAA... Public key for Digital Signature
ssh-rsa AAAA... Public key for PIV Attestation

«Digital Signature» — це слот 9c, той, що ми генерували в 0x01. Саме цей публічний ключ уже лежить в authorized_keys на серверах і в GitHub, тож на серверній стороні нічого робити не треба.

Разовий вхід:

└─$ ssh -I /opt/homebrew/lib/libykcs11.dylib user@host

Щоб не писати -I кожного разу, у Linux-частині ми дописували PKCS11Provider в /etc/ssh/ssh_config. На macOS я б радив користувацький конфіг — системний час від часу перезаписується оновленнями ОС:

└─$ cat >> ~/.ssh/config <<'EOF'
Host *
    PKCS11Provider /opt/homebrew/lib/libykcs11.dylib
EOF
└─$ chmod 600 ~/.ssh/config

Перевіряємо, що ssh підхопив налаштування:

└─$ ssh -G github.com | grep -i pkcs11
pkcs11provider /opt/homebrew/lib/libykcs11.dylib

І власне тест:

└─$ ssh -T git@github.com
Enter PIN for 'YubiKey PIV #XXXXXXXX':
Hi user! You've successfully authenticated, but GitHub does not provide shell access.

Git працює автоматично, бо ходить через той самий ssh.

0x04. Нюанс з ssh-agent

Логічне бажання — додати ключ в ssh-agent через ssh-add -s /opt/homebrew/lib/libykcs11.dylib, щоб вводити PIN раз на сесію. На Apple Silicon це впирається в:

Could not add card "/opt/homebrew/lib/libykcs11.dylib": agent refused operation

Причина — ssh-agent має білий список шляхів для PKCS#11-бібліотек, і за замовчуванням це /usr/lib*/*,/usr/local/lib*/*. /opt/homebrew туди не входить (див. man ssh-agent, опція -P).

Варіанти:

  1. Нічого не робити. Ми обирали слот 9c саме для того, щоб PIN запитувався при кожному з’єднанні. PKCS11Provider у конфігу робить рівно це, агент не потрібен.
  2. Якщо дуже хочеться агента — запускати власний ssh-agent -P '/opt/homebrew/lib/*' або покласти симлінк на бібліотеку в /usr/local/lib/, який у білому списку є.

Я лишив варіант 1. Параноя вона така.

0x05. Підсумок

LinuxmacOS
Бібліотека/usr/lib/x86_64-linux-gnu/libykcs11.so/opt/homebrew/lib/libykcs11.dylib
Пакетиapt install ykcs11 yubico-piv-tool yubikey-manager pcscdbrew install ykman yubico-piv-tool
Демон смарт-картpcscdвбудований
Конфіг/etc/ssh/ssh_config~/.ssh/config

На YubiKey не змінилося нічого. Той самий ключ у слоті 9c, той самий PIN, ті самі authorized_keys.

Refs