Skip to content

migration to new my #1765

Description

@edospadoni

NethSecurity → new my migration — end‑to‑end verification checklist

Target repo: NethServer/nethsecurity
Parent issue: NethServer/my#84
Cutover PR: #1609 feat/backup-my-cutover (pins nethsecurity-ui NethServer/nethsecurity-ui#746)

Goal

Verify, on a single controlled NethSecurity enterprise unit, the full migration flow from the
old my (PHP on my‑ent / legacy.my.nethesis.it) to the new my (my.nethesis.it): credential
rotation, native telemetry/backup/alerts on the new my, enterprise feed authorization, and the
subscription‑gated services (DPI, Threat‑shield, Automatic updates).

Pre‑flip verification. These PRs point the functional endpoints (register, collect_url,
/proxy/credentials, alerts) at my-proxy-prod.onrender.com, and this checklist is run before
the DNS flip. Commands use $MY:

MY=https://my-proxy-prod.onrender.com

(The onrender → my.nethesis.it swap happens later, coupled with the DNS flip.)

Exception — enterprise feeds. The 3 feeds (distfeed/sp/bl) forwardAuth to my.nethesis.it/auth
— this is fixed in the feed hosts (= the transitional broker on my‑ent), independent of $MY and of
the flip. So the feed hosts (distfeed.nethesis.it, bl.nethesis.it) and the /auth checks in Step 5
stay on their real hostnames, not $MY.


Scenario A — migration of an existing (already‑registered) system

(For a clean install that registers fresh on the new my, see Scenario B near the end — Steps 3–7 are shared.)

Step 0 — Pre‑check: the system already exists on the new my

Before touching the unit, confirm the import from the old my landed.

  1. Note the unit's current (old‑my) system id:
    uci get ns-plug.config.system_id     # legacy UUID, e.g. 7DD2C9A8-....
  2. Log in to the new my UI ($MY). Open Systems, find this unit (by name / reseller / customer).
    • The system is present, with the correct plan, company/customer, and subscription state.
    • Its System ID is shown and (once migrated) will match the rotated NETH-… id.

