Skip to content

feat(kubernetes): let CDI trust a self-hosted Talos image factory - #3797

Open
Fedor Batonogov (batonogov) wants to merge 1 commit into
cozystack:mainfrom
batonogov:feat/kubernetes-image-factory-ca
Open

Fedor Batonogov (batonogov) wants to merge 1 commit into
cozystack:mainfrom
batonogov:feat/kubernetes-image-factory-ca

Conversation

@batonogov

@batonogov Fedor Batonogov (batonogov) commented Aug 13, 2026 •

Copy link
Copy Markdown

What this PR does

Adds talos.imageFactoryCA to the kubernetes chart, so CDI can verify a Talos Image Factory served with a certificate from an internal CA. Fixes #3796.

The gap

talos.imageFactoryURL is documented for self-hosted factories and mirrors in "air-gapped, rate-limited, or flaky-egress environments". In such an environment the endpoint is normally served by the organisation's own CA, and CDI — trusting only its system store — refuses the import with x509: certificate signed by unknown authority. The DataVolume never completes, no worker joins, and the release sits in install until it times out.

DataVolume.spec.source.http.certConfigMap exists for exactly this and has since the beginning. The chart just never filled it in: templates/cluster.yaml rendered url alone, and no value reached the field. certConfigMap appears nowhere in this repository.

The change

One optional value on the existing talos group:

talos:
  imageFactoryURL: "https://factory.example.com"
  imageFactoryCA: |
    -----BEGIN CERTIFICATE-----
    ...
    -----END CERTIFICATE-----

Set, it renders a ConfigMap in the release namespace and points the DataVolume at it. Empty — the default — renders no ConfigMap, no certConfigMap key, and no volume: I diffed helm template against main with the field unset and the output is identical byte for byte.

Why the PEM rather than a ConfigMap name

certConfigMap must live in the DataVolume's namespace, so a name-only field would ask the user to create an object in the tenant namespace by hand — which the dashboard offers no path for, and which splits one decision across two places in a GitOps repository. Taking the PEM keeps it inside the application's values like every other setting. A name-reference variant is a small change from here if maintainers prefer it.

Why this cannot break an existing release

CDI appends the bundle to the system pool rather than replacing it — x509.SystemCertPool() followed by AppendCertsFromPEM over every file in the mounted directory (pkg/importer/http-datasource.go). A release that also pulls from a publicly-trusted endpoint keeps verifying. This is the same distinction #3385 drew between SSL_CERT_DIR and AWS_CA_BUNDLE.

The field is optional with an empty default, so values.schema.json gains a property and no required entry.

Fail-closed guards

The value is emitted verbatim into a ConfigMap that anything able to read the namespace can read, so the helper refuses private key material and anything that is not complete PEM certificate blocks — including the realistic accident of cat ca.pem key.pem pasted into the field, where a valid leading certificate must not carry a trailing key through.

Those rules restate the ones in cozy-lib.tls.caCertSecret. I kept them local so the failure names talos.imageFactoryCA rather than that helper's caCert parameter, and because the object here is a ConfigMap consumed by CDI in-namespace rather than a tenant-published trust anchor. If you would rather have one shared guard in cozy-lib, say so and I will extract it.

A non-string value is not guarded in the template — values.schema.json rejects it before rendering starts.

Testing

tests/talos_image_factory_ca_test.yaml, 11 cases: unset renders nothing on both sides; set renders the ConfigMap and a certConfigMap naming it; a whitespace-only value counts as unset; multi-certificate chains work; and each guard is pinned on the input that trips it. Full suite for the package: 216 passed.

make generate run in the package; regenerated values.schema.json, README.md, api/apps/v1alpha1/kubernetes/types.go and the kubernetes-rd CRD are committed.

Not yet exercised end-to-end against a live internal factory — I can report back once it has been.

Not in this PR

packages/apps/vm-disk/templates/dv.yaml renders source.http.url with the same gap. Left alone to keep this reviewable; happy to follow up if this shape is accepted.

Screenshots

Not a UI change.

Downstream repositories

Walked the trigger map against the diff:

  • terraform-provider-cozystack — ticked. The diff adds a field to packages/apps/kubernetes/values.schema.json, which the trigger map names directly ("Add, remove or rename a field in an app's values.schema.json → the schema, the model, and the expand/flatten pair"). Filed as an issue rather than a PR there, matching the existing Move kubeapps cache reloading logic out of installer.sh #19 / Rolling update for Talos nodes #20 / Fix gitignore #26 pattern for upstream field additions.
  • website — not ticked. No package is added, renamed or removed; the reference page for this app is regenerated from the package README.md by the release bot, and that README.md is regenerated and committed here.
  • Every other repository — no path in the trigger map is touched: no hack/ move, no ApplicationDefinition change, no namespace or variant rename, no installer or platform values key, no telemetry metric, no annotation or label contract.

Release note

feat(kubernetes): add `talos.imageFactoryCA` so CDI can verify a self-hosted Talos Image Factory served with a certificate from an internal CA. Previously `talos.imageFactoryURL` could point at such a factory but the image import failed with `x509: certificate signed by unknown authority`, leaving worker nodes stuck in Provisioning. Set the CA in PEM form and the chart wires it into the worker DataVolume; leave it empty and nothing changes.

Summary by CodeRabbit

  • New Features

    • Added optional talos.imageFactoryCA configuration for providing PEM CA certificates.
    • Worker image imports can now connect to HTTPS image factories using private or internal certificate authorities.
    • Added validation to reject invalid certificates, private keys, and incomplete PEM content.
    • The Kubernetes dashboard schema now exposes the new configuration field.
  • Tests

    • Added coverage for valid certificate bundles, omitted values, and invalid certificate inputs.

