diff --git a/docs/k8s.md b/docs/k8s.md index 10884f3ec..536123cb3 100644 --- a/docs/k8s.md +++ b/docs/k8s.md @@ -55,6 +55,7 @@ CoreDNS. For other domain names such as `www.google.com`, node-local DNS cache servers can be configured to send queries to upstream DNS servers defined in [cluster.yml](./cluster.md). +CKE validates the integrity of the replies using DNSSEC validation. ## Certificates for admission webhooks diff --git a/images.go b/images.go index 2ae6e4fa4..49d7e5ea2 100644 --- a/images.go +++ b/images.go @@ -15,7 +15,7 @@ const ( ToolsImage = Image("quay.io/cybozu/cke-tools:1.26.0") PauseImage = Image("quay.io/cybozu/pause:3.9.0.1") CoreDNSImage = Image("quay.io/cybozu/coredns:1.10.1.1") - UnboundImage = Image("quay.io/cybozu/unbound:1.17.1.4") + UnboundImage = Image("ghcr.io/cybozu/unbound:1.18.0.2") UnboundExporterImage = Image("quay.io/cybozu/unbound_exporter:0.4.1.5") ) diff --git a/mtest/kubernetes_test.go b/mtest/kubernetes_test.go index 0d88071f0..7df7af153 100644 --- a/mtest/kubernetes_test.go +++ b/mtest/kubernetes_test.go @@ -149,6 +149,20 @@ func testKubernetes() { } return nil }).Should(Succeed()) + + Eventually(func() error { + stdout, stderr, err := kubectl("get", "service", "-n="+namespace, "httpd", "-o", "jsonpath='{.spec.clusterIP}'") + if err != nil { + return fmt.Errorf("%v: stderr=%s", err, stderr) + } + ip := string(stdout) + + _, stderr, err = kubectl("exec", "-n="+namespace, "client", "getent", "hosts", ip) + if err != nil { + return fmt.Errorf("%v: stderr=%s", err, stderr) + } + return nil + }).Should(Succeed()) }) It("updates unbound config", func() { @@ -236,7 +250,7 @@ func testKubernetes() { return nil }).Should(Succeed()) - By("querying www.google.com using node DNS from ubuntu pod") + By("querying www.cybozu.com using node DNS from ubuntu pod") _, stderr, err = kubectl("run", "-n="+namespace, "--image=quay.io/cybozu/ubuntu:22.04", "--restart=Never", "client", "--", "pause") Expect(err).NotTo(HaveOccurred(), "stderr: %s", stderr) @@ -245,6 +259,12 @@ func testKubernetes() { return err }).Should(Succeed()) + By("querying www.dnssec-failed.org using node DNS from ubuntu pod") + Consistently(func() error { + _, _, err := kubectl("exec", "-n="+namespace, "client", "getent", "hosts", "www.dnssec-failed.org") + return err + }).WithTimeout(time.Second * 5).WithPolling(time.Second * 1).ShouldNot(Succeed()) + By("getting metrics from unbound_exporter") Eventually(func() error { stdout, _, err := kubectl("exec", "-n=kube-system", "daemonset/node-dns", "-c", "unbound", "--", "curl", "-sSf", "http://127.0.0.1:9167/metrics") diff --git a/op/nodedns/nodedns.go b/op/nodedns/nodedns.go index a390891b5..46b049b48 100644 --- a/op/nodedns/nodedns.go +++ b/op/nodedns/nodedns.go @@ -69,6 +69,26 @@ server: local-zone: "29.172.in-addr.arpa." transparent local-zone: "30.172.in-addr.arpa." transparent local-zone: "31.172.in-addr.arpa." transparent + trust-anchor-file: "/usr/local/unbound/etc/unbound/root.key" + domain-insecure: "{{ .Domain }}" + domain-insecure: "10.in-addr.arpa." + domain-insecure: "168.192.in-addr.arpa." + domain-insecure: "16.172.in-addr.arpa." + domain-insecure: "17.172.in-addr.arpa." + domain-insecure: "18.172.in-addr.arpa." + domain-insecure: "19.172.in-addr.arpa." + domain-insecure: "20.172.in-addr.arpa." + domain-insecure: "21.172.in-addr.arpa." + domain-insecure: "22.172.in-addr.arpa." + domain-insecure: "23.172.in-addr.arpa." + domain-insecure: "24.172.in-addr.arpa." + domain-insecure: "25.172.in-addr.arpa." + domain-insecure: "26.172.in-addr.arpa." + domain-insecure: "27.172.in-addr.arpa." + domain-insecure: "28.172.in-addr.arpa." + domain-insecure: "29.172.in-addr.arpa." + domain-insecure: "30.172.in-addr.arpa." + domain-insecure: "31.172.in-addr.arpa." remote-control: control-enable: yes control-interface: {{ if .LocalControl }} /var/run/unbound/unbound.sock {{ else }} 127.0.0.1 {{ end }} diff --git a/static/resources.go b/static/resources.go index 56c331fcb..e2955b457 100644 --- a/static/resources.go +++ b/static/resources.go @@ -60,8 +60,8 @@ var Resources = []cke.ResourceDefinition{ Namespace: "kube-system", Name: "node-dns", Revision: 4, - Image: "quay.io/cybozu/unbound:1.17.1.4,quay.io/cybozu/unbound_exporter:0.4.1.5", - Definition: []byte("kind: DaemonSet\napiVersion: apps/v1\nmetadata:\n name: node-dns\n namespace: kube-system\n annotations:\n cke.cybozu.com/image: \"quay.io/cybozu/unbound:1.17.1.4,quay.io/cybozu/unbound_exporter:0.4.1.5\"\n cke.cybozu.com/revision: \"4\"\nspec:\n selector:\n matchLabels:\n cke.cybozu.com/appname: node-dns\n updateStrategy:\n type: RollingUpdate\n rollingUpdate:\n maxSurge: 35%\n maxUnavailable: 0\n template:\n metadata:\n labels:\n cke.cybozu.com/appname: node-dns\n spec:\n priorityClassName: system-node-critical\n nodeSelector:\n kubernetes.io/os: linux\n hostNetwork: true\n tolerations:\n - operator: Exists\n terminationGracePeriodSeconds: 1\n containers:\n - name: unbound\n image: quay.io/cybozu/unbound:1.17.1.4\n args:\n - -c\n - /etc/unbound/unbound.conf\n securityContext:\n allowPrivilegeEscalation: false\n capabilities:\n add:\n - NET_BIND_SERVICE\n drop:\n - all\n readOnlyRootFilesystem: true\n readinessProbe:\n tcpSocket:\n port: 53\n host: localhost\n periodSeconds: 1\n livenessProbe:\n tcpSocket:\n port: 53\n host: localhost\n periodSeconds: 1\n initialDelaySeconds: 1\n failureThreshold: 6\n volumeMounts:\n - name: config-volume\n mountPath: /etc/unbound\n - name: var-run-unbound\n mountPath: /var/run/unbound\n - name: reload\n image: quay.io/cybozu/unbound:1.17.1.4\n command:\n - /usr/local/bin/reload-unbound\n securityContext:\n allowPrivilegeEscalation: false\n capabilities:\n drop:\n - all\n readOnlyRootFilesystem: true\n volumeMounts:\n - name: config-volume\n mountPath: /etc/unbound\n - name: var-run-unbound\n mountPath: /var/run/unbound\n - name: exporter\n image: quay.io/cybozu/unbound_exporter:0.4.1.5\n args:\n # must be same with the path written in /op/nodedns/nodedns.go\n - --unbound.host=unix:///var/run/unbound/unbound.sock\n - --web.reuse-port=true\n securityContext:\n allowPrivilegeEscalation: false\n capabilities:\n drop:\n - all\n readOnlyRootFilesystem: true\n volumeMounts:\n - name: var-run-unbound\n mountPath: /var/run/unbound\n volumes:\n - name: config-volume\n configMap:\n name: node-dns\n items:\n - key: unbound.conf\n path: unbound.conf\n - name: var-run-unbound\n emptyDir: {}\n"), + Image: "ghcr.io/cybozu/unbound:1.18.0.2,quay.io/cybozu/unbound_exporter:0.4.1.5", + Definition: []byte("kind: DaemonSet\napiVersion: apps/v1\nmetadata:\n name: node-dns\n namespace: kube-system\n annotations:\n cke.cybozu.com/image: \"ghcr.io/cybozu/unbound:1.18.0.2,quay.io/cybozu/unbound_exporter:0.4.1.5\"\n cke.cybozu.com/revision: \"4\"\nspec:\n selector:\n matchLabels:\n cke.cybozu.com/appname: node-dns\n updateStrategy:\n type: RollingUpdate\n rollingUpdate:\n maxSurge: 35%\n maxUnavailable: 0\n template:\n metadata:\n labels:\n cke.cybozu.com/appname: node-dns\n spec:\n priorityClassName: system-node-critical\n nodeSelector:\n kubernetes.io/os: linux\n hostNetwork: true\n tolerations:\n - operator: Exists\n terminationGracePeriodSeconds: 1\n containers:\n - name: unbound\n image: ghcr.io/cybozu/unbound:1.18.0.2\n args:\n - -c\n - /etc/unbound/unbound.conf\n securityContext:\n allowPrivilegeEscalation: false\n capabilities:\n add:\n - NET_BIND_SERVICE\n drop:\n - all\n readOnlyRootFilesystem: true\n readinessProbe:\n tcpSocket:\n port: 53\n host: localhost\n periodSeconds: 1\n livenessProbe:\n tcpSocket:\n port: 53\n host: localhost\n periodSeconds: 1\n initialDelaySeconds: 1\n failureThreshold: 6\n volumeMounts:\n - name: config-volume\n mountPath: /etc/unbound\n - name: var-run-unbound\n mountPath: /var/run/unbound\n - name: reload\n image: ghcr.io/cybozu/unbound:1.18.0.2\n command:\n - /usr/local/bin/reload-unbound\n securityContext:\n allowPrivilegeEscalation: false\n capabilities:\n drop:\n - all\n readOnlyRootFilesystem: true\n volumeMounts:\n - name: config-volume\n mountPath: /etc/unbound\n - name: var-run-unbound\n mountPath: /var/run/unbound\n - name: exporter\n image: quay.io/cybozu/unbound_exporter:0.4.1.5\n args:\n # must be same with the path written in /op/nodedns/nodedns.go\n - --unbound.host=unix:///var/run/unbound/unbound.sock\n - --web.reuse-port=true\n securityContext:\n allowPrivilegeEscalation: false\n capabilities:\n drop:\n - all\n readOnlyRootFilesystem: true\n volumeMounts:\n - name: var-run-unbound\n mountPath: /var/run/unbound\n volumes:\n - name: config-volume\n configMap:\n name: node-dns\n items:\n - key: unbound.conf\n path: unbound.conf\n - name: var-run-unbound\n emptyDir: {}\n"), }, { Key: "Deployment/kube-system/cluster-dns",