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) | |
|---|---|---|
| Поддержка в Git | Git 2.34 (ноябрь 2021 года), gpg.format=ssh | git 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 отключена, так что на вашей машине ничего не меняется.
#!/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.
Заметки о миграции
- Настройте подпись SSH:
git config --global gpg.format ssh, вuser.signingkeyукажите файл открытого ключа (илиkey::с самим ключом), а также задайтеcommit.gpgsign trueиtag.gpgsign true. - Зарегистрируйте ключ как ключ подписи на своей платформе; GitHub и GitLab принимают один и тот же ключ и для аутентификации, и для подписи, но отдельный ключ подписи проще менять.
- Опубликуйте список разрешённых подписантов в репозитории (например,
.gitsigners), укажите его вgpg.ssh.allowedSignersFileв CI и запускайтеgit verify-commitпри слияниях; добавляйтеvalid-after, когда добавляете ключ, иvalid-before, когда выводите его из использования. - Сохраните старые открытые ключи OpenPGP доступными для проверяющих сторон и в аккаунтах на платформах, чтобы коммиты, подписанные до перехода, по-прежнему проходили проверку.
- Сделайте подписанные коммиты обязательными в защищённых ветках (наборы правил GitHub, push rules в GitLab), когда все коммитеры перейдут на новую схему.
Источники
- Документация Git по git-config: параметры gpg.format, gpg.ssh.allowedSignersFile и gpg.ssh.revocationFile
- Примечания к выпуску Git 2.34
- Примечания к выпуску OpenSSH 8.1 и руководство ssh-keygen, раздел ALLOWED SIGNERS
- OpenSSH PROTOCOL.sshsig
- RFC 9580, OpenPGP
- Журнал изменений GitHub: проверка коммитов, подписанных SSH
- GitLab: подпись коммитов и тегов ключами SSH