Подпись коммитов в Git: как и зачем это делать

Перевод статьи «What is Commit Signing in Git?».

В Git есть функция подписи коммитов. Но что это за подпись и какие преимущества она дает?

Photo by Felicity Tai from Pexels

TL;DR: Если вас не интересуют детали и вам просто нужно быстро настроить подпись коммитов, перейдите к разделу «Как подписывать коммиты в Git».

Подпись (в частности — подпись кода) представляет собой процесс использования шифрования для добавления цифровой подписи к данным. Получатель данных может проверить достоверность подписи и убедиться, что послание пришло именно от указанного отправителя.

В общем, это похоже на обычную физическую подпись, но цифровая надежнее.

Как Git ведет себя по умолчанию

Прежде всего отметим, что все коммиты имеют следующие свойства:

  • author (автор) — контрибьютор, выполнивший работу (указывается для справки).
  • committer (коммитер) — пользователь, который закоммитил изменения.

В большинстве случаев автор и коммитер — один и тот же человек, но эти свойства можно переопределить при совершении коммита, поэтому важно понимать разницу.

При первоначальной установке Git вам, вероятно, пришлось настроить несколько параметров, а именно — user.email и user.name. Возможно, это было сделано за вас (это зависит от вашего клиента Git).

В командной строке для установки этих параметров необходимо выполнить следующие команды:

git config --global user.email "seth@example.org"
git config --global user.name "Seth Falco"

Коммиты Git основаны на доверии, поэтому предполагается, что вы указали свой реальный адрес электронной почты и свое имя. После установки этих параметров вы сможете делать коммиты, а затем отправлять изменения в удаленный репозиторий на GitHub или GitLab с указанными сведениями.

А что будет, если кто-то другой укажет ваш адрес электронной почты, а затем запушит изменения удаленно?

git config --global user.email "seth@example.org"
git commit -m "Jen did this."
git push origin main

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

По умолчанию и автор, и коммитер указываются в соответствии с параметрами, заданными в git config.

На GitHub этот коммит уже неотличим от моего. Если пользователь установит мой почтовый адрес как user.email, а мое имя — как user.name, то даже локально будет выглядеть, будто это мой коммит. Мое имя и адрес моей почты легко найти, выполнив git log любого моего коммита.

Это означает, что любой человек может установить у себя в качестве user.email ваш адрес электронной почты, и будет выглядеть так, как будто это вы совершили коммит.

Почему Git ведет себя именно так?

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

Дело в том, что, проходя аутентификацию для отправки изменений в удаленный репозиторий, вы аутентифицируетесь только для этого — для пуша изменений. Коммиты не требуют аутентификации независимо от того, кто их автор или коммитер.

Если бы обязательная аутентификация выполнялась по умолчанию, было бы невозможно переносить или зеркалить проекты на другие платформы. История коммитов включала бы имена бывших сотрудников, умерших пользователей, неактивные аккаунты и email-адреса, отсутствующие на других платформах.

Единственным решением было бы переписывание истории, чтобы убрать информацию о том, что все эти люди когда-либо работали над проектом, а это тоже не идеальный вариант.

Или вот другой сценарий. Допустим, я сделал форк проекта на GitHub, но хочу поддерживать этот форк на GitLab. Мой первый push будет включать все коммиты от предыдущих коммитеров. И если проект большой, аутентифицировать каждого из них будет нереально.

Следует помнить о том, что author в коммите — лишь указание на автора, а не доказательство того, что работу выполнил именно этот человек.

Фактически, вы всегда можете переопределить автора, когда делаете коммит. Используя аргумент --author, можно указать имя и адрес электронной почты, отличающиеся от глобальных настроек. Причем эти данные могут быть не связаны с аккаунтом, в котором размещен репозиторий.

Однако в публичных репозиториях, делая коммит от имени человека без учетной записи, следует быть внимательным. После отправки коммита имена и адреса электронной почты становятся общедоступной информацией, которую можно просматривать при помощи git log!

git commit -m "Jen didn't even author this." --author "Jen jen@example.org"
git push origin main

Здесь поведение Git будет отличаться от варианта с использованием чужого адреса электронной почты в git config. Автором будет значиться человек, указанный при помощи —author, а коммитером — человек, указанный в git config.

Платформы переводов, такие как Weblate, используют эту функцию для указания имен переводчиков, даже если коммиты и пул-реквесты делает автоматизированный пользователь, а не переводчик.

Как доказать, что коммитер — это вы

GNU Privacy Guard (GnuPG или GPG) позволяет создавать асимметричные пары ключей, которые можно использовать для шифрования и подписи данных. Каждая пара состоит из открытого и закрытого ключа.

Открытым ключом можно поделиться с кем угодно. Вы можете загрузить его в свои учетные записи GitHub и GitLab или разместить в Интернете, чтобы любой мог получить к нему доступ.

Закрытый ключ, как следует из названия, является приватным. Считайте его чем-то вроде пароля и ни при каких обстоятельствах никому его не передавайте.

Чтобы рассказать, как ключи применяются на практике, мы сгенерируем пару ключей, а затем загрузим открытый ключ в GitHub и GitLab. Используя свой закрытый ключ, вы можете подписывать свои коммиты, а при помощи открытого ключа серверы смогут убедиться, что подпись действительно ваша.

Как подписывать коммиты в Git

В этой статье я рассмотрю, как все делается через терминал, поскольку этот метод подойдет для любой операционной системы. Если вы не привыкли работать с терминалом, можете просто копировать команды.

Предварительные условия

Единственным предварительным условием, кроме наличия самого Git, является установка утилиты командной строки GPG.

Вы можете проверить, установлена ли она, с помощью команды gpg --version.

Windows

Git BASH

Если у вас установлен Git BASH (возможно, в комплекте с Git for Windows), то у вас уже есть доступ к GPG. Просто запустите экземпляр Git BASH, и утилита сразу станет доступна.

Gpg4win

Если у вас нет Git BASH, устанавливать его не нужно. Вы можете установить Gpg4win, который предоставит GPG глобально, так что вы сможете просто использовать его из PowerShell.

При установке Gpg4win можно отключить все дополнительные компоненты: они нам не понадобятся, так как мы планируем использовать терминал.

Если у вас уже был открыт PowerShell, вам придется перезапустить его, прежде чем вы сможете использовать GPG.

Linux

Ваш дистрибутив, скорее всего, уже включает GPG. Если нет, эту утилиту можно установить через менеджер пакетов.

apt (Debian / Ubuntu)

sudo apt install gnupg

pacman (Arch / Manjaro)

sudo pacman -S gnupg

Как сгенерировать GPG-ключи

Если у вас уже есть GPG-ключ, вы можете пропустить этот шаг. Повторно использовать ключи GPG совершенно нормально. Просто убедитесь, что ваш ключ совместим с Git и GitHub.

Список имеющихся GPG-ключей можно получить с помощью команды:

gpg --list-keys

Сначала нам нужно сгенерировать пару RSA-ключей. Следующая команда запустит интерактивный скрипт, который будет задавать вопросы, чтобы мы могли предоставить необходимую информацию.

gpg --full-gen-key
  1. Для указания того, какой тип ключа вы хотите, введите 1. Это соответствует «RSA and RSA».
  2. В качестве размера ключа введите 4096. Это минимальный размер для GitHub и GitLab и максимальный размер, который GPG позволяет нам сгенерировать.
  3. Сколько должен прослужить ключ, вы определяете самостоятельно. Значение по умолчанию — 0 (срок действия не истекает никогда).
  4. Подтвердите указанные данные, введя y.

GPG запросит личную информацию, которая сохранится в вашем ключе:

  1. Ваше имя (не менее 5 символов).
  2. Ваш адрес электронной почты. Вводите email, который вы планируете использовать. Этот адрес нужно будет подтвердить в удаленном аккаунте, из которого будете пушить свои изменения.
  3. Комментарий. Можно ввести что угодно или нажать клавишу ввода, чтобы оставить это поле пустым.
  4. Подтвердите указанные данные, введя o.
root@799d1cc3c99c:/# gpg --full-gen-key
gpg (GnuPG) 2.2.19; Copyright (C) 2019 Free Software Foundation, Inc.
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.

Please select what kind of key you want:
   (1) RSA and RSA (default)
   (2) DSA and Elgamal
   (3) DSA (sign only)
   (4) RSA (sign only)
  (14) Existing key from card
Your selection? 1
RSA keys may be between 1024 and 4096 bits long.
What keysize do you want? (3072) 4096
Requested keysize is 4096 bits
Please specify how long the key should be valid.
         0 = key does not expire
      <n>  = key expires in n days
      <n>w = key expires in n weeks
      <n>m = key expires in n months
      <n>y = key expires in n years
Key is valid for? (0) 0
Key does not expire at all
Is this correct? (y/N) y

GnuPG needs to construct a user ID to identify your key.

Real name: Seth Falco
Email address: seth@example.org
Comment: 
You selected this USER-ID:
    "Seth Falco <seth@example.org>"

Change (N)ame, (C)omment, (E)mail or (O)kay/(Q)uit? o

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

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

После этого начнется генерация ключа, для которой требуется много случайно сгенерированных данных. Выполнение действий на вашем компьютере в этой время поможет сделать ключ более случайным, поэтому я бы рекомендовал поводить мышкой во время генерации ключа.

Как экспортировать ключи

Затем вам нужно получить идентификатор только что сгенерированного ключа, чтобы можно было ссылаться на него при экспорте этого ключа и настройке Git.

