diff --git a/k8s/argocd/apps/05-networking.yaml b/k8s/argocd/apps/05-networking.yaml index 3c90dec..861e66a 100644 --- a/k8s/argocd/apps/05-networking.yaml +++ b/k8s/argocd/apps/05-networking.yaml @@ -20,24 +20,7 @@ spec: automated: prune: true selfHeal: true ---- -apiVersion: argoproj.io/v1alpha1 -kind: Application -metadata: - name: coredns-config - namespace: argocd - annotations: - argocd.argoproj.io/sync-wave: "0" -spec: - project: homelab - source: - repoURL: git@github.com:Riotpiaole/riotpiao.homelab.com.git - targetRevision: main - path: k8s/bootstrap/coredns - destination: - server: https://kubernetes.default.svc - namespace: kube-system - syncPolicy: - automated: - prune: true - selfHeal: true +# coredns-config Application removed: CoreDNS (incl. homelab hostname rewrites) +# is owned by Talos via an inlineManifest (terraform/files/coredns/Corefile). +# Managing the coredns ConfigMap from ArgoCD too would let the two reconcilers +# fight and revert the rewrites. diff --git a/k8s/bootstrap/coredns/coredns-configmap.yaml b/k8s/bootstrap/coredns/coredns-configmap.yaml deleted file mode 100644 index 5cccd40..0000000 --- a/k8s/bootstrap/coredns/coredns-configmap.yaml +++ /dev/null @@ -1,78 +0,0 @@ -# k8s/coredns/coredns-configmap.yaml -# Patches the CoreDNS Corefile to rewrite homelab hostnames to internal services. -# -# Why this is needed: -# Grafana v10+ does OIDC auto-discovery by fetching -# /.well-known/openid-configuration from Authentik. When Grafana reaches -# Authentik via the external hostname (authentik.riotpiao.com), the -# HTTP Host header is preserved and Authentik returns external URLs in the -# discovery response. Without this rewrite, the hostname doesn't resolve -# inside the cluster and Grafana falls back to the internal service DNS, -# causing all OAuth redirects to go to authentik-server.iam.svc.cluster.local. -# -# Applied by helmfile presync hook on the ingress-nginx release. - -apiVersion: v1 -kind: ConfigMap -metadata: - name: coredns - namespace: kube-system -data: - Corefile: | - .:53 { - errors - health { - lameduck 5s - } - ready - log . { - class error - } - prometheus :9153 - - # VPN Access: Map api-server.cluster.local to cluster API IP - # Allows secure cluster access via WireGuard tunnel (Shadowrocket/Talos) - rewrite name api-server.cluster.local kubernetes.default.svc.cluster.local - - # Forgejo: route through nginx ingress like every other host below. nginx - # terminates TLS (wildcard-tls) on :443 and routes both /v2/ (container - # registry) and web/git to forgejo-gitea-http:3000. - # Do NOT point this at forgejo-gitea-http directly: that service only serves - # port 3000, so containerd image pulls (which use https/:443) get - # `dial tcp :443: i/o timeout`. SSH stays on its own LB service. - rewrite name forgejo.riotpiao.com ingress-nginx-controller.ingress-nginx.svc.cluster.local - rewrite name git.riotpiao.com forgejo-gitea-ssh.cicd.svc.cluster.local - - # Rewrite homelab hostnames to the nginx ingress controller so in-cluster pods - # hit nginx TLS termination (cert-manager cert) and preserve the Host header. - # Routing through nginx — not directly to the backend service — is critical: - # direct rewrites to the backend bypass nginx TLS and expose each app's own - # self-signed cert, which nothing in the cluster trusts. - rewrite name authentik.riotpiao.com ingress-nginx-controller.ingress-nginx.svc.cluster.local - rewrite name grafana.riotpiao.com ingress-nginx-controller.ingress-nginx.svc.cluster.local - rewrite name minio.riotpiao.com ingress-nginx-controller.ingress-nginx.svc.cluster.local - rewrite name minio-api.riotpiao.com ingress-nginx-controller.ingress-nginx.svc.cluster.local - rewrite name argocd.riotpiao.com ingress-nginx-controller.ingress-nginx.svc.cluster.local - rewrite name vault.riotpiao.com ingress-nginx-controller.ingress-nginx.svc.cluster.local - rewrite name loki.riotpiao.com ingress-nginx-controller.ingress-nginx.svc.cluster.local - rewrite name prometheus.riotpiao.com ingress-nginx-controller.ingress-nginx.svc.cluster.local - rewrite name homarr.riotpiao.com ingress-nginx-controller.ingress-nginx.svc.cluster.local - rewrite name portainer.riotpiao.com ingress-nginx-controller.ingress-nginx.svc.cluster.local - rewrite name longhorn.riotpiao.com ingress-nginx-controller.ingress-nginx.svc.cluster.local - - kubernetes cluster.local in-addr.arpa ip6.arpa { - pods insecure - fallthrough in-addr.arpa ip6.arpa - ttl 30 - } - forward . 8.8.8.8 1.1.1.1 { - max_concurrent 1000 - } - cache 30 { - disable success cluster.local - disable denial cluster.local - } - loop - reload - loadbalance - } diff --git a/k8s/bootstrap/coredns/coredns-deployment.yaml b/k8s/bootstrap/coredns/coredns-deployment.yaml deleted file mode 100644 index 004ca27..0000000 --- a/k8s/bootstrap/coredns/coredns-deployment.yaml +++ /dev/null @@ -1,130 +0,0 @@ -# CoreDNS is deployed by Talos's bootstrap manifests (not a helm release we -# own). This tracks the full Deployment spec as the single source of truth -# for any changes we apply on top of the Talos default — currently just -# topologySpreadConstraints so the 2 replicas don't land on the same node. -apiVersion: apps/v1 -kind: Deployment -metadata: - name: coredns - namespace: kube-system - labels: - k8s-app: kube-dns - kubernetes.io/name: CoreDNS -spec: - replicas: 2 - revisionHistoryLimit: 10 - selector: - matchLabels: - k8s-app: kube-dns - strategy: - rollingUpdate: - maxSurge: 25% - maxUnavailable: 1 - type: RollingUpdate - template: - metadata: - labels: - k8s-app: kube-dns - spec: - affinity: - podAntiAffinity: - preferredDuringSchedulingIgnoredDuringExecution: - - podAffinityTerm: - labelSelector: - matchExpressions: - - key: k8s-app - operator: In - values: - - kube-dns - topologyKey: kubernetes.io/hostname - weight: 100 - containers: - - args: - - -conf - - /etc/coredns/Corefile - env: - - name: GOMEMLIMIT - value: 161MiB - image: registry.k8s.io/coredns/coredns:v1.14.2 - imagePullPolicy: IfNotPresent - livenessProbe: - failureThreshold: 5 - httpGet: - path: /health - port: 8080 - scheme: HTTP - initialDelaySeconds: 60 - periodSeconds: 10 - successThreshold: 1 - timeoutSeconds: 5 - name: coredns - ports: - - containerPort: 53 - name: dns - protocol: UDP - - containerPort: 53 - name: dns-tcp - protocol: TCP - - containerPort: 9153 - name: metrics - protocol: TCP - readinessProbe: - failureThreshold: 3 - httpGet: - path: /ready - port: 8181 - scheme: HTTP - periodSeconds: 10 - successThreshold: 1 - timeoutSeconds: 1 - resources: - limits: - memory: 170Mi - requests: - cpu: 100m - memory: 70Mi - securityContext: - allowPrivilegeEscalation: false - capabilities: - add: - - NET_BIND_SERVICE - drop: - - ALL - readOnlyRootFilesystem: true - terminationMessagePath: /dev/termination-log - terminationMessagePolicy: File - volumeMounts: - - mountPath: /etc/coredns - name: config-volume - readOnly: true - dnsPolicy: Default - nodeSelector: - kubernetes.io/os: linux - priorityClassName: system-cluster-critical - restartPolicy: Always - schedulerName: default-scheduler - serviceAccount: coredns - serviceAccountName: coredns - terminationGracePeriodSeconds: 30 - tolerations: - - effect: NoSchedule - key: node-role.kubernetes.io/control-plane - operator: Exists - - effect: NoSchedule - key: node.cloudprovider.kubernetes.io/uninitialized - operator: Exists - topologySpreadConstraints: - - labelSelector: - matchLabels: - k8s-app: kube-dns - maxSkew: 1 - topologyKey: kubernetes.io/hostname - whenUnsatisfiable: ScheduleAnyway - volumes: - - configMap: - defaultMode: 420 - items: - - key: Corefile - path: Corefile - name: coredns - name: config-volume diff --git a/k8s/bootstrap/coredns/kustomization.yaml b/k8s/bootstrap/coredns/kustomization.yaml deleted file mode 100644 index be84455..0000000 --- a/k8s/bootstrap/coredns/kustomization.yaml +++ /dev/null @@ -1,5 +0,0 @@ -apiVersion: kustomize.config.k8s.io/v1beta1 -kind: Kustomization -namespace: kube-system -resources: [] -# CoreDNS deployed via Helm chart diff --git a/terraform/files/coredns/Corefile b/terraform/files/coredns/Corefile new file mode 100644 index 0000000..c1300e8 --- /dev/null +++ b/terraform/files/coredns/Corefile @@ -0,0 +1,52 @@ +.:53 { + errors + health { + lameduck 5s + } + ready + log . { + class error + } + prometheus :9153 + + # VPN access: map api-server.cluster.local to the cluster API service. + rewrite name api-server.cluster.local kubernetes.default.svc.cluster.local + + # Forgejo: route through nginx ingress (TLS on :443, /v2/ registry + web). + # Do NOT point at forgejo-gitea-http directly (port 3000 only) — image pulls + # use https/:443 and would time out. SSH stays on its own LB service. + rewrite name forgejo.riotpiao.com ingress-nginx-controller.ingress-nginx.svc.cluster.local + rewrite name git.riotpiao.com forgejo-gitea-ssh.cicd.svc.cluster.local + + # Homelab hostnames -> nginx ingress controller so in-cluster pods hit nginx + # TLS termination (cert-manager cert) and preserve the Host header. Routing + # via nginx (not the backend service) is critical: a direct rewrite bypasses + # nginx TLS and exposes each app's own self-signed cert, trusted by nothing. + rewrite name authentik.riotpiao.com ingress-nginx-controller.ingress-nginx.svc.cluster.local + rewrite name grafana.riotpiao.com ingress-nginx-controller.ingress-nginx.svc.cluster.local + rewrite name minio.riotpiao.com ingress-nginx-controller.ingress-nginx.svc.cluster.local + rewrite name minio-api.riotpiao.com ingress-nginx-controller.ingress-nginx.svc.cluster.local + rewrite name argocd.riotpiao.com ingress-nginx-controller.ingress-nginx.svc.cluster.local + rewrite name vault.riotpiao.com ingress-nginx-controller.ingress-nginx.svc.cluster.local + rewrite name loki.riotpiao.com ingress-nginx-controller.ingress-nginx.svc.cluster.local + rewrite name prometheus.riotpiao.com ingress-nginx-controller.ingress-nginx.svc.cluster.local + rewrite name homarr.riotpiao.com ingress-nginx-controller.ingress-nginx.svc.cluster.local + rewrite name portainer.riotpiao.com ingress-nginx-controller.ingress-nginx.svc.cluster.local + rewrite name longhorn.riotpiao.com ingress-nginx-controller.ingress-nginx.svc.cluster.local + + kubernetes cluster.local in-addr.arpa ip6.arpa { + pods insecure + fallthrough in-addr.arpa ip6.arpa + ttl 30 + } + forward . 8.8.8.8 1.1.1.1 { + max_concurrent 1000 + } + cache 30 { + disable success cluster.local + disable denial cluster.local + } + loop + reload + loadbalance +} diff --git a/terraform/main.tf b/terraform/main.tf index f4d5a8e..3513188 100644 --- a/terraform/main.tf +++ b/terraform/main.tf @@ -41,6 +41,7 @@ resource "local_file" "controlplane_configs" { forgejo_hostname = var.forgejo_hostname zone = each.value.zone allow_scheduling = each.value.allow_scheduling + coredns_corefile = file("${path.module}/files/coredns/Corefile") # Cloudflare Tunnel cert SANs (talos :50000 and kube-apiserver :6443) cloudflare_talos_sans = each.value.cloudflare_talos_sans diff --git a/terraform/templates/controlplane.tftpl b/terraform/templates/controlplane.tftpl index c31cd1e..ac65de7 100644 --- a/terraform/templates/controlplane.tftpl +++ b/terraform/templates/controlplane.tftpl @@ -164,3 +164,18 @@ cluster: kind: Namespace metadata: name: kube-system + # CoreDNS Corefile with homelab hostname rewrites (single source of truth in + # terraform/files/coredns/Corefile). In-cluster pods resolve *.riotpiao.com to + # the nginx ingress controller so OIDC auto-discovery against + # authentik.riotpiao.com works and the Host header is preserved. Owned by + # Talos — do NOT also manage the coredns ConfigMap via ArgoCD. + - name: coredns-config + contents: | + apiVersion: v1 + kind: ConfigMap + metadata: + name: coredns + namespace: kube-system + data: + Corefile: | + ${indent(12, coredns_corefile)}