Безопасность

GPG YubiKey: автоматическое восстановление scdaemon на macOS

Если вы подписываете git-коммиты YubiKey на macOS и периодически получаете «General error» от GPG — это не баг YubiKey. Это гонка процессов за PC/SC интерфейс. GPG YubiKey scdaemon ошибка macOS — один из самых раздражающих артефактов повседневной работы с аппаратными токенами. Я написал gpg-with-retry — drop-in замену GPG, которая решает проблему полностью, без ручного вмешательства.

Причина ошибки: GPG YubiKey scdaemon ошибка macOS

На macOS за доступ к смарт-карте через PC/SC одновременно конкурируют три процесса:

  • scdaemon (GnuPG) — держит выбранный OpenPGP-апплет
  • ctkpcscd (Apple CryptoTokenKit) — периодически опрашивает карту
  • ssh-pkcs11-helper (OpenSSH) — обращается к PKCS#11-интерфейсу

Когда ctkpcscd или ssh-pkcs11-helper подключается к карте, он снимает выбор OpenPGP-апплета. scdaemon об этом не знает и продолжает использовать устаревшее соединение. Следующий gpg --sign или git commit -S падает с «General error» — хотя YubiKey физически на месте.

Особенно болезненно при коммитах: git воспринимает любую ошибку GPG как окончательную, встроенного retry нет. Приходится вручную запускать gpgconf --kill scdaemon перед каждым вторым коммитом.

Почему стандартные советы не работают

  • disable-ccid в scdaemon.conf — снижает частоту, но не устраняет гонку
  • Алиас pkill scdaemon — нужно запускать вручную
  • Git хуки — коды выхода GPG одинаковы для восстановимых и неустранимых ошибок
  • Перезапуск gpg-agent — сбрасывает все кешированные PIN-коды

Как работает gpg-with-retry

gpg-with-retry — прозрачная обёртка над gpg. Указываешь его как gpg.program в git config, и он берёт на себя управление жизненным циклом scdaemon:

  1. Preflight check — перед первой попыткой проверяет SCD SERIALNO
  2. Прозрачный retry — при восстановимой ошибке убивает scdaemon, очищает PCSC-клиентов, восстанавливает соединение
  3. Умная классификация ошибок — «General error» → retry; «Operation cancelled» → выход; «No card» → выход
  4. Освобождение карты — убивает scdaemon после каждой операции
  5. Race-free захват stderr — FIFO вместо process substitution

Результат: git commit -S работает с первого раза, каждый раз, без ручного вмешательства.

Установка за три команды

cp gpg-with-retry ~/.local/bin/
chmod +x ~/.local/bin/gpg-with-retry
git config --global gpg.program gpg-with-retry

Добавьте в ~/.gnupg/scdaemon.conf:

disable-ccid
pcsc-shared

Переменные окружения и отладка

  • GPG_RETRY_MAX=2 — максимальное число попыток
  • GPG_RETRY_DEBUG=1 — подробный вывод в stderr
  • GPG_BINARY=/path/to/gpg — явный путь к бинарнику GPG

При любых проблемах: GPG_RETRY_DEBUG=1 git commit -S покажет, какая ошибка была обнаружена, сработал ли retry и в каком состоянии был scdaemon.

Практический результат: ноль сбоев на M2 Mac

Я развернул gpg-with-retry для команды разработчиков с обязательной подписью коммитов через YubiKey на macOS 14 (M2). До внедрения: примерно каждый пятый git commit -S падал. После — ноль сбоев. Команда перестала думать о GPG вообще.

Это часть работы по выстраиванию безопасной инфраструктуры разработки. Помогаю командам в рамках ИТ-консалтинга и управления рисками — там, где разрыв между политикой безопасности и её реальным исполнением кроется в инструментарии разработчика.

Технические детали реализации

  • FIFO вместо process substitution: GPG может завершиться до того, как process substitution прочитает весь stderr. FIFO блокирует до полного чтения.
  • Классификация по паттернам ошибок: коды выхода GPG ненадёжны между версиями, скрипт сопоставляет текстовые паттерны.
  • Очистка PCSC-клиентов: на некоторых версиях macOS убийства scdaemon недостаточно — ctkpcscd держит устаревший handle.
  • Никаких внешних зависимостей: чистый POSIX shell, работает на любом macOS с GnuPG.

FAQ: вопросы о GPG YubiKey scdaemon ошибках на macOS

Почему GPG выдаёт «General error», хотя YubiKey вставлен?

Это проблема deselection OpenPGP-апплета через PC/SC. Когда ctkpcscd или ssh-pkcs11-helper обращается к карте, он снимает выбор апплета. scdaemon использует устаревший handle и возвращает «General error». YubiKey исправен. gpg-with-retry обнаруживает этот паттерн и автоматически восстанавливает соединение.

Работает ли gpg-with-retry с SSH-аутентификацией через gpg-agent?

Да. Обёртка перехватывает все вызовы через gpg.program: git commit -S, gpg --sign, gpg --decrypt и любые инструменты, вызывающие бинарник gpg.

Не замедлит ли preflight-проверка каждый коммит?

Только первый вызов после перезапуска scdaemon — около 100–200 мс. Последующие коммиты не имеют накладных расходов.

С какими моделями YubiKey совместим инструмент?

С любой смарт-картой, поддерживающей OpenPGP через scdaemon: YubiKey 4, YubiKey 5 (USB-A, USB-C, NFC), Nitrokey, Gnuk и аналоги. Обёртка управляет scdaemon, не общаясь с картой напрямую.

Получить gpg-with-retry

gpg-with-retry распространяется с открытым исходным кодом под лицензией MIT. Если вы настраиваете инфраструктуру подписи на YubiKey для команды или хотите проконсультироваться по безопасности DevSecOps — свяжитесь со мной.

Ilya Arestov — Fractional CTO | Dubai Airport Free Zone (DAFZ), Dubai, UAE | Almaty, Zenkov Street 59, Kazakhstan | +971-585-930-600 | https://t.me/getmonolith
Оцените статью