UNRUIN
документация · v1.0

Как устроено

Секреты

Встроенное шифрованное хранилище, два запасных пути и две вещи, которые обязаны лежать отдельно.

В jobs.yaml не лежит ни одного значения секрета. В нём лежат ссылки — строки вроде kr://restic/minio-primary, — которые Unruin разрешает ровно в тот момент, когда они нужны. Именно это делает возможной конфигурацию как код: файл, описывающий всё ваше устройство копий, безопасно коммитить, читать в diff и обсуждать.

Ссылки разрешают три схемы.

Схема Где лежит значение Для чего
kr://имя собственное шифрованное хранилище Unruin, в каталоге состояния почти для всего
env://ИМЯ переменная окружения процесса unruin аварийный конверт и CI
file://путь файл на диске секреты, которые кладёт кто-то другой (Docker secrets, примонтированный том)

Неизвестная схема — жёсткая ошибка, и пустое значение от поставщика тоже. Пути, на котором отсутствующий секрет тихо превращается в пустой пароль, нет.

Встроенное хранилище (kr://)

Способ по умолчанию и причина, по которой Unruin не нужен внешний менеджер секретов.

  • Один файл, secrets.kr, права 0600, в каталоге состояния.
  • Значения зашифрованы AES-256-GCM ключом, выведенным из UNRUIN_MASTER_PASSPHRASE через PBKDF2-HMAC-SHA256, 600 000 итераций.
  • Имя записи участвует как связанные данные AEAD, поэтому шифротекст нельзя переставить с одного имени на другое внутри файла.

Как заводить записи

Значения читаются из стандартного ввода, а не из командной строки, — чтобы они не попадали ни в список процессов, ни в историю оболочки:

sh
unruin secret set restic/minio-primary          # ввести или вставить, затем Ctrl-D
printf %s 'сам-пароль' | unruin secret set restic/minio-primary
unruin secret set ssh/vps-prod-1 < ~/.ssh/id_ed25519_backup
unruin secret ls
unruin secret rm restic/old-repo

Ввод сохраняется дословно, вместе с переводом строки на конце. echo 'pw' | сохранит четыре символа, а не три, и хранилище потом откажет в пароле без внятной причины. Для пароля берите printf %s. Для файла ключа перевод строки на конце как раз уместен, поэтому < keyfile — правильно.

Один секрет, несколько полей

Ссылка может нести #поле:

yaml
access: "kr://minio/primary"          # разрешается запись целиком
sh
unruin secret set minio/primary#access_key
unruin secret set minio/primary#secret_key

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

Из панели

Когда задана UNRUIN_MASTER_PASSPHRASE, ключ SSH или ключ хранилища можно вставить прямо в форму сервера или хранилища. Он ляжет зашифрованным, а в конфигурации останется только ссылка. По значениям секретов API работает только на запись: сохранённое нельзя прочитать обратно через панель, можно только заменить или удалить. Когда мастер-фразы нет или хранилище не читается, в панели висит плашка об этом — вместо того чтобы дать вам печатать в форму, которая тихо не сработает.

Наименьшие права, если они вам нужны

У хранилища бывает два доступа:

  • access — доступ, которым Unruin (а для источников-путей и сама цель) пишет;
  • prune_accessнеобязательный привилегированный доступ, которым пользуется только уборка; он живёт у оркестратора и никогда не уезжает на цель.

Разделение существует, чтобы можно было выдать целям доступ только на дозапись: тогда взломанный веб-сервер сможет добавлять точки восстановления, но не сможет удалить историю собственных копий. Не нужен такой размен — не пишите prune_access, и один access будет делать всё, включая уборку. Это сложность по желанию, а не по умолчанию.

Две вещи, которые обязаны лежать отдельно

Всё в kr:// зашифровано одной фразой, а зашифрованный файл лежит на той же машине, что и то, что он защищает. Поэтому:

  1. UNRUIN_MASTER_PASSPHRASE — где-то, где не эта машина.
  2. secrets.kr с тома состояния — туда же.

Потеряете оба — пропадут все секреты kr://: ключи хранилищ, ключи SSH, доступы к бэкендам. Потеряете одну фразу — файл станет нечитаемым. Эти две вещи — корень всего остального, и Unruin их не копирует: средство резервного копирования, которое хранит ключи от своих копий внутри этих копий, не решило ничего.

Аварийный конверт намеренно лежит не в хранилище

Аварийное хранилище существует для случая, когда потеряны оркестратор, машина и том состояния. Значит, его ключ обязан жить снаружи kr:// — на env://, подставляемый из вашего менеджера паролей или с бумажки:

yaml
repo_password: "env://BREAKGLASS_RESTIC_PW"

С этим ключом, адресом хранилища и бинарником restic ваши данные достаются вообще без участия Unruin. В этом весь смысл, и это проверено, а не заявлено — см. восстановление без Unruin.

Как менять секрет

  1. Сохраните новое значение под тем же именем (unruin secret set … перезаписывает).
  2. unruin validate <config> --preflight — он разрешает каждый секрет и действительно соединяется, поэтому криво вставленный ключ упадёт здесь, а не в три часа ночи.
  3. Перезапустите serve, если процесс что-то держит долго (ключи SSH пишутся на каждый запуск, хранилище читается по требованию).

Для ключа хранилища restic смена — это его собственное управление ключами (restic key add / restic key remove) на самом хранилище, и потом правка ссылки. Изменить только ссылку — значит не изменить в хранилище ничего, и Unruin просто перестанет его открывать.

Где секретов нет

  • Не в jobs.yaml, никогда: API конфигурации принимает только ссылки и отвергает всё, что похоже на значение.
  • Не в списке процессов: значения читаются со стандартного ввода или из окружения.
  • Не в журналах: разрешённое значение — тип, который при печати сам себя затирает.
  • Не в оповещениях: сообщение о сбое говорит, какое хранилище и что проверить, а не чем оно пыталось представиться.

Бесплатно, целиком ваше, наружу ничего не уходит. Сайт статический: ни аналитики, ни куки, ни внешних запросов.