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.
- Note the unit's current (old‑my) system id:
uci get ns-plug.config.system_id # legacy UUID, e.g. 7DD2C9A8-....
- Log in to the new
my UI ($MY). Open Systems, find this unit (by name / reseller / customer).
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.
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:
Idempotency: re‑run and confirm it exits at the marker, no re‑rotation:
/usr/sbin/migrate-to-my ; echo "exit=$?" # exits early, no change
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:
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}'
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:
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
Threat‑shield (blocklists from bl.nethesis.it)
Automatic updates
Step 7 — Subscription UX (re‑register is one‑shot by design)
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").
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
B2 — Verify native registration (no rotation, no legacy)
uci show ns-plug | grep -E 'system_id|secret|type|migrated|collect_url|repository_url|legacy'
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
NethSecurity → new
mymigration — end‑to‑end verification checklistTarget repo:
NethServer/nethsecurityParent issue: NethServer/my#84
Cutover PR: #1609
feat/backup-my-cutover(pinsnethsecurity-uiNethServer/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 newmy(my.nethesis.it): credentialrotation, native telemetry/backup/alerts on the new
my, enterprise feed authorization, and thesubscription‑gated services (DPI, Threat‑shield, Automatic updates).
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
myBefore touching the unit, confirm the import from the old
mylanded.uci get ns-plug.config.system_id # legacy UUID, e.g. 7DD2C9A8-....myUI ($MY). Open Systems, find this unit (by name / reseller / customer).NETH-…id.Step 1 — Install the cutover firmware (PR #1609)
Flash /
sysupgradethe 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 pinnedns-uiNethServer/nethsecurity-ui#746.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) triggersmigrate-to-my, whichGET $MY/proxy/credentialswith the legacy Basic‑Auth, writes the new creds, preserves the legacy ones, and sets
migrated=1.Expected:
ns-plug.config.system_id=NETH-…(new format)ns-plug.config.secret=my_…(new format)ns-plug.config.migrated=1ns-plug.config.collect_url=https://my-proxy-prod.onrender.com/collect/api/systemsns-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:
Step 3 — Verify heartbeat / inventory / backup write to the new
myOn the new
my(UI or API), for this system:my(Systems → backups /GET $MY/collect/api/systems/backups).facts.migration.from_legacy_system_idon the system equals the old system id (theauthoritative "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/alertsto the native collectMimir path derived from
collect_url.…/collect/api/services/mimir/alertmanager(native), not/proxy/alerts.new
myalerting view for this system (native Mimir), authenticated with the new creds./proxy/alertskept translating until the reload).Step 5 — Verify enterprise repositories +
/authauthorizationThe 3 enterprise feeds authenticate
system_id:secretvia forwardAuth tomy.nethesis.it/auth(handled by the transitional broker on my‑ent:
NETH-…active → 200 grant‑all; legacy → unchanged).Expected:
/auth→ 200 and/auth/service/ng-blacklist→ 200 with the new creds.apk updatefetches the enterprise repo without auth errors (401/403). (A404on aspecific dev/PR‑build version path is a firmware‑version artifact, not an auth failure.)
Step 6 — Verify subscription‑gated services are active
DPI (netifyd — license + signatures)
issued_to: "NethSecurity Enterprise Edition"), netifyd reloaded.Threat‑shield (blocklists from
bl.nethesis.it)nethesis-blacklists/*) fetched (200), banip active with the feeds.Automatic updates
Step 7 — Subscription UX (re‑register is one‑shot by design)
my: System ID linkable, plan + company shown.system_already_registered(the new‑my key is one‑shot,cannot be freed/re‑registered by design).
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.
endpoint/username/password form is collapsed under Legacy login.
my.nethesis.itsession(SSO) it completes by itself; on a fresh browser Logto asks credentials (+2FA) and the OAuth
consent screen.
and Unit, then can be closed.
persists across page reloads (stored in uci).
OIDC device pairing by <email> (<sub>), unit: <name>(auditable, revocable selectively)."Unit not linked (unauthorized_role)" and the unit reports the failure (no token issued).
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, norotation, no
proxy_mappings.packages/ns-plug/files/register(enterprise) POSTs the secret to$MY/backend/api/systems/register, stores the returned nativesystem_key, and setsmigrated=1fromthe start so
migrate-to-mystays a no‑op and telemetry/alerts go native from the first cycle. This isthe 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 thesystem/subscription on the
myUI → copies its secret).B1 — Flash + register
POST $MY/backend/api/systems/registerreturns the nativesystem_key(NETH-…).system_already_registered, one‑shot key).B2 — Verify native registration (no rotation, no legacy)
system_id=NETH-…,secret=my_…,type=enterprisemigrated=1(set byregister, not bymigrate-to-my)collect_url=https://my-proxy-prod.onrender.com/collect/api/systemsrepository_url=https://<id>:<secret>@distfeed.nethesis.it/repository/enterprise/nethsecuritylegacy_*keys (nothing to preserve — correct)migrate-to-myis a no‑op (exits at themigrated=1guard)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
mythe system'sfacts.migration.from_legacy_system_idis 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:
The unit returns to legacy heartbeat/
/proxy/*dual‑send (translated by the my‑ent proxy). No data loss.Summary checklist
my(Step 0)NETH-…/my_…,migrated=1, legacy preserved, idempotent (Step 2)my;from_legacy_system_idset (Step 3)/auth200 + enterprise apk repo OK (Step 5)