release: prepare agent 0.6.8 publication
This commit is contained in:
@@ -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
|
||||
|
||||
|
||||
+16
-14
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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`.
|
||||
@@ -0,0 +1,139 @@
|
||||
<p align="center">
|
||||
<img src="web/web-app-192.png" width="112" alt="OpenWrt RMM logo">
|
||||
</p>
|
||||
|
||||
# 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).
|
||||
+140
@@ -0,0 +1,140 @@
|
||||
<p align="center">
|
||||
<img src="web/web-app-192.png" width="112" alt="Логотип OpenWrt RMM">
|
||||
</p>
|
||||
|
||||
# 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).
|
||||
+14
-9
@@ -62,22 +62,27 @@ transition period; it should not silently reuse `v1`.
|
||||
|
||||
## Release commands
|
||||
|
||||
Before creating a tag, add an English `## <tag>` 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.
|
||||
|
||||
@@ -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/<package>/LICENSE`.
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 "Запустить агент"
|
||||
@@ -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 \
|
||||
|
||||
+15
-16
@@ -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.
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
if [ "$#" -ne 3 ]; then
|
||||
echo "usage: $0 <tag> <changelog> <output>" >&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
|
||||
Reference in New Issue
Block a user