Step 1 — Install the cutover firmware (PR #1609)

Flash / sysupgrade the unit to the image built from PR #1609 (keeps config).
This firmware bundles: the rotation (migrate-to-my), single‑send native collect, the alerts cutover
(vmalert.initd), the subscription UX, and the pinned ns-ui NethServer/nethsecurity-ui#746.

  • Unit boots on the PR build (cat /etc/nethsecurity-release / ubus call system board).

Step 2 — Verify credential rotation (new creds set, legacy preserved)

The first send-* cron (or a manual run) triggers migrate-to-my, which GET $MY/proxy/credentials
with the legacy Basic‑Auth, writes the new creds, preserves the legacy ones, and sets migrated=1.

uci show ns-plug | grep -E 'system_id|secret|type|migrated|collect_url|legacy'

Expected:

  • ns-plug.config.system_id = NETH-… (new format)
  • ns-plug.config.secret = my_… (new format)
  • ns-plug.config.migrated = 1
  • ns-plug.config.collect_url = https://my-proxy-prod.onrender.com/collect/api/systems
  • ns-plug.config.legacy_system_id / legacy_secret = the old UUID/hex (preserved for rollback)

Idempotency: re‑run and confirm it exits at the marker, no re‑rotation:

/usr/sbin/migrate-to-my ; echo "exit=$?"      # exits early, no change
  • Second run is a no‑op (idempotent).

Step 3 — Verify heartbeat / inventory / backup write to the new my

# force a cycle
/usr/sbin/send-heartbeat ; /usr/sbin/send-inventory ; /usr/sbin/send-backup

On the new my (UI or API), for this system:

  • Last seen / heartbeat updates (recent timestamp).
  • Inventory is present and current.
  • Backup is stored on the new my (Systems → backups / GET $MY/collect/api/systems/backups).
  • facts.migration.from_legacy_system_id on the system equals the old system id (the
    authoritative "this unit migrated" signal).

Step 4 — Verify native alerts (Mimir alertmanager, not the legacy proxy)

Post‑rotation the running vmalert switches (at reload) from /proxy/alerts to the native collect
Mimir path derived from collect_url.

/etc/init.d/vmalert reload
uci get ns-plug.config.system_id ; tail -20 /var/log/messages
ps -ef | grep vmalert | awk '{print $15}'
  • vmalert notifier.url points to …/collect/api/services/mimir/alertmanager (native), not /proxy/alerts.
  • Fire a test alert (e.g. stop a monitored service / a test rule) and confirm it appears in the
    new my alerting view for this system (native Mimir), authenticated with the new creds.
  • No alert gap across the switch (legacy /proxy/alerts kept translating until the reload).

Step 5 — Verify enterprise repositories + /auth authorization

The 3 enterprise feeds authenticate system_id:secret via forwardAuth to my.nethesis.it/auth
(handled by the transitional broker on my‑ent: NETH-… active → 200 grant‑all; legacy → unchanged).

⚠️ Transitional grant‑all — Advanced Threat Shield / ng-blacklist is NOT truly gated yet.
In this migration phase https://my.nethesis.it/auth/service/ng-blacklist (and every /auth/service/*,
/auth/product/*) always resolves to 200 as long as the system's license is valid (active on the
new my). The new my does not yet implement the old my's per‑add‑on entitlement check, so
ng-blacklist (a granular paid add‑on on the old my) is served to any active enterprise unit
regardless of purchase. Not‑yet‑migrated units keep the granular (403‑capable) check via the old
my. Accepted as non‑blocking; per‑add‑on enforcement returns once the new my has entitlement
management.

SID=$(uci get ns-plug.config.system_id); SEC=$(uci get ns-plug.config.secret)

# /auth checks (via the feed hosts' forwardAuth target)
curl -s -o /dev/null -w "auth            -> %{http_code}\n"  -u "$SID:$SEC" https://my.nethesis.it/auth
curl -s -o /dev/null -w "auth/ng-blacklist-> %{http_code}\n" -u "$SID:$SEC" https://my.nethesis.it/auth/service/ng-blacklist

# apk enterprise repository
update-packages --enable-custom-repo 2>&1 | tail -20    # or: apk update

Expected:

  • /auth200 and /auth/service/ng-blacklist200 with the new creds.
  • apk update fetches the enterprise repo without auth errors (401/403). (A 404 on a
    specific dev/PR‑build version path is a firmware‑version artifact, not an auth failure.)
  • Wrong/empty creds → 401 (negative check).

Step 6 — Verify subscription‑gated services are active

DPI (netifyd — license + signatures)

LOGLEVEL=DEBUG dpi-license-update ; echo "exit=$?"
head -c 200 /etc/netifyd/license.json      # expect: "NethSecurity Enterprise Edition"
dpi-data-update ; /etc/init.d/netifyd status
  • Enterprise license downloaded (issued_to: "NethSecurity Enterprise Edition"), netifyd reloaded.
  • Application/protocol catalogs updated; netifyd running.

Threat‑shield (blocklists from bl.nethesis.it)

/etc/init.d/banip reload
  • Enterprise blocklists (nethesis-blacklists/*) fetched (200), banip active with the feeds.

Automatic updates

  • Automatic‑updates feed reachable with the new creds (part of the enterprise apk repo above).

Step 7 — Subscription UX (re‑register is one‑shot by design)

  • System detail on the new my: System ID linkable, plan + company shown.
  • Re‑register the same key → 409 system_already_registered (the new‑my key is one‑shot,
    cannot be freed/re‑registered by design).
  • Cancel / remove subscription shows the irreversibility warning.

Step 8 — Hotspot (dedalo): login via My Nethesis (if the unit runs a hotspot)

The hotspot page authenticates to the hotspot manager through the My Nethesis OIDC device pairing
(nethesis/icaro#200) instead of account credentials: the unit starts a pairing, the browser popup runs
the SSO login on the manager, and the unit polls for the session token. Everything downstream
(hotspot list, unit registration, wax) is unchanged.

Prereq: nethesis/icaro#200 deployed on the hotspot manager. Until my.nethspot.com (prod) ships
it, test against staging: open the Legacy login accordion and set the endpoint to
my.staging.nethspot.com before clicking the button (on prod the UI reports "not supported yet").

  • Network → Hotspot → Settings: primary action is Login with My Nethesis; the classic
    endpoint/username/password form is collapsed under Legacy login.
  • Click Login with My Nethesis → popup on the manager: with an active my.nethesis.it session
    (SSO) it completes by itself; on a fresh browser Logto asks credentials (+2FA) and the OAuth
    consent screen.
  • The popup ends on the themed result page showing Account, Logged in as (my user email)
    and Unit, then can be closed.
  • The unit picks the token up automatically (success notification) and the parent hotspot list loads.
  • The summary above the configuration shows Account / Authenticated by / Hotspot manager and
    persists across page reloads (stored in uci).
  • Configure and Save → the unit appears under the chosen parent hotspot on the manager.
  • On the manager, the session token description reads OIDC device pairing by <email> (<sub>), unit: <name> (auditable, revocable selectively).
  • Deny path: a my user without the required roles (global Admin + org Reseller) → popup shows
    "Unit not linked (unauthorized_role)" and the unit reports the failure (no token issued).
  • Unregister removes the hotspot config and clears the account summary.

Scenario B — clean install (fresh registration, never registered)

A unit flashed clean from the PR image and registered directly on the new my — no legacy creds, no
rotation, no proxy_mappings. packages/ns-plug/files/register (enterprise) POSTs the secret to
$MY/backend/api/systems/register, stores the returned native system_key, and sets migrated=1 from
the start so migrate-to-my stays a no‑op and telemetry/alerts go native from the first cycle. This is
the steady‑state path for every new enterprise unit after the migration.

Prereq: a valid registration secret for a system created on the new my (reseller creates the
system/subscription on the my UI → copies its secret).

B1 — Flash + register

  • Flash the clean unit with the PR feat(migration): move to new my #1609 image (never registered before).
  • Register from the UI subscription page (paste the secret) or:
    register enterprise <REGISTRATION_SECRET>
    POST $MY/backend/api/systems/register returns the native system_key (NETH-…).
  • Re‑registering the same secret → exit 2 / HTTP 409 (system_already_registered, one‑shot key).

B2 — Verify native registration (no rotation, no legacy)

uci show ns-plug | grep -E 'system_id|secret|type|migrated|collect_url|repository_url|legacy'
  • system_id = NETH-…, secret = my_…, type=enterprise
  • migrated = 1 (set by register, not by migrate-to-my)
  • collect_url = https://my-proxy-prod.onrender.com/collect/api/systems
  • repository_url = https://<id>:<secret>@distfeed.nethesis.it/repository/enterprise/nethsecurity
  • No legacy_* keys (nothing to preserve — correct)
  • migrate-to-my is a no‑op (exits at the migrated=1 guard)

B3–B7 — same as Scenario A

Heartbeat/inventory/backup (Step 3), native alerts (Step 4), /auth + enterprise repos (Step 5), DPI /
Threat‑shield / Automatic updates (Step 6), subscription UX (Step 7), hotspot via My Nethesis
(Step 8, if used) — verify exactly as in Scenario A.
Only difference: on the new my the system's facts.migration.from_legacy_system_id is NOT set
(there is no legacy system) — expected for a clean install.


Rollback (single‑unit blast radius)

Restore the preserved legacy creds and clear the migration flag:

uci set ns-plug.config.system_id="$(uci get ns-plug.config.legacy_system_id)"
uci set ns-plug.config.secret="$(uci get ns-plug.config.legacy_secret)"
uci set ns-plug.config.migrated='0'
uci delete ns-plug.config.collect_url
uci commit ns-plug
/etc/init.d/vmalert reload

The unit returns to legacy heartbeat//proxy/* dual‑send (translated by the my‑ent proxy). No data loss.


Summary checklist

  • System present on the new my (Step 0)
  • Cutover firmware installed (Step 1)
  • Creds rotated NETH-…/my_…, migrated=1, legacy preserved, idempotent (Step 2)
  • Heartbeat + inventory + backup native on new my; from_legacy_system_id set (Step 3)
  • Native alerts on new‑my Mimir, no gap (Step 4)
  • /auth 200 + enterprise apk repo OK (Step 5)
  • DPI enterprise license + signatures, Threat‑shield blocklists, Automatic updates active (Step 6)
  • Subscription UX incl. one‑shot re‑register (Step 7)
  • Hotspot login via My Nethesis + unit attached to the parent hotspot (Step 8, if used)
  • Rollback path validated (optional)

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    Status
    ToDo 🕐

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions