diff --git a/internal/cluster/init.go b/internal/cluster/init.go index 6ace40d..69c2aca 100644 --- a/internal/cluster/init.go +++ b/internal/cluster/init.go @@ -135,6 +135,14 @@ func (c *Cluster) Init(ctx context.Context, opts InitOptions) error { return fmt.Errorf("failed to install CoreDNS: %w", err) } + // Enable fallthrough in CoreDNS so that non-Kubernetes cluster.local names + // (e.g. registry.cluster.local) are forwarded to the bink DNS container. + c.logger.Info("") + c.logger.Info("=== Patching CoreDNS for cluster.local fallthrough ===") + if err := kubeClient.EnableCoreDNSFallthrough(ctx); err != nil { + return fmt.Errorf("failed to enable CoreDNS fallthrough: %w", err) + } + // Patch CoreDNS to run as root - CRI-O doesn't set ambient capabilities // for non-root users, so NET_BIND_SERVICE doesn't take effect for UID 65532 c.logger.Info("") diff --git a/internal/kube/resources.go b/internal/kube/resources.go index 728411e..b10e2cb 100644 --- a/internal/kube/resources.go +++ b/internal/kube/resources.go @@ -7,6 +7,7 @@ import ( "context" "encoding/json" "fmt" + "strings" "time" policyv1 "k8s.io/api/policy/v1" @@ -127,6 +128,34 @@ func (c *Client) DeleteNode(ctx context.Context, nodeName string) error { return nil } +// EnableCoreDNSFallthrough patches the CoreDNS ConfigMap so that unresolved +// cluster.local queries fall through to the forward plugin (which reaches the +// bink dnsmasq container via the node's /etc/resolv.conf). +func (c *Client) EnableCoreDNSFallthrough(ctx context.Context) error { + cm, err := c.clientset.CoreV1().ConfigMaps("kube-system").Get(ctx, "coredns", metav1.GetOptions{}) + if err != nil { + return fmt.Errorf("getting coredns ConfigMap: %w", err) + } + + corefile, ok := cm.Data["Corefile"] + if !ok { + return fmt.Errorf("Corefile key not found in coredns ConfigMap") + } + + updated := strings.Replace(corefile, "fallthrough in-addr.arpa ip6.arpa", "fallthrough", 1) + if updated == corefile { + return nil + } + + cm.Data["Corefile"] = updated + _, err = c.clientset.CoreV1().ConfigMaps("kube-system").Update(ctx, cm, metav1.UpdateOptions{}) + if err != nil { + return fmt.Errorf("updating coredns ConfigMap: %w", err) + } + + return nil +} + // LabelNode adds the given labels to a node, overwriting any that already exist. func (c *Client) LabelNode(ctx context.Context, nodeName string, labels map[string]string) error { patch := map[string]interface{}{ diff --git a/test/integration/cluster_test.go b/test/integration/cluster_test.go index 0fd0055..11cf63e 100644 --- a/test/integration/cluster_test.go +++ b/test/integration/cluster_test.go @@ -143,6 +143,12 @@ var _ = Describe("Cluster Lifecycle", func() { Expect(err).ToNot(HaveOccurred()) Expect(pod.Status.Phase).To(Equal(corev1.PodRunning)) + By("Verifying pod can resolve registry.cluster.local via CoreDNS") + nslookupOutput, err := helpers.PodExec(kubeconfigPath, "default", "busybox-test", + []string{"nslookup", "registry.cluster.local"}) + Expect(err).ToNot(HaveOccurred(), "nslookup registry.cluster.local should succeed from pod") + Expect(nslookupOutput).To(ContainSubstring(config.RegistryStaticIP)) + By("Cleaning up the busybox pod") helpers.DeletePod(kubeClient, "default", "busybox-test")