mirror of
https://github.com/kevinveenbirkenbach/computer-playbook.git
synced 2026-11-06 10:59:59 +00:00
test(lint): require the onion in every variant that switches SSO on
SSO over a plaintext onion is where this platform actually breaks, and none of it shows on clearnet: a framework's Secure session-cookie default makes Chromium drop the cookie, an https identity provider cannot be embedded in an http page, and crypto.subtle is undefined because an onion is no secure context. Pixelfed, Taiga, Joomla and WordPress each cost a red job over exactly that. The combination is only exercised if a variant deploys both, so the lint requires it: services.sso.enabled true implies services.tor.enabled true. 69 variants enable SSO; 59 already complied. Of the ten that did not, seven are flipped on because the mechanism is proven there - matrix and nextcloud both run SSO behind the onion in variant 0 already (20ddda8c1), and their later variants were pinned false only by the blanket rule from1d9d355bc. Nextcloud variant 1 stays false: SSO is off there, and it is what gives the coverage test its false-pinning variant. Three roles opt out, each for a structural reason named in the marker. jitsi and bigbluebutton pin tor.enabled false in meta/services.yml because the conference needs getUserMedia, which no plaintext onion can offer. seaweedfs gates its own sso flag on 'svc-net-tor not in group_names', so on a tor node there is no login flow to test. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
bc1d2d86f6
commit
7a70d27b7c
6 files changed
+129
-16
No files matched your search
@@ -1,6 +1,10 @@
|
||||
---
|
||||
# Matrix-deploy variant overrides for web-app-bigbluebutton.
|
||||
# See docs/contributing/artefact/files/role/variants.md for the file format.
|
||||
#
|
||||
# nocheck: sso-variant-tor — meta/services.yml pins tor.enabled false: the conference
|
||||
# needs getUserMedia, which Chromium withholds outside a secure context, and a
|
||||
# plaintext onion is not one. There is no onion to log into.
|
||||
|
||||
# variant pinning every dynamic services.yml flag to true so the
|
||||
# matrix exercises the in-group_names branch of web-app-bigbluebutton.
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
---
|
||||
# Matrix-deploy variant overrides for web-app-jitsi.
|
||||
# See docs/contributing/artefact/files/role/variants.md for the file format.
|
||||
#
|
||||
# nocheck: sso-variant-tor — meta/services.yml pins tor.enabled false: the conference
|
||||
# needs getUserMedia, which Chromium withholds outside a secure context, and a
|
||||
# plaintext onion is not one. There is no onion to log into.
|
||||
|
||||
# V1: oidc + ldap + everything that can be true, is true
|
||||
- services:
|
||||
@@ -32,7 +36,6 @@
|
||||
enabled: false # nocheck: variants-static-override
|
||||
shared: false # nocheck: variants-static-override
|
||||
|
||||
# V2: no auth — everything off, AUTH_TYPE=internal, anonymous rooms
|
||||
- services:
|
||||
css:
|
||||
enabled: false
|
||||
@@ -62,7 +65,6 @@
|
||||
enabled: false # nocheck: variants-static-override
|
||||
shared: false # nocheck: variants-static-override
|
||||
|
||||
# V3: ldap only — direct prosody mod_auth_ldap2 against svc-db-openldap
|
||||
- services:
|
||||
css:
|
||||
enabled: false
|
||||
|
||||
@@ -120,8 +120,7 @@
|
||||
enabled: true
|
||||
shared: true
|
||||
tor:
|
||||
enabled: false
|
||||
shared: false # nocheck: variants-static-override
|
||||
enabled: true
|
||||
addons:
|
||||
mautrix-whatsapp:
|
||||
enabled: true
|
||||
|
||||
@@ -2,6 +2,9 @@
|
||||
# Matrix-deploy variant overrides for web-app-nextcloud.
|
||||
# See docs/contributing/artefact/files/role/variants.md for the file format.
|
||||
#
|
||||
# nocheck: comments-valid: eight variants of ~90 near-identical service lines are
|
||||
# unreadable without a heading naming what each one adds.
|
||||
#
|
||||
# nocheck: variant-limit: the loosely-coupled (bond<1) bridge partners are too heavy
|
||||
# to co-host, so each budget-bin of them needs its own variant.
|
||||
#
|
||||
@@ -272,8 +275,7 @@
|
||||
enabled: false
|
||||
shared: false # nocheck: variants-static-override backup shared stays literally true
|
||||
tor:
|
||||
enabled: false
|
||||
shared: false # nocheck: variants-static-override
|
||||
enabled: true
|
||||
# Variant 3: openproject + mattermost + matrix
|
||||
- services:
|
||||
container_backup:
|
||||
@@ -361,8 +363,7 @@
|
||||
enabled: false
|
||||
shared: false # nocheck: variants-static-override backup shared stays literally true
|
||||
tor:
|
||||
enabled: false
|
||||
shared: false # nocheck: variants-static-override
|
||||
enabled: true
|
||||
# Variant 4: onlyoffice + mastodon
|
||||
- services:
|
||||
container_backup:
|
||||
@@ -450,8 +451,7 @@
|
||||
enabled: false
|
||||
shared: false # nocheck: variants-static-override backup shared stays literally true
|
||||
tor:
|
||||
enabled: false
|
||||
shared: false # nocheck: variants-static-override
|
||||
enabled: true
|
||||
# Variant 5: zammad
|
||||
- services:
|
||||
container_backup:
|
||||
@@ -539,8 +539,7 @@
|
||||
enabled: false
|
||||
shared: false # nocheck: variants-static-override backup shared stays literally true
|
||||
tor:
|
||||
enabled: false
|
||||
shared: false # nocheck: variants-static-override
|
||||
enabled: true
|
||||
# Variant 6: gitlab
|
||||
- services:
|
||||
container_backup:
|
||||
@@ -628,8 +627,7 @@
|
||||
enabled: false
|
||||
shared: false # nocheck: variants-static-override backup shared stays literally true
|
||||
tor:
|
||||
enabled: false
|
||||
shared: false # nocheck: variants-static-override
|
||||
enabled: true
|
||||
# Variant 7: discourse + peertube
|
||||
- services:
|
||||
container_backup:
|
||||
@@ -717,5 +715,4 @@
|
||||
enabled: true
|
||||
shared: true
|
||||
tor:
|
||||
enabled: false
|
||||
shared: false # nocheck: variants-static-override
|
||||
enabled: true
|
||||
@@ -1,4 +1,7 @@
|
||||
---
|
||||
# nocheck: sso-variant-tor — meta/services.yml gates sso on
|
||||
# "svc-net-tor not in group_names": the frontend is off on a tor node by design, so
|
||||
# there is no SSO-over-onion flow to exercise.
|
||||
- services:
|
||||
container_backup:
|
||||
enabled: true
|
||||
|
||||
@@ -0,0 +1,108 @@
|
||||
"""Lint guard: a variant that turns SSO on must also turn the onion on.
|
||||
|
||||
Single sign-on over a plaintext ``http://<onion>`` origin is where the platform
|
||||
breaks in ways clearnet never shows: a framework's ``Secure`` session-cookie
|
||||
default silently drops the cookie, an https identity provider cannot be embedded
|
||||
in an http page, and ``crypto.subtle`` is undefined because an onion is not a
|
||||
secure context. None of that surfaces unless a variant deploys SSO and the onion
|
||||
together, so every ``meta/variants.yml`` entry pinning
|
||||
``services.sso.enabled: true`` MUST also pin ``services.tor.enabled: true``.
|
||||
|
||||
A role that genuinely cannot serve SSO over the onion opts out with
|
||||
``# nocheck: sso-variant-tor`` anywhere in its ``meta/variants.yml`` or
|
||||
``meta/services.yml``, naming the reason. Three kinds qualify: a role whose
|
||||
``meta/services.yml`` pins ``tor.enabled`` to a literal false (it has no onion at
|
||||
all), a role whose SSO gate is itself disabled on a tor node, and a role whose
|
||||
login flow needs a browser API that a non-secure context withholds.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import unittest
|
||||
from typing import TYPE_CHECKING, Any
|
||||
|
||||
import yaml
|
||||
|
||||
from utils.annotations.suppress import line_has_rule
|
||||
from utils.cache.files import read_text
|
||||
from utils.cache.yaml import load_yaml_str
|
||||
from utils.roles.mapping import ROLE_FILE_META_SERVICES, ROLE_FILE_META_VARIANTS
|
||||
|
||||
from . import PROJECT_ROOT
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from pathlib import Path
|
||||
|
||||
ROLES_DIR = PROJECT_ROOT / "roles"
|
||||
_RULE = "sso-variant-tor"
|
||||
|
||||
|
||||
def _load_yaml(path: Path) -> Any:
|
||||
if not path.is_file():
|
||||
return None
|
||||
try:
|
||||
text = read_text(str(path))
|
||||
except UnicodeDecodeError:
|
||||
return None
|
||||
if not text.strip():
|
||||
return None
|
||||
try:
|
||||
return load_yaml_str(text)
|
||||
except yaml.YAMLError:
|
||||
return None
|
||||
|
||||
|
||||
def _suppressed(paths: tuple[Path, ...]) -> bool:
|
||||
for path in paths:
|
||||
if not path.is_file():
|
||||
continue
|
||||
if any(
|
||||
line_has_rule(line, _RULE) for line in read_text(str(path)).splitlines()
|
||||
):
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def _flag(variant: Any, service: str) -> Any:
|
||||
services = variant.get("services") if isinstance(variant, dict) else None
|
||||
entry = services.get(service) if isinstance(services, dict) else None
|
||||
return entry.get("enabled") if isinstance(entry, dict) else None
|
||||
|
||||
|
||||
class TestSsoVariantRequiresTor(unittest.TestCase):
|
||||
def test_every_sso_variant_pins_the_onion_on(self) -> None:
|
||||
offenders: list[str] = []
|
||||
for role_dir in sorted(p for p in ROLES_DIR.iterdir() if p.is_dir()):
|
||||
variants_path = role_dir / ROLE_FILE_META_VARIANTS
|
||||
variants = _load_yaml(variants_path)
|
||||
if not isinstance(variants, list) or not variants:
|
||||
continue
|
||||
if _suppressed((variants_path, role_dir / ROLE_FILE_META_SERVICES)):
|
||||
continue
|
||||
|
||||
for index, variant in enumerate(variants):
|
||||
if _flag(variant, "sso") is not True:
|
||||
continue
|
||||
tor = _flag(variant, "tor")
|
||||
if tor is True:
|
||||
continue
|
||||
offenders.append(
|
||||
f"{role_dir.name}: variant {index} pins services.sso.enabled "
|
||||
f"true but services.tor.enabled is {tor!r} — SSO must be "
|
||||
f"exercised over the onion, where it actually breaks."
|
||||
)
|
||||
|
||||
if offenders:
|
||||
self.fail(
|
||||
f"Variants enabling SSO without the onion ({len(offenders)}):\n"
|
||||
+ "\n".join(f" - {o}" for o in offenders)
|
||||
+ "\n\nAdd to the variant's services block:\n"
|
||||
+ " tor:\n"
|
||||
+ " enabled: true\n"
|
||||
+ f"\nOr opt the role out with `# nocheck: {_RULE} — <reason>` in "
|
||||
+ "meta/variants.yml when it cannot serve SSO over an onion."
|
||||
)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
Reference in new issue
Block a user