Stabiliteit, security en parallellisme: AGE-churn, Keycloak-credentials, wizard-fixes, parallelle deploys#124
Merged
Conversation
…aarden De alias-editor verstuurt vrije YAML; KeyValueConverter splitste echter elke regel naar voren op '=' waardoor URL-waarden als 'KEY: "postgres://...?schema=x"' onleesbaar werden opgeslagen. Daarnaast detecteerde _detect_env_var_format een YAML-regel niet zodra de waarde een '=' bevatte, zodat de validator dezelfde fallback maakte. - _detect_env_var_format herkent nu YAML aan 'KEY:' aan regelbegin, ongeacht een '=' verderop in de waarde. - KeyValueConverter._parse_env_text delegeert naar validate_and_parse_env_vars zodat YAML- en KEY=VALUE-invoer beide correct geparseerd worden; bestaande KEY=VALUE-flow (user-env-vars) blijft ongewijzigd door write_as="string" pad.
…oppen
CSRFMiddleware parste de form-body om het token te valideren, maar onder
BaseHTTPMiddleware consumeerde dat de body zonder hem te cachen, waardoor
de downstream handler een lege body kreeg en elk veld als ontbrekend las
('Dit veld is verplicht'). Cache de body nu voor het parsen.
Voeg daarnaast de X-CSRF-Token toe aan de tune-, preset- en refresh-knoppen
(htmx hx-post) en geef 'request' door aan de subdomein-modal render, zodat
deze POSTs niet langer op centrale CSRF-enforcement stuklopen (403/500).
_decrypt_and_clean_env_vars stripte onvoorwaardelijk omringende quotes van elke waarde, nadat validate_and_parse_env_vars de quote-semantiek al correct had afgehandeld. Daardoor werd een waarde die de quotes bewust als inhoud nodig heeft (bv. cal.com ALLOWED_HOSTNAMES: '"host"' zodat de app deze tot een JSON-array kan wrappen) stil gecorrumpeerd tot de kale waarde -- geen enkele quote-vorm in het project-bestand overleefde dit. Verwijder de dubbele strip; quote-afhandeling hoort op een plek thuis (validate_and_parse_env_vars). Met regressietests.
…orkPolicy
cert-manager's HTTP-01 solver pod luistert op 8089 (de router stuurt de
challenge door naar de pod op 8089, niet 80). De gegenereerde
acme-http-<app>-network-policy selecteert alle pods (podSelector:{}) maar
stond alleen poort 80 toe, waardoor verkeer naar de solver op 8089 werd
gedropt: TCP connect lukt, HTTP-request hangt, self-check faalt met
'context deadline exceeded' en het certificaat wordt nooit Ready.
Eén van de drie generatie-locaties was al gecorrigeerd; de andere twee
(o.a. het pad dat rig-prd-ia-fky genereerde) stonden nog op [80]. Alle
drie nu op [80, 8089].
De twee 'Tune resources' c-buttons gebruikten hx-headers met enkele
quotes rond JSON; de ROOS c-button re-serialiseert attributen met dubbele
quotes, waardoor de JSON-quotes botsten en de browser het attribuut in
ongeldige losse attributen opbrak. Daardoor kwam er geen geldig
X-CSRF-Token header mee en faalde de tune-POST.
Gebruik de "-entityvorm die de c-button passthrough overleeft,
identiek aan de reeds werkende c-buttons (modal_wizard_review,
modals, wizard_review). Geverifieerd door het renderen via de echte
c-button: levert geldig {"X-CSRF-Token": "<token>"} op.
…ride toe op sandbox Custom images met een ingebakken non-root user op een andere UID dan 1001 (bijvoorbeeld openproject met UID 999) clasht met de hardcoded sandbox pod-securityContext, waardoor er per image een chown-wrap nodig is. Een verborgen `components[].security` blok in het project-YAML laat de user nu run-as-user / run-as-group / fs-group per component overschrijven. Alle drie de velden zijn optioneel; ontbrekende velden vallen terug op 1001 (behoud van bestaand gedrag). Op odcn-production wordt het blok genegeerd — daar wijst OpenShift SCC zelf een UID per namespace toe. Het veld is bewust niet in de wizard / detail-edit UI zichtbaar: users bewerken de YAML rechtstreeks in Forgejo. Wel toegevoegd aan de Pydantic ProjectFileModel en het JSON-schema zodat de validator het accepteert zonder dat onbekende typo's (zoals camelCase) doorslippen.
… / per deployment Wie een upstream image als `openproject/openproject:17.4-slim` wil draaien hoefde tot nu toe een eigen wrap-Dockerfile te bouwen om de ENTRYPOINT te vervangen (bijv. een seeder voor de hoofd-proces). Kubernetes heeft daar `containers[].command` voor — die werd alleen niet doorgegeven via ZAD. Een nieuw, verborgen YAML-veld `command: list[str]` op `components[]` en `deployments[].components[]` mapt 1-op-1 op het K8s-veld. Bij beide niveaus gezet wint de deployment-override. Bij allebei leeg wordt geen `command:` geemitteerd en blijft de ENTRYPOINT/CMD van de image leidend (bestaand gedrag). Bewust list[str] (geen shell-string) en `minItems: 1` zodat een lege lijst de ENTRYPOINT niet stilletjes wist. Niet in de wizard / detail-edit UI — users bewerken de YAML zelf in Forgejo, net als de security override.
…ij projectverwijdering
Bij het verwijderen van een project bleven de AppProject-manifest, de
repository-secret en kustomization.yaml in {cluster}/{project}/ van de
argo-applications-repo staan. De root-app (selfHeal) bleef die resources
daardoor eindeloos terugzetten - de bestaande kubectl-cleanup van
AppProjects vocht tegen GitOps en verloor altijd.
Nieuwe stap 4.7 in delete_project verwijdert de map (zelfde patroon als
de -infrastructure/-mapdeletie), commit, en refresht user-applications
zodat AppProject en repo-secret direct gepruned worden.
Geconstateerd op productie: 11 weesmappen met levende AppProjects en
repo-credential-secrets van projecten verwijderd tussen jan en mei 2026.
…sh-on-web Apps zoals OpenProject hebben host-name configvelden die een volledige URL weigeren. Geef daarom naast PUBLIC_HOST (met scheme) ook PUBLIC_HOSTNAME (alleen de hostname) door, zodat gebruikers niet zelf de scheme uit PUBLIC_HOST hoeven te strippen in user-env-vars.
…nt, met slim verval Drie problemen in de OOM-floor die neerwaartse tuning structureel blokkeerden: 1. De floor klemde ook de request vast, terwijl OOM-bescherming alleen over de limit gaat. De request mag nu vrij zakken naar usage+buffer (genormeerd op request <= limit); de limit behoudt zijn burst-headroom. Facturatie loopt op requests - dit kostte clusterbreed ~5 GiB aan gereserveerd-maar-ongebruikt geheugen. 2. De component-definitie-tak van get_resource_history_floor filterde niet op deployment, waardoor een OOM in een PR-omgeving alle deployments (incl. productie) pinde. Def-level entries tellen nu alleen mee voor de deployment die ze heeft gezet (het deployment-veld wordt al sinds altijd meegeschreven; legacy entries zonder dat veld vervallen daarmee bewust). 3. De floor verliep nooit. Nieuw: een floor verloopt zodra de OOM-entry ouder is dan RESOURCE_TUNING_OOM_FLOOR_MIN_AGE_DAYS (10) en de waargenomen max-usage onder RESOURCE_TUNING_OOM_FLOOR_STABLE_PERCENT (50%) van de floor ligt - aantoonbaar stabiel, dus de floor is irrelevant geworden. Onparseerbare timestamps verlopen nooit (fail-safe). Productie-data: alle OOM-bursts lagen 43-73 dagen terug zonder herhaling. Daarnaast onderdrukte de floor het memory-check-scherm volledig (return None): gebruikers zagen niet dat er iets te besparen viel. Floor-geblokkeerde componenten geven nu een eigen kaart met de request-besparing en de floor-datum. get_resource_history_floor geeft nu een ResourceFloor (waarde+timestamp) terug i.p.v. een kale float.
The handler had @requires_sso but lived under /api/, where the authorization middleware passes through (API-key path). The page route was already gated; this closes the matching JSON endpoint by moving it under /ui/, where the standard session gate applies. Adds a regression test.
…ctbestand-blokken Elke 'Process project' run hercodeerde het deployment 'configuration:'-blok (AGE is niet-deterministisch: zelfde plaintext, compleet andere ciphertext). Gevolg: ~52 regels pure ciphertext-churn per run, git-historie-bloat en non-fast-forward pushconflicten tussen gelijktijdige runs (verloren user-edit bij regel-k4c). Wizard-saves hercodeerden daarnaast elk user-env-vars blok, ook zonder wijziging. Het configuration-blok bleek een write-only kopie van alle credentials: processing leest het nooit terug (creds komen uit live k8s-secrets), de enige UI-decrypt voedde geen enkele template, geen API exposet het. Daarom: - Drop het blok volledig: _save_encrypted_configs_to_project_file, _normalize_secret_keys, de _env_vars trackingmap en to_config_data verwijderd; dode decrypt in web/router.py weg; configuration-veld uit DeploymentModel/i18n/wizard-literalize. - Strip bestaande 'configuration'/'decrypted_configuration'-blokken via de onvoorwaardelijke _fixup_v2_data (eenmalige cleanup per project). - Wizard-saves: keep_existing_ciphertext_if_unchanged() in de editables- pipeline houdt opgeslagen ciphertext byte-identiek wanneer de plaintext niet wijzigde (key-rotatie decrypt-faalt en hercodeert dus gewoon). Geverifieerd in sandbox: process schrijft geen configuration-blok meer, fixup stripte bestaand blok, overige AGE-blokken byte-identiek na save. NB: bevat ook de catalog-root fixup-hunks die in PR #120 zitten (zelfde inhoud; merge-volgorde maakt niet uit).
nodes/nodes-proxy/nodes-metrics leesrechten waren per ongeluk verwijderd in de RBAC-inscoping van #80, waardoor Prometheus kubelet/cAdvisor-scrapes 403 gaven (role:node service-discovery + resource-SAR op nodes/metrics; nonResourceURLs dekt dat niet).
…user-env-vars ontsleuteld Twee bugs in de modal-wizard samenvatting: 1. Een leeggemaakt veld (bijv. aliases in Component bewerken) kwam terug met de oude waarde: de additieve merge in get_merged_data() kan een verwijderde sleutel niet uitdrukken. Gewiste velden krijgen nu een expliciete tombstone in het stap-fragment; get_merged_data() ruimt die op en verwijdert daarbij ook de oude waarde uit de snapshot. 2. user-env-vars verschenen als rauw AGE-blok: converter.view() werd aangeroepen met kwarg yaml_data= die geen enkele converter kent, waarna de except-TypeError fallback de context (en dus decryptie) liet vallen. Aanroep gebruikt nu context_data= en de fallback is weg.
Het gegenereerde admin-wachtwoord bestaat nergens anders dan in het projectbestand. Het werd pas aan het einde van de taak gecommit; elke fout daartussen (zoals een alias-validatiefout) liet de admin-user achter in Keycloak met een onherstelbaar wachtwoord, waarna de duplicate-admin guard elke herverwerking blokkeerde (napp-avm). Het configuratieblok wordt nu direct na het aanmaken van de admin-user gecommit en gepusht. De guard zelf blijft ongewijzigd weigeren (geen automatische wachtwoordresets); alleen de fouttekst beschrijft nu de echte oorzaak en de handmatige herstelprocedure.
…og taak-concurrency Een project-refresh met N deployments wachtte serieel per applicatie (tot 120s creatie + 300s sync elk); 20 deployments duurde daardoor tot een uur. De creatie- en sync-waits (read-only polls) draaien nu concurrent via asyncio.gather; de wandkloktijd is die van de traagste deployment. Remediatie (OOM-tuning, image-pull uitschakelen) blijft serieel omdat die het projectbestand in git muteert. Bijvangst: component-failures van meerdere falende deployments worden nu geaggregeerd in het taakresultaat (voorheen overschreef elke falende app de vorige). Capaciteit daarop afgestemd: - TASK_WORKER_CONCURRENCY 4 -> 12 (backup-limiet blijft 2) - DB-pool max_size 10 -> 20 (12 workers verhongerden op 10 connecties) - git push rebase-retries 3 -> 5 (meer gelijktijdige pushers per repo)
…ere syncs Afgestemd op het parallelle deployment-wachten in de operations manager: - controller.status.processors 5 -> 20, operation.processors 5 -> 10 - kubectl.parallelism.limit en reposerver.parallelism.limit 5 -> 10 - controller 2 CPU/4Gi -> 4 CPU/8Gi (OOMKilled vandaag op 4Gi door resync-churn; meer parallelisme vraagt sowieso meer geheugen) - repo-server 1 CPU/1Gi -> 2 CPU/2Gi (rendert kustomize+SOPS voor 10 parallelle syncs)
pip-audit op main faalde op 5 bekende kwetsbaarheden in 3 packages: - aiohttp 3.13.5 -> 3.14.1 (CVE-2026-47265 cross-origin redirect met per-request cookies, CVE-2026-34993 deserialisatie van untrusted data) - starlette 0.50.0 -> 1.2.1 (PYSEC-2026-161: ontbrekende Host-header validatie vergiftigt request.url.path en omzeilt padgebaseerde security-checks; direct relevant voor onze authorization-middleware) - fastapi 0.135.1 -> 0.136.3 (mee voor starlette 1.x compatibiliteit) - pip 26.1.1 -> 26.1.2 (PYSEC-2026-196 entry-point pad-escape) Volledige testsuite groen op de nieuwe stack (3737 tests; alleen integratietests zonder cluster geven verwachte errors). pip-audit lokaal: geen bevindingen meer.
uittenbroekrobbert
added a commit
that referenced
this pull request
Jun 10, 2026
…emplateResponse-signatuur PR #124 hergenereerde uv.lock waardoor fastapi 0.136.3 → starlette 1.2.1 meekwam. Starlette 1.x verwijdert de deprecated TemplateResponse(name, context)-signatuur; de context-dict wordt dan als templatenaam doorgegeven en elke render faalt met 'cannot use tuple as a dict key (unhashable type: dict)'. OPI heeft 57 call-sites in de oude stijl — productie gaf 500 op elke pagina. Pin terug naar de laatst werkende versies. Echte fix volgt: alle TemplateResponse-calls migreren naar request-first signatuur, daarna unpinnen.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Wat zit erin
Verzamelbranch met de fixes en features van de afgelopen periode, in vier clusters:
Stabiliteit / churn
01712fee): deploymentconfiguration:-blokken en user-env-vars worden niet meer hercodeerd als de inhoud ongewijzigd is. Dit stopt de permanente git-churn, de non-fast-forward pushconflicten en de ArgoCD resync-loop (OutOfSync↔Synced flapping) die vandaag de application-controller op 4Gi OOM joeg.faa2399d): het gegenereerde admin-wachtwoord wordt meteen na aanmaak gecommit/gepusht. Een latere taakfout kan geen onherstelbaar wachtwoord meer achterlaten (napp-avm-incident); de duplicate-admin guard blijft bewust weigeren, met een fouttekst die nu de echte oorzaak en remediatie beschrijft.Parallellisme / snelheid
d17c87ab): een refresh met N deployments wacht nu concurrent; wandkloktijd ≈ traagste deployment i.p.v. de som. Remediatie (OOM-tune, image-pull disable) blijft serieel. Taak-concurrency 4→12, DB-pool 10→20, push-retries 3→5.2f87ff09): processors 5→20/10, parallelism-limits →10, controller 4 CPU/8Gi, repo-server 2 CPU/2Gi.Wizard / UI
a94d33a2)Overig
/ui/)command:en runAsUser/runAsGroup/fsGroup overrides (sandbox)Test