Add Cozystack security self-assessment snapshot - #2306
Conversation
Point-in-time snapshot of Cozystack's security self-assessment for the Incubation due-diligence record (cncf#1916), per the Security section of the DD guide and requested in cozystack/community#81. Signed-off-by: tym83 <6355522@gmail.com>
JustinCappos
left a comment
There was a problem hiding this comment.
I've added a few minor issues to fix. However, the assessment is in good shape overall.
|
|
||
| ### Background | ||
|
|
||
| Cozystack is delivered as a **management (root) Kubernetes cluster** onto which tenants, managed services, virtual machines, and tenant Kubernetes clusters are layered. The centre of the security model is the aggregated API server **`cozystack-api`**. Tenants never write privileged Kubernetes objects (HelmRelease, Deployment, Secret, RBAC) directly. Instead they write thin, virtual `apps.cozystack.io/*` **Application** custom resources, and `cozystack-api` translates each one into a Flux `HelmRelease` whose chart reference is fixed server-side. The set of Application kinds is registered dynamically from `ApplicationDefinition` custom resources, so the platform's catalogue of managed services is data-driven rather than hard-coded. |
There was a problem hiding this comment.
This is helpful, but at least for me is missing a sentence or so of "why" / "what is the impact". Can you explain how writing to apps.cozystack.io/* application custom resources changes the security / usability properties?
|
|
||
| Cozystack is delivered as a **management (root) Kubernetes cluster** onto which tenants, managed services, virtual machines, and tenant Kubernetes clusters are layered. The centre of the security model is the aggregated API server **`cozystack-api`**. Tenants never write privileged Kubernetes objects (HelmRelease, Deployment, Secret, RBAC) directly. Instead they write thin, virtual `apps.cozystack.io/*` **Application** custom resources, and `cozystack-api` translates each one into a Flux `HelmRelease` whose chart reference is fixed server-side. The set of Application kinds is registered dynamically from `ApplicationDefinition` custom resources, so the platform's catalogue of managed services is data-driven rather than hard-coded. | ||
|
|
||
| When OIDC is enabled (opt-in; `authentication.oidc.enabled` is off by default), authentication is centralized through Keycloak/OIDC; in the default OIDC-disabled mode the dashboard is fronted by a token-proxy. Authorization uses Kubernetes RBAC (with roles aggregated per tenant) plus ValidatingAdmissionPolicy. Networking isolation is provided by Cilium. Platform PKI (including the aggregated-API serving certificate) is issued by cert-manager. Tenant Kubernetes clusters run on dedicated Kamaji-hosted control planes with Talos worker nodes running as KubeVirt virtual machines. |
There was a problem hiding this comment.
It might also be useful here to indicate which of these is therefore trusted and what users really need to understand. E.g., is it that you're doing all the Cilium / Cert-Manager work for them or does the user need to do these parts correctly?
| | Delete a platform-critical object | Any actor → core kube-apiserver | DELETE request | `cozystack-no-delete-guardrail` denies DELETE on `platform.cozystack.io/no-delete=true` objects. Operational guidance, not adversarial defense: an actor able to update the object can remove the label first (tenants cannot reach these roots; only T0/T1). | Accidental teardown of platform roots blocked. | | ||
| | East-west traffic | Workload (T3) → workload/system | Packets | Cilium enforces policy at both endpoints. Each tenant pod's egress is confined to its own subtree, platform system services, and `world` (out-of-cluster); ingress is open to `world`+`cluster`. A flow needs the source's egress to allow it, so an unrelated tenant has no egress path to another tenant's pod. | Cross-tenant pod-to-pod traffic is **denied** (by source egress). Within a subtree, parent→descendant is allowed; child→parent is denied except for specific ancestor services (vminsert, etcd, ingress). Caveats (see Non-goals): open ingress and LoadBalancer hairpin. | | ||
|
|
||
| ### Goals |
There was a problem hiding this comment.
Great set of goals here that are very clear. How do you expose these to your users?
| ### Non-goals | ||
|
|
||
| - Defending against a compromised **cluster administrator, node root, or infrastructure administrator** — trusted by design and out of scope (`SECURITY.md`). | ||
| - Treating the **T1 platform control plane** (`cozystack-api` ServiceAccount, Flux, system operators) as a sandbox; these hold cluster-admin-equivalent power. |
There was a problem hiding this comment.
Do you mean isolating from a malicious T1 platform control plane? This might be a clearer phrasing...
|
|
||
| - Defending against a compromised **cluster administrator, node root, or infrastructure administrator** — trusted by design and out of scope (`SECURITY.md`). | ||
| - Treating the **T1 platform control plane** (`cozystack-api` ServiceAccount, Flux, system operators) as a sandbox; these hold cluster-admin-equivalent power. | ||
| - **Upstream-only vulnerabilities** not introduced or worsened by Cozystack packaging or defaults. |
There was a problem hiding this comment.
Have you reviewed your dependency footprint?
| - **Dependency and image CVE scanning (SCA).** An organization-wide pipeline built on Trivy scans every non-fork, non-archived repository in the `cozystack` organization — Go modules, Dockerfile base images, and container images referenced by the packaged charts — against NVD, vendor advisories and the GitHub Advisory Database. CRITICAL findings are scanned every 6 hours, the remaining severities weekly; findings are noise-filtered and tracked as issues under a triage matrix with per-severity targets (CRITICAL 1 business day to triage, 7-day fix target). Two limitations are worth stating: image references assembled at render time by the `cozy-lib.image` helper are invisible to values-file discovery, and per-CVE findings and triage state are currently held in a private repository, so the control is not yet externally verifiable. Aggregate reporting is now published at [`docs/security/reports/`](reports/): monthly reports reduced to counts by severity, plus a backlog reconciliation auditing every tracked finding against what shipped. Publishing the pipeline policy, the triage matrix, the finding status model and the scanning scripts, while keeping per-finding detail embargoed until fixed, is outstanding. The documented triage target is not being met and the role that owns it is unassigned; remediation is happening but the pipeline cannot record it, and the reconciliation sets out both. | ||
| - **Dependency updates.** Renovate manages Go modules, Dockerfiles, and GitHub Actions, plus a small set of regex-pinned references, with OpenSSF OSV vulnerability alerts enabled and automerge disabled. The `helm-values` manager is disabled repo-wide, so the curated component images that make up the shipped platform are bumped by maintainers when preparing a release rather than automatically. There is no Dependabot configuration; Renovate is the dependency-update tool. | ||
| - **Supply-chain posture.** OpenSSF Scorecard runs weekly with published results. Most GitHub Actions are pinned by commit SHA; a small number of recently added workflows still use floating tags and are being brought into line. Renovate is configured to pin digests; applying digest pins across container base images is in progress. | ||
| - **Signed / reproducible builds.** Honestly noted as gaps: release images are **not** signed (no cosign/sigstore), build provenance is currently disabled (`--provenance=false`), SBOM generation is plumbed but off by default, and reproducible builds are not yet established. These are recognized improvement areas for the incubation timeframe. |
There was a problem hiding this comment.
reproducible builds is likely hard. Doing in-toto attestations is an easy first step.
…, T1 non-goal, and in-toto step Incorporates @JustinCappos's review on cncf#2306. Signed-off-by: tym83 <6355522@gmail.com>
|
Thanks @JustinCappos — all addressed (source doc updated in cozystack/cozystack#4352, this snapshot re-synced to match):
|
JustinCappos
left a comment
There was a problem hiding this comment.
Okay, thanks!
Based upon what I've seen here, you're well positioned to do a joint assessment. It probably shouldn't be a huge lift.
Point the docs/security/reports link at the absolute cozystack/cozystack URL so it resolves from the cncf/toc snapshot (was a repo-relative path). Per @sherine-k review on the incubation self-assessment. Signed-off-by: tym83 <6355522@gmail.com>
|
Hi @sherine-k — thanks, both addressed. Broken link: fixed — the OIDC / tenant clusters: your reading is correct, with one v1.6 addition. Cozystack has two independent auth layers:
Docs: https://cozystack.io/docs/v1.6/operations/oidc/ and https://cozystack.io/docs/v1.6/kubernetes/oidc-authentication/ Happy to fold a one-line clarification into the assessments Authenticate row if that would help readers. |
This adds a point-in-time snapshot of Cozystack's security self-assessment to
projects/cozystack/security-assessment/self-assessment.md, for the Incubation due-diligence record tracked in #1916 and requested in cozystack/community#81.The self-assessment is maintained at
docs/security/self-assessment.mdin cozystack/cozystack; this is a point-in-time copy for the TOC record.