Перейти к содержанию

Git и репозитории. Часть 1 — «SSH и GPG» 🔐

Когда начинаешь нормально работать с репозиториями, довольно быстро упираешься в две вещи: SSH и GPG. Первая нужна, чтобы удобно и безопасно ходить в удалённые репозитории без вечного ввода логина и пароля. Вторая — чтобы подписывать коммиты и теги, подтверждая: да, это действительно вы, а не какой‑то рандомный Вася с соседнего подвала.

В этой статье спокойно и без лишнего пафоса настроим оба механизма: создадим SSH‑ключ для доступа к GitHub / Gitea / серверам, затем сделаем GPG‑ключ для подписи коммитов. Заодно разберёмся, что это вообще такое, как это использовать и почему приватные ключи нужно беречь как зеницу ока.

Поехали 😎


🔑 Что такое SSH и зачем он нужен

SSH (Secure Shell) — это защищённый протокол для удалённого подключения и аутентификации. Если по‑простому: он позволяет доказать удалённой стороне, что вы — это вы, без передачи пароля по сети каждый раз.

В контексте Git это особенно удобно. Вместо того чтобы вводить логин, пароль, токены и прочую тоску при каждом git clone, git pull или git push, вы один раз создаёте пару ключей:

  • приватный ключ — хранится только у вас;
  • публичный ключ — отдаётся на GitHub, Gitea, сервер или куда нужно.

Дальше сервер смотрит: «ага, публичный ключ знакомый» — и пускает. Красота.

Важно: приватный ключ никому не отдаём. Вообще. Никогда. Если публичный ключ можно показывать и копировать на сервер, то приватный — это ваш цифровой пропуск.


⚙️ Почему именно ed25519

Для SSH‑ключей сегодня нормальный дефолт — это ed25519.

Что это вообще такое? Это современный алгоритм на основе эллиптических кривых EdDSA over Curve25519. Звучит как заклинание из тёмного подвала криптографов, но нам важна практика: ed25519 даёт хороший уровень безопасности, работает быстро, использует компактные ключи и поддерживается всеми нормальными современными системами.

Почему он хорош:

  • компактный;
  • быстрый;
  • безопасный;
  • поддерживается почти везде.

Если у вас нет какой‑то древней инфраструктуры из музея, где всё держится на скотче и rsa, то ed25519 — лучший выбор.


🛠️ Создаём SSH‑ключ

Сгенерируем один обычный ключ для локальной системы:

ssh-keygen -t ed25519

Дальше система спросит:

  1. Куда сохранить ключ. Можно оставить путь по умолчанию.
  2. Парольную фразу на ключ. Я очень рекомендую её ставить.

Если вы оставите путь по умолчанию, получите два файла:

  • ~/.ssh/id_ed25519 — приватный ключ;
  • ~/.ssh/id_ed25519.pub — публичный ключ.

🧠 Добавляем SSH‑ключ в агент

Чтобы не вводить парольную фразу каждый раз, ключ можно загрузить в ssh-agent:

eval "$(ssh-agent -s)"
ssh-add ~/.ssh/id_ed25519

Да, команду с eval удобнее выполнять в bash или совместимом шелле. Но вообще это спокойно адаптируется и под другие оболочки.

У меня логика обычно такая: после первого использования ключ попадает в агент и живёт там до перезапуска сессии терминала. Это удобно и без перегибов.

Если используете fish, имеет смысл добавить в ~/.config/fish/config.fish вот это:

# Start ssh-agent if no agent socket is available.
if not set -q SSH_AUTH_SOCK
    eval (ssh-agent -c) > /dev/null
end

Тогда агент будет подниматься автоматически, если для текущей сессии ещё нет доступного сокета. Мелочь, а жить становится приятнее.


📄 Как получить публичный SSH‑ключ

Публичную часть ключа можно просто вывести в терминал:

cat ~/.ssh/id_ed25519.pub

Именно этот текст вы копируете:

  • в GitHubSettingsSSH and GPG keys;
  • в Gitea → настройки SSH‑ключей;
  • на удалённые сервера в ~/.ssh/authorized_keys.

Ещё раз: файл с .pub — это публичный ключ, его и используем для прокидывания наружу.


🧩 Настраиваем SSH‑конфиг

Чтобы не писать каждый раз кучу параметров руками, правим ~/.ssh/config.

Пример:

Host *
  ServerAliveInterval 30
  ServerAliveCountMax 3
  TCPKeepAlive yes
  AddKeysToAgent yes
  IdentitiesOnly yes
  HashKnownHosts yes
  StrictHostKeyChecking ask
  IdentityAgent $SSH_AUTH_SOCK

