Первые шаги
Подключить сервер (пользователь для копий и ключ SSH)
Пользователь для копий, ключ SSH и минимум прав, при котором всё ещё работает.
Как подготовить хост, чтобы Unruin мог с него копировать. Unruin работает без
агентов: оркестратор ходит на каждую цель по обычному SSH пользователем user
сервера, ключом, на который указывает ssh_key. Ставить restic на хост больше
не нужно — для источников-путей (папки, docker-тома) Unruin сам доставит туда
свой статический restic по SSH, когда тот впервые понадобится. Единственное, что
требуется от хоста, — чтобы пользователь SSH мог читать то, что копируется.
Доставка restic: перед копированием источника-пути Unruin щупает цель; если годного restic там нет, он передаёт свой в
~/.cache/unruin/restic(у пользователя, без root) и работает им. Когда restic уже есть — всё это незаметно. Отказаться:UNRUIN_NO_RESTIC_PUSH=1; отправить другой бинарник:UNRUIN_RESTIC_PUSH_BIN. См. SIMPLIFICATION.md.
Это делается один раз на хост, после чего сервер заводится (в конфигурации или
в панели), а его ключ хоста записывается через validate --preflight.
Наименьшие права. Не тянитесь к
rootрефлекторно. Копии базы нужны роль только на чтение и обычный непривилегированный пользователь — и всё. Повышенные права на чтение нужны только копиям файлов, принадлежащих root, да и там возможность файла лучше, чемroot(см. §4).
0. Что пользователь SSH должен уметь
| Тип источника | Где работает | Что нужно пользователю SSH | restic на цели? |
|---|---|---|---|
postgres |
дамп на цели, restic --stdin на оркестраторе |
запускать pg_dump для базы (роль только на чтение) |
нет |
mysql |
дамп на цели | запускать mysqldump --single-transaction (SELECT + LOCK/RELOAD) |
нет |
command |
команда на цели | то, что нужно самой команде | нет |
files |
restic на цели |
читать путь | доставится сам |
docker_volume |
restic на цели |
docker volume inspect и читать данные тома |
доставится сам |
Доступы к хранилищу restic (ключи только на дозапись и ключ шифрования)
класть на хост не нужно: Unruin подставляет их в удалённый вызов restic в
момент запуска, из своего хранилища секретов. У цели вообще бывают только доступы
на дозапись; привилегированные доступы для уборки оркестратор не отдаёт
никому.
1. Сделать отдельную пару ключей SSH (на оркестраторе)
Один ключ на хост держит радиус поражения маленьким: взломанный хост не сможет притвориться остальными. Без парольной фразы: Unruin работает без присмотра, а закрытый ключ лежит зашифрованным в его хранилище секретов — там он и защищён.
ssh-keygen -t ed25519 -C "unruin@orchestrator" -f ~/unruin-keys/<host>
# получатся ~/unruin-keys/<host> (закрытый) и <host>.pub (открытый)
Дальше вы:
- положите открытый ключ в
authorized_keysпользователя для копий на цели (шаг 3); - отдадите закрытый ключ хранилищу секретов Unruin (шаг 5) и удалите копию с диска.
2. Завести на цели пользователя для копий
Запускается на каждом хосте — под root или через ваш обычный административный доступ. Отдельная учётная запись с заблокированным паролем, только по ключу:
sudo useradd --create-home --shell /bin/bash unruin
sudo passwd --lock unruin # входа по паролю нет, только ключ
Хочется совсем без оболочки?
--shell /usr/sbin/nologinподойдёт, только если все источники этого хоста — источники-пути: restic по SSH работает и без интерактивной оболочки. Источники-потоки запускают команду в оболочке, поэтому, если вы копируете хоть одну базу илиcommand, оставьте/bin/bash.
3. Положить открытый ключ на цель
sudo install -d -m 700 -o unruin -g unruin /home/unruin/.ssh
# вставьте содержимое <host>.pub:
sudo tee /home/unruin/.ssh/authorized_keys >/dev/null <<'KEY'
ssh-ed25519 AAAA...ваш-открытый-ключ... unruin@orchestrator
KEY
sudo chmod 600 /home/unruin/.ssh/authorized_keys
sudo chown -R unruin:unruin /home/unruin/.ssh
Можно укрепить — прибить исходный адрес оркестратора, чтобы ключ работал только с вашей управляющей машины. Допишите ограничения перед строкой ключа:
from="203.0.113.10",no-port-forwarding,no-X11-forwarding,no-agent-forwarding ssh-ed25519 AAAA... unruin@orchestrator
(Не используйте command="…" и restrict без pty: Unruin запускает на хосте
несколько разных команд, и принудительная команда или запрет pty сломают
источники-потоки.)
4. Выдать только то право, которое нужно источнику
Возьмите строку, которая соответствует тому, что копируется с этого хоста.
База (postgres / mysql) — никакого root, никогда
Заведите принципала базы только на чтение и дайте пользователю unruin
входить им без вопросов.
PostgreSQL — роль, которая читает всё и не меняет ничего:
CREATE ROLE unruin LOGIN PASSWORD 'придумайте-стойкий';
GRANT pg_read_all_data TO unruin; -- PG 14+; раньше: GRANT SELECT по схемам
Дайте пользователю системы входить без запроса пароля — одним из способов:
~unruin/.pgpass(chmod 600):localhost:5432:app:unruin:придумайте-стойкий;- аутентификация peer: имя пользователя системы совпадает с ролью базы, ходим через локальный сокет.
Источник postgres запускает pg_dump <база>; хост, порт и пользователя задавайте
через .pgpass и переменные PG*, либо возьмите источник command с в точности
тем вызовом pg_dump, который вам нужен.
MySQL / MariaDB — минимум прав для согласованного дампа:
CREATE USER 'unruin'@'localhost' IDENTIFIED BY 'придумайте-стойкий';
GRANT SELECT, LOCK TABLES, SHOW VIEW, EVENT, TRIGGER, RELOAD, PROCESS
ON *.* TO 'unruin'@'localhost';
Положите доступ в ~unruin/.my.cnf (chmod 600), чтобы mysqldump читал его и
пароль не оказывался в командной строке:
[client]
user=unruin
password=придумайте-стойкий
Unruin всегда передаёт --single-transaction ради дампа, согласованного на
момент времени в InnoDB. Это охранное правило: зелёного запуска над рваным дампом
не бывает.
Файлы (files) — достаточно права читать; возможность необязательна
Пользователю SSH нужно только читать путь. Свой restic Unruin доставит на
хост сам, ставить нечего — restic в PATH не нужен вовсе. Для путей, которые
пользователь и так читает, на этом всё.
Файлы, принадлежащие root (необязательный приём): если путь читает только
root, а user: root вам не хочется, выдайте доставленному restic ровно ту
возможность, которая ему нужна, чтобы обойти права на чтение. Бинарник Unruin
кладёт в ~/.cache/unruin/restic, туда возможность и ставится — после того как
первый запуск его создаст:
sudo setcap cap_dac_read_search=+ep /home/unruin/.cache/unruin/restic
cap_dac_read_search даёт обход только для чтения: ни записи, ни прочих
возможностей root. После замены доставленного бинарника её надо поставить заново.
Хотите вести restic сами? Поставьте его в PATH, задайте
UNRUIN_NO_RESTIC_PUSH=1, повесьте setcap на свой бинарник — Unruin возьмёт
его. Иначе задайте этому серверу user: root.
Docker-тома (docker_volume) — нужен Docker и чтение тома
Unruin зовёт docker volume inspect, а затем restic по найденной точке
монтирования внутри /var/lib/docker/volumes, которая принадлежит root. Добавьте
пользователя в docker и учтите, что это на хосте фактически равно root (сам
restic доставляется, ставить нечего):
sudo usermod -aG docker unruin # группа docker ≈ root; можно сразу взять root
# данные тома принадлежат root; дайте доставленному restic обход на чтение (после первого запуска):
sudo setcap cap_dac_read_search=+ep /home/unruin/.cache/unruin/restic
Для контейнера с состоянием лучше взять источник-поток из базы, чем копировать горячий том: такая копия согласована только на момент падения. Холодное копирование тома (остановить, снять, поднять) пока не сделано.
5. Отдать закрытый ключ Unruin и удалить копию с диска
Положите закрытый ключ в то хранилище секретов, которым вы пользуетесь; в
jobs.yaml попадает только ссылка, никогда сам ключ. Каким бы способом вы ни
воспользовались, ключ обязан быть:
- закрытым (не
.pub); - без парольной фразы — Unruin соединяется в режиме
BatchMode=yesи не может её ввести. Снять:ssh-keygen -p -N '' -f <ключ>; - настоящим многострочным PEM, а не слепленным в одну строку. Перевод строк
CRLF и лишний или недостающий перевод в конце Unruin исправит сам, прежде чем
отдать ключ
ssh, но раскатать обратно тело, у которого переводы строк вырезали, он не сможет — оно так и останется сломанным (Load key: error in libcrypto, см. разбор поломок).
Выберите один из трёх способов.
| Способ | Кому подходит | Что нужно |
|---|---|---|
kr:// (командная строка) |
у себя, без внешнего сервера секретов (по умолчанию) | UNRUIN_MASTER_PASSPHRASE и постоянный том состояния |
| Вставить в панели | машина с одним оператором, работа мышкой | разрешённая правка (не только чтение) и UNRUIN_MASTER_PASSPHRASE |
file:// |
бой, Docker, k8s | примонтированный файл с ключом |
file:// — примонтированный файл (в бою рекомендуется)
Самый устойчивый к порче: ключ остаётся настоящим файлом и не проходит ни через
переменную окружения, ни через шаг set, поэтому переводы строк потерять негде.
Монтируйте секретом Docker или k8s и ссылайтесь на путь:
# docker-compose.yml
secrets: [ <host>_key ]
secrets:
<host>_key: { file: ./secrets/<host>_key } # сам файл закрытого ключа, режим 600
ssh_key: "file:///run/secrets/<host>_key"
kr:// — встроенное шифрованное хранилище (командная строка)
Внешнего сервера не нужно. Сохраняйте ключ дословно из файла (< keyfile, и
никогда echo или вставка — они портят переводы строк), изнутри контейнера, чтобы
он лёг на постоянный том состояния:
export UNRUIN_MASTER_PASSPHRASE=… # та же, что у serve/run
unruin secret set ssh/<host> < ~/unruin-keys/<host> # значение со stdin, не из argv
shred -u ~/unruin-keys/<host> # убрать открытую копию
# ссылка: ssh_key: "kr://ssh/<host>"
Вставить в панели — поле «Закрытый ключ SSH» при добавлении машины
В ⚙ Хозяйство → Добавить машину вставьте многострочный PEM целиком в поле
ключа. Панель положит значение в хранилище kr:// (зашифрованным, только на
запись — обратно не прочитать) и сама напишет ssh_key: "kr://ssh/<имя-машины>";
сырой ключ до jobs.yaml не доходит. Что должно быть: правка разрешена
(loopback или UNRUIN_API_TOKEN; под UNRUIN_READONLY — нет) и задана
UNRUIN_MASTER_PASSPHRASE. Вставляйте весь блок -----BEGIN…END----- с
настоящими переводами строк: буфер обмена, схлопнувший их или добавивший CRLF,
даёт ту самую ошибку libcrypto.
6. Завести сервер и записать его ключ хоста
Добавьте сервер в jobs.yaml (или в панели, ⚙ Хозяйство → Добавить сервер,
когда правка разрешена):
servers:
<host>:
host: 10.0.0.11
port: 22
user: unruin # учётная запись из шага 2 (или root)
ssh_key: "kr://ssh/<host>" # ссылка из шага 5
# host_key_policy: tofu # по умолчанию; первое соединение доверяет ключу и запоминает его
Затем докажите, что всё работает, и закрепите ключ хоста:
unruin validate jobs.yaml --preflight
# разрешит ключ, соединится по SSH (запишет known_hosts при первой встрече), пощупает хранилища
--preflight — это и есть ответ на вопрос «а это вообще пойдёт?»: неверный путь к
ключу, недоступный хост или недостающее право в базе падают сейчас, а не в три
часа ночи. В панели кнопка Проверить связь в форме сервера запускает ту же
проверку.
Перейти на strict (в бою рекомендуется): после того как живая проверка записала
ключ, сверьте отпечаток с хостом по другому каналу и задайте
host_key_policy: strict — глобально или для сервера. Тогда неизвестный или
изменившийся ключ станет жёстким отказом, а не тихим новым доверием.
7. Первая копия
unruin run jobs.yaml <job> # разово; видно копирование → рассылку → контрольное восстановление
unruin serve jobs.yaml # или пусть планировщик сам по расписанию
Шпаргалка — весь скрипт на хост (источник-поток из базы)
Обычный случай: пользователь без root, который копирует базу Postgres. Запускается на цели; ключ SSH был сделан на оркестраторе в шаге 1.
sudo useradd --create-home --shell /bin/bash unruin
sudo passwd --lock unruin
sudo install -d -m 700 -o unruin -g unruin /home/unruin/.ssh
echo 'ssh-ed25519 AAAA... unruin@orchestrator' | \
sudo tee /home/unruin/.ssh/authorized_keys >/dev/null
sudo chmod 600 /home/unruin/.ssh/authorized_keys
sudo chown -R unruin:unruin /home/unruin/.ssh
sudo -u postgres psql -c "CREATE ROLE unruin LOGIN PASSWORD 'pw';"
sudo -u postgres psql -c "GRANT pg_read_all_data TO unruin;"
printf 'localhost:5432:app:unruin:pw\n' | sudo -u unruin tee /home/unruin/.pgpass >/dev/null
sudo -u unruin chmod 600 /home/unruin/.pgpass
Разбор поломок
Load key "…": error in libcrypto → Permission denied (publickey)
Сохранённый закрытый ключ не разбирается. Unruin приводит в порядок переводы
строк и хвостовой перевод, прежде чем записать временный файл для -i, и
соединяется в BatchMode=yes без запросов — то есть CRLF и обрезанный хвостовой
перевод он уже исправил, а OpenSSL всё равно отвергает тело ключа. Остаются
причины: ключ слепили в одну строку (внутренние переводы потерялись, проходя
через скаляр YAML, однострочную переменную окружения или echo), это по ошибке
.pub, или он закрыт парольной фразой (в BatchMode её не ввести).
Разбираться надо внутри контейнера или на машине оркестратора — там та же libcrypto, которая отказывает:
ssh-keygen -y -f /путь/к/ключу # напечатал открытый ключ — хорошо; ошибка libcrypto — испорчен; спрашивает пароль — зашифрован
wc -l /путь/к/ключу # должно быть > 1 (однострочный ключ — обычный виновник)
file /путь/к/ключу # "OpenSSH private key" хорошо; "with CRLF" плохо
Починить и сохранить заново, дословно:
sed -i 's/\r$//' /путь/к/ключу # убрать CRLF, если он есть
ssh-keygen -p -N '' -f /путь/к/ключу # снять парольную фразу, если она есть
unruin secret set ssh/<host> < /путь/к/ключу # сохраняет переводы строк (kr://)
unruin validate <cfg> --preflight
Многострочный PEM не переживает однострочную env:// — для ключей SSH берите
kr:// или файл через file://.
Каждое поле — в CONFIGURATION.md, путь от нуля до копии — в GETTING-STARTED.md, восстановление после аварии — в RESTORE.md.