К GPG-ключам можно обращаться по-разному. Хорошая привычка — использовать и передавать контрольную сумму целиком, чтобы минимизировать риск двусмысленности при запросе с сервера ключей. Длинные (64-битные) идентификаторы пока подходят, но коротких (32-битных) идентификаторов лучше избегать, так как легко вызвать коллизию. (Больше об этом здесь).

Мы будем использовать полную контрольную сумму GPG, которую можно получить с помощью команды:

gpg --list-keys

Вы получите следующий результат:

pub   rsa4096 2021-05-23 [SC]
      C6656513A0F9B7B7F4E76389EF39187D04795745
uid           [ultimate] Seth Falco <seth@example.org>
sub   rsa4096 2021-05-23 [E]

У меня это C6656513A0F9B7B7F4E76389EF39187D04795745. Обязательно используйте свою контрольную сумму вместо моей при выполнении остальных команд.

Чтобы загрузить открытый ключ на GitHub, нужно его экспортировать. Мы используем аргумент --armor, чтобы указать, что мы хотим экспортировать ключ в ASCII-армированном формате вместо двоичного. Следующая команда запишет открытый ключ в файл с именем gpg-key.pub.

gpg --export --armor C6656513A0F9B7B7F4E76389EF39187D04795745 > ./gpg-key.pub

Как сделать резервную копию ключей

Стоит иметь удаленную резервную копию GPG-ключей, потому что вы, вероятно, будете использовать их в разных сервисах. И если вы потеряете свой ключ, будет сложно все обновить.

Закрытый ключ можно экспортировать так же, как мы экспортировали открытый, при этом закрытый ключ записывается в файл с именем gpg-key.asc:

gpg --export-secret-keys --armor C6656513A0F9B7B7F4E76389EF39187D04795745 > ./gpg-key.asc

Теперь можно сделать бэкап открытого и закрытого ключа. Помните, что ни в коем случае не следует отправлять незашифрованную копию закрытого ключа в облако. Для резервного копирования конфиденциальных данных используйте облачное хранилище со сквозным шифрованием или менеджер паролей, например Bitwarden.

Как включить подпись коммитов

Чтобы включить подпись всех коммитов, установите параметр commit.gpgsign с помощью git config. Это заставит git commit подписывать коммиты по умолчанию.

git config --global commit.gpgsign true

Если у вас есть несколько GPG-ключей, также можно установить user.signingkey. (Это можно сделать и просто на будущее, даже если у вас пока только одна пара ключей). Таким образом вы укажете, какой именно ключ Git должен использовать для подписи, чтобы избежать двусмысленности.

git config --global user.signingkey C6656513A0F9B7B7F4E76389EF39187D04795745

Как использовать свой ключ

Наконец, нужно загрузить свой открытый ключ. Вы можете использовать один и тот же GPG- ключ как для GitHub, так и для GitLab или любого другого Git-провайдера.

Для следующих шагов нам понадобится экспортированный открытый ключ, поэтому откройте файл gpg-key.pub в любом редакторе, например Visual Studio Code, и скопируйте его содержимое в буфер обмена.

На GitHub зайдите в свои настройки, в раздел «SSH and GPG keys», и нажмите «New GPG key». Вставьте содержимое gpg-key.pub в поле «Key» и нажмите «Add GPG key».

В GitLab шаги практически идентичны, просто зайдите в настройки, затем в «GPG Keys». Вставьте содержимое gpg-key.pub в поле «Key» и нажмите «Add key».

Теперь вы можете делать подписанные коммиты в свои репозитории! При следующем коммите у вас запросят пароль для вашего GPG-ключа, поскольку он используется впервые. После этого пароль уже не нужно будет вводить.

Как проверять коммиты в Git

GitHub и GitLab будут показывать значок «Verified» рядом с вашими новыми коммитами.

И последнее, что нужно запомнить. Подпись коммита позволяет удостовериться лишь в том, кто является коммитером. Авторство не проверяется. То есть, когда вы видите подтвержденный коммит, подтверждение никак не касается автора.

Режим бдительности (vigilant mode)

На GitHub есть настройка vigilant mode.

Вы можете использовать этот режим, если хотите, чтобы все неподписанные коммиты были явно помечены «Unverified». Включается это в настройках в разделе «SSH and GPG keys». Установите метку на «Flag unsigned commits as unverified».

Теперь рядом с коммитом, который Джен сделала с моим адресом электронной почты, показывается пометка «Unverified». Эта пометка указывает на то, что коммит не был подписан с помощью ключа, связанного с моей учетной записью.

[customscript]techrocks_custom_after_post_html[/customscript]

[customscript]techrocks_custom_script[/customscript]

1 комментарий к “Подпись коммитов в Git: как и зачем это делать”

Оставьте комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *

Прокрутить вверх