Подпись коммитов и тегов: SSH, GPG и Sigstore

Рейтинг: 67.6% · 8 голосов
Самый подробный курс по Git - от первого коммита до объектной базы и packfiles. Терминал и первый репозиторий, ветки и слияние, конфликты, удалённая работа и форджи (GitHub, GitLab), rebase и переписывание истории, спасение кода через reflog и fsck, stash и worktree, bisect и blame, подмодули и монорепо, хуки и подпись коммитов, внутреннее устройство и масштаб. Для новичков и профи. Актуально на 2026 (Git 2.54, дорожная карта 3.0).
Ответить
Аватара пользователя
Pavel_Git
Сообщения: 52
Зарегистрирован: 11 май 2026, 05:31

Подпись коммитов и тегов: SSH, GPG и Sigstore

Сообщение Pavel_Git »

Оглавление курса (52)
  1. Что такое контроль версий и почему победил Git
  2. Установка Git на Windows, macOS и Linux и первая настройка
  3. Терминал и как спрашивать у Git: выживание в командной строке
  4. Ментальная модель Git: три состояния, коммит-снимок и граф
  5. Первый репозиторий: init, add, commit, status
  6. Просмотр изменений: status, diff и индексация по частям
  7. История проекта: git log, диапазоны и поиск
  8. .gitignore: что не должно попасть в репозиторий
  9. .gitattributes: окончания строк, бинарники и атрибуты
  10. Ветки как указатели: branch, switch и detached HEAD
  11. Слияние веток: fast-forward против трёхстороннего merge
  12. Конфликты слияния: анатомия и уверенное разрешение
  13. Практикум: твой первый день с Git от нуля до push
  14. Удалённые репозитории: remote, fetch, базовый pull и refspec
  15. git push: безопасная отправка и защита от перезаписи
  16. Клонирование: shallow, partial clone, bundle и backfill
  17. GitHub, GitLab и форки: pull request, rulesets и защита веток
  18. Рабочие процессы команды: GitFlow, GitHub Flow, trunk-based
  19. Merge queue и merge trains: безопасное вливание в trunk
  20. Культура code review и Conventional Commits
  21. git reset: три дерева и отмена индексации и коммитов
  22. git revert: безопасная отмена в общей истории
  23. Правка последнего коммита: git commit --amend
  24. git rebase: пересадка истории и золотое правило
  25. Интерактивный rebase: переписать серию коммитов
  26. git cherry-pick: перенос отдельных коммитов
  27. Stacked diffs: цепочка зависимых PR
  28. Массовое переписывание истории: git filter-repo и git replace
  29. git reflog: журнал, который помнит всё
  30. Восстановление утерянных коммитов, веток и файлов
  31. Откат опасных операций: reset --hard, сломанный rebase, плохой merge
  32. git stash: тайник для незавершённой работы
  33. git worktree: несколько рабочих деревьев одного репозитория
  34. git bisect: бинарный поиск регрессии
  35. git blame, mailmap и археология кода
  36. Теги, релизы, archive и bundle
  37. Подмодули: внешние репозитории внутри проекта
  38. Subtree, монорепозитории и sparse-checkout
  39. Git LFS и большие файлы
  40. Вспомогательные команды: clean, mv, rm, restore
  41. Псевдонимы и кастомизация .gitconfig
  42. Хуки Git: автоматизация на клиенте и сервере
  43. Подпись коммитов и тегов: SSH, GPG и Sigstore (вы здесь)
  44. Внутреннее устройство I: объектная база Git
  45. Внутреннее устройство II: ссылки, HEAD, notes и reftable
  46. Внутреннее устройство III: индекс, packfiles, gc и обслуживание
  47. От SHA-1 к SHA-256: переход на новый хеш
  48. Огромные репозитории: partial clone, FSMonitor, Scalar
  49. Git в CI/CD: shallow, кеш, merge_group и безопасность
  50. Куда движется Git: дорожная карта 3.0 и экосистема
  51. Профессиональный setup: собираем рабочее окружение Git
  52. 30+ типичных ошибок Git и как из них выбираться
Зачем вообще подписывать коммиты

Открой любой свой коммит и посмотри, кто его автор. Имя и почта в полях author и committer - это просто строки, которые Git берёт из твоего user.name и user.email. Никакой проверки. Я прямо сейчас могу настроить себе чужое имя и закоммитить от лица твоего тимлида:

Код: Выделить всё