talos.imageFactoryURL is documented for self-hosted factories and mirrors
in air-gapped, rate-limited or flaky-egress environments. Such an endpoint
is normally served with a certificate from an internal CA, and CDI — which
trusts only its system store — refuses the import with "x509: certificate
signed by unknown authority". The DataVolume never completes, no worker
joins, and the release sits in install until it times out.

DataVolume.spec.source.http.certConfigMap exists for exactly this, but the
chart rendered source.http.url alone and no value reached the field.

Add an optional talos.imageFactoryCA taking the CA in PEM form. When set,
the chart renders it into a ConfigMap in the release namespace — CDI
requires certConfigMap to sit alongside the DataVolume — and references it
from the worker DataVolume. When empty, nothing is rendered and the output
is byte-for-byte what it was.

CDI appends the bundle to the system pool rather than replacing it, so a
release pulling from a publicly-trusted endpoint keeps verifying either way.

The value is emitted verbatim into a ConfigMap readable by anything that
can read the namespace, so the helper fails closed on private key material
and on anything that is not complete PEM certificate blocks.

Assisted-By: Claude <[email protected]>
Signed-off-by: Fedor Batonogov <[email protected]>
@coderabbitai

coderabbitai Bot commented Aug 13, 2026 •

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 77e8e769-4af3-4e2d-add6-0f79d45b067a

📥 Commits

Reviewing files that changed from the base of the PR and between 09dc7f4 and 5bb2dd5.

📒 Files selected for processing (9)
  • api/apps/v1alpha1/kubernetes/types.go
  • packages/apps/kubernetes/README.md
  • packages/apps/kubernetes/templates/_helpers.tpl
  • packages/apps/kubernetes/templates/cluster.yaml
  • packages/apps/kubernetes/templates/talos/image-factory-ca.yaml
  • packages/apps/kubernetes/tests/talos_image_factory_ca_test.yaml
  • packages/apps/kubernetes/values.schema.json
  • packages/apps/kubernetes/values.yaml
  • packages/system/kubernetes-rd/cozyrds/kubernetes.yaml

📝 Walkthrough

Walkthrough

The Kubernetes application adds an optional talos.imageFactoryCA value. Helm validates the PEM bundle, renders it into a namespaced ConfigMap, and references that ConfigMap from Talos image DataVolumes. API schemas, documentation, and Helm tests cover the new value.

Changes

Talos Image Factory CA

Layer / File(s) Summary
CA value and API contracts
api/apps/v1alpha1/kubernetes/types.go, packages/apps/kubernetes/values.yaml, packages/apps/kubernetes/values.schema.json, packages/system/kubernetes-rd/cozyrds/kubernetes.yaml, packages/apps/kubernetes/README.md
The optional talos.imageFactoryCA string is added to the API, chart values, schema, CRD, dashboard ordering, and documentation.
CA validation and CDI wiring
packages/apps/kubernetes/templates/_helpers.tpl, packages/apps/kubernetes/templates/talos/image-factory-ca.yaml, packages/apps/kubernetes/templates/cluster.yaml
The chart validates certificate-only PEM content, renders ca.pem in a stable release-scoped ConfigMap, and conditionally sets the DataVolume certConfigMap.
CA rendering validation
packages/apps/kubernetes/tests/talos_image_factory_ca_test.yaml
Helm tests cover empty values, valid single and multi-certificate bundles, private keys, malformed certificates, non-certificate content, and schema type validation.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Mergeability Score: ⚪ Minimal · up to 5bb2d

This PR adds optional support for internal CA certificates while preserving existing behavior when unset. No actionable merge-blocking risk remains after normal checks and review.

Sequence Diagram(s)

sequenceDiagram
  participant ChartValues
  participant HelmTemplates
  participant CAConfigMap
  participant TalosDataVolume
  participant CDI
  ChartValues->>HelmTemplates: Provide talos.imageFactoryCA
  HelmTemplates->>HelmTemplates: Validate and trim PEM certificates
  HelmTemplates->>CAConfigMap: Render ca.pem
  HelmTemplates->>TalosDataVolume: Set source.http.certConfigMap
  TalosDataVolume->>CDI: Supply image URL and CA ConfigMap
Loading

Suggested labels: kind/feature, size/L, area/kubernetes, area/storage, area/testing

Suggested reviewers: ivanhunters

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Linked Issues check ✅ Passed The changes implement issue #3796 by adding the CA value, rendering a ConfigMap, wiring certConfigMap, preserving defaults, and adding validation and tests.
Out of Scope Changes check ✅ Passed The changes remain within the Kubernetes chart scope and include only related API, schema, documentation, CRD, template, and test updates.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the main change: enabling CDI to trust certificates from self-hosted Talos image factories.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actions github-actions Bot added size/L This PR changes 100-499 lines, ignoring generated files area/kubernetes Issues or PRs related to the tenant Kubernetes app kind/feature Categorizes issue or PR as related to a new feature labels Aug 13, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/kubernetes Issues or PRs related to the tenant Kubernetes app kind/feature Categorizes issue or PR as related to a new feature size/L This PR changes 100-499 lines, ignoring generated files

Projects

None yet

Development

Successfully merging this pull request may close these issues.

kubernetes: no way to trust a self-hosted Talos Image Factory served by an internal CA

1 participant