SECURITY MODEL
Security model
Trust boundaries¶
- Local device/app -> nodrad: protect HTTP ingress with
local_token; use a network boundary/TLS termination for MQTT where required. - nodrad -> control plane: bearer site credentials are supported; optional client certificates provide mTLS identity.
- operator -> management API: admin bearer token (
NODRA_ADMIN_TOKEN) or optional viewer bearer (NODRA_VIEWER_TOKEN, GET-only). - operator -> web console: username/password for admin or viewer. Successful login returns the matching bearer; the token and role are held in
sessionStorage. - control plane -> webhook destination: HTTPS is recommended. Route headers may carry destination credentials; protect the control-plane data volume.
- simulator -> control plane:
nodra-simuses the same admin and enrollment secrets as operators; treat it as a privileged demo client, not a public surface.
Enrollment¶
The enrollment token is a bootstrap secret. Site bearer tokens are random and only their SHA-256 hash is persisted centrally.
When PKI enrollment is enabled, the edge generates an ECDSA P-256 private key and CSR. Only the CSR is sent. The control plane signs it; the private key never leaves the site.
SSO / OIDC login¶
NODRA_OIDC_ISSUER_URL + NODRA_OIDC_CLIENT_ID add "Sign in with SSO" as a third way to obtain the console's existing admin/viewer bearer tokens. Scope, stated precisely:
- ID token verification is RS256 only —
alg: noneand anyHS*algorithm are explicitly rejected (the classic JWT algorithm-confusion mitigation). Verification is hand-rolled against stdlibcrypto/rsa, not a third-party JWT library. - A configured groups claim (
NODRA_OIDC_GROUPS_CLAIM, defaultgroups) maps to the SAME two roles admin/viewer already have —NODRA_OIDC_ADMIN_GROUP/NODRA_OIDC_VIEWER_GROUPdecide which. There is no third role and no per-permission granularity beyond what admin/viewer already grant. - A successful OIDC login mints the exact same static bearer token
/auth/loginissues for that role — there is no separate OIDC session, no cookie, no token expiry distinct from the admin/viewer token's own lifetime. - Explicit non-goals: no per-user account directory (the audit actor is
oidc:<sub>, not a stored user record), no multi-tenant organizations, no per-org site scoping. Those would need a real user/org data model and are tracked separately inROADMAP.md. - The OIDC login
state/noncepair is held in-memory only (10-minute TTL, single-use) — it does not survive a control-plane restart mid-flow, and is not shared across replicas.
Docker image signature verification (cosign)¶
nodrad can shell out to an externally-installed cosign CLI before every docker pull, gated by signature_mode (agent default) or a per-deployment override:
enforcerefuses to pull an image that failscosign verify— the deployment is markedfailedand never runs.warn(the default, so existing unsigned deployments keep working unchanged) logs the failure, audits it, and pulls anyway.skipdisables verification entirely.
What this actually guarantees: nodrad trusts whatever cosign binary and version happen to be on the agent host's PATH — the same trust model this project already has for docker itself, not a new kind of dependency. cosign_certificate_identity_regexp/cosign_certificate_oidc_issuer are passed straight through to cosign verify --certificate-identity-regexp/--certificate-oidc-issuer; nodrad does not parse, cache, or independently validate the signature/attestation itself. A compromised or absent cosign binary defeats this control entirely — it is a deployment-time gate on the agent host, not a control-plane-enforced invariant.
Health-gated deployment rollback¶
After a docker pull/run, if a deployment stays unable to reach the running state past a configurable grace period (deploy_health_grace, default 60s), nodrad asks the control plane to revert it to the last image/version that was observed running before the change (POST /agent/deployments/{id}/rollback). This is binary health only — Docker's own running/not-running state via docker inspect — not a real application-level health check (no such contract exists yet), and not a staged or canary rollout; a rollback is an all-or-nothing revert of one deployment, audited (deployment.rollback) and raised as a deployment_rollback alert.
Kubernetes¶
The default manifests enforce Restricted Pod Security, run as UID/GID 65532, drop all capabilities, use RuntimeDefault seccomp, use read-only root filesystems and disable service-account token mounting.
Demo values inject admin user/password into the control-plane Secret for console login. Optional viewer token and Postgres URL keys are supported. Change defaults before any shared or internet-facing lab.
Operational guidance¶
- terminate public traffic with TLS;
- rotate admin/enrollment/viewer tokens after exposure;
- revoke a lost site from the control plane;
- keep agent data volumes encrypted where device telemetry is sensitive;
- do not enable Docker reconciliation unless the node is intentionally permitted to manage containers;
- prefer
spool_policy=rejectfor loss-sensitive data; - do not expose continuous
nodra-simagainst production fleets; - do not treat
--public-readas a substitute for the viewer role.