diff --git a/.github/workflows/build-legacy.yml b/.github/workflows/build-legacy.yml index 6248028..71529e1 100644 --- a/.github/workflows/build-legacy.yml +++ b/.github/workflows/build-legacy.yml @@ -184,66 +184,62 @@ jobs: while IFS= read -r file; do artifact="$(basename "$(dirname "$file")")" cp "$file" "legacy-publish/${artifact}-$(basename "$file")" - done < <(find legacy-artifacts -type f ! -name 'SHA256SUMS' -print | sort) + done < <(find legacy-artifacts -type f \( -name '*.ipk' -o -name '*.apk' \) -print | sort) + find legacy-publish -type f -print -quit | grep -q . - name: Attest legacy release assets uses: actions/attest-build-provenance@v2 with: subject-path: legacy-publish/* - - name: Combine and reconstruct all release package artifacts + - name: Download current release build artifacts env: AGENT_TAG: ${{ inputs.agent_tag }} GH_TOKEN: ${{ github.token }} shell: bash run: | set -euo pipefail - mkdir release-assets - gh release download "$AGENT_TAG" \ + release_run_id="$( + gh run list \ + --repo "$GITHUB_REPOSITORY" \ + --workflow build.yml \ + --branch "$AGENT_TAG" \ + --event push \ + --status success \ + --limit 20 \ + --json databaseId \ + --jq '.[0].databaseId' + )" + test -n "$release_run_id" + gh run download "$release_run_id" \ --repo "$GITHUB_REPOSITORY" \ --pattern 'openwrt-*' \ - --dir release-assets - cp legacy-publish/* release-assets/ - bash scripts/unpack-release-package-assets.sh \ - release-assets \ - .github/openwrt-sdk-lock.tsv \ - combined-artifacts + --dir current-artifacts + + - name: Prepare combined signed package repository + env: + AGENT_TAG: ${{ inputs.agent_tag }} + shell: bash + run: | + set -euo pipefail + mkdir combined-artifacts + cp -a current-artifacts/openwrt-* combined-artifacts/ + cp -a legacy-artifacts/openwrt-* combined-artifacts/ bash scripts/prepare-package-repository.sh \ combined-artifacts \ package-repository \ "${AGENT_TAG#agent-v}" - - uses: sigstore/cosign-installer@v3 - - - name: Prepare combined release checksums - shell: bash - run: | - set -euo pipefail - ( - cd release-assets - sha256sum ./openwrt-* > SHA256SUMS - ) - - - name: Attest combined release checksums - uses: actions/attest-build-provenance@v2 - with: - subject-path: release-assets/SHA256SUMS - - - name: Sign and upload combined release checksums + - name: Upload legacy packages env: AGENT_TAG: ${{ inputs.agent_tag }} GH_TOKEN: ${{ github.token }} shell: bash run: | set -euo pipefail - cosign sign-blob --yes \ - --bundle release-assets/SHA256SUMS.sigstore.json \ - release-assets/SHA256SUMS mapfile -t legacy_files < <(find legacy-publish -type f -print | sort) gh release upload "$AGENT_TAG" \ "${legacy_files[@]}" \ - release-assets/SHA256SUMS \ - release-assets/SHA256SUMS.sigstore.json \ --repo "$GITHUB_REPOSITORY" \ --clobber diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 677758c..146556a 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -208,7 +208,7 @@ jobs: name: openwrt-${{ matrix.release }}-${{ matrix.label }} path: artifacts/* if-no-files-found: error - retention-days: 30 + retention-days: 90 release: name: Publish agent GitHub release assets @@ -233,8 +233,17 @@ jobs: while IFS= read -r file; do artifact="$(basename "$(dirname "$file")")" cp "$file" "publish/${artifact}-$(basename "$file")" - done < <(find release-assets -type f ! -name 'SHA256SUMS' -print | sort) - (cd publish && sha256sum ./* > SHA256SUMS) + done < <(find release-assets -type f \( -name '*.ipk' -o -name '*.apk' \) -print | sort) + find publish -type f -print -quit | grep -q . + + - name: Prepare release notes + shell: bash + run: | + set -euo pipefail + bash scripts/extract-release-notes.sh \ + "$GITHUB_REF_NAME" \ + CHANGELOG.md \ + release-notes.md - name: Prepare signed package repository shell: bash @@ -257,16 +266,6 @@ jobs: with: subject-path: publish/* - - uses: sigstore/cosign-installer@v3 - - - name: Sign release checksums with GitHub OIDC - shell: bash - run: | - set -euo pipefail - cosign sign-blob --yes \ - --bundle publish/SHA256SUMS.sigstore.json \ - publish/SHA256SUMS - - name: Create release and upload packages env: GH_TOKEN: ${{ github.token }} @@ -278,10 +277,13 @@ jobs: gh release upload "${GITHUB_REF_NAME}" "${files[@]}" \ --repo "${GITHUB_REPOSITORY}" \ --clobber + gh release edit "${GITHUB_REF_NAME}" \ + --repo "${GITHUB_REPOSITORY}" \ + --notes-file release-notes.md else gh release create "${GITHUB_REF_NAME}" "${files[@]}" \ --repo "${GITHUB_REPOSITORY}" \ - --generate-notes \ + --notes-file release-notes.md \ --title "OpenWrt RMM Agent ${GITHUB_REF_NAME#agent-v}" fi diff --git a/.github/workflows/release-server.yml b/.github/workflows/release-server.yml index 3ace70d..bd04f5c 100644 --- a/.github/workflows/release-server.yml +++ b/.github/workflows/release-server.yml @@ -132,9 +132,17 @@ jobs: shell: bash run: | set -euo pipefail - if ! gh release view "$GITHUB_REF_NAME" --repo "$GITHUB_REPOSITORY" >/dev/null 2>&1; then + bash scripts/extract-release-notes.sh \ + "$GITHUB_REF_NAME" \ + CHANGELOG.md \ + release-notes.md + if gh release view "$GITHUB_REF_NAME" --repo "$GITHUB_REPOSITORY" >/dev/null 2>&1; then + gh release edit "$GITHUB_REF_NAME" \ + --repo "$GITHUB_REPOSITORY" \ + --notes-file release-notes.md + else gh release create "$GITHUB_REF_NAME" \ --repo "$GITHUB_REPOSITORY" \ - --generate-notes \ + --notes-file release-notes.md \ --title "OpenWrt RMM Server ${GITHUB_REF_NAME#server-v}" fi diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..b242be5 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,57 @@ +# Changelog + +This file contains user-facing release notes. Every section must match its Git tag; +the release workflow fails when notes for a new tag have not been prepared. + +## agent-v0.6.8 + +Stable agent release with more reliable LAN client presence detection. + +### Added + +- The agent safely probes private IPv4 addresses found in DHCP leases and reports the + results with its heartbeat. +- Probing is capped at 32 addresses, six concurrent requests, and a short timeout to + avoid noticeable load on the router or LAN. +- The server stores `first_seen`, `last_seen`, and the most recent probe time for each + client. +- The LuCI application keeps English as its default language and provides Russian through + the separate `luci-i18n-rmm-agent-ru` package. + +### Fixed + +- A static DHCP reservation is no longer treated as proof that a client is currently + connected. +- Clients are separated into Online, Recently online, and Unconfirmed states. +- DHCP, Wi-Fi, neighbor-table, and active-probe data are merged without duplicate + clients. + +### Compatibility + +- The API protocol remains `v1`; the server and agent can still be upgraded + independently. +- Existing agent configuration remains compatible without changes. +- The primary workflow builds OpenWrt 24.10 and 25.12 packages. OpenWrt 21.02, 22.03, + and 23.05 packages are added by the separate legacy workflow. + +### Installation + +Download the `.ipk` or `.apk` matching the OpenWrt release and target architecture. +Install `luci-app-rmm-agent` as well to configure the agent through LuCI. Install +`luci-i18n-rmm-agent-ru` for the Russian interface. + +## server-v0.8.1 + +Corrective server release for cloud SSH and LuCI tunnels. + +### Fixed + +- The server validates SSH and LuCI ports issued to agents and rejects invalid + endpoints. +- Reverse-tunnel parameters are validated before they are exposed to an operator. +- Boundary-value and malformed tunnel-service response tests were added. + +### Compatibility + +- No database migration is required. +- The agent protocol remains `v1`. diff --git a/README.md b/README.md new file mode 100644 index 0000000..d269973 --- /dev/null +++ b/README.md @@ -0,0 +1,139 @@ +

+ OpenWrt RMM logo +

+ +# OpenWrt RMM + +[Русская версия](README_RU.md) · [Releases](https://github.com/Benya9669/openwrt-rmm/releases) · [Changelog](CHANGELOG.md) · [Security](docs/security.md) + +OpenWrt RMM is a self-hosted remote monitoring and management platform for OpenWrt +routers. It combines a Go cloud server, a lightweight outbound agent, a responsive web +dashboard, temporary SSH/LuCI access, notifications, and signed OpenWrt package feeds. + +The agent does not require an inbound port on the router. It connects to the server over +HTTPS and opens a restricted reverse tunnel only when an authorized operator requests +remote access. + +## Highlights + +- Separate user accounts and router ownership. +- One-time router enrollment grants. +- Live inventory, health metrics, connectivity checks, and LAN client presence. +- Safe allowlisted diagnostics and package/UCI operations. +- Temporary SSH and LuCI access through an isolated cloud tunnel. +- Built-in notification center, e-mail, Telegram, and signed webhooks. +- Quiet hours, maintenance pauses, and per-router alert preferences. +- Responsive web UI and a LuCI application for agent configuration. +- Signed IPK/APK repositories for supported OpenWrt releases. + +## Components + +```text +Browser ──HTTPS──> RMM server ──SQLite + │ + ├── notification workers + └── isolated SSH tunnel service + ▲ +OpenWrt router ──outbound HTTPS/SSH───┘ + ├── rmm-agent-go-production + ├── luci-app-rmm-agent + └── luci-i18n-rmm-agent-ru (optional) +``` + +| Component | Purpose | License | +| --- | --- | --- | +| `server/`, `web/` | API, persistence, dashboard, notifications | AGPL-3.0-only | +| `deploy/tunnel/` | Restricted reverse SSH tunnel service | AGPL-3.0-only | +| `agent/` | Go agent, LuCI app, and OpenWrt packages | MIT | + +## Deployment + +Requirements: + +- Docker Engine with Compose v2; +- an HTTPS reverse proxy such as NPMplus; +- a wildcard DNS record and certificate for cloud LuCI access; +- an SSH key dedicated to router tunnels. + +Copy the example configuration and follow the production deployment guide: + +```sh +cp .env.example .env +docker compose -f compose.yaml -f compose.release.yaml pull +docker compose -f compose.yaml -f compose.release.yaml up -d +docker compose -f compose.yaml -f compose.release.yaml ps +``` + +Do not start production with the example secrets. Generate the tunnel key and configure +the required environment values first. + +- [Docker Compose deployment](docs/docker-compose.md) +- [NPMplus configuration](docs/npmplus.md) +- [KeenDNS-like wildcard router access](docs/keendns.md) +- [Architecture](docs/architecture.md) + +## Installing the OpenWrt agent + +The recommended method is the signed package feed: + +- OpenWrt 24.10 and older: IPK/opkg; +- OpenWrt 25.12 and newer: APK; +- OpenWrt 21.02, 22.03, and 23.05: legacy support tier. + +Install the runtime and LuCI application: + +```sh +opkg install rmm-agent-go-production luci-app-rmm-agent +# or +apk add rmm-agent-go-production luci-app-rmm-agent +``` + +English is the default LuCI language. Install the optional Russian translation: + +```sh +opkg install luci-i18n-rmm-agent-ru +# or +apk add luci-i18n-rmm-agent-ru +``` + +See [Signed OpenWrt package repository](docs/package-repository.md) for feed URLs, +verification keys, and architecture-specific instructions. + +## Development + +```sh +go test ./... +go vet ./... +node --check web/app.js +docker compose config +``` + +Run the server in explicit local development mode: + +```sh +RMM_INSECURE_DEV_MODE=true \ +RMM_OPERATOR_PASSWORD='replace-with-a-long-development-password' \ +go run ./server/cmd/rmm-server +``` + +Never enable insecure development mode on an internet-facing deployment. + +## Releases + +The server and agent use independent version lines: + +- `server-v*` publishes the server and tunnel container images; +- `agent-v*` publishes installable IPK/APK packages; +- release descriptions are maintained in English in [CHANGELOG.md](CHANGELOG.md). + +GitHub Releases contain only installable packages. Signed repository indexes and public +keys are published through GitHub Pages, while provenance is kept in GitHub attestations. +See [Release policy](RELEASES.md). + +## Security and license + +Review [the security model](docs/security.md) before exposing the service publicly. +Back up the SQLite volume before every server upgrade. + +The cloud application is licensed under `AGPL-3.0-only`; the OpenWrt agent and LuCI +packages are licensed under MIT. See [LICENSE.md](LICENSE.md) and [NOTICE.md](NOTICE.md). diff --git a/README_RU.md b/README_RU.md new file mode 100644 index 0000000..714b659 --- /dev/null +++ b/README_RU.md @@ -0,0 +1,140 @@ +

+ Логотип OpenWrt RMM +

+ +# OpenWrt RMM + +[English](README.md) · [Релизы](https://github.com/Benya9669/openwrt-rmm/releases) · [Changelog](CHANGELOG.md) · [Безопасность](docs/security.md) + +OpenWrt RMM — самостоятельная платформа удалённого мониторинга и управления роутерами +OpenWrt. В неё входят облачный сервер на Go, компактный исходящий агент, адаптивный +веб-интерфейс, временный доступ по SSH и LuCI, уведомления и подписанные репозитории +пакетов OpenWrt. + +На роутере не требуется открывать входящий порт. Агент подключается к серверу по HTTPS, +а ограниченный обратный туннель создаётся только по запросу авторизованного оператора. + +## Возможности + +- Отдельные аккаунты пользователей и привязка роутеров к владельцу. +- Одноразовые коды подключения роутеров. +- Инвентаризация, метрики, проверки соединения и определение присутствия LAN-клиентов. +- Безопасный ограниченный набор диагностических, пакетных и UCI-команд. +- Временный доступ к SSH и LuCI через изолированный облачный туннель. +- Центр уведомлений, e-mail, Telegram и подписанные webhook-запросы. +- Тихие часы, режим обслуживания и настройки алертов для каждого роутера. +- Адаптивный веб-интерфейс и LuCI-приложение для настройки агента. +- Подписанные IPK/APK-репозитории для поддерживаемых версий OpenWrt. + +## Компоненты + +```text +Браузер ──HTTPS──> RMM-сервер ──SQLite + │ + ├── обработчики уведомлений + └── изолированный SSH tunnel-сервис + ▲ +OpenWrt-роутер ──исходящие HTTPS/SSH─────┘ + ├── rmm-agent-go-production + ├── luci-app-rmm-agent + └── luci-i18n-rmm-agent-ru (необязательно) +``` + +| Компонент | Назначение | Лицензия | +| --- | --- | --- | +| `server/`, `web/` | API, база данных, интерфейс и уведомления | AGPL-3.0-only | +| `deploy/tunnel/` | Ограниченный обратный SSH-туннель | AGPL-3.0-only | +| `agent/` | Go-агент, LuCI и пакеты OpenWrt | MIT | + +## Развёртывание + +Потребуются: + +- Docker Engine и Compose v2; +- HTTPS reverse proxy, например NPMplus; +- wildcard DNS-запись и сертификат для облачного доступа к LuCI; +- отдельный SSH-ключ для туннелей роутеров. + +Скопируйте пример конфигурации и следуйте инструкции по развёртыванию: + +```sh +cp .env.example .env +docker compose -f compose.yaml -f compose.release.yaml pull +docker compose -f compose.yaml -f compose.release.yaml up -d +docker compose -f compose.yaml -f compose.release.yaml ps +``` + +Не запускайте прод с примерными секретами. Сначала создайте ключ туннеля и заполните +обязательные переменные окружения. + +- [Развёртывание через Docker Compose](docs/docker-compose.md) +- [Настройка NPMplus](docs/npmplus.md) +- [Wildcard-доступ к роутерам в стиле KeenDNS](docs/keendns.md) +- [Архитектура](docs/architecture.md) + +## Установка агента OpenWrt + +Рекомендуемый способ — подписанный пакетный репозиторий: + +- OpenWrt 24.10 и старше: IPK/opkg; +- OpenWrt 25.12 и новее: APK; +- OpenWrt 21.02, 22.03 и 23.05: уровень legacy-поддержки. + +Установите агент и LuCI-приложение: + +```sh +opkg install rmm-agent-go-production luci-app-rmm-agent +# или +apk add rmm-agent-go-production luci-app-rmm-agent +``` + +По умолчанию интерфейс LuCI остаётся английским. Русский язык устанавливается отдельным +стандартным пакетом: + +```sh +opkg install luci-i18n-rmm-agent-ru +# или +apk add luci-i18n-rmm-agent-ru +``` + +Адреса репозиториев, ключи проверки и инструкции для архитектур находятся в документе +[Подписанный репозиторий OpenWrt](docs/package-repository.md). + +## Разработка и проверки + +```sh +go test ./... +go vet ./... +node --check web/app.js +docker compose config +``` + +Запуск сервера в явно включённом локальном режиме разработки: + +```sh +RMM_INSECURE_DEV_MODE=true \ +RMM_OPERATOR_PASSWORD='replace-with-a-long-development-password' \ +go run ./server/cmd/rmm-server +``` + +Небезопасный режим разработки нельзя использовать на публичном сервере. + +## Релизы + +Сервер и агент версионируются независимо: + +- `server-v*` публикует контейнеры сервера и tunnel-сервиса; +- `agent-v*` публикует устанавливаемые IPK/APK-пакеты; +- подробные описания релизов ведутся на английском в [CHANGELOG.md](CHANGELOG.md). + +В GitHub Release загружаются только устанавливаемые пакеты. Подписанные индексы и +публичные ключи публикуются через GitHub Pages, а provenance хранится в GitHub +attestations. Подробнее: [политика релизов](RELEASES.md). + +## Безопасность и лицензирование + +Перед публикацией сервиса изучите [модель безопасности](docs/security.md). Перед каждым +обновлением серверной части создавайте резервную копию SQLite volume. + +Облачная часть распространяется по `AGPL-3.0-only`, агент и LuCI-пакеты — по MIT. +Подробнее: [LICENSE.md](LICENSE.md) и [NOTICE.md](NOTICE.md). diff --git a/RELEASES.md b/RELEASES.md index b0a4cf7..dcc2dd9 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -62,22 +62,27 @@ transition period; it should not silently reuse `v1`. ## Release commands +Before creating a tag, add an English `## ` section to `CHANGELOG.md`. The release +workflow uses that section verbatim and fails instead of publishing empty autogenerated +notes. + ```sh -git tag -a server-v0.8.1 -m "OpenWrt RMM Server 0.8.1" +git tag -s server-v0.8.1 -m "OpenWrt RMM Server 0.8.1" git push origin server-v0.8.1 -git tag -a agent-v0.6.8 -m "OpenWrt RMM Agent 0.6.8" +git tag -s agent-v0.6.8 -m "OpenWrt RMM Agent 0.6.8" git push origin agent-v0.6.8 ``` Pushing a server tag publishes the container image and creates a GitHub Release. Pushing -an agent tag builds the full OpenWrt matrix and creates a GitHub Release with packages and -checksums. It also publishes signed package feeds through GitHub Pages. Repository setup, -key generation and router configuration are documented in +an agent tag builds the full OpenWrt matrix and creates a GitHub Release containing only +installable `.ipk` and `.apk` packages. Repository indexes, public signing keys and +provenance metadata stay in the signed GitHub Pages package feed and GitHub attestations. +Repository setup, key generation and router configuration are documented in [`docs/package-repository.md`](docs/package-repository.md). `workflow_dispatch` can test either workflow without creating a release. -Server and tunnel images are published with SBOM/provenance attestations and keyless -Sigstore signatures bound to the release workflow identity. Agent checksums receive the -same GitHub OIDC-backed signature. IPK and APK feeds additionally use their native OpenWrt -repository signatures so `opkg` and `apk` can enforce trust on the router. +Server and tunnel images are published with SBOM/provenance attestations bound to the +release workflow identity. Agent packages receive GitHub build-provenance attestations. +IPK and APK feeds additionally use their native OpenWrt repository signatures so `opkg` +and `apk` can enforce trust on the router. diff --git a/agent/README.md b/agent/README.md index 272ce24..ccf0a71 100644 --- a/agent/README.md +++ b/agent/README.md @@ -7,6 +7,10 @@ Production package upgrades restart an already running agent so the new binary t Production Go agent for OpenWrt, with the shell implementation retained as a fallback runtime. +The LuCI application uses English by default. Russian is distributed as the standard +optional `luci-i18n-rmm-agent-ru` package so the router can select either language +through its normal LuCI language settings. + The agent, LuCI application and OpenWrt packaging under `agent/` are licensed under the MIT License. Binary OpenWrt packages install a copy of the license under `/usr/share/licenses//LICENSE`. diff --git a/agent/package/luci-app-rmm-agent/Makefile b/agent/package/luci-app-rmm-agent/Makefile index 7ef3dcb..c52a8e5 100644 --- a/agent/package/luci-app-rmm-agent/Makefile +++ b/agent/package/luci-app-rmm-agent/Makefile @@ -1,44 +1,16 @@ # SPDX-License-Identifier: MIT include $(TOPDIR)/rules.mk -PKG_NAME:=luci-app-rmm-agent -PKG_VERSION:=0.2.1 -PKG_RELEASE:=2 +PKG_VERSION:=0.2.2 +PKG_RELEASE:=1 PKG_MAINTAINER:=RMM OpenWrt PKG_LICENSE:=MIT PKG_LICENSE_FILES:=LICENSE -include $(INCLUDE_DIR)/package.mk +LUCI_TITLE:=OpenWrt RMM agent configuration +LUCI_DESCRIPTION:=Configure and control the OpenWrt RMM agent. +LUCI_DEPENDS:=+luci-base +LUCI_PKGARCH:=all -define Package/luci-app-rmm-agent - SECTION:=luci - CATEGORY:=LuCI - SUBMENU:=3. Applications - TITLE:=LuCI support for the OpenWrt RMM agent - DEPENDS:=+luci-base - PKGARCH:=all -endef - -define Package/luci-app-rmm-agent/description - LuCI application for configuring and controlling the OpenWrt RMM agent. -endef - -define Build/Compile -endef - -define Package/luci-app-rmm-agent/install - $(INSTALL_DIR) $(1)/www/luci-static/resources/view/services - $(INSTALL_DATA) ./htdocs/luci-static/resources/view/services/rmm-agent.js \ - $(1)/www/luci-static/resources/view/services/rmm-agent.js - $(INSTALL_DIR) $(1)/usr/share/luci/menu.d - $(INSTALL_DATA) ./root/usr/share/luci/menu.d/luci-app-rmm-agent.json \ - $(1)/usr/share/luci/menu.d/luci-app-rmm-agent.json - $(INSTALL_DIR) $(1)/usr/share/rpcd/acl.d - $(INSTALL_DATA) ./root/usr/share/rpcd/acl.d/luci-app-rmm-agent.json \ - $(1)/usr/share/rpcd/acl.d/luci-app-rmm-agent.json - $(INSTALL_DIR) $(1)/usr/share/licenses/luci-app-rmm-agent - $(INSTALL_DATA) ./LICENSE $(1)/usr/share/licenses/luci-app-rmm-agent/LICENSE -endef - -$(eval $(call BuildPackage,luci-app-rmm-agent)) +include $(TOPDIR)/feeds/luci/luci.mk diff --git a/agent/package/luci-app-rmm-agent/README.md b/agent/package/luci-app-rmm-agent/README.md index f436041..459f407 100644 --- a/agent/package/luci-app-rmm-agent/README.md +++ b/agent/package/luci-app-rmm-agent/README.md @@ -27,6 +27,16 @@ opkg install rmm-agent_*.ipk luci-app-rmm-agent_*.ipk On apk-based OpenWrt releases, install the generated `.apk` packages instead. +English remains the built-in default language. The same build produces the standard +optional Russian translation package: + +```text +luci-i18n-rmm-agent-ru +``` + +After it is installed, LuCI selects Russian according to the router's configured +interface language. + ## Reproducible Docker build The repository includes a containerized OpenWrt SDK build. By default it uses the diff --git a/agent/package/luci-app-rmm-agent/po/ru/rmm-agent.po b/agent/package/luci-app-rmm-agent/po/ru/rmm-agent.po new file mode 100644 index 0000000..528d5bd --- /dev/null +++ b/agent/package/luci-app-rmm-agent/po/ru/rmm-agent.po @@ -0,0 +1,81 @@ +msgid "" +msgstr "" +"Project-Id-Version: luci-app-rmm-agent\n" +"Language: ru\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : " +"n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n" + +msgid "RMM agent" +msgstr "RMM Агент" + +msgid "Command failed with code %d" +msgstr "Команда завершилась с кодом %d" + +msgid "RMM agent action completed." +msgstr "Действие RMM Агента выполнено." + +msgid "Unable to control RMM agent: %s" +msgstr "Не удалось управлять RMM Агентом: %s" + +msgid "Connect this router to your RMM account. Create a one-time enrollment grant in the RMM control panel, paste it below, save and restart the agent." +msgstr "Подключите этот роутер к своей учётной записи RMM. Создайте одноразовый код подключения в панели RMM, вставьте его ниже, сохраните настройки и перезапустите агент." + +msgid "Connection" +msgstr "Подключение" + +msgid "Enable agent" +msgstr "Включить агент" + +msgid "RMM server URL" +msgstr "Адрес сервера RMM" + +msgid "Use an HTTPS URL, or explicitly allow insecure HTTP in advanced settings." +msgstr "Используйте адрес HTTPS или явно разрешите небезопасный HTTP в дополнительных настройках." + +msgid "One-time enrollment grant" +msgstr "Одноразовый код подключения" + +msgid "The grant is consumed once and removed from the router after successful enrollment." +msgstr "Код используется один раз и удаляется с роутера после успешного подключения." + +msgid "Polling interval" +msgstr "Интервал опроса" + +msgid "Connectivity check targets" +msgstr "Адреса для проверки соединения" + +msgid "Advanced settings" +msgstr "Дополнительные настройки" + +msgid "Tunnel identity file" +msgstr "Файл ключа туннеля" + +msgid "Allow insecure HTTP" +msgstr "Разрешить небезопасный HTTP" + +msgid "Use only in an isolated lab. Agent credentials can otherwise be intercepted." +msgstr "Используйте только в изолированной тестовой сети. Иначе учётные данные агента могут быть перехвачены." + +msgid "Re-enroll on next restart" +msgstr "Подключить заново при следующем запуске" + +msgid "Deletes the current device identity. Create and enter a fresh one-time grant first." +msgstr "Удаляет текущую идентификацию устройства. Сначала создайте и введите новый одноразовый код подключения." + +msgid "Service status" +msgstr "Состояние службы" + +msgid "The RMM agent is running." +msgstr "RMM Агент запущен." + +msgid "The RMM agent is stopped." +msgstr "RMM Агент остановлен." + +msgid "Restart agent" +msgstr "Перезапустить агент" + +msgid "Start agent" +msgstr "Запустить агент" diff --git a/deploy/luci-builder/Dockerfile b/deploy/luci-builder/Dockerfile index a31a3f7..6170de9 100644 --- a/deploy/luci-builder/Dockerfile +++ b/deploy/luci-builder/Dockerfile @@ -141,10 +141,13 @@ RUN rm -rf tmp/info \ tmp/.packagedeps \ tmp/.packageinfo -RUN printf '%s\n' 'CONFIG_PACKAGE_luci-app-rmm-agent=m' >> .config \ +RUN printf '%s\n' \ + 'CONFIG_PACKAGE_luci-app-rmm-agent=m' \ + 'CONFIG_PACKAGE_luci-i18n-rmm-agent-ru=m' \ + >> .config \ && make defconfig \ && awk ' \ - /^CONFIG_PACKAGE_(ca-bundle|ip-tiny|iwinfo|libopenssl|luci-app-rmm-agent|openssh-client|openssh-keygen|rmm-agent-go-production|zlib)=m$/ { \ + /^CONFIG_PACKAGE_(ca-bundle|ip-tiny|iwinfo|libopenssl|luci-app-rmm-agent|luci-i18n-rmm-agent-ru|openssh-client|openssh-keygen|rmm-agent-go-production|zlib)=m$/ { \ print; \ next; \ } \ @@ -156,7 +159,8 @@ RUN printf '%s\n' 'CONFIG_PACKAGE_luci-app-rmm-agent=m' >> .config \ { print; } \ ' .config > .config.packages-pruned \ && mv .config.packages-pruned .config \ - && grep -q '^CONFIG_PACKAGE_luci-app-rmm-agent=m$' .config + && grep -q '^CONFIG_PACKAGE_luci-app-rmm-agent=m$' .config \ + && grep -q '^CONFIG_PACKAGE_luci-i18n-rmm-agent-ru=m$' .config RUN make -j1 package/luci-app-rmm-agent/compile V=s @@ -171,10 +175,13 @@ RUN --mount=type=secret,id=openwrt_usign_secret_b64,uid=1000,required=false \ find bin -type f \ \( -name 'luci-app-rmm-agent*.apk' \ -o -name 'luci-app-rmm-agent*.ipk' \ + -o -name 'luci-i18n-rmm-agent-ru*.apk' \ + -o -name 'luci-i18n-rmm-agent-ru*.ipk' \ -o -name 'rmm-agent*.apk' \ -o -name 'rmm-agent*.ipk' \) \ -exec cp '{}' /home/builder/artifacts/ \;; \ test -n "$(find /home/builder/artifacts -maxdepth 1 -type f \( -name '*.apk' -o -name '*.ipk' \) -print -quit)"; \ + test -n "$(find /home/builder/artifacts -maxdepth 1 -type f \( -name 'luci-i18n-rmm-agent-ru*.apk' -o -name 'luci-i18n-rmm-agent-ru*.ipk' \) -print -quit)"; \ cd /home/builder/artifacts; \ if find . -maxdepth 1 -type f -name '*.ipk' -print -quit | grep -q .; then \ MKHASH=/home/builder/sdk/staging_dir/host/bin/mkhash \ diff --git a/docs/package-repository.md b/docs/package-repository.md index 65ae4c9..420c8cf 100644 --- a/docs/package-repository.md +++ b/docs/package-repository.md @@ -86,10 +86,14 @@ Run it only after the main agent release has completed. The legacy workflow: 1. checks out and builds the exact agent tag; 2. signs the IPK repositories with the configured `usign` key; -3. combines them with the current release and reconstructs the complete repository; -4. refreshes signed checksums and uploads the legacy files to the existing GitHub Release; +3. downloads retained artifacts from the original tagged workflow and reconstructs the + complete repository; +4. uploads only installable legacy packages to the existing GitHub Release; 5. redeploys the complete current plus legacy repository to GitHub Pages. +Run the legacy workflow within 90 days of the tagged build while its internal artifacts +are retained. + The workflow refuses an older agent tag because publishing it would roll the `stable` feed back from the latest agent version. @@ -112,6 +116,8 @@ wget -O "/etc/opkg/keys/${key_id}" "${key_base}/${key_id}" printf 'src/gz rmm %s\n' "$feed" > /etc/opkg/customfeeds.conf.d/rmm.conf opkg update opkg install rmm-agent-go-production luci-app-rmm-agent +# Optional Russian LuCI translation: +opkg install luci-i18n-rmm-agent-ru ``` The workflow creates `Packages`, `Packages.gz` and `Packages.sig`. `opkg` verifies the @@ -130,6 +136,8 @@ wget -O /etc/apk/keys/rmm-openwrt.pem "${base}/keys/apk/rmm-openwrt.pem" printf '%s\n' "$repo" > /etc/apk/repositories.d/rmm.list apk update apk add rmm-agent-go-production luci-app-rmm-agent +# Optional Russian LuCI translation: +apk add luci-i18n-rmm-agent-ru ``` APK verifies the signed `packages.adb` index. Installation should not require @@ -138,17 +146,8 @@ not configured correctly. ## Release verification -Release assets also include a keyless Sigstore bundle for the combined checksums: - -```sh -cosign verify-blob \ - --bundle SHA256SUMS.sigstore.json \ - --certificate-identity-regexp \ - '^https://github.com/Benya9669/openwrt-rmm/.github/workflows/(build\.yml@refs/tags/agent-v.*|build-legacy\.yml@refs/heads/main)$' \ - --certificate-oidc-issuer https://token.actions.githubusercontent.com \ - SHA256SUMS -sha256sum --check SHA256SUMS -``` - -This Sigstore verification complements native package-manager trust; it does not replace -the `usign` or APK repository signature. +GitHub Releases contain only installable `.ipk` and `.apk` files. GitHub build-provenance +attestations remain available through the repository attestations page and GitHub CLI. +Package-manager installations rely on the signed feed: `opkg` verifies `Packages.sig`, +while APK verifies `packages.adb`. Public verification keys are served through GitHub +Pages and are not duplicated as release assets. diff --git a/scripts/extract-release-notes.sh b/scripts/extract-release-notes.sh new file mode 100644 index 0000000..f1b8b25 --- /dev/null +++ b/scripts/extract-release-notes.sh @@ -0,0 +1,39 @@ +#!/usr/bin/env bash +set -euo pipefail + +if [ "$#" -ne 3 ]; then + echo "usage: $0 " >&2 + exit 2 +fi + +tag="$1" +changelog="$2" +output="$3" + +if [ ! -f "$changelog" ]; then + echo "changelog does not exist: $changelog" >&2 + exit 1 +fi + +mkdir -p "$(dirname "$output")" + +if ! awk -v heading="## ${tag}" ' + $0 == heading { found = 1; next } + found && /^## / { exit } + found { print } + END { + if (!found) { + exit 3 + } + } +' "$changelog" > "$output"; then + rm -f "$output" + echo "release notes section is missing: ## ${tag}" >&2 + exit 1 +fi + +if ! grep -q '[^[:space:]]' "$output"; then + rm -f "$output" + echo "release notes section is empty: ## ${tag}" >&2 + exit 1 +fi