OSEN-НИЙ SAAALEСкидка 50% на виртуальный хостинг и VDS
до 30.11.2025 Подробнее
Выберите продукт

sops + age: управляем секретами репозитория и деплоим на сервер без утечек

Храните секреты в Git без риска утечек. Разбираем связку sops + age: генерация ключей, настройка .sops.yaml, шифрование .env/YAML/JSON, командный процесс, ротация ключей и безопасный деплой, где расшифровка выполняется только на целевом сервере.
sops + age: управляем секретами репозитория и деплоим на сервер без утечек

Секреты — самая опасная часть инфраструктурного кода. Пароли БД, API-токены, ключи OAuth и вебхуки часто «живут» в .env и YAML, попадают в Git, а затем гуляют по локальным машинам, CI-логам и артефактам. Связка sops + age решает проблему: секреты хранятся в репозитории только в зашифрованном виде, а расшифровка происходит строго там, где это необходимо — при деплое на сервер или в контролируемом окружении разработчика.

Зачем sops и age в GitOps-процессе

GitOps подразумевает «всё как код», включая конфиги продакшна. Но это безопасно лишь до тех пор, пока чувствительные данные никогда не появляются в открытом виде вне доверенных границ. sops шифрует поля в YAML/JSON/ENV-файлах, управляет ключами и прозрачно встраивается в рабочий процесс: файлы остаются в Git, диффы читаемы (метаданные sops), а расшифровка — по требованию. age — современный, простой и криптографически аккуратный инструмент шифрования, который sops поддерживает «из коробки».

Золотое правило: секрет не должен появляться в незашифрованном виде вне целевой машины и краткого контролируемого интервала редактирования.

Базовая архитектура: где хранятся ключи и кто чем владеет

Минимальная модель:

  • Публичные получатели (age-recipients) хранятся в репозитории внутри .sops.yaml и определяют, кто может расшифровать конкретные файлы.
  • Приватные ключи age хранятся только на целевых серверах деплоя и у конкретных разработчиков (по необходимости). В CI/CD — через защищённые хранилища переменных или подключённые секреты.
  • Каждому окружению (prod, stage) — свой ключ сервера. Это упрощает отзыв доступа и ротацию.

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

Политика .sops.yaml и получатели age для разных окружений

Установка sops и age

На Linux пакеты часто доступны из репозиториев дистрибутива, на macOS — через менеджеры пакетов. Примеры:

# Debian/Ubuntu
sudo apt update
sudo apt install -y age sops

# RHEL/CentOS/AlmaLinux/Rocky
sudo dnf install -y age sops

# Arch
sudo pacman -S --noconfirm age sops

# macOS (Homebrew)
brew install age sops

Проверяем версии:

age --version
sops --version

Генерируем ключи age и наводим порядок

Сгенерируйте приватный ключ для сервера каждого окружения и для нужных участников команды:

# На сервере продакшна
sudo mkdir -p /etc/sops/age
sudo age-keygen -o /etc/sops/age/keys.txt
sudo chmod 600 /etc/sops/age/keys.txt
sudo chown root:root /etc/sops/age/keys.txt

# Посмотреть публичного получателя (recipient)
sudo sed -n 's/^# public key: //p' /etc/sops/age/keys.txt

Полученную публичную строку формата age1... добавьте в политику .sops.yaml. Для локальных разработчиков процедуру повторяйте в домашнем каталоге, например ~/.config/sops/age/keys.txt (с теми же правами доступа 600).

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

Настраиваем .sops.yaml: правила шифрования

Создайте в корне репозитория файл .sops.yaml и опишите правила для разных окружений и типов файлов. Пример:

creation_rules:
  - path_regex: secrets/prod/.*\.(ya?ml|json|env)$
    age:
      - age1exampleRecipientForProdServer...
      - age1exampleRecipientForAdmin1...
    encrypted_regex: '^(data|stringData|.+_SECRET|PASSWORD|TOKEN|KEY)$'
  - path_regex: secrets/stage/.*\.(ya?ml|json|env)$
    age:
      - age1exampleRecipientForStageServer...
      - age1exampleRecipientForDev...
    encrypted_regex: '^(data|stringData|.+_SECRET|PASSWORD|TOKEN|KEY)$'

Разбор:

  • path_regex определяет, на какие файлы распространяется правило.
  • age — список получателей, у кого есть право расшифровать.
  • encrypted_regex позволяет шифровать только нужные поля. В .env всё содержимое файла шифруется целиком, а в YAML/JSON — только совпадающие ключи.

Шифруем .env, YAML и JSON

Добавьте файлы секретов в нужные директории и зашифруйте. Примеры:

