Skip to content

🌱 spokes still use per-host certs - wildcard exists but nothing serves it (oke + lke) #5977

Description

@clubanderson

Summary

A fleet wildcard certificate exists and is valid, but no spoke actually uses it. Every spoke ingress still carries its own tls: block pointing at a per-namespace hive-tls-hc secret, and an explicit tls: block always wins over the controller's --default-ssl-certificate. So the wildcard is currently inert, and the fleet is still minting one certificate per host against a 50-per-week-per-registered-domain ACME cap.

This is the durable half of the mitigation applied during the 2026-09-03 rate-limit incident.

Current state (measured 2026-09-04)

The wildcard: hive-wildcard-tls in ns hive-hub on hive-oke, issuer letsencrypt-dns01 (DNS-01 via Cloudflare). SANs:

*.hive.hivecommons.dev
*.lke.hive.hivecommons.dev
hive.hivecommons.dev
hive-oke lke648397
ingress refs to hive-tls-hc 59 136
Certificate objects 23 37
hosts under hive.hivecommons.dev 107 156
hosts NOT wildcard-covered dibs.kubestellar.io, hive.kubestellar.io none
--default-ssl-certificate on nginx set (hive-hub/hive-wildcard-tls) not set
wildcard secret present yes no

So ~195 ingress TLS references and ~60 Certificate objects exist to do what one wildcard already covers.

Why this matters

Per-host certs make the ACME cap structurally reachable: ~263 wildcard-covered hostnames across the two clusters against a 50/week limit. Every hostname change costs a certificate. A live example on hive-hosted-hosted-projectbluefin-knuckle-gjvq — the stored secret still carried the pre-migration SAN

DNS:hosted-projectbluefin-common-nmq5.hive.kubestellar.io
DNS:hosted-projectbluefin-knuckle-gjvq.hive.hivecommons.dev

while the Certificate requested the .hive.hivecommons.dev form of both, so cert-manager reported RequestChanged: Fields on existing CertificateRequest resource not up to date: [spec.dnsNames] and tried to re-issue. That churn is what exhausted the quota.

Confirmed during this investigation: deleting a spoke's hive-tls-hc secret causes cert-manager to mint a brand new per-host certificate rather than falling through to the wildcard — direct evidence the wildcard is not in the serving path.

Work required

1. lke648397 prerequisites (cluster-wide, additive, low risk)

  • Copy hive-wildcard-tls from hive-hub on hive-oke into the lke cluster (or issue a second wildcard there with the same DNS-01 issuer - note this costs one ACME issuance).
  • Add --default-ssl-certificate=<ns>/hive-wildcard-tls to the lke ingress-nginx-controller args.
  • These two alone change nothing observable, since explicit tls: blocks still win. They are just the prerequisite.

2. Provisioner/template change (the actual fix)

  • Stop emitting a tls: block on provisioned spoke ingresses (hive, hive-api, hive-contribute, hive-snapshot, hive-terminal) when the cluster domain is wildcard-covered.
  • Stop creating the per-hive hive-tls-hc Certificate on those clusters.
  • Keep per-host certs only for hosts outside the wildcard: currently dibs.kubestellar.io and hive.kubestellar.io on hive-oke. Note dibs is separately slated to move to dibs.hivecommons.dev (🌱 dibs should be canonically dibs.hivecommons.dev, with dibs.kubestellar.io as a redirect #5925), which is one level up and so is NOT covered by *.hive.hivecommons.dev - it needs its own SAN on the wildcard.

3. Cleanup after the template change lands

  • Remove the now-unused hive-tls-hc Certificates and secrets.
  • Verify each spoke still serves a valid cert by SNI before deleting anything.

Why this cannot be done by hand

The provisioner reconciles spoke ingresses back within minutes. During the incident, manual issuerRef edits on 15 Certificates were reverted the same way. A kubectl sweep across ~195 objects would not hold - this has to be a template/provisioner change.

Renewal consideration

Once spokes rely on the wildcard, its renewal becomes a single point of failure for every dashboard URL on the domain. Worth adding an alert on hive-wildcard-tls approaching expiry, and keeping the DNS-01 path monitored, since a failed wildcard renewal would take down all ~263 hostnames at once rather than one.

Related: the re-mint loop that consumed the quota is tracked separately; dibs canonical-host move is #5925.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions