feat(r-p1): make the portal configuration a release prerequisite, and say so everywhere - #272
Conversation
ac59bbf to
00c8cf8
Compare
|
Is it expected that the CI job "Build Debian Package" fails silently? See https://github.com/linagora/open-bastion/actions/runs/34227502715/job/102065857202 Note that |
|
@orontee Good catch, thanks — no, it is not expected, and it is not specific to this PR: main's CI shows the same two lines. The cpack The released packages are not affected: Fix in #273: the links are now installed with |
… say so everywhere R-P1 is the release gate of #268 and the only item in it that changes what the product claims. With `pamAccessServerGroups` empty -- the shipped default, and the multi-group model doc/bastion-architecture.md recommended -- `server_group` is read from the request body, so any enrolled host of the project that is compromised can declare itself a bastion on /pam/authorize and obtain a hop voucher for a user. `allowed_bastions` empty still means "accept any", for Ansible compatibility. Two positions were tenable: make the configuration a documented prerequisite, or accept it as a signed residual risk. This takes the first. A shipped default that contradicts the configuration the architecture document recommends is not a residual risk, it is a condition of service, and it is not recoverable after the fact: a host compromised during the window already has its vouchers. CE03 loses its "ou mode hérité explicitement accepté" escape and becomes blocking, CE06 and CE16 are marked blocking, and CE21 is new -- `pamAccessAllowedRps` plus bastions enrolled under a client_id of their own, without which the upstream audience binding does nothing at all, since an empty list means "no change". MT40 and MT50 become publication prerequisites and MT53 is their `pamAccessAllowedRps` counterpart, which the plan had no row for. The R-P1 sheet says the residual score is no longer conditioned on an assumption. Three operator documents told the reader to leave `pamAccessServerGroups` empty -- "for the usual model of one client_id per project covering several server groups". That model is precisely the configuration in which the gap is exploitable, so all three now say the opposite, including what it costs: one client_id per server group, because an unmapped one is refused. Saying "this is now required" without saying "and here is the enrolment work it implies" would just move the surprise. The product can neither set nor verify the portal half. These are Manager settings, and reading them back from a host would need an API that publishes the project's bastions, server groups and RPs -- which is exactly what an SSO must not offer. The only enforcement available is declarative, so it is put on every path an operator takes: - ob-bastion-setup, ob-backend-setup and ob-desktop-setup print the requirement in their summary, every run. A desktop is not a bastion, but it is an enrolled host, which is the whole point of the risk; - ob-post-upgrade prints it too -- it is the path an existing host takes to 0.7.0, the only one that reaches hosts set up before any of this existed; - ob-builder writes PORTAL-CHECKLIST.md next to each artefact, pre-filled with that deployment's client_id and server_group. That one matters most for the Ansible role, whose setup task runs with nobody reading its stdout; - UPGRADE-NOTES.md gets B0, and Part B stops being "only if you are also moving the portal". The text is shipped once, at /usr/lib/open-bastion/ob-portal-prerequisites.txt, and packaged in the deb and the rpm as well as by cmake -- a helper that existed in the tree and not in the package is #263, and a warning nobody receives is worse than none. tests/test_ob_portal_prerequisites.sh runs the code rather than reading it: it executes each script's printer, runs ob-post-upgrade --dry-run, and drives ob-builder's run_outputs_for_role with the renderers stubbed to check both artefact kinds get a checklist with no unresolved placeholder. Three catalogue entries, all caught; the whole catalogue is 14/14. The dossier's CE02 row was also repaired: unescaped pipes inside the `^/ssh/(admin|certs|revoke)` regex had collapsed it into the neighbouring columns, taking R-S4 and R-P2 with it. It is the table this commit adds a row to, and an evaluator reads it. Closes part 5 of #268.
00c8cf8 to
9c7a7f6
Compare
Let's merge #272 then ? |
|
It's ok for me. |
Item 5 of #268 — the release gate, and the only item that changes what the
product claims.
The decision
With
pamAccessServerGroupsempty — the shipped default, and themulti-group model
doc/bastion-architecture.mdrecommended —server_groupisread from the request body, so any enrolled host of the project that is
compromised can declare itself a bastion on
/pam/authorizeand obtain a hopvoucher for a user.
allowed_bastionsempty still means "accept any", forAnsible compatibility.
#268 named two honest positions. This PR takes the first: release
prerequisite, not accepted residual risk. A shipped default that contradicts
the configuration the architecture document recommends is a condition of
service, and it is not recoverable after the fact — a host compromised during
the window already has its vouchers.
Record
blocking; CE06 and CE16 are marked blocking.
pamAccessAllowedRps+ bastions under aclient_idof theirown. Without it the upstream audience binding does nothing at all — an empty
list means "no change".
pamAccessAllowedRpsrow the plan had no entry for.assumption.
Three operator docs said the opposite
doc/llng-configuration.md,doc/admin-guide.mdanddoc/bastion-architecture.mdall told the reader to leavepamAccessServerGroupsempty "for the usual model of oneclient_idperproject covering several server groups". That model is precisely the
configuration in which the gap is exploitable. All three now say the opposite —
including what it costs: one
client_idper server group, because anunmapped one is refused. Saying "this is now required" without saying "and here
is the enrolment work it implies" would only move the surprise.
Enforcement (what is possible, and what is not)
The product can neither set nor verify the portal half. These are LLNG Manager
settings, and reading them back from a host would need an API that publishes the
project's bastions, server groups and RPs — which is exactly what an SSO must
not offer. So the enforcement is declarative, and it is on every path an
operator takes:
ob-bastion-setup,ob-backend-setup,ob-desktop-setupob-post-upgradeob-builderPORTAL-CHECKLIST.mdnext to each artefact, pre-filled with that deployment'sclient_idandserver_groupUPGRADE-NOTES.mdThe builder checklist matters most for the Ansible role, whose setup task runs
with nobody reading its stdout.
The text is shipped once, at
/usr/lib/open-bastion/ob-portal-prerequisites.txt, and packaged in the.deband the
.rpmas well as by cmake — a helper that existed in the tree and notin the package is #263, and a warning nobody receives is worse than none.
Tests
tests/test_ob_portal_prerequisites.shruns the code rather than reading it:it executes each script's printer, runs
ob-post-upgrade --dry-run, and drivesob-builder'srun_outputs_for_rolewith the renderers stubbed to check bothartefact kinds get a checklist with no unresolved
@@PLACEHOLDER@@.Every
tests/test_ob_*.shsuite green,ctest22/22.Also
The dossier's CE02 row was repaired: unescaped pipes inside the
^/ssh/(admin|certs|revoke)regex had collapsed it into the neighbouringcolumns, taking R-S4 and R-P2 with it. It is the table this PR adds a row to.