0x05. YubiKey + SSH/Git під macOS
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).
Варіанти:
- Нічого не робити. Ми обирали слот 9c саме для того, щоб PIN запитувався при кожному з’єднанні.
PKCS11Providerу конфігу робить рівно це, агент не потрібен. - Якщо дуже хочеться агента — запускати власний
ssh-agent -P '/opt/homebrew/lib/*'або покласти симлінк на бібліотеку в/usr/local/lib/, який у білому списку є.
Я лишив варіант 1. Параноя вона така.
0x05. Підсумок
| Linux | macOS | |
|---|---|---|
| Бібліотека | /usr/lib/x86_64-linux-gnu/libykcs11.so | /opt/homebrew/lib/libykcs11.dylib |
| Пакети | apt install ykcs11 yubico-piv-tool yubikey-manager pcscd | brew install ykman yubico-piv-tool |
| Демон смарт-карт | pcscd | вбудований |
| Конфіг | /etc/ssh/ssh_config | ~/.ssh/config |
На YubiKey не змінилося нічого. Той самий ключ у слоті 9c, той самий PIN, ті самі authorized_keys.
Refs
- 0x00. YubiKey + SSH/Git під Windows
- 0x01. YubiKey + SSH/Git під Linux
- https://developers.yubico.com/PIV/Guides/SSH_with_PIV_and_PKCS11.html
- https://developers.yubico.com/SSH/Securing_SSH_with_FIDO2.html (FIDO2 — не наш випадок, але корисно знати, чим відрізняється)
- https://developers.yubico.com/yubico-piv-tool/
- https://formulae.brew.sh/formula/yubico-piv-tool
- https://docs.yubico.com/software/yubikey/tools/ykman/Install_ykman.html#macos
man ssh-agent, опція-P