Подпись коммитов: SSH signing вместо GPG
В предыдущем уроке мы настроили SSH для аутентификации при push. Это решает «может ли этот клиент пушить в репозиторий». Подпись коммитов решает другую задачу — «действительно ли этот коммит написан тем, кто заявлен в Author»? В этом уроке разберём, зачем это нужно и как настраивать.
После урока у вас будут подписанные коммиты, GitHub будет показывать на них «Verified» зелёным значком, и вы будете понимать, как защититься от impersonation-атак в open-source.
Зачем подписывать коммиты
В Git поле Author коммита — это просто строка. Никаких проверок. Я могу прямо сейчас сделать коммит «от имени Linus Torvalds»:
git -c user.name="Linus Torvalds" \
-c user.email="[email protected]" \
commit -m "Look, mom, I'm Linus"
И в git log этот коммит будет числиться от Linus. Если я запушу его на GitHub в свой публичный репозиторий — в истории будет «Linus Torvalds wrote this code». Это и есть impersonation problem: ничто в Git не гарантирует, что автор реален.
Для open-source это реальная угроза. Например, злоумышленник может:
- Сделать PR в популярный репозиторий под именем известного мейнтейнера
- Включить в PR коммиты с вредоносным кодом, помеченные «от имени» доверенного контрибьютора
- Невнимательный мейнтейнер замержит, думая, что коммиты — от своих
Подпись коммитов решает это. Подписанный коммит подтверждается криптографически: только владелец приватного ключа мог его создать. GitHub проверяет подпись по публичному ключу, опубликованному в профиле — и показывает «Verified».
Verified-коммиты на GitHub
Если открыть commit на GitHub, рядом с автором будет один из значков:
Чем хорош Verified-значок:
- В open-source: показывает, что коммиты от настоящего автора
- В энтерпрайз-командах: некоторые компании требуют, чтобы все коммиты были verified (политики через branch protection)
- В вашем профиле: красивые зелёные значки в commit history
Современный Git предлагает два способа подписи: SSH signing (новое, Git 2.34+) и GPG (классическое). Разберём оба, начнём с SSH — это проще.
SSH signing: используем уже сгенерированный ключ
Самый красивый подход: использовать тот же SSH-ключ, что и для аутентификации. Один ключ — две задачи: и push, и подпись. Не нужно генерировать ничего нового.
Эта возможность появилась в Git 2.34 (ноябрь 2021), в GitHub поддерживается с августа 2022. На май 2026 — стабильная, проверенная фича.
Настройка SSH signing — пошагово
1. Настроить Git использовать SSH для подписи
git config --global gpg.format ssh
git config --global user.signingkey ~/.ssh/id_ed25519.pub
Первая команда: «формат подписи — ssh» (по умолчанию openpgp).
Вторая: «ключ для подписи — публичный ключ из ~/.ssh/».
2. Включить автоподпись коммитов
git config --global commit.gpgsign true
git config --global tag.gpgsign true
С этого момента каждый ваш git commit будет подписан. git tag -a тоже.
Названия настроек называются gpgsign (legacy от GPG), но они работают и для SSH — параметр gpg.format определяет реальный формат подписи.
3. Указать список «доверенных подписантов»
Локально Git тоже проверяет подписи (например, при git log --show-signature). Для этого ему нужен список «кому я доверяю». Создаём файл:
mkdir -p ~/.config/git
echo "$(git config user.email) $(cat ~/.ssh/id_ed25519.pub)" > ~/.config/git/allowed_signers
git config --global gpg.ssh.allowedSignersFile ~/.config/git/allowed_signers
Файл allowed_signers имеет формат: одна строка на ключ, формат <email> <key>. Например:
[email protected] ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGxV6...
4. Загрузить публичный ключ на GitHub как Signing Key
На GitHub: Settings -> SSH and GPG keys -> New SSH key
- Title: «Signing key — MacBook Pro M3»
- Key type: Signing Key (важно — не Authentication!)
- Key: содержимое
~/.ssh/id_ed25519.pub
GitHub различает Authentication Keys (для push) и Signing Keys (для проверки подписи коммитов). ОДИН и ТОТ ЖЕ публичный ключ можно добавить с разными типами — Git будет использовать его и для push, и для signing. Просто добавьте его дважды: один раз как Authentication, второй — как Signing.
5. Проверка
Сделайте коммит в любом репозитории:
cd ~/git-sandbox
mkdir test-signing && cd test-signing
git init
echo "test" > file.txt
git add file.txt
git commit -m "Test signed commit"
Посмотрите, что коммит подписан:
git log --show-signature
# commit a1b2c3d4...
# Good "git" signature for [email protected] with ED25519 key SHA256:...
# Author: Ivan Petrov <[email protected]>
# ...
Запушите в GitHub — рядом с коммитом появится зелёный «Verified».
GPG signing (классический подход)
Если SSH signing у вас не работает (старый Git, специфичный сервер), или вы хотите более «классический» путь — используйте GPG. Это сложнее в настройке, но универсально.
Установка GPG
# macOS
brew install gnupg
# Ubuntu/Debian
sudo apt install -y gnupg
# Windows: входит в Git for Windows
Генерация ключа
gpg --full-generate-key
Программа задаст серию вопросов:
- Kind of key: (9) ECC (sign and encrypt) (быстрый и современный)
- Elliptic curve: (1) Curve 25519 (тот же ed25519 что и для SSH)
- Valid for: 0 (никогда не истекает) или например
2y(2 года) - Real name: ваше имя (должно совпадать с user.name в Git)
- Email: ваш email (должен совпадать с user.email в Git и быть в GitHub)
- Comment: можно оставить пустым
- Passphrase: пароль для приватного ключа (рекомендуется)
После генерации:
gpg --list-secret-keys --keyid-format=long
# /Users/ivan/.gnupg/pubring.kbx
# sec ed25519/3AA5C34371567BD2 2026-05-13 [SC]
# ABCD1234...
# uid [ultimate] Ivan Petrov <[email protected]>
Ваш Key ID — 3AA5C34371567BD2. Запомните.
Настройка Git на GPG
git config --global gpg.format openpgp
git config --global user.signingkey 3AA5C34371567BD2
git config --global commit.gpgsign true
Экспорт публичного ключа
gpg --armor --export 3AA5C34371567BD2
# -----BEGIN PGP PUBLIC KEY BLOCK-----
# ...многострочный текст...
# -----END PGP PUBLIC KEY BLOCK-----
Скопируйте всё (включая BEGIN/END строки) и добавьте на GitHub: Settings -> SSH and GPG keys -> New GPG key.
Проверка из командной строки
git log --show-signature -1
Покажет последний коммит с информацией о подписи. Что вы должны увидеть:
commit 1a2b3c4d...
gpg: Signature made Tue May 13 15:30:21 2026 +0300
gpg: using EDDSA key ABCD1234...
gpg: Good signature from "Ivan Petrov <[email protected]>" [ultimate]
Или для SSH:
commit 1a2b3c4d...
Good "git" signature for [email protected] with ED25519 key SHA256:abc...
«Good signature» — это успех.
Когда подпись срывается
gpg failed to sign the data
Самая частая ошибка GPG. Причины:
gpg-agentне запущен. На macOS поставьтеpinentry-mac:
brew install pinentry-mac
echo "pinentry-program /opt/homebrew/bin/pinentry-mac" >> ~/.gnupg/gpg-agent.conf
killall gpg-agent
GPG_TTYне настроен. Добавьте в.bashrc/.zshrc:
export GPG_TTY=$(tty)
- Ключ истёк. Проверьте:
gpg --list-secret-keys.
Подпись «Unverified» на GitHub
- Проверьте, что email коммита совпадает с email, к которому привязан ключ
- Проверьте, что email подтверждён в GitHub Account -> Emails
- Проверьте, что ключ добавлен как Signing Key (не только Authentication)
- Для GPG: проверьте, что вы добавили ВЕСЬ блок включая BEGIN/END
Подпись существующих коммитов (postfactum)
Если коммит уже сделан без подписи, переподписать его:
# Подписать последний коммит
git commit --amend --no-edit -S
# Подписать N последних коммитов
git rebase -i HEAD~N
# В редакторе изменить pick -> reword (или edit) для каждого
# Дальше для каждого: git commit --amend --no-edit -S; git rebase --continue
Внимание: Если коммиты уже запушены — переподпись меняет SHA, и git push потребует --force. Это плохо для shared веток. Подробно про переписывание истории — модуль 8.
Лучшая практика: настроить commit.gpgsign true с самого начала, и не думать.
Стоит ли подписывать каждый коммит?
Это компромисс:
Моё мнение как автора курса: да, стоит. Один раз настроить SSH signing — это 5 минут. Дальше всё работает. Защита от impersonation бесплатна. Verified-значки на коммитах — хороший профессиональный сигнал.
В крупных open-source проектах (Linux kernel, Kubernetes, etc.) подпись обязательна для core-мейнтейнеров. В корпоративных командах через 1-2 года эта практика тоже распространяется.
Попробуй сам
- Если у вас есть SSH-ключ (из предыдущего урока), настройте SSH signing:
git config --global gpg.format ssh
git config --global user.signingkey ~/.ssh/id_ed25519.pub
git config --global commit.gpgsign true
mkdir -p ~/.config/git
echo "$(git config user.email) $(cat ~/.ssh/id_ed25519.pub)" \
> ~/.config/git/allowed_signers
git config --global gpg.ssh.allowedSignersFile ~/.config/git/allowed_signers
-
Добавьте
id_ed25519.pubна GitHub как Signing Key. -
Сделайте тестовый коммит и проверьте:
cd ~/git-sandbox
mkdir test-sign && cd test-sign
git init
echo "test" > f.txt
git add . && git commit -m "Signed test"
git log --show-signature
- Если есть удалённый репо — запушьте и посмотрите Verified-значок на GitHub.
Управление секретами: vault, env-переменные, ротация