# .env для продакшна
mkdir -p secrets/prod
printf 'DB_PASSWORD=strongpass\nAPI_TOKEN=abcd1234\n' > secrets/prod/app.env
sops -e -i secrets/prod/app.env

# YAML (например, конфиг приложения)
cat > secrets/prod/config.yaml << 'YAML'
api:
  url: api.internal
  token: abcd1234
bb:
  host: db.internal
  user: app
  password: strongpass
YAML
sops -e -i secrets/prod/config.yaml

# JSON
printf '{"password":"strongpass","token":"abcd1234"}\n' > secrets/prod/config.json
sops -e -i secrets/prod/config.json

Редактирование шифрованного файла выполняйте через sops — он прозрачно расшифрует во временный буфер и перешифрует при сохранении:

sops secrets/prod/app.env
sops secrets/prod/config.yaml

Получатели берутся из .sops.yaml по правилу path_regex. При добавлении новых получателей используйте команду обновления ключей, чтобы пересчитать заголовки:

sops updatekeys -r secrets/prod

Командный процесс и минимизация рисков

Рабочая модель в команде:

  1. Каждый разработчик, кому нужна расшифровка, генерирует свой ключ age и публикует в MR/PR только публичный recipient.
  2. Мейнтейнер добавляет recipient в .sops.yaml, обновляет ключи файлами sops updatekeys и мёржит.
  3. Секреты редактируют через sops. В Git попадают только зашифрованные блоки и метаданные.
  4. На сервере — только приватный ключ окружения. Личные ключи разработчиков на сервер не ставятся.

Такой процесс даёт управляемую модель доступа: доступ легко выдавать и отзывать, а аудит изменений — в истории Git.

Ротация и отзыв доступа без простоев

Сценарий ротации ключа прод-сервера:

  1. Сгенерировать новый ключ сервера, извлечь публичный recipient.
  2. Добавить его в .sops.yaml рядом со старым.
  3. Выполнить sops updatekeys -r для всего каталога секретов продакшна и закоммитить изменения.
  4. Доставить новый приватный ключ на прод-сервер и перезапустить воркфлоу деплоя.
  5. Удалить старый recipient из .sops.yaml, снова выполнить sops updatekeys -r и закоммитить.

Благодаря списку получателей можно плавно перейти с одного ключа на другой без простоев и без рисков расшифровки посторонними.

Деплой: расшифровываем только на сервере

Базовый приём — сохранять приватный ключ в /etc/sops/age/keys.txt и использовать sops -d в процессе деплоя. Примеры:

# Расшифровать .env в память и экспортировать переменные для процесса
export $(sops -d --output-type dotenv secrets/prod/app.env)

# Расшифровать в файл с жёсткими правами, затем запуск
umask 177
sops -d secrets/prod/app.env > /run/app.env
systemctl restart myapp.service

Фрагмент юнита systemd, где окружение берётся из заранее расшифрованного файла:

[Unit]
Description=MyApp service
After=network.target

[Service]
Type=simple
EnvironmentFile=/run/app.env
ExecStart=/usr/local/bin/myapp
Restart=on-failure

[Install]
WantedBy=multi-user.target

При деплое расшифровку выполняйте кратковременно перед рестартом сервиса, пишите файл в tmpfs (/run), ограничивайте права и не сохраняйте секреты в артефактах CI. Для сервисов на выделенном сервере или VDS удобно закрепить ключи на уровне хоста и отдавать секреты только локальным юнитам.

FastFox VDS
Облачный VDS-сервер в России
Аренда виртуальных серверов с моментальным развертыванием инфраструктуры от 195₽ / мес

Безопасное использование в shell-скриптах

  • Не включайте set -x там, где работают секреты — это печатает переменные в логи.
  • Проводите минимальное время жизни расшифрованных файлов; чистите их после использования.
  • Отдавайте предпочтение потоковой подаче: sops -d file | command, когда это возможно.

Docker Compose: .env из sops без утечек

Можно запускать Compose, подавая окружение из расшифрованного потока или временного файла. Пример с временным файлом:

umask 177
sops -d secrets/prod/app.env > /run/app.env
docker compose --env-file /run/app.env up -d

Если используете подстановку процессов Bash, не забудьте экранировать угловые скобки в документации и помнить о совместимости шелла:

docker compose --env-file <(sops -d secrets/prod/app.env) up -d

В CI лучше избегать такой подстановки и работать с временным файлом в /dev/shm или /run (tmpfs), чтобы не оставлять дисковый след.

Ansible и sops: короткий путь

Есть два подхода:

  • Использовать коллекцию плагинов для нативной поддержки sops в vars_files.
  • Расшифровывать файлы заранее в задаче и подавать их как extra_vars или через шаблоны.

Минимальный универсальный приём без дополнительных плагинов — перед запуском playbook расшифровать в tmpfs и подставить путь:

