От японского 降ろす (órosu) — «разгружать», «сгружать».

Проблема

CI-системы хорошо справляются со сборкой ПО и плохо — с его доставкой. Перенос артефакта сборки из пайплайна на продакшн-машину обычно сводится к одному из нескольких привычных, но хрупких паттернов:

  • Приватные SSH-ключи, скопированные в секреты CI и разбросанные по каждому пайплайну и репозиторию, которому нужен деплой
  • SFTP/rsync-скрипты, скопированные из одного проекта в другой, каждый со своими небольшими багами
  • Захардкоженные пути и права доступа, которые ломаются при малейшем изменении структуры сервера
  • Продакшн-машины, напрямую доступные для CI-раннеров, расширяющие поверхность атаки с каждым новым пайплайном
  • Разрастание секретов, из-за которого ротация одного скомпрометированного ключа превращается в аудит всех репозиториев, которые могли на него ссылаться

Это не специфично для какой-то одной -платформы — это стандартная форма задачи «выполнить скрипт на удалённой машине из пайплайна», и риски накапливаются с каждым проектом, который её перенимает.

Как Orosu решает эту задачу

Orosu устанавливается один раз на целевой машине как orosu-server и превращает деплой из открытой SSH-сессии в ограниченный, аутентифицированный запрос:

  1. CI собирает приложение — бинарник, образ контейнера, статические файлы — что бы ни производил пайплайн
  2. CI запускает задачу через соединение , при необходимости прикладывая файлы
  3. orosu-server аутентифицирует запрос с помощью подписей Ed25519 — без паролей и общих секретов в передаче
  4. orosu-server выполняет заранее заданный скрипт — один из фиксированного набора, настроенного на сервере, а не произвольную команду от CI
  5. Скрипт выполняет деплой, используя файлы и аргументы, приложенные к задаче

Никакого прямого SSH, никакого жонглирования учётными данными по пайплайнам и никакой серверной поверхности за пределами конкретных скриптов, явно настроенных оператором.

Установка

orosu-server и сопутствующий CLI orosu-keygen поставляются как пакеты для Debian/Ubuntu из apt-репозитория:

curl -fsSL https://packages.nerdy.pro/NerdyPro.gpg | sudo gpg --dearmor -o /usr/share/keyrings/nerdy-pro.gpg
echo "deb [signed-by=/usr/share/keyrings/nerdy-pro.gpg] https://packages.nerdy.pro/ stable main" | sudo tee /etc/apt/sources.list.d/nerdy-pro.list
sudo apt update
sudo apt install orosu

GitHub Releases также публикует готовые бинарники orosu-server и orosu-keygen напрямую — для машин, на которые нельзя добавить apt-репозиторий.

Быстрый старт

Сгенерируйте пару ключей клиента с помощью orosu-keygen, находясь в /etc/orosu:

orosu-keygen --name my-ci-client --private-key-output my-ci-client.key --public-key-output my-ci-client.pub

Публичный ключ идёт в конфиг сервера; приватный становится секретом CI.

Настройте сервер в /etc/orosu/orosu-server.toml — адрес прослушивания и публичный ключ клиента:

listen:
  tcp: "127.0.0.1:8081"
clients:
  - name: my-ci-client
    secret_file: /etc/orosu/my-ci-client.pub

Сервер, слушающий localhost, обычно стоит за обратным прокси, который терминирует и пробрасывает апгрейды WebSocket — в README приведена конфигурация nginx для этого случая.

Определите скрипт, который клиенту разрешено запускать:

#!/bin/bash
echo "Hello, $1!"
clients:
  - name: my-ci-client
    secret_file: /etc/orosu/my-ci-client.pub
    scripts:
      - name: test-script
        command:
          - "bash"
          - "/etc/orosu/scripts/test.sh"

Запустите его из CI с помощью сопутствующего GitHub Action, передав адрес сервера и приватный ключ клиента через секреты:

- name: Remotely execute a script
  uses: orosu-ci/orosu@v0
  with:
    address: ${{ secrets.OROSU_SERVER_URL }}
    script: test-script
    key: ${{ secrets.OROSU_CLIENT_KEY }}
    arguments: "from CI pipeline"

Сквозное шифрование

WSS/:termTLS защищает соединение, но когда TLS терминируется на обратном прокси перед orosu-server — а это стандартная схема выше — аргументы скрипта, загруженные файлы и потоковый вывод остаются открытым текстом на этом прокси. Опциональное рукопожатие (X25519 + HKDF-SHA256 + ChaCha20-Poly1305) закрывает этот разрыв:

orosu-keygen --kind server --private-key-output server.key --public-key-output server.pub

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

Безопасность

Безопасность — требование первого класса при проектировании, а не запоздалая мысль:

  • Криптографическая аутентификация — каждая задача подписывается ключом Ed25519 клиента; в канале нет ни пароля, ни общего секрета, который мог бы перехватить атакующий.
  • Закрытый набор действий — CI может запускать только те скрипты, которые оператор явно задал на сервере. Пути от задачи CI к произвольной команде оболочки не существует.
  • Защищённая обработка вложений — имена записей в zip проверяются на обход путей, а количество записей и суммарный размер после распаковки ограничены, поэтому специально сформированное или чрезмерно большое вложение не может выйти за пределы каталога извлечения или исчерпать диск.
  • Многоуровневое шифрование — опциональное рукопожатие сквозного шифрования проверяет атаку low-order-point на обмене X25519 в дополнение к уже обеспечиваемой конфиденциальности.
  • Безопасные права доступа по умолчаниюorosu-keygen записывает файлы приватных ключей с правами 0600 независимо от текущего umask.

Эти механизмы вошли в релиз 0.7.0 как обновление, не требующее дополнительных действий — без изменений конфига, CLI или протокола. Полную историю релизов см. в CHANGELOG.md.

Лицензия

Orosu распространяется под лицензией Apache-2.0.

Разрозненные шаги деплоя по SSH/SCP в CI-пайплайнах — приватные ключи в секретах, скопированные rsync-скрипты и напрямую доступные продакшн-серверы. CI запускает аутентифицированную задачу по WebSocket вместо открытия SSH-сессии.
У каждого CI-клиента есть пара ключей Ed25519. Публичный ключ регистрируется в конфиге сервера; приватный хранится как секрет CI и используется для подписи запросов на выполнение задач. Сервер выполняет только те скрипты, которые явно настроены для этого клиента.
Нет. orosu-server выполняет только заранее заданные скрипты, настроенные в orosu-server.toml. Задача CI выбирает скрипт по имени и передаёт аргументы и вложения — она не может передать произвольную команду.
По умолчанию соединение защищено WSS/TLS, обычно терминируемым на обратном прокси перед orosu-server. Можно включить опциональный слой сквозного шифрования (X25519 + ChaCha20-Poly1305), чтобы аргументы скрипта, файлы и вывод оставались конфиденциальными даже для этого прокси.
Через apt-репозиторий (packages.nerdy.pro) для Debian/Ubuntu, либо в виде готовых бинарников, прикреплённых к GitHub Releases, для orosu-server и CLI orosu-keygen.
Подписанные Ed25519 запросы, закрытый набор скриптов, заданных на сервере и доступных для запуска из CI, защита от обхода путей и ограничения размера при извлечении вложений, защита от атаки low-order-point в опциональном рукопожатии сквозного шифрования и безопасные права доступа по умолчанию для сгенерированных ключей.