← Policy catalog
</> ValidatingAdmissionPolicy · Apache-2.0

Has Hosts Not Covered by Its TLS Configuration

ingress-tls-hosts

severityModerate resourceIngresses productKubernetes bundles1

A routed host missing from every TLS block is served over cleartext HTTP or with a default certificate that does not match it, exposing session cookies and credentials to anyone on the network path.

Rejects unless

size(variables.hosts) == 0 || (has(object.spec.tls) && variables.hosts.all(h,
  object.spec.tls.exists(t, has(t.hosts) && t.hosts.exists(th,
    th == h ||
    (th.startsWith("*.") &&
     h.endsWith(th.substring(1)) &&
     !h.substring(0, h.size() - th.size() + 1).contains("."))
  ))
))

Every host in spec.rules[*].host must appear in some spec.tls[*].hosts, either exactly or via a wildcard entry such as "*.example.com" that covers one label.

Variables

hosts

has(object.spec.rules) ? object.spec.rules.filter(r, has(r.host)).map(r, r.host) : []

Remediation

Add each hostname from the rules to the hosts list of a spec.tls entry backed by a certificate valid for it. Fields: spec.rules[*].host, spec.tls[*].hosts.

Applies to

  • ingresses · networking.k8s.io/v1 · CREATE, UPDATE

Manifest

apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingAdmissionPolicy
metadata:
  name: ingress-tls-hosts
  annotations:
    kubeapt.io/uuid: "9e07fcea-9c2c-40dc-9807-5d10c98b1a40"
    security.kubeapt.io/displayName: "Has Hosts Not Covered by Its TLS Configuration"
    security.kubeapt.io/description: "A routed host missing from every TLS block is served over cleartext HTTP or with a default certificate that does not match it, exposing session cookies and credentials to anyone on the network path."
    security.kubeapt.io/resource: "Ingresses"
    security.kubeapt.io/severity: "Moderate"
    security.kubeapt.io/remediation: "Add each hostname from the rules to the hosts list of a spec.tls entry backed by a certificate valid for it. Fields: spec.rules[*].host, spec.tls[*].hosts."
    security.kubeapt.io/product: "Kubernetes"
spec:
  failurePolicy: Fail
  matchConstraints:
    resourceRules:
    - apiGroups:
      - networking.k8s.io
      apiVersions:
      - v1
      operations:
      - CREATE
      - UPDATE
      resources:
      - ingresses
  variables:
  - name: hosts
    expression: |
      has(object.spec.rules) ? object.spec.rules.filter(r, has(r.host)).map(r, r.host) : []
  validations:
  - expression: |
      size(variables.hosts) == 0 || (has(object.spec.tls) && variables.hosts.all(h,
        object.spec.tls.exists(t, has(t.hosts) && t.hosts.exists(th,
          th == h ||
          (th.startsWith("*.") &&
           h.endsWith(th.substring(1)) &&
           !h.substring(0, h.size() - th.size() + 1).contains("."))
        ))
      ))
    message: |
      Every host in spec.rules[*].host must appear in some spec.tls[*].hosts, either exactly or via a wildcard entry such as "*.example.com" that covers one label.

Save it as ingress-tls-hosts.yaml — the commands below assume that name.

Validate with kubeapt

Check a workload against this one policy, before anything is installed on a cluster.

$ kubeapt validate -p ./ingress-tls-hosts.yaml -r ./workload.yaml

Or without the file, from the published policy set — -r takes a directory too:

$ kubeapt policies download && kubeapt validate --policy-name ingress-tls-hosts -r ./manifests

Or against every namespace in the cluster your kubeconfig points at:

$ kubeapt validate --policy-name ingress-tls-hosts -A