From 0c66c2c378d8dd7aa451846a3d2250f14ff1cfe2 Mon Sep 17 00:00:00 2001 From: Adam Weingarten <6517820+aweingarten@users.noreply.github.com> Date: Tue, 28 Jul 2026 10:52:49 -0400 Subject: [PATCH] feat(netpols): ship the cert-manager-webhook-linode allow-rule Under default-deny the apiserver's discovery probe to the linode DNS-01 solver webhook (:443) is dropped, so the aggregated APIService `v1alpha1.acme.slicen.me` stays Available=False with FailedDiscoveryCheck. Convergence then hangs waiting on a certificate that can never be issued, and nothing in the symptom points at a NetworkPolicy. The webhook can't be covered by the policy that already exists: it is deployed by the cert-manager-webhook-linode chart, which labels its pods `app: cert-manager-webhook-linode` with no `app.kubernetes.io/name`, while the cert-manager chart's own webhook policy selects `app.kubernetes.io/name: webhook`. That selector is right for cert-manager's own webhook, so this gets its own rule rather than a widened one. Adds `netpols.certManagerWebhookLinode`, driven by `apps.cert-manager.networkPolicies.enabled` (default false) and only rendered when linode is the configured DNS provider, since that is the only case in which the webhook is installed at all. Refs #3387 --- .../cert-manager-webhook-linode.yaml | 30 +++++++++++++++++++ charts/apl-network-policies/values.yaml | 9 ++++++ helmfile.d/snippets/defaults.yaml | 2 ++ values-schema.yaml | 2 ++ .../apl-network-policies.gotmpl | 4 +++ 5 files changed, 47 insertions(+) create mode 100644 charts/apl-network-policies/templates/networkpolicies/cert-manager-webhook-linode.yaml diff --git a/charts/apl-network-policies/templates/networkpolicies/cert-manager-webhook-linode.yaml b/charts/apl-network-policies/templates/networkpolicies/cert-manager-webhook-linode.yaml new file mode 100644 index 0000000000..02c6d53934 --- /dev/null +++ b/charts/apl-network-policies/templates/networkpolicies/cert-manager-webhook-linode.yaml @@ -0,0 +1,30 @@ +{{- if .Values.netpols.certManagerWebhookLinode }} +--- +apiVersion: networking.k8s.io/v1 +kind: NetworkPolicy +metadata: + name: cert-manager-webhook-linode-policy + namespace: cert-manager + labels: + {{- include "apl-network-policies.labels" . | nindent 4 }} + app: cert-manager-webhook-linode +spec: + # This webhook is deployed by the cert-manager-webhook-linode chart, which labels its pods + # `app: cert-manager-webhook-linode` with no `app.kubernetes.io/name`. The cert-manager chart's + # own webhook policy selects `app.kubernetes.io/name: webhook`, so it does not — and should not — + # cover this one. + podSelector: + matchLabels: + app: cert-manager-webhook-linode + policyTypes: + - Ingress + ingress: + # Allow the apiserver's discovery probe to reach the aggregated APIService + # (v1alpha1.acme.slicen.me). The apiserver is off the pod network on a managed control plane and + # has no address a namespaceSelector or ipBlock can pin down, so this rule is scoped by port + # rather than by source. Without it the APIService stays Available=False with + # FailedDiscoveryCheck and convergence hangs waiting on a certificate that can never be issued. + - ports: + - protocol: TCP + port: 443 +{{- end }} diff --git a/charts/apl-network-policies/values.yaml b/charts/apl-network-policies/values.yaml index 7c0f0a7adb..f435966286 100644 --- a/charts/apl-network-policies/values.yaml +++ b/charts/apl-network-policies/values.yaml @@ -32,5 +32,14 @@ # * Monitoring namespace (for metrics scraping) # * Internal otomi namespace communication # +# For cert-manager-webhook-linode (netpols.certManagerWebhookLinode: true): +# - Creates a NetworkPolicy in the cert-manager namespace, scoped to the linode DNS-01 solver +# webhook pods (labelled `app: cert-manager-webhook-linode`, which the cert-manager chart's own +# webhook policy does not select) +# - Allows ingress from: +# * anywhere on 443 — the apiserver's discovery probe for the aggregated APIService, which has +# no address a selector or ipBlock can pin down on a managed control plane +# - Only rendered when linode is the configured DNS provider +# # The complex network policy rules are baked into the chart templates, # so users only need to toggle network policies on/off per application. diff --git a/helmfile.d/snippets/defaults.yaml b/helmfile.d/snippets/defaults.yaml index 7d9d569960..4f3ac4efe7 100644 --- a/helmfile.d/snippets/defaults.yaml +++ b/helmfile.d/snippets/defaults.yaml @@ -89,6 +89,8 @@ environments: _rawValues: {} cert-manager: issuer: custom-ca + networkPolicies: + enabled: false resources: certManager: requests: diff --git a/values-schema.yaml b/values-schema.yaml index ce355a21ee..3e437e1297 100644 --- a/values-schema.yaml +++ b/values-schema.yaml @@ -1552,6 +1552,8 @@ properties: properties: _rawValues: $ref: '#/definitions/rawValues' + networkPolicies: + $ref: '#/definitions/appNetworkPolicyConfig' resources: certManager: $ref: '#/definitions/resources' diff --git a/values/apl-network-policies/apl-network-policies.gotmpl b/values/apl-network-policies/apl-network-policies.gotmpl index cb3798134e..8883d16a79 100644 --- a/values/apl-network-policies/apl-network-policies.gotmpl +++ b/values/apl-network-policies/apl-network-policies.gotmpl @@ -1,12 +1,16 @@ {{- $v := .Values }} {{- $a := $v.apps }} {{- $oa := $v.apps | get "otomi-api" }} +{{- $cm := $v.apps | get "cert-manager" }} +{{- $dnsProvider := $v | get "dns.provider" dict }} # Simple per-app network policy configuration netpols: gitServer: {{ not ($a | get "git-server.enabled") }} gitea: {{ $a.gitea.networkPolicies.enabled}} otomiApi: {{ $oa.networkPolicies.enabled}} + # The linode DNS-01 solver webhook only exists when linode is the DNS provider. + certManagerWebhookLinode: {{ and ($cm.networkPolicies.enabled) (hasKey $dnsProvider "linode") }} ingressGatewaySelectors: - namespaceSelector: