Перейти к содержимому
ecdsa.com

SSH или GPG для подписи коммитов в Git: какой ключ выбрать

Оба способа помещают в объект коммита подпись, которую могут проверить Git и платформы хостинга кода. Подпись SSH использует ключи, которые у разработчиков уже есть; у OpenPGP есть собственные сроки действия, отзыв и модель доверия.

SSH или GPG для подписи коммитов — это выбор ключа, который Git использует, когда вы запускаете git commit -S или git tag -s. С OpenPGP (вариант по умолчанию, обычно это GnuPG) Git сохраняет подпись OpenPGP в заголовке коммита gpgsig. С SSH (gpg.format=ssh, Git 2.34 и новее) Git вызывает ssh-keygen -Y sign и сохраняет в то же место подпись SSHSIG. В обоих случаях подпись охватывает объект коммита, в котором указаны дерево, родительские коммиты, автор и сообщение, поэтому одна подпись подтверждает подлинность всей истории вплоть до этого коммита.

Коротко о главном

Подпись SSHПодпись OpenPGP (GPG)
Поддержка в GitGit 2.34 (ноябрь 2021 года), gpg.format=sshgit commit -S начиная с Git 1.7.9 (2012); подписанные теги — гораздо раньше
Инструмент подписиssh-keygen -Y sign, OpenSSH 8.1 или новее (но не 8.7)gpg или другая реализация OpenPGP
Формат подписиSSHSIG с пространством имён git, в формате ASCII armor BEGIN SSH SIGNATUREПакет подписи OpenPGP, в формате ASCII armor BEGIN PGP SIGNATURE
Размер в объекте коммита (Ed25519, по замеру)306 байт279 байт
Кому доверяютКлючам, перечисленным в gpg.ssh.allowedSignersFileКлючам из связки ключей с достаточным уровнем доверия к владельцу (owner trust)
Срок действия и ротацияvalid-after и valid-before для каждого ключа в списке разрешённых подписантов (OpenSSH 8.8+)Срок действия записан в самом ключе, его можно продлить; подключи
Отзывgpg.ssh.revocationFile (список ключей или KRL)Сертификат отзыва, опубликованный вместе с ключом
Аппаратные ключиКлючи FIDO2 (ed25519-sk, ecdsa-sk), агенты, которые хранят ключи в защищённых анклавахСмарт-карты OpenPGP и ключи безопасности с апплетом OpenPGP
Центры сертификацииСертификаты SSH через строки cert-authorityНет; идентификаторы пользователей заверяются подписями других ключей
Отметка «Verified» в GitHubС 23 августа 2022 годаС апреля 2016 года
Отметка «Verified» в GitLabС версии 15.7 (декабрь 2022 года), в общем доступе с 15.8Да

Подпись и проверка обоими способами

Скрипт ниже во временном каталоге создаёт ключ SSH и ключ OpenPGP с одним и тем же идентификатором пользователя, подписывает каждым ключом по одному коммиту и проверяет обе подписи (Git 2.54, OpenSSH 10.3, GnuPG 2.5.21). Глобальная конфигурация Git отключена, так что на вашей машине ничего не меняется.

Один репозиторий: коммит с подписью SSH и коммит с подписью OpenPGP
#!/bin/sh
base=$(pwd)
dir=$(mktemp -d "$base/commitdemo.XXXXXX")
cd "$dir" || exit 1
GNUPGHOME=$(mktemp -d /tmp/gpg.XXXXXX)   # short path: gpg-agent sockets need one
export GNUPGHOME GIT_CONFIG_GLOBAL=/dev/null GIT_CONFIG_NOSYSTEM=1
export GIT_AUTHOR_NAME=Alice GIT_AUTHOR_EMAIL=alice@example.org
export GIT_COMMITTER_NAME=Alice GIT_COMMITTER_EMAIL=alice@example.org

# One SSH key with an allowed_signers entry, one OpenPGP key
ssh-keygen -q -t ed25519 -N '' -C alice@example.org -f alice
echo "alice@example.org namespaces=\"git\" $(cat alice.pub)" > allowed_signers
gpg --batch --quiet --passphrase '' --quick-gen-key "Alice <alice@example.org>" ed25519 sign 1y
fpr=$(gpg --list-keys --with-colons alice@example.org 2>/dev/null | awk -F: '/^fpr/ { print $10; exit }')

# Two commits: the first signed with SSH, the second with OpenPGP
git init -q repo && cd repo || exit 1
git config gpg.ssh.allowedSignersFile ../allowed_signers
echo one > file && git add file
git -c gpg.format=ssh -c user.signingkey=../alice.pub commit -q -S -m "signed with SSH"
echo two >> file
git commit -q -a -S"$fpr" -m "signed with OpenPGP" 2>/dev/null

for c in HEAD~1 HEAD; do
  echo "== $(git log -1 --format=%s "$c")"
  git verify-commit "$c" 2>&1 | grep -E "Good|using"
  git cat-file commit "$c" | awk '/^gpgsig/,/END/' > sig.txt
  echo "$(head -1 sig.txt | cut -c8-) ($(wc -c < sig.txt | tr -d ' ') bytes in the commit object)"
done
gpgconf --kill gpg-agent
cd "$base" && rm -rf "$dir" "$GNUPGHOME"
Результат
== signed with SSH
Good "git" signature for alice@example.org with ED25519 key SHA256:7z1vKkx5UJmp2Qrc5ucSDqImz8+r8pfG21roc32f/2A
-----BEGIN SSH SIGNATURE----- (306 bytes in the commit object)
== signed with OpenPGP
gpg:                using EDDSA key 6A4E1CC8308072568C9E240CDDBB1B4BE59BFC00
gpg: Good signature from "Alice <alice@example.org>" [ultimate]
-----BEGIN PGP SIGNATURE----- (279 bytes in the commit object)

Проверка SSH называет принципала из allowed_signers и отпечаток ключа; проверка OpenPGP называет идентификатор пользователя и сообщает уровень доверия из связки ключей. Размеры близки, потому что в обоих случаях внутри одна подпись Ed25519; при этом SSHSIG встраивает открытый ключ целиком, а OpenPGP — только его отпечаток. Проверка SSH-подписей и инспектор OpenPGP декодируют любой из этих блоков из вывода git cat-file commit.

Как решить, чьи подписи принимать

Верная подпись имеет значение, только если проверяющая сторона знает, какой ключ кому принадлежит. В SSH это соответствие задаёт файл allowed_signers: по одной строке на каждого принципала и его ключ, при необходимости с ограничением пространством имён git и периодом действия. Git сравнивает valid-after и valid-before с моментом создания подписи, поэтому выведенный из использования ключ можно оставить в файле с датой окончания, и старые коммиты по-прежнему будут проходить проверку. Строка с cert-authority задаёт доверие всем ключам, которые заверил центр сертификации SSH-сертификатов организации; это удобно компаниям, которые уже выпускают короткоживущие сертификаты SSH. Файл может лежать в репозитории, и тогда его изменения сами проходят ревью и подписываются.

В OpenPGP это соответствие хранится в связке ключей и базе доверия каждой проверяющей стороны, поэтому системы CI и платформы хостинга кода импортируют ключи явно. Сеть доверия может связать незнакомых людей, но за пределами давно сложившихся сообществ ею почти не пользуются. Ключи несут собственные сроки действия и сертификаты отзыва, а подключи позволяют разработчику держать основной ключ офлайн и менять подключи для подписи, не меняя удостоверения.

Платформы добавляют собственный уровень: GitHub и GitLab показывают «Verified», если ключ подписи зарегистрирован в аккаунте, которому принадлежит подтверждённый адрес электронной почты коммитера. Эта отметка зависит от записей платформы; постоянная проверка (persistent verification) в GitHub, которая с ноября 2024 года доступна в предварительной версии, сохраняет результат в момент push, так что последующее удаление ключа его не меняет.

Что когда выбирать

  • Отдельные разработчики на GitHub или GitLab: SSH, в идеале с ключом ed25519-sk на ключе безопасности, чтобы для подписи требовалось касание.
  • Команды и компании: SSH с файлом allowed_signers в репозитории или с сертификатами SSH от уже существующего у вас CA.
  • Проекты, которые проверяют теги исходного проекта (upstream) через OpenPGP (ядро Linux, многие дистрибутивы, релиз-менеджеры с опубликованными ключами): OpenPGP.
  • Коммиты из CI или от ботов: ни тот ни другой долгоживущий ключ; используйте подпись без ключа с короткоживущими сертификатами (например, gitsign из Sigstore, см. Sigstore или GPG) или подписи, которые сама платформа ставит на коммиты ботов.
  • Регулируемые среды со смарт-картами X.509: gpg.format=x509 с инструментом подписи S/MIME, поддерживается начиная с Git 2.19.

Заметки о миграции

  1. Настройте подпись SSH: git config --global gpg.format ssh, в user.signingkey укажите файл открытого ключа (или key:: с самим ключом), а также задайте commit.gpgsign true и tag.gpgsign true.
  2. Зарегистрируйте ключ как ключ подписи на своей платформе; GitHub и GitLab принимают один и тот же ключ и для аутентификации, и для подписи, но отдельный ключ подписи проще менять.
  3. Опубликуйте список разрешённых подписантов в репозитории (например, .gitsigners), укажите его в gpg.ssh.allowedSignersFile в CI и запускайте git verify-commit при слияниях; добавляйте valid-after, когда добавляете ключ, и valid-before, когда выводите его из использования.
  4. Сохраните старые открытые ключи OpenPGP доступными для проверяющих сторон и в аккаунтах на платформах, чтобы коммиты, подписанные до перехода, по-прежнему проходили проверку.
  5. Сделайте подписанные коммиты обязательными в защищённых ветках (наборы правил GitHub, push rules в GitLab), когда все коммитеры перейдут на новую схему.

Источники

Все сравнения