Skip to content

docs(adr): ADR-0011 — owner-issued grant for remoteControl.tls credentials - #328

Closed
thc1006 wants to merge 2 commits into
mainfrom
docs/adr-0010-credential-grant
Closed

docs(adr): ADR-0011 — owner-issued grant for remoteControl.tls credentials#328
thc1006 wants to merge 2 commits into
mainfrom
docs/adr-0010-credential-grant

Conversation

@thc1006

@thc1006 thc1006 commented Jul 31, 2026

Copy link
Copy Markdown
Owner

Proposed — no CRD, controller or flag ships here. This records a design and corrects part of an earlier one.

Why now

Three gaps remain on main after #219, #251/#300, #296, #297, #309 and #313. I checked each against main @ 93c47f3 rather than assuming. They look separate but share one shape: the Secret's owner cannot express, revocably, what their credential may be used for.

Gap State on main
Authorization is not revocable #309's ValidatingAdmissionPolicy is evaluated at admission only — ADR-0009's own amendment says stored objects are not re-validated. Withdrawing an author's RBAC does not re-evaluate the CRs they already created.
A credential is not bound to a destination --remote-control-allowed-endpoint-hosts is admin-global. Grepped the tree: zero per-Secret endpoint binding. Any labelled Secret can be paired with any allow-listed endpoint.
Revocation is invisible Secrets are get-only by design (an informer needs cluster-wide list/watch, rejected in #298). Withdrawn consent is unseen until the next real push — measured at 166 s on a live 1.36.3 cluster, driven by the referenced SatelliteEphemeris's heartbeat, not by any per-cell retry.

A failing push is already fine — RemoteEndpointRejected, RemoteControlCredentialUnavailable and RemoteControlEndpointNotAllowed all self-heal on the 5-minute poll. The gap is the healthy cell, where the dedup early-return sits ahead of resolveRemoteControlTLS, so nothing re-reads the credential at all.

The design

RemoteControlCredentialGrant, namespaced, created by the Secret's owner in the Secret's namespace: secretRef, allowedConfigs (name + optional uid), allowedEndpoints, allowedModes.

Being an object rather than a check is what buys all three properties at once — it can be deleted (revocable), it can be watched and field-indexed so revocation enqueues the affected NTNCellConfigs instead of waiting on an unrelated heartbeat, and it has somewhere to put an endpoint and a mode.

Enforcement is admin-gated and default-off behind --require-credential-grant. The tempting "enforce whenever a grant exists for that Secret" variant is rejected in the ADR: an attacker simply would not create one, so the feature would do nothing precisely when it matters.

It corrects ADR-0009, and says which part

ADR-0009 recommended SAR-at-admission over a grant CRD, calling the latter "heavier than the problem warrants". That was sound for the requirement as stated there — deciding who may create the reference. It does not survive two requirements that were not on the table:

  • Revocability — a SAR is a point-in-time check on a write. ADR-0009's amendment records this limitation itself.
  • Endpoint binding — a SAR answers "may this principal read this Secret?" and can never express "…and only to reach that gNB".

So a grant is not a heavier way to do the same job; it does three jobs, which is what changes the calculus. ADR-0009 now carries a forward pointer in its header so nobody lands on it and acts on the superseded bullet.

#309 is explicitly not replaced. It stays the cheap default tier — no CRD, no controller, one declarative policy — and stops the confused deputy at write time. The grant is the opt-in stronger tier for multi-tenant namespaces. #300's allow-list remains the admin's outer bound on destinations.

What the ADR refuses to claim

  • It does not close track: remoteControl.tls credential rotation/revocation is bounded by the ~3m epoch cadence (deferred from #219) #298. Rotating a Secret's contents touches no grant and fires no watch, so that path keeps the ~3-minute bound. The optional credentialRevision field is an opt-in escape hatch that depends on the owner remembering to bump it — not a Secret watch, and the ADR says so.
  • A patch-capable principal in the Secret's namespace can still write a grant they should not; namespace RBAC remains the ceiling.
  • A grant governs what the operator will present, not what the gNB accepts.

Cost, stated up front

A fifth CRD means make manifests bundle nephio-sync across four copies plus docs/api-reference.md, chart RBAC and samples. The controller gains list/watch on the new CRD but not on Secrets — that is the point: consent becomes watchable without widening Secret access, which is the trade #298 rejected an informer for.

Follow-up, deliberately not bundled

Automated E2E for the credentialed push. The chain is verified manually and shipped as a sample (#326), but no test in test/e2e/ exercises wss/mTLS, so a regression would ship green. Tracked separately — it is independent of this design decision.

…tials

Three gaps remain on main after #219, #251/#300, #296, #297, #309 and #313. They
look separate but share one shape: the Secret's owner cannot express, revocably,
what their credential may be used for.

  1. #309's ValidatingAdmissionPolicy is evaluated at ADMISSION only — ADR-0009's
     own amendment says stored objects are not re-validated, so withdrawing an
     author's RBAC afterwards does not re-evaluate the CRs they already created.
  2. --remote-control-allowed-endpoint-hosts is ADMIN-GLOBAL. Verified against
     main: there is no per-Secret endpoint binding anywhere in the tree, so any
     labelled Secret can be paired with any allow-listed endpoint.
  3. Secrets are get-only by design (an informer would need cluster-wide
     list/watch, rejected in #298), so withdrawing consent is invisible until the
     next real push — measured at 166s on a live 1.36.3 cluster, driven by the
     referenced SatelliteEphemeris's heartbeat rather than any per-cell retry.

A grant is an OBJECT rather than a check, which is what buys all three: it can be
deleted (revocable), it can be watched and indexed so revocation enqueues the
affected NTNCellConfigs, and it has somewhere to put allowedEndpoints and
allowedModes.

Partly supersedes ADR-0009. Its recommendation to prefer a SubjectAccessReview
over a ReferenceGrant-style CRD was sound for the requirement as stated there —
deciding who may create the reference — but does not survive two requirements that
were not on the table: revocability (a SAR is point-in-time on a write) and
endpoint binding (a SAR cannot express a destination). So a grant is not a heavier
way to do the same job; it does three jobs. ADR-0009 now carries a forward pointer
so a reader cannot act on the superseded bullet.

#309 is explicitly NOT replaced — it stays the cheap default tier (no CRD, no
controller, one declarative policy). Enforcement is admin-gated and default-off
behind --require-credential-grant; the "enforce whenever a grant exists" variant is
rejected in the ADR because an attacker simply would not create one.

Deliberately candid about the limit: rotating a Secret's CONTENTS touches no grant
and fires no watch, so that path keeps the ~3-minute bound. The optional
credentialRevision field is an opt-in escape hatch, not a Secret watch, and this
ADR does not close #298.

Proposed only — no CRD, controller or flag ships here.
@thc1006
thc1006 force-pushed the docs/adr-0010-credential-grant branch from de07d2b to b1addac Compare July 31, 2026 06:26
…to one current decision

Resolves the ADR-0010 number collision: the concurrent secure-by-default
transport decision (#330) takes 0010, this grant takes 0011, following the
layering 0009 threat model -> 0010 transport -> 0011 continuous authorization.
Flips the grant's numbering note and its transport-enum dependency refs, the
ADR-0009 forward-pointer, and the CHANGELOG entry.

Also flattens ADR-0009 per the review: a single "Net current state" block up
front states the three composing tiers (endpoint allow-list / #309 admission
policy / ADR-0011 grant) and marks the original "prefer SAR, defer authz"
bullet superseded; the original two-part decision and both amendments are kept
below as history.

Signed-off-by: thc1006 <hctsai@linux.com>
@thc1006 thc1006 changed the title docs(adr): ADR-0010 — owner-issued grant for remoteControl.tls credentials docs(adr): ADR-0011 — owner-issued grant for remoteControl.tls credentials Jul 31, 2026
thc1006 added a commit that referenced this pull request Jul 31, 2026
Replace docs/adr/ with the maintainer's SHA256-verified authoritative bundle.
This folds in the review's remaining corrections and supersedes the piecemeal
edits in PRs #328/#330/#338.

- rename 0001 -> nr-mobility-measurement-scope, 0008 -> constellation-pool-
  availability, 0009 -> remote-control-credential-boundary (old slugs removed);
- 0010 = v1alpha2 secure-by-default transport + refreshInterval duration
  admission validation;
- 0011 = owner-issued credential grant (resolves the two-ADR-0010 collision);
- add 0012 = evidence-based ground-station antenna health;
- every ADR now carries YAML front matter (adr/status/supersedes/superseded_by/
  implementation/tracking/last_verified) per ADR_STYLE_GUIDE.md.

Governance: checks/check_adr_metadata.py (front matter + numbering) and
hack/check-adr-index.sh (index membership + repo-internal link/anchor
resolution) both run via `make adr-lint` and the ADR lint CI job. SOURCES.md and
REVIEW_FINDINGS.md record verification provenance.

Signed-off-by: thc1006 <hctsai@linux.com>
@thc1006

thc1006 commented Jul 31, 2026

Copy link
Copy Markdown
Owner Author

Superseded by #337 (authoritative consolidated ADR set, now on main). The owner-issued credential grant lives at docs/adr/0011-remote-control-credential-grant.md, with the layered-authorization context consolidated in 0009-remote-control-credential-boundary.md. Closing in favour of that.

@thc1006 thc1006 closed this Jul 31, 2026
@thc1006
thc1006 deleted the docs/adr-0010-credential-grant branch July 31, 2026 10:34
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

track: remoteControl.tls credential rotation/revocation is bounded by the ~3m epoch cadence (deferred from #219)

1 participant