umask 177
sops -d inventory/group_vars/prod/secrets.yaml > /run/ansible-prod-secrets.yaml
ansible-playbook -i inventory/hosts site.yaml -e @/run/ansible-prod-secrets.yaml

Не храните расшифрованные файлы в репозитории или артефактах. Чистите их после завершения:

shred -u /run/ansible-prod-secrets.yaml

Расшифровка секретов sops на сервере и запуск сервиса через systemd

Проверки на стороне Git: pre-commit и защита от случайных утечек

Что полезно добавить в проект:

  • .gitignore для всех раскрытых временных файлов: *.dec, *.plain, /run/*.env, прочее.
  • pre-commit хук, который проверяет, что secrets/ содержит только файлы с заголовком sops и нет «голых» .env.
  • Линтеры, запрещающие вызов set -x и эха переменных с шаблонами вроде PASS, TOKEN, KEY.

Диагностика типичных ошибок

  • Ошибка расшифровки: нет подходящего ключа. Убедитесь, что recipient сервера присутствует в .sops.yaml, а файл перезашифрован sops updatekeys -r. На сервере проверьте права и путь к keys.txt.
  • Разработчик не может расшифровать локально. Его публичный ключ не добавлен в .sops.yaml. Добавьте recipient и обновите ключи.
  • CI «видит» секреты в логах. Отключите эхо команд с переменными, проверьте флаги shell и вывод команд. Никогда не печатайте расшифрованный файл целиком.
  • Конфликт при мёрже зашифрованных файлов. Всегда редактируйте через sops, чтобы он корректно пересчитал метаданные. При конфликте повторно откройте файл через sops и сохраните.
  • .env парсится неверно. Убедитесь в корректных кавычках и экранировании. Используйте --output-type dotenv при чтении.

Политики и базовые нормы безопасности

  • Ни один секрет не должен храниться в открытом виде в репозитории, артефактах, образах контейнеров и снапшотах.
  • Приватные ключи серверов живут только на соответствующих серверах. Резервируйте их в офлайн-хранилищах с контролем доступа.
  • Отдельные ключи на окружение. Упрощает отзыв и снижает blast radius.
  • Ротация по событию и по расписанию (например, ежеквартально).
  • Аудит: периодически проверяйте историю на появление секретов в открытом виде, используйте сканеры по шаблонам.
  • CI: секреты подавайте как переменные среды в момент задачи, запрещайте печать значений, ограничивайте видимость джобов.

Полезные приёмы работы с sops

  • Селективное шифрование в YAML/JSON через encrypted_regex, чтобы оставлять несекретные поля видимыми.
  • sops -d --output-type dotenv для корректного экспорта .env в окружение процесса.
  • sops updatekeys -r при изменении списка получателей в .sops.yaml.
  • Собирайте инфрастуктурные переменные без секретов отдельно от секретных — это упрощает ревью и диффы.

После внедрения sops+age стоит подумать и о наблюдении за продом: пригодится алертинг по Nginx и системным метрикам — см. гайд по Prometheus, Node Exporter и алертам Nginx. А для высоконагруженных PHP-проектов на VDS обратите внимание на тюнинг PHP-FPM.

Итог

Пара sops + age даёт предсказуемый и проверяемый способ хранить секреты в Git и деплоить конфигурации без утечек. Вы описываете политику шифрования в .sops.yaml, шифруете секретные поля, храните файлы в репозитории и расшифровываете их только на целевом хосте. Добавьте контроль в CI, внедрите ротацию и дисциплину редактирования через sops — и тема секретов перестанет быть источником случайных инцидентов.

Поделиться статьей

Вам будет интересно

systemd-run: ограничиваем CPU и RAM для одноразовых задач и интерактивных команд OpenAI Статья написана AI (GPT 5)

systemd-run: ограничиваем CPU и RAM для одноразовых задач и интерактивных команд

Как быстро ограничить CPU и память для разовых команд без unit-файлов: используем systemd-run, transient units в режимах --service ...
OpenSearch на VDS: практический гид по памяти JVM heap, ISM-политикам и снапшотам OpenAI Статья написана AI (GPT 5)

OpenSearch на VDS: практический гид по памяти JVM heap, ISM-политикам и снапшотам

Поднимем OpenSearch на VDS: настроим JVM heap без сюрпризов с GC, спроектируем ISM с rollover и удалением, организуем регулярные s ...
ACME DNS‑01 через RFC2136: свой DNS‑API без облаков OpenAI Статья написана AI (GPT 5)

ACME DNS‑01 через RFC2136: свой DNS‑API без облаков

DNS‑01 решает выпуск wildcard и закрытых сервисов, но нужен API к авторитетному DNS. Покажу, как поднять свой «API» на RFC2136: BI ...