git config user.name "Linus Torvalds"
git config user.email "torvalds@linux-foundation.org"
git commit -m "fix: critical security patch"
И в git log это будет выглядеть абсолютно настоящим коммитом Линуса. Это называется спуфинг имени автора, и на публичных репозиториях это реальный вектор атаки: подсунуть в цепочку коммитов вредоносную правку с именем человека, которому доверяют ревьюеры. Git подпись коммитов решает ровно эту проблему - она криптографически привязывает коммит к владельцу ключа, и подделать её, не имея приватного ключа, нельзя.

Второй мотив - требование форджей и регуляторов. GitHub и GitLab показывают бейдж Verified рядом с подписанными коммитами, а во многих компаниях правило ветки прямо требует, чтобы в main попадали только verified commit. В supply-chain-аудитах (SLSA, требования заказчиков) подпись - часть чек-листа. Так что подпись - это не паранойя сеньоров, а гигиена 2026 года.

Важно сразу понять границы. Подпись доказывает: "этот коммит создан владельцем ключа K". Она НЕ доказывает, что код хороший, что человек реально тот, за кого себя выдаёт в паспорте, и не шифрует ничего. Это аутентификация авторства плюс защита целостности (изменишь хоть байт в дереве - подпись разъедется), не более.

Изображение

Три способа подписи и почему SSH стал дефолтом 2026

Исторически в Git был один путь - GPG. Он работает, но болит: генерация ключа с правильным типом, экспирация, web of trust, демон gpg-agent, который то спит, то не находит pinentry, ключи в отдельном keyring. Куча людей бросали подпись именно на этапе "почему gpg висит и ничего не спрашивает".

В Git 2.34 (конец 2021) появилась git ssh подпись - подпись тем же SSH-ключом, которым ты и так пушишь. С тех пор это де-факто рекомендованный дефолт, а к 2026 - просто здравый смысл для большинства. Логика железная: ключ у тебя уже есть, агент уже настроен, форджи умеют его верифицировать. Раскладка по трём вариантам такая:
  • SSH-подпись - дефолт 2026. Ключ уже есть, настройка за 5 минут, ноль возни с keyring. Берёшь её, если нет причины не брать.
  • GPG - легаси и enterprise. Жив там, где уже выстроена корпоративная PKI, smartcard/YubiKey в GPG-режиме, политики на GPG. Новый проект с нуля на GPG в 2026 заводить смысла мало.
  • Sigstore/gitsign - keyless OIDC-подпись для CI и ботов, где нельзя держать долгоживущий приватный ключ на раннере.
Технически все три кладут блоб подписи в заголовок объекта-коммита (поле gpgsig), просто формат подписи разный. Поэтому git log --show-signature един для всех, а различается только бэкенд, который задаётся ключом gpg.format.

Практический SSH-setup за 5 минут

Сначала ключ. Если у тебя уже есть ~/.ssh/id_ed25519, которым ты пушишь - можно подписывать им же. Хочешь отдельный ключ под подпись (хорошая практика - разделить аутентификацию и подпись) - сгенерь новый:

Код: Выделить всё

ssh-keygen -t ed25519 -C "signing key" -f ~/.ssh/git_sign_ed25519
Теперь говорим Git: формат подписи - ssh, ключ - публичная часть, подписывай коммиты и теги автоматически:

Код: Выделить всё

git config --global gpg.format ssh
git config --global user.signingkey ~/.ssh/git_sign_ed25519.pub
git config --global commit.gpgsign true
git config --global tag.gpgsign true
Обрати внимание: в user.signingkey для SSH указывается путь к публичному .pub-файлу (или сам публичный ключ строкой). Это нормально - приватный ключ Git возьмёт через ssh-agent или по парному пути. Делаем тестовый коммит:

Код: Выделить всё

git commit --allow-empty -m "test: signed commit"
Если коммит прошёл без ошибок - подпись уже встроена. Проверим, что внутри:

Код: Выделить всё

git log --show-signature -1

Код: Выделить всё

commit 4f1c2a9... (HEAD -> main)
Good "git" signature with ED25519 key SHA256:3xY...
Author: Ivan <ivan@example.com>
Date:   Sun Jun 15 12:40:11 2026 +0300

    test: signed commit
Видишь Good "git" signature - подпись на месте. Но тут засада, на которой спотыкаются все.

allowedSignersFile: без него проверка не работает

SSH-подпись по своей природе не знает, какому человеку принадлежит ключ - это просто пара ключей, без удостоверяющего центра, как в GPG/PKI. Чтобы Git мог сказать не просто "подпись валидна", а "подпись валидна И принадлежит ivan@example.com", ему нужен список доверенных подписантов - allowedSignersFile. Без него ты увидишь:

Код: Выделить всё