Host github.com
  HostName github.com
  User git
  IdentityFile ~/.ssh/id_ed25519

Host myvps
  HostName your.server.domain_or_ip
  User your_user
  Port 22
  IdentityFile ~/.ssh/id_ed25519

Что здесь полезно:

  • AddKeysToAgent yes — ключ можно автоматически добавлять в агент;
  • IdentitiesOnly yes — SSH не будет перебирать весь зоопарк ключей подряд;
  • HashKnownHosts yes — список известных хостов будет храниться в зашифрованном виде;
  • StrictHostKeyChecking ask — SSH спросит подтверждение при первом подключении.

После правки не забудьте защитить права:

chmod 700 ~/.ssh
chmod 600 ~/.ssh/config

✅ Проверяем SSH на GitHub

Теперь можно проверить подключение:

ssh -T git@github.com

Если всё в порядке, GitHub ответит приветствием и сообщит, что аутентификация успешна. Шелл при этом он вам не даст — и это нормально.


🖥️ SSH для своего сервера

С GitHub всё довольно цивилизованно: вставили публичный ключ в настройки — и поехали. Со своим сервером всё чуть более руками, но логика та же.

1. Первый вход — по паролю

Чтобы впервые зайти на сервер, доступ по паролю должен быть включён. Обычно это настраивается в sshd_config примерно так:

PasswordAuthentication yes
KbdInteractiveAuthentication yes
UsePAM yes
PermitRootLogin no

Что здесь означает каждая строка:

  • PasswordAuthentication yes — разрешает обычный вход по паролю;
  • KbdInteractiveAuthentication yes — включает keyboard-interactive аутентификацию; на некоторых системах она используется вместе с обычным входом по паролю;
  • UsePAM yes — разрешает PAM участвовать в аутентификации; во многих дистрибутивах это часть нормальной работы парольного входа;
  • PermitRootLogin no — запрещает прямой вход под root по SSH. И это правильно. Лезть снаружи сразу под root — плохая привычка и обычно вообще не нужно.

Но тут есть важный нюанс: в системе могут быть дополнительные файлы конфигурации SSH, которые переопределят эти правила. Например, что‑то в духе sshd_config.d/*.conf. То есть вы вроде бы всё честно прописали, а сервер всё равно упрямо делает вид, что ничего не видел. Проверяйте не только основной конфиг, но и каталог с дополнительными включаемыми файлами.

2. Прокидываем ключ на сервер

После первого входа по паролю копируем публичный ключ на сервер:

ssh-copy-id your_user@your.server.domain_or_ip

Если у вас нестандартный порт, указывайте его явно:

ssh-copy-id -p 2222 your_user@your.server.domain_or_ip

Эта команда добавит ваш публичный ключ в ~/.ssh/authorized_keys на сервере.

3. Теперь вход по ключу доступен

После этого можно пробовать обычный вход:

ssh your_user@your.server.domain_or_ip

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

4. Отключаем вход по паролю

И вот это обязательно: после того как убедились, что вход по ключу работает, отключайте вход по паролю.

В конфиге сервера должно остаться примерно так:

PasswordAuthentication no
KbdInteractiveAuthentication no
UsePAM yes
PermitRootLogin no

После этого перезапускаете sshd и живёте спокойнее.

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

  1. либо зайти со старого доверенного устройства;
  2. либо использовать веб‑консоль / VNC / панель хостера;
  3. временно снова включить вход по паролю;
  4. добавить новый ключ;
  5. и снова выключить парольный доступ.

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


🔏 Что такое GPG и зачем он нужен

Теперь к GPG.

GPG (GNU Privacy Guard) — это инструмент для криптографии: шифрования, проверки подписи и работы с ключами. В контексте Git нас интересует не шифрование, а именно цифровая подпись.

Зачем нужна подпись коммитов:

  • GitHub / Gitea могут показать, что коммит подписан и верифицирован;
  • становится проще доказать, что коммит действительно сделан вами;
  • это просто хороший тон, если вы ведёте публичные репозитории или серьёзные проекты.

Если SSH говорит: «я могу войти на сервер», то GPG говорит: «этот коммит подписал именно я».


🛠️ Создаём GPG‑ключ

Сгенерируем ключ в интерактивном режиме:

gpg --full-generate-key

Рекомендованные ответы:

  • Тип ключаECC (sign only) / «только для подписи»;
  • КриваяCurve 25519;
  • Срок действия — любой на ваш вкус. Я обычно ставлю 1 год;
  • дальше вводите имя, почту и парольную фразу.

Почему срок действия на год — нормальная идея:

  • ключи полезно периодически ротировать;
  • если ключ утёк или потерял актуальность, не придётся жить с ним вечно;
  • при этом год — срок комфортный и не раздражает.

🔍 Смотрим созданный GPG‑ключ

После генерации посмотрим список секретных ключей:

gpg --list-secret-keys --keyid-format=long

Вывод будет примерно такой:

sec   ed25519/AAAAAAAAAAAAAAAA 2026-03-09 [SC] [expires: 2027-03-09]
      XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
uid                 [ultimate] Your Name <your_email@example.com>

Здесь нас интересует ID ключа — в примере это AAAAAAAAAAAAAAAA.

Теперь экспортируем публичную часть:

gpg --armor --export AAAAAAAAAAAAAAAA

Её и нужно вставить:

  • в GitHubSettingsSSH and GPG keysNew GPG key;
  • либо в аналогичный раздел Gitea, если он у вас поддерживает верификацию подписей.

🧷 Настраиваем gpg-agent

Чтобы ввод пароля был удобным, нужно настроить gpg-agent. Правим файл ~/.gnupg/gpg-agent.conf:

pinentry-program /usr/bin/pinentry-curses
default-cache-ttl 2147483647
max-cache-ttl 2147483647

Что это значит:

  • pinentry-program — какая программа будет спрашивать пароль;
  • default-cache-ttl и max-cache-ttl — как долго агент будет держать пароль в памяти.

Да, значения выглядят так, будто мы решили сломать математику. На практике это даёт очень долгий кэш. В реальности поведение часто сводится к тому, что пароль живёт до перезапуска терминальной сессии или агента.

Если вы работаете в терминале, pinentry-curses — хороший вариант. Если предпочитаете графику, можно использовать pinentry-gtk или pinentry-qt.

Если используете fish, добавьте в ~/.config/fish/config.fish ещё и это:

# Ensure GPG pinentry can access the correct TTY.
set -gx GPG_TTY (tty)

Без этой строки pinentry иногда начинает жить своей жизнью и делать вид, что терминал ему не указ. А нам такое счастье не нужно.

После изменения конфига перезапускаем агент:

gpgconf --kill gpg-agent
gpgconf --launch gpg-agent

И не забываем про права на директорию:

chmod 700 ~/.gnupg

⚙️ Привязываем GPG к Git

Теперь говорим Git, какой ключ использовать для подписи:

git config --global user.name "Ваше Имя"
git config --global user.email "your_email@example.com"
git config --global user.signingkey AAAAAAAAAAAAAAAA
git config --global commit.gpgsign true
git config --global tag.gpgsign true
git config --global gpg.program gpg

Что здесь происходит:

  • user.name и user.email — авторство коммитов;
  • user.signingkey — какой именно GPG‑ключ использовать;
  • commit.gpgsign true — подписывать все коммиты по умолчанию;
  • tag.gpgsign true — подписывать теги;
  • gpg.program gpg — какой бинарник использовать.

После этого Git начнёт подписывать коммиты автоматически.


✅ Проверяем подпись коммитов

Проще всего — создать тестовый коммит:

mkdir test-gpg && cd test-gpg
git init
echo "test" > README.md
git add README.md
git commit -m "test: проверка GPG подписи"

Если всё настроено правильно, Git попросит парольную фразу через pinentry, создаст коммит, а платформа покажет, что он подписан.

Посмотреть подпись локально можно так:

git log --show-signature -1

🧠 Как лучше хранить ключи

Здесь логика простая.

Для локальной системы вы создаёте один SSH‑ключ и один GPG‑ключ. Этого обычно достаточно. SSH‑ключ используете для GitHub, Gitea и при желании для своих серверов. GPG‑ключ — для подписи коммитов и тегов.

Главные правила:

  • приватные ключи не копируем куда попало;
  • не отправляем их в мессенджеры, облака и «временные заметки»;
  • по возможности ставим парольную фразу;
  • публичные ключи можно добавлять на разные серверы и в разные сервисы.

Если устройство потеряли или есть подозрение, что ключ утёк, всё просто:

  1. удаляем публичный ключ с GitHub / Gitea / серверов;
  2. создаём новую пару;
  3. обновляем привязки.

Без паники, но и без режима «да ладно, и так сойдёт».


🏁 Что в итоге получилось

Мы настроили два важных механизма:

  • SSH — для безопасного доступа к GitHub / Gitea / серверам;
  • GPG — для подписи коммитов и тегов.

Теперь у вас есть нормальная база для работы с репозиториями: без лишнего ввода паролей, с аккуратной аутентификацией и с верифицируемыми коммитами. Уже не просто «что‑то там пушится», а более‑менее взрослая инфраструктура 🙂

А дальше главное — не сливать приватники куда попало и не устраивать цифровой колхоз там, где можно один раз сделать нормально 🖤