refactor: remove direct DNS mode

This commit is contained in:
benya
2026-07-21 20:24:25 +03:00
parent 5e4e659cae
commit 15b5b7f792
33 changed files with 24 additions and 1148 deletions
+2 -20
View File
@@ -1,8 +1,7 @@
# OpenWrt RMM Agent
Current stable Go agent: `0.6.0`. It reports runtime health, pending command results,
the last heartbeat transport error after connectivity is restored, and optionally keeps
the router's direct DNS addresses current.
Current stable Go agent: `0.6.1`. It reports runtime health, pending command results,
and the last heartbeat transport error after connectivity is restored.
Production Go agent for OpenWrt, with the shell implementation retained as a fallback runtime.
@@ -29,10 +28,6 @@ SERVER_URL="https://rmm.example.com"
ENROLLMENT_TOKEN="paste-a-one-time-grant-from-your-account"
INTERVAL_SECONDS="30"
TUNNEL_IDENTITY_FILE="/etc/rmm-agent/tunnel_key"
DIRECT_DNS_ENABLED="false"
DNS_UPDATE_INTERVAL_SECONDS="300"
PUBLIC_IPV4_URL="https://api.ipify.org"
PUBLIC_IPV6_URL="https://api6.ipify.org"
```
After enrollment the agent writes:
@@ -84,19 +79,6 @@ The Go agent is available as the production OpenWrt package and supports the mig
Other queued commands are reported as failed with a clear message.
### Direct DNS updates
Direct DNS reporting is opt-in. When enabled, the agent queries the configured HTTPS
endpoints in parallel, validates that their responses are public IPv4/IPv6 addresses, and
sends the complete address state through `/api/agent/dns/update` using its existing device
token. A successful update is refreshed every five minutes by default; failures retry after
one minute and do not make the main heartbeat unhealthy.
The default discovery endpoints are operated by ipify. Clear the IPv6 URL on routers without
public IPv6, or replace either endpoint with trusted HTTPS services under your control. The
agent refuses HTTP URLs, embedded URL credentials, redirects, oversized responses, and
private or reserved addresses.
Build locally:
```sh
-150
View File
@@ -1,150 +0,0 @@
package main
import (
"context"
"errors"
"fmt"
"io"
"net/http"
"net/netip"
"net/url"
"strings"
"time"
)
var nonPublicAddressPrefixes = []netip.Prefix{
netip.MustParsePrefix("100.64.0.0/10"),
netip.MustParsePrefix("192.0.0.0/24"),
netip.MustParsePrefix("192.0.2.0/24"),
netip.MustParsePrefix("198.18.0.0/15"),
netip.MustParsePrefix("198.51.100.0/24"),
netip.MustParsePrefix("203.0.113.0/24"),
netip.MustParsePrefix("240.0.0.0/4"),
netip.MustParsePrefix("2001:db8::/32"),
}
type directDNSUpdateState struct {
nextAttempt time.Time
}
func (s directDNSUpdateState) due(now time.Time) bool {
return s.nextAttempt.IsZero() || !now.Before(s.nextAttempt)
}
func (s *directDNSUpdateState) schedule(now time.Time, interval time.Duration, updateErr error) {
if interval < time.Minute {
interval = 5 * time.Minute
}
if updateErr != nil && interval > time.Minute {
interval = time.Minute
}
s.nextAttempt = now.Add(interval)
}
func updateDirectDNS(ctx context.Context, client *http.Client, cfg config) error {
discoveryContext, cancel := context.WithTimeout(ctx, 12*time.Second)
defer cancel()
type result struct {
address string
ipv4 bool
err error
}
results := make(chan result, 2)
requests := 0
for _, request := range []struct {
endpoint string
ipv4 bool
}{{cfg.PublicIPv4URL, true}, {cfg.PublicIPv6URL, false}} {
if strings.TrimSpace(request.endpoint) == "" {
continue
}
requests++
go func(endpoint string, ipv4 bool) {
address, err := fetchPublicIPAddress(discoveryContext, client, endpoint, ipv4)
results <- result{address: address, ipv4: ipv4, err: err}
}(request.endpoint, request.ipv4)
}
var ipv4, ipv6 string
var discoveryErrors []string
for range requests {
result := <-results
if result.err != nil {
discoveryErrors = append(discoveryErrors, result.err.Error())
continue
}
if result.ipv4 {
ipv4 = result.address
} else {
ipv6 = result.address
}
}
if ipv4 == "" && ipv6 == "" {
if len(discoveryErrors) == 0 {
return errors.New("no public IP discovery service is configured")
}
return fmt.Errorf("public IP discovery failed: %s", strings.Join(discoveryErrors, "; "))
}
body := map[string]string{
"device_id": cfg.DeviceID,
"ipv4": ipv4,
"ipv6": ipv6,
}
return postJSON(ctx, client, cfg.ServerURL+"/api/agent/dns/update", cfg.DeviceToken, body, nil)
}
func fetchPublicIPAddress(ctx context.Context, client *http.Client, endpoint string, wantIPv4 bool) (string, error) {
parsed, err := url.Parse(strings.TrimSpace(endpoint))
if err != nil || parsed.Scheme != "https" || parsed.Host == "" || parsed.User != nil {
return "", errors.New("public IP discovery URL must use HTTPS without credentials")
}
request, err := http.NewRequestWithContext(ctx, http.MethodGet, parsed.String(), nil)
if err != nil {
return "", err
}
request.Header.Set("Accept", "text/plain")
discoveryClient := *client
discoveryClient.CheckRedirect = func(_ *http.Request, _ []*http.Request) error {
return http.ErrUseLastResponse
}
response, err := discoveryClient.Do(request)
if err != nil {
return "", err
}
defer response.Body.Close()
if response.StatusCode != http.StatusOK {
_, _ = io.Copy(io.Discard, io.LimitReader(response.Body, 4096))
return "", fmt.Errorf("public IP discovery returned HTTP %d", response.StatusCode)
}
data, err := io.ReadAll(io.LimitReader(response.Body, 129))
if err != nil {
return "", err
}
if len(data) > 128 {
return "", errors.New("public IP discovery response is too large")
}
address, err := normalizePublicAddress(string(data), wantIPv4)
if err != nil {
return "", err
}
return address, nil
}
func normalizePublicAddress(raw string, wantIPv4 bool) (string, error) {
address, err := netip.ParseAddr(strings.TrimSpace(raw))
if err != nil {
return "", errors.New("public IP discovery returned an invalid address")
}
address = address.Unmap()
if address.Is4() != wantIPv4 || !address.IsGlobalUnicast() || address.IsPrivate() || address.IsLoopback() || address.IsLinkLocalUnicast() || address.IsMulticast() || address.IsUnspecified() {
return "", errors.New("public IP discovery returned a non-public address")
}
for _, prefix := range nonPublicAddressPrefixes {
if prefix.Contains(address) {
return "", errors.New("public IP discovery returned a reserved address")
}
}
return address.String(), nil
}
+1 -75
View File
@@ -23,7 +23,7 @@ import (
"time"
)
const agentVersion = "0.6.0"
const agentVersion = "0.6.1"
type agentRuntimeHealth struct {
StartedAt time.Time
@@ -31,9 +31,6 @@ type agentRuntimeHealth struct {
LastHeartbeatError string
LastHeartbeatErrorAt time.Time
LastHeartbeatSuccess time.Time
LastDNSUpdateError string
LastDNSUpdateErrorAt time.Time
LastDNSUpdateSuccess time.Time
}
func (h *agentRuntimeHealth) recordFailure(err error) {
@@ -50,20 +47,6 @@ func (h *agentRuntimeHealth) recordSuccess() {
h.LastHeartbeatSuccess = time.Now().UTC()
}
func (h *agentRuntimeHealth) recordDirectDNSUpdate(err error) {
if err == nil {
h.LastDNSUpdateSuccess = time.Now().UTC()
h.LastDNSUpdateError = ""
h.LastDNSUpdateErrorAt = time.Time{}
return
}
h.LastDNSUpdateError = strings.TrimSpace(err.Error())
if len(h.LastDNSUpdateError) > 512 {
h.LastDNSUpdateError = h.LastDNSUpdateError[:512]
}
h.LastDNSUpdateErrorAt = time.Now().UTC()
}
func (h *agentRuntimeHealth) snapshot(spoolDir string) map[string]any {
result := map[string]any{
"started_at": h.StartedAt.UTC().Format(time.RFC3339),
@@ -77,13 +60,6 @@ func (h *agentRuntimeHealth) snapshot(spoolDir string) map[string]any {
result["last_heartbeat_error"] = h.LastHeartbeatError
result["last_heartbeat_error_at"] = h.LastHeartbeatErrorAt.UTC().Format(time.RFC3339)
}
if !h.LastDNSUpdateSuccess.IsZero() {
result["last_dns_update_success_at"] = h.LastDNSUpdateSuccess.UTC().Format(time.RFC3339)
}
if h.LastDNSUpdateError != "" {
result["last_dns_update_error"] = h.LastDNSUpdateError
result["last_dns_update_error_at"] = h.LastDNSUpdateErrorAt.UTC().Format(time.RFC3339)
}
return result
}
@@ -101,10 +77,6 @@ type config struct {
CheckTargets []string
HostnameOverride string
HostnameSuffix string
DirectDNSEnabled bool
DNSUpdateSeconds int
PublicIPv4URL string
PublicIPv6URL string
ConfigFile string
}
@@ -166,7 +138,6 @@ func main() {
backoff := time.Duration(cfg.IntervalSeconds) * time.Second
health := &agentRuntimeHealth{StartedAt: time.Now().UTC()}
dnsState := directDNSUpdateState{}
for {
if err := heartbeatOnce(ctx, client, cfg, health.snapshot(cfg.SpoolDir)); err != nil {
health.recordFailure(err)
@@ -178,14 +149,6 @@ func main() {
} else {
health.recordSuccess()
backoff = time.Duration(cfg.IntervalSeconds) * time.Second
if cfg.DirectDNSEnabled && dnsState.due(time.Now()) {
err := updateDirectDNS(ctx, client, cfg)
health.recordDirectDNSUpdate(err)
dnsState.schedule(time.Now(), time.Duration(cfg.DNSUpdateSeconds)*time.Second, err)
if err != nil {
logf("direct DNS update warning: %v", err)
}
}
}
if *once {
return
@@ -213,10 +176,6 @@ func loadConfig(path string) (config, error) {
CheckTargets: splitWords(envDefault("CHECK_TARGETS", "1.1.1.1 8.8.8.8")),
HostnameOverride: os.Getenv("HOSTNAME_OVERRIDE"),
HostnameSuffix: os.Getenv("HOSTNAME_SUFFIX"),
DirectDNSEnabled: boolDefault(os.Getenv("DIRECT_DNS_ENABLED"), false),
DNSUpdateSeconds: intDefault(os.Getenv("DNS_UPDATE_INTERVAL_SECONDS"), 300),
PublicIPv4URL: envDefault("PUBLIC_IPV4_URL", "https://api.ipify.org"),
PublicIPv6URL: envDefault("PUBLIC_IPV6_URL", "https://api6.ipify.org"),
ConfigFile: path,
}
data, err := os.ReadFile(path)
@@ -266,28 +225,10 @@ func loadConfig(path string) (config, error) {
if value := values["HOSTNAME_SUFFIX"]; value != "" {
cfg.HostnameSuffix = value
}
if value, ok := values["DIRECT_DNS_ENABLED"]; ok {
cfg.DirectDNSEnabled = boolDefault(value, cfg.DirectDNSEnabled)
}
if value := values["DNS_UPDATE_INTERVAL_SECONDS"]; value != "" {
cfg.DNSUpdateSeconds = intDefault(value, cfg.DNSUpdateSeconds)
}
if value, ok := values["PUBLIC_IPV4_URL"]; ok {
cfg.PublicIPv4URL = strings.TrimSpace(value)
}
if value, ok := values["PUBLIC_IPV6_URL"]; ok {
cfg.PublicIPv6URL = strings.TrimSpace(value)
}
cfg.ServerURL = strings.TrimRight(cfg.ServerURL, "/")
if cfg.IntervalSeconds <= 0 {
cfg.IntervalSeconds = 30
}
if cfg.DNSUpdateSeconds < 60 || cfg.DNSUpdateSeconds > 86400 {
cfg.DNSUpdateSeconds = 300
}
if cfg.DirectDNSEnabled && cfg.PublicIPv4URL == "" && cfg.PublicIPv6URL == "" {
return cfg, errors.New("direct DNS requires at least one public IP discovery URL")
}
return cfg, nil
}
@@ -318,10 +259,6 @@ func saveConfig(cfg config) error {
writeConfigLine(&b, "BACKUP_DIR", cfg.BackupDir)
writeConfigLine(&b, "TUNNEL_IDENTITY_FILE", cfg.TunnelIdentity)
writeConfigLine(&b, "TUNNEL_STATE_DIR", cfg.TunnelStateDir)
writeConfigLine(&b, "DIRECT_DNS_ENABLED", strconv.FormatBool(cfg.DirectDNSEnabled))
writeConfigLine(&b, "DNS_UPDATE_INTERVAL_SECONDS", strconv.Itoa(cfg.DNSUpdateSeconds))
writeConfigLine(&b, "PUBLIC_IPV4_URL", cfg.PublicIPv4URL)
writeConfigLine(&b, "PUBLIC_IPV6_URL", cfg.PublicIPv6URL)
if cfg.HostnameOverride != "" {
writeConfigLine(&b, "HOSTNAME_OVERRIDE", cfg.HostnameOverride)
}
@@ -1801,17 +1738,6 @@ func intDefault(value string, fallback int) int {
return parsed
}
func boolDefault(value string, fallback bool) bool {
switch strings.ToLower(strings.TrimSpace(value)) {
case "1", "true", "yes", "on":
return true
case "0", "false", "no", "off":
return false
default:
return fallback
}
}
func splitWords(value string) []string {
fields := strings.Fields(value)
if len(fields) == 0 {
+1 -104
View File
@@ -1,11 +1,7 @@
package main
import (
"context"
"encoding/json"
"errors"
"net/http"
"net/http/httptest"
"os"
"path/filepath"
"testing"
@@ -13,102 +9,11 @@ import (
)
func TestAgentVersionIsStable(t *testing.T) {
if agentVersion != "0.6.0" {
if agentVersion != "0.6.1" {
t.Fatalf("unexpected agent version %q", agentVersion)
}
}
func TestLoadConfigDirectDNS(t *testing.T) {
path := filepath.Join(t.TempDir(), "rmm-agent.conf")
data := `SERVER_URL="https://rmm.example.test"
DIRECT_DNS_ENABLED="1"
DNS_UPDATE_INTERVAL_SECONDS="600"
PUBLIC_IPV4_URL="https://ipv4.example.test"
PUBLIC_IPV6_URL=""
`
if err := os.WriteFile(path, []byte(data), 0o600); err != nil {
t.Fatal(err)
}
cfg, err := loadConfig(path)
if err != nil {
t.Fatal(err)
}
if !cfg.DirectDNSEnabled || cfg.DNSUpdateSeconds != 600 || cfg.PublicIPv4URL != "https://ipv4.example.test" || cfg.PublicIPv6URL != "" {
t.Fatalf("unexpected direct DNS config: %#v", cfg)
}
}
func TestUpdateDirectDNS(t *testing.T) {
updates := make(chan map[string]string, 1)
server := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
switch r.URL.Path {
case "/v4":
_, _ = w.Write([]byte("8.8.8.8\n"))
case "/v6":
_, _ = w.Write([]byte("2606:4700:4700::1111\n"))
case "/api/agent/dns/update":
if r.Header.Get("Authorization") != "Bearer device-secret" {
t.Errorf("unexpected authorization header: %q", r.Header.Get("Authorization"))
}
var request map[string]string
if err := json.NewDecoder(r.Body).Decode(&request); err != nil {
t.Errorf("decode update: %v", err)
}
updates <- request
w.Header().Set("Content-Type", "application/json")
_, _ = w.Write([]byte(`{"changed":true}`))
default:
http.NotFound(w, r)
}
}))
defer server.Close()
cfg := config{
ServerURL: server.URL,
DeviceID: "dev_test",
DeviceToken: "device-secret",
PublicIPv4URL: server.URL + "/v4",
PublicIPv6URL: server.URL + "/v6",
}
if err := updateDirectDNS(context.Background(), server.Client(), cfg); err != nil {
t.Fatal(err)
}
request := <-updates
if request["device_id"] != "dev_test" || request["ipv4"] != "8.8.8.8" || request["ipv6"] != "2606:4700:4700::1111" {
t.Fatalf("unexpected DNS update: %#v", request)
}
}
func TestPublicIPAddressValidation(t *testing.T) {
if _, err := normalizePublicAddress("192.168.1.1", true); err == nil {
t.Fatal("private IPv4 address was accepted")
}
if _, err := normalizePublicAddress("2001:db8::1", false); err == nil {
t.Fatal("documentation IPv6 address was accepted")
}
if got, err := normalizePublicAddress("8.8.8.8", true); err != nil || got != "8.8.8.8" {
t.Fatalf("public IPv4 address rejected: got=%q err=%v", got, err)
}
if _, err := fetchPublicIPAddress(context.Background(), &http.Client{}, "http://example.test", true); err == nil {
t.Fatal("insecure discovery URL was accepted")
}
}
func TestDirectDNSUpdateSchedule(t *testing.T) {
now := time.Unix(1000, 0)
state := directDNSUpdateState{}
if !state.due(now) {
t.Fatal("initial direct DNS update is not due")
}
state.schedule(now, 5*time.Minute, nil)
if state.due(now.Add(4*time.Minute)) || !state.due(now.Add(5*time.Minute)) {
t.Fatal("successful direct DNS interval was not respected")
}
state.schedule(now, 5*time.Minute, errors.New("temporary failure"))
if state.due(now.Add(59*time.Second)) || !state.due(now.Add(time.Minute)) {
t.Fatal("failed direct DNS update retry interval was not respected")
}
}
func TestAgentRuntimeHealthSnapshot(t *testing.T) {
spoolDir := t.TempDir()
if err := os.WriteFile(filepath.Join(spoolDir, "pending.json"), []byte(`{}`), 0o600); err != nil {
@@ -127,14 +32,6 @@ func TestAgentRuntimeHealthSnapshot(t *testing.T) {
if health.ConsecutiveFailures != 0 || health.LastHeartbeatSuccess.IsZero() {
t.Fatalf("recordSuccess did not reset runtime state: %#v", health)
}
health.recordDirectDNSUpdate(errors.New("discovery unavailable"))
if health.snapshot(spoolDir)["last_dns_update_error"] != "discovery unavailable" {
t.Fatalf("unexpected direct DNS health: %#v", health.snapshot(spoolDir))
}
health.recordDirectDNSUpdate(nil)
if health.LastDNSUpdateSuccess.IsZero() || health.LastDNSUpdateError != "" {
t.Fatalf("direct DNS recovery was not recorded: %#v", health)
}
}
func TestAcquireLockRemovesDirectoryOnUnlock(t *testing.T) {
-4
View File
@@ -2,10 +2,6 @@ SERVER_URL="https://rmm.example.com"
ENROLLMENT_TOKEN=""
INTERVAL_SECONDS="30"
CHECK_TARGETS="1.1.1.1 8.8.8.8"
DIRECT_DNS_ENABLED="false"
DNS_UPDATE_INTERVAL_SECONDS="300"
PUBLIC_IPV4_URL="https://api.ipify.org"
PUBLIC_IPV6_URL="https://api6.ipify.org"
TUNNEL_IDENTITY_FILE="/etc/rmm-agent/tunnel_key"
LOCK_FILE="/tmp/rmm-agent-go.lock"
SPOOL_DIR="/tmp/rmm-agent-go-results"
+1 -1
View File
@@ -1,7 +1,7 @@
include $(TOPDIR)/rules.mk
PKG_NAME:=luci-app-rmm-agent
PKG_VERSION:=0.2.0
PKG_VERSION:=0.2.1
PKG_RELEASE:=1
PKG_MAINTAINER:=RMM OpenWrt
+4 -5
View File
@@ -8,7 +8,6 @@ It provides:
- HTTPS server URL and polling interval;
- one-time enrollment grant input;
- connectivity targets and tunnel identity path;
- opt-in public IPv4/IPv6 discovery and direct DNS update interval;
- explicit lab-only HTTP override;
- controlled device identity reset for re-enrollment.
@@ -68,8 +67,8 @@ application to the router. Do not copy or install the shell runtime at the same
cd dist/rmm-openwrt-25.12.4-ramips-mt7621
sha256sum -c SHA256SUMS
scp \
rmm-agent-go-production-0.6.0-r1.apk \
luci-app-rmm-agent-0.2.0-r1.apk \
rmm-agent-go-production-0.6.1-r1.apk \
luci-app-rmm-agent-0.2.1-r1.apk \
root@ROUTER_IP:/tmp/
```
@@ -77,8 +76,8 @@ Then install the locally built, unsigned packages over SSH:
```sh
apk add --allow-untrusted \
/tmp/rmm-agent-go-production-0.6.0-r1.apk \
/tmp/luci-app-rmm-agent-0.2.0-r1.apk
/tmp/rmm-agent-go-production-0.6.1-r1.apk \
/tmp/luci-app-rmm-agent-0.2.1-r1.apk
/etc/init.d/rpcd restart
/etc/init.d/uhttpd restart
```
@@ -18,12 +18,6 @@ var callRcInit = rpc.declare({
params: [ 'name', 'action' ]
});
function validateDiscoveryURL(sectionId, value) {
if (!value || /^https:\/\/[A-Za-z0-9.-]+(?::[0-9]+)?(?:\/.*)?$/.test(value))
return true;
return _('Public IP discovery must use an HTTPS URL without embedded credentials.');
}
return view.extend({
load: function() {
return Promise.all([ uci.load('rmm-agent'), callServiceList('rmm-agent') ]);
@@ -76,36 +70,6 @@ return view.extend({
option.placeholder = '1.1.1.1 8.8.8.8';
option.rmempty = false;
section = map.section(form.NamedSection, 'main', 'agent', _('Direct DNS'));
section.anonymous = true;
section.addremove = false;
option = section.option(form.Flag, 'direct_dns_enabled', _('Update public DNS addresses'));
option.default = '0';
option.rmempty = false;
option.description = _('Opt-in feature for Go agent 0.6.0 or newer. The router periodically reports its public addresses to RMM.');
option = section.option(form.Value, 'dns_update_interval_seconds', _('Update interval'));
option.datatype = 'range(60,86400)';
option.default = '300';
option.rmempty = false;
option.depends('direct_dns_enabled', '1');
option = section.option(form.Value, 'public_ipv4_url', _('Public IPv4 discovery URL'));
option.placeholder = 'https://api.ipify.org';
option.default = 'https://api.ipify.org';
option.rmempty = false;
option.depends('direct_dns_enabled', '1');
option.validate = validateDiscoveryURL;
option = section.option(form.Value, 'public_ipv6_url', _('Public IPv6 discovery URL'));
option.placeholder = 'https://api6.ipify.org';
option.default = 'https://api6.ipify.org';
option.rmempty = true;
option.description = _('Clear this field on routers without public IPv6.');
option.depends('direct_dns_enabled', '1');
option.validate = validateDiscoveryURL;
section = map.section(form.NamedSection, 'main', 'agent', _('Advanced settings'));
section.anonymous = true;
section.addremove = false;
@@ -1,7 +1,7 @@
include $(TOPDIR)/rules.mk
PKG_NAME:=rmm-agent-go-production
PKG_VERSION:=0.6.0
PKG_VERSION:=0.6.1
PKG_RELEASE:=1
PKG_MAINTAINER:=RMM OpenWrt
@@ -34,5 +34,5 @@ opkg install /tmp/rmm-agent-go-production_*.ipk
This package is intended for the final shell-to-Go migration when the router should keep the same RMM object identity.
Version `0.6.0` supports opt-in direct DNS updates through the UCI/LuCI settings supplied
by `luci-app-rmm-agent` `0.2.0`.
Version `0.6.1` uses the cloud tunnel exclusively and no longer discovers or publishes the
router's public WAN addresses.
@@ -3,10 +3,6 @@ config agent 'main'
option server_url 'https://rmm.example.com'
option interval_seconds '30'
option check_targets '1.1.1.1 8.8.8.8'
option direct_dns_enabled '0'
option dns_update_interval_seconds '300'
option public_ipv4_url 'https://api.ipify.org'
option public_ipv6_url 'https://api6.ipify.org'
option tunnel_identity_file '/etc/rmm-agent/tunnel_key'
option allow_insecure_http '0'
option reset_identity '0'
@@ -2,10 +2,6 @@ SERVER_URL="https://rmm.example.com"
ENROLLMENT_TOKEN=""
INTERVAL_SECONDS="30"
CHECK_TARGETS="1.1.1.1 8.8.8.8"
DIRECT_DNS_ENABLED="false"
DNS_UPDATE_INTERVAL_SECONDS="300"
PUBLIC_IPV4_URL="https://api.ipify.org"
PUBLIC_IPV6_URL="https://api6.ipify.org"
TUNNEL_IDENTITY_FILE="/etc/rmm-agent/tunnel_key"
DEVICE_ID=""
DEVICE_TOKEN=""
@@ -60,10 +60,6 @@ temporary="${SHELL_CONFIG}.tmp"
printf 'ENROLLMENT_TOKEN="%s"\n' "$(escape_value "$enrollment_token")"
printf 'INTERVAL_SECONDS="%s"\n' "$(escape_value "$(uci_get interval_seconds)")"
printf 'CHECK_TARGETS="%s"\n' "$(escape_value "$(uci_get check_targets)")"
printf 'DIRECT_DNS_ENABLED="%s"\n' "$(escape_value "$(uci_get direct_dns_enabled)")"
printf 'DNS_UPDATE_INTERVAL_SECONDS="%s"\n' "$(escape_value "$(uci_get dns_update_interval_seconds)")"
printf 'PUBLIC_IPV4_URL="%s"\n' "$(escape_value "$(uci_get public_ipv4_url)")"
printf 'PUBLIC_IPV6_URL="%s"\n' "$(escape_value "$(uci_get public_ipv6_url)")"
printf 'TUNNEL_IDENTITY_FILE="%s"\n' "$(escape_value "$(uci_get tunnel_identity_file)")"
printf 'DEVICE_ID="%s"\n' "$(escape_value "$device_id")"
printf 'DEVICE_TOKEN="%s"\n' "$(escape_value "$device_token")"
+1 -1
View File
@@ -1,7 +1,7 @@
include $(TOPDIR)/rules.mk
PKG_NAME:=rmm-agent-go
PKG_VERSION:=0.6.0
PKG_VERSION:=0.6.1
PKG_RELEASE:=1
PKG_MAINTAINER:=RMM OpenWrt
@@ -2,10 +2,6 @@ SERVER_URL="https://rmm.example.com"
ENROLLMENT_TOKEN=""
INTERVAL_SECONDS="30"
CHECK_TARGETS="1.1.1.1 8.8.8.8"
DIRECT_DNS_ENABLED="false"
DNS_UPDATE_INTERVAL_SECONDS="300"
PUBLIC_IPV4_URL="https://api.ipify.org"
PUBLIC_IPV6_URL="https://api6.ipify.org"
TUNNEL_IDENTITY_FILE="/etc/rmm-agent/tunnel_key"
LOCK_FILE="/tmp/rmm-agent-go.lock"
SPOOL_DIR="/tmp/rmm-agent-go-results"
+1 -1
View File
@@ -1,7 +1,7 @@
include $(TOPDIR)/rules.mk
PKG_NAME:=rmm-agent
PKG_VERSION:=0.1.0
PKG_VERSION:=0.1.1
PKG_RELEASE:=2
PKG_MAINTAINER:=RMM OpenWrt
@@ -3,10 +3,6 @@ config agent 'main'
option server_url 'https://rmm.example.com'
option interval_seconds '30'
option check_targets '1.1.1.1 8.8.8.8'
option direct_dns_enabled '0'
option dns_update_interval_seconds '300'
option public_ipv4_url 'https://api.ipify.org'
option public_ipv6_url 'https://api6.ipify.org'
option tunnel_identity_file '/etc/rmm-agent/tunnel_key'
option allow_insecure_http '0'
option reset_identity '0'
@@ -60,10 +60,6 @@ temporary="${SHELL_CONFIG}.tmp"
printf 'ENROLLMENT_TOKEN="%s"\n' "$(escape_value "$enrollment_token")"
printf 'INTERVAL_SECONDS="%s"\n' "$(escape_value "$(uci_get interval_seconds)")"
printf 'CHECK_TARGETS="%s"\n' "$(escape_value "$(uci_get check_targets)")"
printf 'DIRECT_DNS_ENABLED="%s"\n' "$(escape_value "$(uci_get direct_dns_enabled)")"
printf 'DNS_UPDATE_INTERVAL_SECONDS="%s"\n' "$(escape_value "$(uci_get dns_update_interval_seconds)")"
printf 'PUBLIC_IPV4_URL="%s"\n' "$(escape_value "$(uci_get public_ipv4_url)")"
printf 'PUBLIC_IPV6_URL="%s"\n' "$(escape_value "$(uci_get public_ipv6_url)")"
printf 'TUNNEL_IDENTITY_FILE="%s"\n' "$(escape_value "$(uci_get tunnel_identity_file)")"
printf 'DEVICE_ID="%s"\n' "$(escape_value "$device_id")"
printf 'DEVICE_TOKEN="%s"\n' "$(escape_value "$device_token")"