error: gpg.ssh.allowedSignersFile needs to be configured and exist
       for ssh signature verification
Заводим файл. Формат строки: почта (или паттерн), затем тип и сам публичный ключ - буквально содержимое .pub:

Код: Выделить всё

mkdir -p ~/.config/git
echo "ivan@example.com $(cat ~/.ssh/git_sign_ed25519.pub)" \
  >> ~/.config/git/allowed_signers
git config --global gpg.ssh.allowedSignersFile ~/.config/git/allowed_signers
Теперь повторяем git log --show-signature -1 и получаем полноценную проверку с именем:

Код: Выделить всё

Good "git" signature for ivan@example.com with ED25519 key SHA256:3xY...
Слово for ivan@example.com - вот ради чего всё затевалось: Git подтвердил, что подпись сделана ключом, который ты явно объявил принадлежащим этому адресу. В команде этот файл обычно держат в самом репозитории (например .github/allowed_signers) и указывают на него относительным путём, чтобы CI и коллеги верифицировали по общему списку. Точечная проверка одного объекта - отдельными командами:

Код: Выделить всё

git verify-commit HEAD
git verify-tag v1.0.0
Они возвращают 0 при валидной подписи и ненулевой код при провале - удобно дёргать в скриптах и хуках.

Verified-бейдж на GitHub и GitLab

Локальная проверка - это про твою машину. Чтобы получить verified commit github, форджу нужно знать твой публичный ключ. Шаги:
  • GitHub: Settings -> SSH and GPG keys -> New SSH key, и в типе выбрать Signing Key (не Authentication!). Это критично: один и тот же ключ можно добавить дважды - отдельно как auth, отдельно как signing.
  • GitLab: Preferences -> SSH Keys, при добавлении выбрать usage type Signing (или Authentication & Signing).
После этого подписанные коммиты получают зелёный бейдж Verified. Если видишь Unverified - почти всегда одно из двух: ключ не загружен как signing, или почта в коммите не совпадает с верифицированной почтой аккаунта. Git проверяет ключ, форж дополнительно сверяет, что email коммита привязан к твоему профилю.

GPG как легаси-путь

Если на проекте уже принят git gpg - формат тот же, бэкенд другой. Коротко, чтобы ты узнал это в чужих конфигах. Смотрим/генерим ключ и берём его long id:

Код: Выделить всё

gpg --list-secret-keys --keyid-format=long
gpg --full-generate-key
Настройка симметрична SSH, но gpg.format не трогаем (gpg - дефолтный формат), а в user.signingkey кладём GPG key id:

Код: Выделить всё

git config --global user.signingkey 3AA5C34371567BD2
git config --global commit.gpgsign true
Подписать конкретный коммит вручную - флаг git commit -S (заглавная S). Если включён commit.gpgsign, -S не нужен, он подразумевается; -S пригождается для разовой подписи там, где автоподпись выключена, а --no-gpg-sign - наоборот, отключить для одного коммита. Грабли GPG почти всегда про агента: если подпись висит или валится с "cannot open tty", добавь в окружение export GPG_TTY=$(tty), а в ~/.gnupg/gpg.conf - use-agent. На сервере/в неинтерактивной среде подпись GPG требует, чтобы агент мог достать пароль без терминала, и это вечный источник боли - ещё одна причина, почему SSH в 2026 приятнее.

Для форжа публичный GPG-ключ экспортируется так и вставляется в Settings -> SSH and GPG keys -> New GPG key:

Код: Выделить всё

gpg --armor --export 3AA5C34371567BD2
Sigstore и gitsign: подпись без ключей для CI

Отдельная история - боты и пайплайны. Держать долгоживущий приватный ключ подписи на CI-раннере опасно: утёк раннер - утёк ключ, и им можно подписывать что угодно месяцами. Решение - gitsign sigstore, keyless-подпись.

Идея такая. Раннер аутентифицируется по OIDC (короткоживущий identity-token от GitHub Actions, Google и т.п.). Этот токен уходит в Fulcio - центр сертификации Sigstore, который выдаёт сертификат, живущий минуты, привязанный к OIDC-личности (например к workflow-идентичности репозитория). Этим эфемерным сертификатом подписывается коммит, а сам факт подписи плюс сертификат записываются в публичный прозрачный журнал Rekor. Сертификат протух через минуты - но запись в Rekor вечна, и по ней подпись проверяется и спустя год: "в такой-то момент эта личность подписала этот коммит". Долгоживущих секретов на раннере - ноль.

Ставится gitsign отдельно (это не часть Git), а подключается через тот же механизм форматов - как кастомный gpg-program:

Код: Выделить всё

git config --global gpg.x509.program gitsign
git config --global gpg.format x509
git config --global commit.gpgsign true
Проверка - gitsign verify с указанием ожидаемой identity. Ключевая мысль для практики: SSH/GPG - для людей с их машинами, Sigstore - для машин и автоматики, где принцип "нет ключа - нечего красть" важнее всего.

Типичные грабли
  • Подписал, а форж пишет Unverified - почта коммита не совпадает с почтой, привязанной к аккаунту, либо ключ загружен как auth, а не как signing.
  • error про allowedSignersFile - забыл создать файл или указать на него; на SSH-подписи это обязательный шаг для верификации.
  • Rebase/cherry-pick/amend пересоздают коммиты, и старые подписи теряются - новые коммиты подпишутся заново только если включён commit.gpgsign. После переписывания истории проверяй, что всё осталось подписанным.
  • Слияния и коммиты, сделанные через веб-интерфейс или ботом без подписи, разрывают цепочку verified - учитывай это в правилах ветки.
  • Указал в user.signingkey приватный ключ вместо .pub для SSH - бывает работает, но правильно и переносимо указывать публичную часть.
  • GPG висит без запроса пароля - проблема gpg-agent/GPG_TTY, а не Git.
Мини-лаба: подпиши свой первый коммит

Повтори руками по шагам, это минут десять:
  • Сгенерь ключ подписи: ssh-keygen -t ed25519 -f ~/.ssh/git_sign_ed25519 -C "signing".
  • Настрой Git: gpg.format ssh, user.signingkey на .pub, commit.gpgsign true, tag.gpgsign true (команды выше).
  • Сделай git commit --allow-empty -m "signed" и подписанный тег: git tag -s v0.0.1 -m "first signed tag".
  • Создай ~/.config/git/allowed_signers со строкой "своя-почта содержимое-pub" и пропиши gpg.ssh.allowedSignersFile.
  • Проверь: git log --show-signature -1 и git verify-tag v0.0.1 - убедись, что есть Good signature for твоя-почта.
  • Загрузи .pub на GitHub как Signing Key, запушь и найди бейдж Verified.
Контрольные вопросы
  • Что именно доказывает подпись коммита, а что НЕ доказывает?
  • Почему для SSH-подписи без allowedSignersFile верификация падает, а для GPG такого файла не нужно?
  • В чём принципиальная разница между ключом, добавленным на GitHub как Authentication, и как Signing?
  • Почему для CI выгоднее keyless-подпись Sigstore, чем положить приватный ключ в секреты раннера?
Итог

Имя автора в коммите - недоверенная строка, и подпись превращает её в проверяемый факт. В 2026 дефолт - SSH-подпись: gpg.format=ssh, user.signingkey, commit.gpgsign=true, плюс allowedSignersFile для верификации. GPG оставляем легаси и enterprise-инфраструктуре, а Sigstore/gitsign берём для CI и ботов, где долгоживущий ключ - это риск. Загрузи публичный ключ на форж, получи Verified - и твоя история перестанет быть подделываемой.
👍2 ❤️3 🔥1 😄 🤔5
Аватара пользователя
master_petr
Сообщения: 1
Зарегистрирован: 17 май 2026, 10:46

Re: Подпись коммитов и тегов: SSH, GPG и Sigstore

Сообщение master_petr »

Поймал ту самую ошибку про allowedSignersFile, думал что-то сломал. Оказалось просто файл не создал. Спасибо, без этого урока тупил бы долго.
👍 ❤️ 🔥 😄 🤔
Аватара пользователя
therudeone
Сообщения: 1
Зарегистрирован: 29 май 2026, 02:10

Re: Подпись коммитов и тегов: SSH, GPG и Sigstore

Сообщение therudeone »

А если у меня уже один id_ed25519 для пуша - его реально можно тем же использовать для подписи, или лучше отдельный завести? Склоняюсь к отдельному после прочтения.
👍 ❤️1 🔥 😄 🤔
Ответить
← Предыдущая глава
Хуки Git: автоматизация на клиенте и сервере
Следующая глава →
Внутреннее устройство I: объектная база Git

Все главы курса «Git профессионально: от первого коммита до внутреннего устройства»

Поделиться темой: ✈ Telegram VK
Похожие запросы: git commit: фиксация измененийgit tag: теги и релизыgit blame: кто изменил строкуПодпись коммитов в Git (SSH, GPG)git cherry-pick: перенос коммитовGit hooks: хуки и автоматизация

Вернуться в «Git профессионально: от первого коммита до внутреннего устройства»

Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и 2 гостя