From 01e0350cde290a53ce0a8bf37f3ba6b5639a58e0 Mon Sep 17 00:00:00 2001 From: benya Date: Tue, 21 Jul 2026 00:20:48 +0300 Subject: [PATCH] feat(agent): package stable Go runtime for OpenWrt --- agent/README.md | 21 ++- agent/go/cmd/rmm-agent/main.go | 6 +- agent/go/cmd/rmm-agent/main_test.go | 32 +++- agent/openwrt/rmm-agent-go-production.init | 28 +++- agent/openwrt/rmm-agent-go.init | 28 +++- agent/openwrt/rmm-agent.init | 28 +++- agent/openwrt/rmm-agent.sh | 2 +- agent/package/luci-app-rmm-agent/Makefile | 42 ++++- agent/package/luci-app-rmm-agent/README.md | 64 ++++++++ .../package/rmm-agent-go-production/Makefile | 4 +- .../files/etc/init.d/rmm-agent | 28 +++- agent/package/rmm-agent-go/Makefile | 2 +- .../files/etc/init.d/rmm-agent-go | 28 +++- agent/package/rmm-agent/Makefile | 3 +- .../rmm-agent/files/etc/init.d/rmm-agent | 28 +++- .../package/rmm-agent/files/usr/bin/rmm-agent | 2 +- deploy/luci-builder/Dockerfile | 151 ++++++++++++++++++ web/app.js | 2 +- 18 files changed, 461 insertions(+), 38 deletions(-) create mode 100644 deploy/luci-builder/Dockerfile diff --git a/agent/README.md b/agent/README.md index cf0f07c..116a48d 100644 --- a/agent/README.md +++ b/agent/README.md @@ -1,6 +1,6 @@ # OpenWrt RMM Agent -Production MVP shell agent for OpenWrt, plus an experimental Go agent preview. +Production Go agent for OpenWrt, with the shell implementation retained as a fallback runtime. The agent uses outbound HTTP polling: @@ -40,7 +40,7 @@ DEVICE_TOKEN="..." sh ./agent/openwrt/rmm-agent.sh ``` -## Go Agent Preview +## Go Agent The Go agent lives at: @@ -50,7 +50,7 @@ agent/go/cmd/rmm-agent It is protocol-compatible with the server for enrollment, heartbeat, inventory, metrics, command polling, command result reporting, lock handling, backoff, graceful shutdown, and result spooling. Its inventory payload includes system metadata, interfaces, routes, WAN IP, DHCP leases, Wi-Fi clients, memory, disk, interface counters, package manager metadata, and connectivity checks. -The Go agent is not packaged as the production OpenWrt agent yet, but it supports the migrated command allowlist: +The Go agent is available as the production OpenWrt package and supports the migrated command allowlist: - `ping` - `traceroute` @@ -88,7 +88,7 @@ Run once against an existing config: ./tmp/rmm-agent-go -config /etc/rmm-agent.conf -once ``` -For side-by-side testing, use a separate config and hostname suffix so the Go preview enrolls as a separate device: +For side-by-side testing, use a separate config and hostname suffix so the Go agent enrolls as a separate device: ```sh cp /etc/rmm-agent.conf /etc/rmm-agent-go.conf @@ -129,13 +129,24 @@ chmod +x /usr/bin/rmm-agent cp ./agent/openwrt/rmm-agent-go-production.init /etc/init.d/rmm-agent chmod +x /etc/init.d/rmm-agent sed -i '/^HOSTNAME_SUFFIX=/d;/^HOSTNAME_OVERRIDE=/d;/^LOCK_FILE=/d;/^SPOOL_DIR=/d;/^BACKUP_DIR=/d;/^TUNNEL_STATE_DIR=/d' /etc/rmm-agent.conf -rm -rf /tmp/rmm-agent.lock +rmdir /tmp/rmm-agent.lock 2>/dev/null || true /etc/init.d/rmm-agent enable /etc/init.d/rmm-agent start ``` This keeps the existing `/etc/rmm-agent.conf`, including `DEVICE_ID` and `DEVICE_TOKEN`, so the router continues as the same RMM object instead of enrolling as a duplicate `-go` device. Keep the shell script backup until the Go service has been stable for at least one maintenance window. +The procd service removes the configured `LOCK_FILE` only after the agent process has +stopped. To verify the lifecycle during a maintenance window: + +```sh +/etc/init.d/rmm-agent stop +test ! -e /tmp/rmm-agent.lock && echo "lock removed" +/etc/init.d/rmm-agent start +``` + +If `LOCK_FILE` is overridden in `/etc/rmm-agent.conf`, check that path instead. + ## OpenWrt Package Package skeleton: diff --git a/agent/go/cmd/rmm-agent/main.go b/agent/go/cmd/rmm-agent/main.go index 79d4027..088d998 100644 --- a/agent/go/cmd/rmm-agent/main.go +++ b/agent/go/cmd/rmm-agent/main.go @@ -23,7 +23,7 @@ import ( "time" ) -const agentVersion = "0.5.0-go-preview" +const agentVersion = "0.5.0" type config struct { ServerURL string @@ -249,7 +249,7 @@ func acquireLock(path string) (func(), error) { if err := os.Mkdir(path, 0o700); err != nil { return nil, fmt.Errorf("another rmm-agent instance is running") } - return func() { _ = os.RemoveAll(path) }, nil + return func() { _ = os.Remove(path) }, nil } func enroll(ctx context.Context, client *http.Client, cfg *config) error { @@ -485,7 +485,7 @@ func runCommand(ctx context.Context, cfg config, cmd command) (string, int) { case "remote_ssh_close": return remoteSSHCloseOutput(cfg, args) default: - return fmt.Sprintf("go agent preview does not implement command %q yet; shell agent remains the production command runner\n", cmd.Type), 2 + return fmt.Sprintf("rmm-agent does not implement command %q\n", cmd.Type), 2 } } diff --git a/agent/go/cmd/rmm-agent/main_test.go b/agent/go/cmd/rmm-agent/main_test.go index e9feb87..159366b 100644 --- a/agent/go/cmd/rmm-agent/main_test.go +++ b/agent/go/cmd/rmm-agent/main_test.go @@ -1,6 +1,36 @@ package main -import "testing" +import ( + "errors" + "os" + "path/filepath" + "testing" +) + +func TestAgentVersionIsStable(t *testing.T) { + if agentVersion != "0.5.0" { + t.Fatalf("unexpected agent version %q", agentVersion) + } +} + +func TestAcquireLockRemovesDirectoryOnUnlock(t *testing.T) { + lockPath := filepath.Join(t.TempDir(), "rmm-agent.lock") + unlock, err := acquireLock(lockPath) + if err != nil { + t.Fatalf("acquireLock() error: %v", err) + } + if _, err := os.Stat(lockPath); err != nil { + t.Fatalf("lock directory was not created: %v", err) + } + if _, err := acquireLock(lockPath); err == nil { + t.Fatal("second acquireLock() unexpectedly succeeded") + } + + unlock() + if _, err := os.Stat(lockPath); !errors.Is(err, os.ErrNotExist) { + t.Fatalf("lock directory still exists after unlock: %v", err) + } +} func TestParseIwinfoAssocList(t *testing.T) { output := `AA:BB:CC:DD:EE:FF -49 dBm / -95 dBm (SNR 46) 210 ms ago diff --git a/agent/openwrt/rmm-agent-go-production.init b/agent/openwrt/rmm-agent-go-production.init index d66f254..3a58af8 100644 --- a/agent/openwrt/rmm-agent-go-production.init +++ b/agent/openwrt/rmm-agent-go-production.init @@ -6,6 +6,29 @@ USE_PROCD=1 PROG=/usr/bin/rmm-agent CONFIG_FILE=/etc/rmm-agent.conf +DEFAULT_LOCK_FILE=/tmp/rmm-agent.lock + +configured_lock_file() { + local lock_file + lock_file="$(sed -n 's/^LOCK_FILE="\([^" ]*\)"$/\1/p' "$CONFIG_FILE" 2>/dev/null | tail -n 1)" + printf '%s\n' "${lock_file:-$DEFAULT_LOCK_FILE}" +} + +remove_lock_file() { + local lock_file + lock_file="$(configured_lock_file)" + case "$lock_file" in + /*) ;; + *) logger -t rmm-agent "refusing to remove non-absolute lock path"; return 0 ;; + esac + case "$lock_file" in + /|/tmp|/run|/var|/var/run|/var/lock) + logger -t rmm-agent "refusing to remove unsafe lock path" + return 0 + ;; + esac + rmdir "$lock_file" 2>/dev/null || true +} start_service() { procd_open_instance @@ -17,7 +40,6 @@ start_service() { procd_close_instance } -stop_service() { - service_stop "$PROG" - rm -rf /tmp/rmm-agent.lock +service_stopped() { + remove_lock_file } diff --git a/agent/openwrt/rmm-agent-go.init b/agent/openwrt/rmm-agent-go.init index abc0aa0..1ada49e 100644 --- a/agent/openwrt/rmm-agent-go.init +++ b/agent/openwrt/rmm-agent-go.init @@ -6,6 +6,29 @@ USE_PROCD=1 PROG=/usr/bin/rmm-agent-go CONFIG_FILE=/etc/rmm-agent-go.conf +DEFAULT_LOCK_FILE=/tmp/rmm-agent-go.lock + +configured_lock_file() { + local lock_file + lock_file="$(sed -n 's/^LOCK_FILE="\([^" ]*\)"$/\1/p' "$CONFIG_FILE" 2>/dev/null | tail -n 1)" + printf '%s\n' "${lock_file:-$DEFAULT_LOCK_FILE}" +} + +remove_lock_file() { + local lock_file + lock_file="$(configured_lock_file)" + case "$lock_file" in + /*) ;; + *) logger -t rmm-agent-go "refusing to remove non-absolute lock path"; return 0 ;; + esac + case "$lock_file" in + /|/tmp|/run|/var|/var/run|/var/lock) + logger -t rmm-agent-go "refusing to remove unsafe lock path" + return 0 + ;; + esac + rmdir "$lock_file" 2>/dev/null || true +} start_service() { procd_open_instance @@ -17,7 +40,6 @@ start_service() { procd_close_instance } -stop_service() { - service_stop "$PROG" - rm -rf /tmp/rmm-agent-go.lock +service_stopped() { + remove_lock_file } diff --git a/agent/openwrt/rmm-agent.init b/agent/openwrt/rmm-agent.init index 41872c1..a783cc8 100644 --- a/agent/openwrt/rmm-agent.init +++ b/agent/openwrt/rmm-agent.init @@ -6,6 +6,29 @@ USE_PROCD=1 PROG=/usr/bin/rmm-agent CONFIG_FILE=/etc/rmm-agent.conf +DEFAULT_LOCK_FILE=/tmp/rmm-agent.lock + +configured_lock_file() { + local lock_file + lock_file="$(sed -n 's/^LOCK_FILE="\([^" ]*\)"$/\1/p' "$CONFIG_FILE" 2>/dev/null | tail -n 1)" + printf '%s\n' "${lock_file:-$DEFAULT_LOCK_FILE}" +} + +remove_lock_file() { + local lock_file + lock_file="$(configured_lock_file)" + case "$lock_file" in + /*) ;; + *) logger -t rmm-agent "refusing to remove non-absolute lock path"; return 0 ;; + esac + case "$lock_file" in + /|/tmp|/run|/var|/var/run|/var/lock) + logger -t rmm-agent "refusing to remove unsafe lock path" + return 0 + ;; + esac + rmdir "$lock_file" 2>/dev/null || true +} start_service() { procd_open_instance @@ -18,7 +41,6 @@ start_service() { procd_close_instance } -stop_service() { - service_stop "$PROG" - rm -rf /tmp/rmm-agent.lock +service_stopped() { + remove_lock_file } diff --git a/agent/openwrt/rmm-agent.sh b/agent/openwrt/rmm-agent.sh index 8ad3d8e..730398b 100644 --- a/agent/openwrt/rmm-agent.sh +++ b/agent/openwrt/rmm-agent.sh @@ -28,7 +28,7 @@ cleanup() { if [ -n "${SLEEP_PID:-}" ]; then kill "$SLEEP_PID" 2>/dev/null || true fi - rm -rf "$LOCK_FILE" + rmdir "$LOCK_FILE" 2>/dev/null || true } stop_agent() { diff --git a/agent/package/luci-app-rmm-agent/Makefile b/agent/package/luci-app-rmm-agent/Makefile index e944aba..b7d0b3c 100644 --- a/agent/package/luci-app-rmm-agent/Makefile +++ b/agent/package/luci-app-rmm-agent/Makefile @@ -1,8 +1,40 @@ include $(TOPDIR)/rules.mk -LUCI_TITLE:=LuCI support for the OpenWrt RMM agent -LUCI_DEPENDS:=+luci-base -LUCI_PKGARCH:=all -PKG_MAINTAINER:=RMM OpenWrt +PKG_NAME:=luci-app-rmm-agent +PKG_VERSION:=0.1.0 +PKG_RELEASE:=1 -include $(TOPDIR)/feeds/luci/luci.mk +PKG_MAINTAINER:=RMM OpenWrt +PKG_LICENSE:=MIT + +include $(INCLUDE_DIR)/package.mk + +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 +endef + +$(eval $(call BuildPackage,luci-app-rmm-agent)) diff --git a/agent/package/luci-app-rmm-agent/README.md b/agent/package/luci-app-rmm-agent/README.md index 107d697..511e63e 100644 --- a/agent/package/luci-app-rmm-agent/README.md +++ b/agent/package/luci-app-rmm-agent/README.md @@ -26,3 +26,67 @@ opkg install rmm-agent_*.ipk luci-app-rmm-agent_*.ipk ``` On apk-based OpenWrt releases, install the generated `.apk` packages instead. + +## Reproducible Docker build + +The repository includes a containerized OpenWrt SDK build. By default it uses the +official OpenWrt 25.12.4 `ramips/mt7621` SDK for ASUS RT-AX53U and compiles the Go +agent as little-endian MIPS with software floating point. It builds the LuCI +application, the shell agent and the production Go agent together: + +```sh +docker build \ + --file deploy/luci-builder/Dockerfile \ + --target artifacts \ + --output type=local,dest=dist/rmm-openwrt-25.12.4-ramips-mt7621 \ + . +``` + +The output directory contains the installable packages and `SHA256SUMS`. +`luci-app-rmm-agent` and the shell `rmm-agent` are architecture-independent. The Go +runtime is architecture-specific and is built as `linux/mipsle` with +`GOMIPS=softfloat` by default. Use an SDK from the same OpenWrt release and target as +the router, and set the matching Go architecture when targeting another device. For +example, an x86_64 router needs this additional build argument: + +```sh +--build-arg RMM_GOARCH=amd64 --build-arg RMM_GOAMD64=v1 +``` + +Override `OPENWRT_SDK_URL` and `OPENWRT_SDK_SHA256` with `--build-arg` when building for +another release or target. Install exactly one runtime package: `rmm-agent` or +`rmm-agent-go-production`. They intentionally conflict because both own the same service +and executable paths. + +## Install on OpenWrt 25.12.4 + +For the ASUS RT-AX53U (`ramips/mt7621`), copy the production Go runtime and the LuCI +application to the router. Do not copy or install the shell runtime at the same time: + +```sh +cd dist/rmm-openwrt-25.12.4-ramips-mt7621 +sha256sum -c SHA256SUMS +scp \ + rmm-agent-go-production-0.5.0-r2.apk \ + luci-app-rmm-agent-0.1.0-r1.apk \ + root@ROUTER_IP:/tmp/ +``` + +Then install the locally built, unsigned packages over SSH: + +```sh +apk add --allow-untrusted \ + /tmp/rmm-agent-go-production-0.5.0-r2.apk \ + /tmp/luci-app-rmm-agent-0.1.0-r1.apk +/etc/init.d/rpcd restart +/etc/init.d/uhttpd restart +``` + +Open LuCI, go to **Services → RMM agent**, enter the HTTPS server URL and a fresh +one-time enrollment grant, enable the agent, save the settings, and start or restart the +service. Verify it from SSH with: + +```sh +/etc/init.d/rmm-agent status +logread -e rmm-agent +``` diff --git a/agent/package/rmm-agent-go-production/Makefile b/agent/package/rmm-agent-go-production/Makefile index 2a4422c..50ae4d2 100644 --- a/agent/package/rmm-agent-go-production/Makefile +++ b/agent/package/rmm-agent-go-production/Makefile @@ -2,7 +2,7 @@ include $(TOPDIR)/rules.mk PKG_NAME:=rmm-agent-go-production PKG_VERSION:=0.5.0 -PKG_RELEASE:=1 +PKG_RELEASE:=2 PKG_MAINTAINER:=RMM OpenWrt PKG_LICENSE:=MIT @@ -14,6 +14,8 @@ define Package/rmm-agent-go-production CATEGORY:=Administration TITLE:=OpenWrt RMM Go agent (production replacement) DEPENDS:=+ca-bundle +ip-tiny +iwinfo +openssh-client +openssh-keygen + PROVIDES:=rmm-agent-runtime + CONFLICTS:=rmm-agent endef define Package/rmm-agent-go-production/description diff --git a/agent/package/rmm-agent-go-production/files/etc/init.d/rmm-agent b/agent/package/rmm-agent-go-production/files/etc/init.d/rmm-agent index a0ecea3..946e1f9 100644 --- a/agent/package/rmm-agent-go-production/files/etc/init.d/rmm-agent +++ b/agent/package/rmm-agent-go-production/files/etc/init.d/rmm-agent @@ -6,6 +6,29 @@ USE_PROCD=1 PROG=/usr/bin/rmm-agent CONFIG_FILE=/etc/rmm-agent.conf +DEFAULT_LOCK_FILE=/tmp/rmm-agent.lock + +configured_lock_file() { + local lock_file + lock_file="$(sed -n 's/^LOCK_FILE="\([^" ]*\)"$/\1/p' "$CONFIG_FILE" 2>/dev/null | tail -n 1)" + printf '%s\n' "${lock_file:-$DEFAULT_LOCK_FILE}" +} + +remove_lock_file() { + local lock_file + lock_file="$(configured_lock_file)" + case "$lock_file" in + /*) ;; + *) logger -t rmm-agent "refusing to remove non-absolute lock path"; return 0 ;; + esac + case "$lock_file" in + /|/tmp|/run|/var|/var/run|/var/lock) + logger -t rmm-agent "refusing to remove unsafe lock path" + return 0 + ;; + esac + rmdir "$lock_file" 2>/dev/null || true +} start_service() { [ -x /usr/libexec/rmm-agent-uci-sync ] && /usr/libexec/rmm-agent-uci-sync @@ -30,7 +53,6 @@ service_triggers() { procd_add_reload_trigger "rmm-agent" } -stop_service() { - service_stop "$PROG" - rm -rf /tmp/rmm-agent.lock +service_stopped() { + remove_lock_file } diff --git a/agent/package/rmm-agent-go/Makefile b/agent/package/rmm-agent-go/Makefile index 1c475db..8a42eef 100644 --- a/agent/package/rmm-agent-go/Makefile +++ b/agent/package/rmm-agent-go/Makefile @@ -2,7 +2,7 @@ include $(TOPDIR)/rules.mk PKG_NAME:=rmm-agent-go PKG_VERSION:=0.5.0 -PKG_RELEASE:=1 +PKG_RELEASE:=2 PKG_MAINTAINER:=RMM OpenWrt PKG_LICENSE:=MIT diff --git a/agent/package/rmm-agent-go/files/etc/init.d/rmm-agent-go b/agent/package/rmm-agent-go/files/etc/init.d/rmm-agent-go index abc0aa0..1ada49e 100644 --- a/agent/package/rmm-agent-go/files/etc/init.d/rmm-agent-go +++ b/agent/package/rmm-agent-go/files/etc/init.d/rmm-agent-go @@ -6,6 +6,29 @@ USE_PROCD=1 PROG=/usr/bin/rmm-agent-go CONFIG_FILE=/etc/rmm-agent-go.conf +DEFAULT_LOCK_FILE=/tmp/rmm-agent-go.lock + +configured_lock_file() { + local lock_file + lock_file="$(sed -n 's/^LOCK_FILE="\([^" ]*\)"$/\1/p' "$CONFIG_FILE" 2>/dev/null | tail -n 1)" + printf '%s\n' "${lock_file:-$DEFAULT_LOCK_FILE}" +} + +remove_lock_file() { + local lock_file + lock_file="$(configured_lock_file)" + case "$lock_file" in + /*) ;; + *) logger -t rmm-agent-go "refusing to remove non-absolute lock path"; return 0 ;; + esac + case "$lock_file" in + /|/tmp|/run|/var|/var/run|/var/lock) + logger -t rmm-agent-go "refusing to remove unsafe lock path" + return 0 + ;; + esac + rmdir "$lock_file" 2>/dev/null || true +} start_service() { procd_open_instance @@ -17,7 +40,6 @@ start_service() { procd_close_instance } -stop_service() { - service_stop "$PROG" - rm -rf /tmp/rmm-agent-go.lock +service_stopped() { + remove_lock_file } diff --git a/agent/package/rmm-agent/Makefile b/agent/package/rmm-agent/Makefile index 33cdcbf..b2f1ead 100644 --- a/agent/package/rmm-agent/Makefile +++ b/agent/package/rmm-agent/Makefile @@ -2,7 +2,7 @@ include $(TOPDIR)/rules.mk PKG_NAME:=rmm-agent PKG_VERSION:=0.1.0 -PKG_RELEASE:=1 +PKG_RELEASE:=2 PKG_MAINTAINER:=RMM OpenWrt PKG_LICENSE:=MIT @@ -14,6 +14,7 @@ define Package/rmm-agent CATEGORY:=Administration TITLE:=OpenWrt RMM agent DEPENDS:=+uclient-fetch +ubus +ip-tiny + PROVIDES:=rmm-agent-runtime PKGARCH:=all endef diff --git a/agent/package/rmm-agent/files/etc/init.d/rmm-agent b/agent/package/rmm-agent/files/etc/init.d/rmm-agent index f8e163b..00205e0 100644 --- a/agent/package/rmm-agent/files/etc/init.d/rmm-agent +++ b/agent/package/rmm-agent/files/etc/init.d/rmm-agent @@ -6,6 +6,29 @@ USE_PROCD=1 PROG=/usr/bin/rmm-agent CONFIG_FILE=/etc/rmm-agent.conf +DEFAULT_LOCK_FILE=/tmp/rmm-agent.lock + +configured_lock_file() { + local lock_file + lock_file="$(sed -n 's/^LOCK_FILE="\([^" ]*\)"$/\1/p' "$CONFIG_FILE" 2>/dev/null | tail -n 1)" + printf '%s\n' "${lock_file:-$DEFAULT_LOCK_FILE}" +} + +remove_lock_file() { + local lock_file + lock_file="$(configured_lock_file)" + case "$lock_file" in + /*) ;; + *) logger -t rmm-agent "refusing to remove non-absolute lock path"; return 0 ;; + esac + case "$lock_file" in + /|/tmp|/run|/var|/var/run|/var/lock) + logger -t rmm-agent "refusing to remove unsafe lock path" + return 0 + ;; + esac + rmdir "$lock_file" 2>/dev/null || true +} start_service() { [ -x /usr/libexec/rmm-agent-uci-sync ] && /usr/libexec/rmm-agent-uci-sync @@ -31,7 +54,6 @@ service_triggers() { procd_add_reload_trigger "rmm-agent" } -stop_service() { - service_stop "$PROG" - rm -rf /tmp/rmm-agent.lock +service_stopped() { + remove_lock_file } diff --git a/agent/package/rmm-agent/files/usr/bin/rmm-agent b/agent/package/rmm-agent/files/usr/bin/rmm-agent index 9d44d85..e9c91c3 100644 --- a/agent/package/rmm-agent/files/usr/bin/rmm-agent +++ b/agent/package/rmm-agent/files/usr/bin/rmm-agent @@ -28,7 +28,7 @@ cleanup() { if [ -n "${SLEEP_PID:-}" ]; then kill "$SLEEP_PID" 2>/dev/null || true fi - rm -rf "$LOCK_FILE" + rmdir "$LOCK_FILE" 2>/dev/null || true } stop_agent() { diff --git a/deploy/luci-builder/Dockerfile b/deploy/luci-builder/Dockerfile new file mode 100644 index 0000000..d737e17 --- /dev/null +++ b/deploy/luci-builder/Dockerfile @@ -0,0 +1,151 @@ +# syntax=docker/dockerfile:1 + +FROM golang:1.26.5-bookworm AS go-agent + +ARG RMM_GOARCH=mipsle +ARG RMM_GOARM=7 +ARG RMM_GOMIPS=softfloat +ARG RMM_GOAMD64=v1 + +WORKDIR /src + +COPY go.mod go.sum ./ +COPY agent/go agent/go + +RUN export CGO_ENABLED=0 GOOS=linux GOARCH="${RMM_GOARCH}" \ + && case "${RMM_GOARCH}" in \ + arm) export GOARM="${RMM_GOARM}" ;; \ + mips|mipsle) export GOMIPS="${RMM_GOMIPS}" ;; \ + amd64) export GOAMD64="${RMM_GOAMD64}" ;; \ + esac \ + && mkdir /out \ + && go build -trimpath -ldflags="-s -w" \ + -o /out/rmm-agent ./agent/go/cmd/rmm-agent + +FROM debian:bookworm-slim AS builder + +ARG OPENWRT_SDK_URL="https://downloads.openwrt.org/releases/25.12.4/targets/ramips/mt7621/openwrt-sdk-25.12.4-ramips-mt7621_gcc-14.3.0_musl.Linux-x86_64.tar.zst" +ARG OPENWRT_SDK_SHA256="03f4766fcfbae86a814cbbf194226fa7e9ec66be2d4273bc1a0927a72a43a333" + +RUN apt-get update \ + && DEBIAN_FRONTEND=noninteractive apt-get install --yes --no-install-recommends \ + build-essential \ + ca-certificates \ + curl \ + file \ + gawk \ + gettext \ + git \ + libncurses-dev \ + libssl-dev \ + perl \ + python3 \ + python3-setuptools \ + rsync \ + unzip \ + zstd \ + && rm -rf /var/lib/apt/lists/* \ + && useradd --create-home --uid 1000 builder + +USER builder +WORKDIR /home/builder + +RUN curl --fail --location --show-error --silent \ + --output openwrt-sdk.tar.zst "${OPENWRT_SDK_URL}" \ + && echo "${OPENWRT_SDK_SHA256} openwrt-sdk.tar.zst" | sha256sum --check --strict \ + && mkdir sdk \ + && tar --zstd --extract --file openwrt-sdk.tar.zst --strip-components=1 --directory sdk \ + && rm openwrt-sdk.tar.zst + +USER root + +RUN apt-get update \ + && DEBIAN_FRONTEND=noninteractive apt-get install --yes --no-install-recommends wget \ + && rm -rf /var/lib/apt/lists/* + +USER builder +WORKDIR /home/builder/sdk + +RUN ./scripts/feeds update -a \ + && for package_name in \ + ca-bundle \ + ip-tiny \ + iwinfo \ + luci-base \ + mbedtls \ + openssh-client \ + openssh-keygen \ + ubus \ + uclient-fetch; \ + do \ + ./scripts/feeds install "${package_name}"; \ + done + +COPY --chown=builder:builder agent/package/rmm-agent package/rmm-agent +COPY --chown=builder:builder agent/package/rmm-agent-go-production package/rmm-agent-go-production +COPY --from=go-agent --chown=builder:builder /out/rmm-agent package/rmm-agent-go-production/files/usr/bin/rmm-agent + +RUN printf '%s\n' \ + '# CONFIG_ALL is not set' \ + '# CONFIG_ALL_KMODS is not set' \ + '# CONFIG_ALL_NONSHARED is not set' \ + 'CONFIG_PACKAGE_libmbedtls=y' \ + 'CONFIG_PACKAGE_libustream-mbedtls=y' \ + 'CONFIG_PACKAGE_rmm-agent=m' \ + 'CONFIG_PACKAGE_rmm-agent-go-production=m' \ + > .config \ + && make defconfig \ + && awk ' \ + /^CONFIG_PACKAGE_[^=]+=[ym]$/ { \ + split($0, parts, "="); \ + print "# " parts[1] " is not set"; \ + next; \ + } \ + { print; } \ + ' .config > .config.packages-pruned \ + && mv .config.packages-pruned .config \ + && printf '%s\n' \ + 'CONFIG_PACKAGE_libmbedtls=y' \ + 'CONFIG_PACKAGE_libustream-mbedtls=y' \ + 'CONFIG_PACKAGE_rmm-agent=m' \ + 'CONFIG_PACKAGE_rmm-agent-go-production=m' \ + >> .config \ + && make defconfig + +RUN grep -E '^CONFIG_PACKAGE_(libmbedtls|libustream-mbedtls)=' .config \ + && make -j"$(nproc)" package/feeds/base/mbedtls/compile \ + && make -j"$(nproc)" package/feeds/base/ustream-ssl/compile + +RUN make -j"$(nproc)" \ + package/rmm-agent/compile \ + package/rmm-agent-go-production/compile + +COPY --chown=builder:builder agent/package/luci-app-rmm-agent package/luci-app-rmm-agent + +RUN rm -rf tmp/info \ + && rm -f \ + tmp/.config-package.in \ + tmp/.packagedeps \ + tmp/.packageinfo + +RUN printf '%s\n' 'CONFIG_PACKAGE_luci-app-rmm-agent=m' >> .config \ + && make defconfig + +RUN make -j1 package/luci-app-rmm-agent/compile V=s + +RUN mkdir -p /home/builder/artifacts \ + && find bin -type f \ + \( -name 'luci-app-rmm-agent*.apk' \ + -o -name 'luci-app-rmm-agent*.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)" \ + && cd /home/builder/artifacts \ + && find . -maxdepth 1 -type f \( -name '*.apk' -o -name '*.ipk' \) -print \ + | LC_ALL=C sort \ + | xargs sha256sum > SHA256SUMS + +FROM scratch AS artifacts + +COPY --from=builder /home/builder/artifacts/ / diff --git a/web/app.js b/web/app.js index b3e7de8..461facf 100644 --- a/web/app.js +++ b/web/app.js @@ -25,7 +25,7 @@ const state = { presetReview: null, }; -const EXPECTED_AGENT_VERSION = "0.4.0"; +const EXPECTED_AGENT_VERSION = "0.5.0"; const els = { loginView: document.querySelector("#loginView"),