diff --git a/CONTEXT.md b/CONTEXT.md new file mode 100644 index 0000000000..af534282a7 --- /dev/null +++ b/CONTEXT.md @@ -0,0 +1,38 @@ +# APL Core + +The platform layer that installs, configures, and reconciles all APL components on a Kubernetes cluster. It owns the values repo structure, the GitOps reconciliation loop, and the ArgoCD project model. + +## Language + +### GitOps + +**Values repo**: The single git repository (`ENV_DIR`) that is the source of truth for all platform configuration, Helm values, and raw Kubernetes manifests. The apl-operator reads from and writes to it; ArgoCD reconciles it continuously. +_Avoid_: config repo, gitops repo, env repo + +**Manifests directory**: The `env/manifests/` subtree of the values repo. The only place in the values repo where raw Kubernetes resource YAMLs live. Reconciled directly by ArgoCD, not by Helmfile. +_Avoid_: raw manifests, k8s manifests folder + +**Namespace directory**: A subdirectory of `env/manifests/namespaces/` whose name matches a Kubernetes namespace. The apl-operator creates one ArgoCD Application per namespace directory that syncs its contents into that namespace. + +**Global directory**: `env/manifests/global/` — contains cluster-scoped resources (CRDs, ClusterRoles). Synced by a single ArgoCD Application with no destination namespace. + +**Operator-owned directory**: A namespace directory whose name starts with `apl-` and whose contents are written exclusively by the apl-operator program (e.g. `apl-secrets/`, `apl-users/`). Platform admins must not write to these. +_Avoid_: system directory, reserved directory + +**Platform-admin-owned directory**: A namespace directory whose name starts with `apl-` and whose contents are written by human platform admins, not the apl-operator program. `apl-addons/` is the only current example. The operator bootstraps the directory but does not manage its contents. + +### ArgoCD model + +**AppProject**: An ArgoCD `AppProject` resource that scopes which source repos, destination namespaces/clusters, and Kubernetes resource types an Application is permitted to use. +_Avoid_: project, argo project + +**Team project**: An AppProject named `team-{id}` scoped to a single team namespace. Generated by `charts/team-ns/templates/argocd/argocd-project.yaml`. + +**apl-addons project**: The AppProject named `apl-addons` that governs Applications dropped by platform admins. Fully unrestricted: any source repo, any destination namespace, any cluster, any resource type. + +**gitops-ns Application**: An ArgoCD Application named `gitops-ns-{namespace}` created by the apl-operator for each namespace directory. Syncs all manifests in that directory into the corresponding namespace with `prune: true` and `CreateNamespace: true`. +_Avoid_: parent app, bootstrap app + +**App-of-Apps**: The pattern where one ArgoCD Application manages a directory of other Application CRs. The `gitops-ns-apl-addons` Application is an App-of-Apps: it syncs Application CRs into the `apl-addons` namespace, where ArgoCD picks them up via the "app in any namespace" feature. + +**App in any namespace**: The ArgoCD feature (controlled by `application.namespaces` in `argocd-cm`) that allows ArgoCD to watch and reconcile Application CRs living in namespaces other than `argocd`. diff --git a/adr/2026-07-07-apl-addons-argocd-project.md b/adr/2026-07-07-apl-addons-argocd-project.md new file mode 100644 index 0000000000..79c760966a --- /dev/null +++ b/adr/2026-07-07-apl-addons-argocd-project.md @@ -0,0 +1,41 @@ +# apl-addons ArgoCD project for platform-admin addon deployments + +- Status: accepted + +## Context and Problem Statement + +Platform admins need a way to deploy arbitrary Kubernetes addons to any namespace without going through the team-scoped AppProject model. Every existing AppProject is locked to a single team namespace and forbids cluster-scoped resources. There is no supported escape hatch for platform admins who need to deploy, say, a CRD, a cross-namespace operator, or a Helm chart that spans multiple namespaces. + +## Decision Outcome + +A new `apl-addons` AppProject and namespace are introduced. Platform admins drop ArgoCD `Application` CRs into `env/manifests/namespaces/apl-addons/` in the values repo. The existing `addGitOpsApps` mechanism (which scans `env/manifests/namespaces/*` and creates one `gitops-ns-{namespace}` ArgoCD Application per directory) picks this up automatically and syncs those Application CRs into the `apl-addons` namespace. ArgoCD's "app in any namespace" feature then reconciles them under the `apl-addons` project. + +### AppProject spec + +The `apl-addons` AppProject is fully unrestricted: + +- `sourceRepos: ['*']` — platform admins must be free to pull from any registry or git host +- `sourceNamespaces: ['apl-addons']` — only Application CRs living in the `apl-addons` namespace may reference this project +- `destinations: [{namespace: '*', server: '*'}]` — any namespace on any registered cluster +- `clusterResourceWhitelist: [{group: '*', kind: '*'}]` — cluster-scoped resources allowed +- `namespaceResourceBlacklist: []` — no restrictions + +### ArgoCD "app in any namespace" wiring + +`application.namespaces: apl-addons` is set in both `configs.cm` (argocd-cm) and `configs.params` (argocd-cmd-params-cm). The latter triggers the conditional extra verbs on the server ClusterRole in `charts/argocd/templates/argocd-server/clusterrole.yaml`. + +### Protection via ValidatingAdmissionPolicy + +The `apl-addons` AppProject carries no ArgoCD finalizer. Instead, a `ValidatingAdmissionPolicy` at the API-server level blocks DELETE operations on the AppProject. A finalizer can be stripped by anyone with sufficient kubectl access and then the project deleted; a VAP cannot be bypassed without first modifying the VAP itself, which requires a separate privilege escalation step. + +### Naming convention deviation + +[ADR-2026-06-25](2026-06-25-manifests-directory.md) establishes that directories with an `apl-` prefix under `namespaces/` are operator-owned (written by the apl-operator program, not by humans). `apl-addons/` deviates from this: the operator bootstraps the directory with a `.gitkeep` but its contents are written by human platform admins. The `apl-` prefix is retained to signal that this directory is privileged and not a regular user-owned namespace directory. + +### Constraints not enforced by the AppProject + +ArgoCD AppProject `destinations` is a whitelist only — there is no native destination blacklist. Platform admins must not target the `argocd` namespace as a destination; this is documented but not enforced. Application CRs must set `project: apl-addons`; if they do not, ArgoCD will reject them with an RBAC error (no operator-level pre-validation is added). + +### ORCS registry policy + +Pods deployed into the `apl-addons` namespace remain subject to the Kyverno ORCS registry enforcement policy. The unrestricted AppProject scope does not imply unrestricted image provenance. diff --git a/adr/index.md b/adr/index.md index 5c59009b3c..35be829537 100644 --- a/adr/index.md +++ b/adr/index.md @@ -25,6 +25,7 @@ This log lists the architectural decisions for apl-core. - [ADR-2026-06-25](2026-06-25-manifests-directory.md) - Manifests directory in the values repo - [ADR-2026-06-25](2026-06-25-git-server-as-default-values-repo.md) - Lightweight git-server as the default values repository backend - [ADR-2026-06-25](2026-06-25-git-credential-management.md) - Git credential management via Kubernetes Secret +- [ADR-2026-07-07](2026-07-07-apl-addons-argocd-project.md) - apl-addons ArgoCD project for platform-admin addon deployments