diff --git a/apps/base/bind-external/cluster.yaml b/apps/base/bind-external/cluster.yaml new file mode 100644 index 0000000..18d822a --- /dev/null +++ b/apps/base/bind-external/cluster.yaml @@ -0,0 +1,52 @@ +--- +# Externally-reachable authoritative BIND for zones we delegate to ourselves. +# First tenant: acme.unkin.net, the DNS-01 challenge zone Let's Encrypt validates +# via a one-time _acme-challenge.unkin.net CNAME. Authoritative-only, recursion +# off, no forwarding, no open transfers -- the primaryService is the single +# dmz-pinned LoadBalancer that public NAT targets and that cert-manager writes to. +apiVersion: bind.unkin.net/v1alpha1 +kind: BindCluster +metadata: + name: bind-external + namespace: bind-external +spec: + mode: authoritative + recursion: false + replicas: 2 + storageClassName: cephrbd-fast-delete + storageSize: 1Gi + # Public server: answer queries from anywhere (Let's Encrypt validates over the + # internet), deny recursion and open zone transfers. localhost + pod net are + # implied by "any" and cover in-pod nsupdate and secondary SOA refresh; per-zone + # allow-transfer (catalog + acme zone) still permits key-authenticated AXFR. + extraOptions: + - "allow-query { any; }" + - "allow-transfer { none; }" + service: + type: ClusterIP + primaryService: + type: LoadBalancer + externalTrafficPolicy: Local + annotations: + purelb.io/service-group: dmz + purelb.io/addresses: 198.18.199.53 + external-dns.alpha.kubernetes.io/hostname: bind-external-primary.k8s.syd1.au.unkin.net + resources: + requests: + cpu: 20m + memory: 128Mi + limits: + cpu: "1" + memory: 512Mi +--- +# Catalog zone so the acme zone replicates onto the secondary (AXFR/IXFR keyed +# with the certmanager TSIG key, reused here as the transfer key). +apiVersion: bind.unkin.net/v1alpha1 +kind: BindCatalogZone +metadata: + name: bind-external-catalog + namespace: bind-external +spec: + clusterRef: bind-external + zoneName: catalog.external + transferKeyRef: certmanager diff --git a/apps/base/bind-external/kustomization.yaml b/apps/base/bind-external/kustomization.yaml new file mode 100644 index 0000000..de5a685 --- /dev/null +++ b/apps/base/bind-external/kustomization.yaml @@ -0,0 +1,9 @@ +--- +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization + +resources: + - namespace.yaml + - cluster.yaml + - tsigkey.yaml + - zones.yaml diff --git a/apps/base/bind-external/namespace.yaml b/apps/base/bind-external/namespace.yaml new file mode 100644 index 0000000..51863e3 --- /dev/null +++ b/apps/base/bind-external/namespace.yaml @@ -0,0 +1,5 @@ +--- +apiVersion: v1 +kind: Namespace +metadata: + name: bind-external diff --git a/apps/base/bind-external/tsigkey.yaml b/apps/base/bind-external/tsigkey.yaml new file mode 100644 index 0000000..6517ad5 --- /dev/null +++ b/apps/base/bind-external/tsigkey.yaml @@ -0,0 +1,20 @@ +--- +# TSIG key cert-manager uses to send RFC2136 dynamic updates (the DNS-01 TXT +# records) to the primary, and that the secondary reuses for AXFR. The operator +# generates the material into Secret certmanager-tsig in this namespace; +# secretTemplate stamps emberstack reflector hints so the Secret is mirrored into +# the cert-manager namespace, where the rfc2136 solver reads its "secret" key. +apiVersion: bind.unkin.net/v1alpha1 +kind: BindTSIGKey +metadata: + name: certmanager + namespace: bind-external +spec: + clusterRef: bind-external + algorithm: hmac-sha256 + secretTemplate: + annotations: + reflector.v1.k8s.emberstack.com/reflection-allowed: "true" + reflector.v1.k8s.emberstack.com/reflection-allowed-namespaces: "cert-manager" + reflector.v1.k8s.emberstack.com/reflection-auto-enabled: "true" + reflector.v1.k8s.emberstack.com/reflection-auto-namespaces: "cert-manager" diff --git a/apps/base/bind-external/zones.yaml b/apps/base/bind-external/zones.yaml new file mode 100644 index 0000000..05ece86 --- /dev/null +++ b/apps/base/bind-external/zones.yaml @@ -0,0 +1,19 @@ +--- +# Self-delegated ACME challenge zone. Google Cloud DNS holds a one-time +# _acme-challenge.unkin.net CNAME -> _acme-challenge.acme.unkin.net and an +# acme.unkin.net NS delegation pointing here; cert-manager writes the challenge +# TXT records via RFC2136 authenticated with the certmanager key. +apiVersion: bind.unkin.net/v1alpha1 +kind: BindZone +metadata: + name: acme-unkin-net + namespace: bind-external +spec: + clusterRef: bind-external + zoneName: acme.unkin.net + type: primary + defaultTTL: 60 + dynamicUpdate: true + updateKeyRef: certmanager + allowTransfer: + - key certmanager diff --git a/apps/overlays/au-syd1/bind-external/kustomization.yaml b/apps/overlays/au-syd1/bind-external/kustomization.yaml new file mode 100644 index 0000000..007dc53 --- /dev/null +++ b/apps/overlays/au-syd1/bind-external/kustomization.yaml @@ -0,0 +1,6 @@ +--- +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization + +resources: + - ../../../base/bind-external diff --git a/argocd/applicationsets/platform.yaml b/argocd/applicationsets/platform.yaml index 9b5a9db..2f7f87a 100644 --- a/argocd/applicationsets/platform.yaml +++ b/argocd/applicationsets/platform.yaml @@ -14,6 +14,7 @@ spec: - path: apps/overlays/*/artifactapi - path: apps/overlays/*/bind-system - path: apps/overlays/*/bind-internal + - path: apps/overlays/*/bind-external - path: apps/overlays/*/age-api - path: apps/overlays/*/cattle-system - path: apps/overlays/*/cert-manager diff --git a/argocd/projects/platform.yaml b/argocd/projects/platform.yaml index 1eabece..29ad278 100644 --- a/argocd/projects/platform.yaml +++ b/argocd/projects/platform.yaml @@ -25,6 +25,8 @@ spec: server: https://kubernetes.default.svc - namespace: 'bind-internal' server: https://kubernetes.default.svc + - namespace: 'bind-external' + server: https://kubernetes.default.svc - namespace: 'cert-manager' server: https://kubernetes.default.svc - namespace: 'certificates'