diff --git a/docs/flags.md b/docs/flags.md index bbe78c71d..4469100ce 100644 --- a/docs/flags.md +++ b/docs/flags.md @@ -49,6 +49,7 @@ | `--[no-]traefik-disable-legacy` | Disable listeners on Resources under the traefik.containo.us API Group | | `--[no-]traefik-disable-new` | Disable listeners on Resources under the traefik.io API Group | | `--nat64-networks=NAT64-NETWORKS` | Adding an A record for each AAAA record in NAT64-enabled networks; specify multiple times for multiple possible nets (optional) | +| `--[no-]expose-internal-ipv6` | Expose internal IPv6 addresses for services with IPv6 addresses (optional). Default is true. | | `--provider=provider` | The DNS provider where the DNS records will be created (required, options: akamai, alibabacloud, aws, aws-sd, azure, azure-dns, azure-private-dns, civo, cloudflare, coredns, designate, digitalocean, dnsimple, exoscale, gandi, godaddy, google, ibmcloud, inmemory, linode, ns1, oci, ovh, pdns, pihole, plural, rfc2136, scaleway, skydns, tencentcloud, transip, ultradns, webhook) | | `--provider-cache-time=0s` | The time to cache the DNS provider record list requests. | | `--domain-filter=` | Limit possible target zones by a domain suffix; specify multiple times for multiple domains (optional) | diff --git a/main.go b/main.go index 47d1695dc..4baf46ee5 100644 --- a/main.go +++ b/main.go @@ -155,6 +155,7 @@ func main() { ResolveLoadBalancerHostname: cfg.ResolveServiceLoadBalancerHostname, TraefikDisableLegacy: cfg.TraefikDisableLegacy, TraefikDisableNew: cfg.TraefikDisableNew, + ExposeInternalIPv6: cfg.ExposeInternalIPV6, } // Lookup all the selected sources by names and pass them the desired configuration. diff --git a/pkg/apis/externaldns/types.go b/pkg/apis/externaldns/types.go index f0697d498..0699906b4 100644 --- a/pkg/apis/externaldns/types.go +++ b/pkg/apis/externaldns/types.go @@ -61,6 +61,7 @@ type Config struct { IgnoreIngressTLSSpec bool IgnoreIngressRulesSpec bool ListenEndpointEvents bool + ExposeInternalIPV6 bool GatewayName string GatewayNamespace string GatewayLabelFilter string @@ -240,6 +241,7 @@ var defaultConfig = &Config{ Compatibility: "", PublishInternal: false, PublishHostIP: false, + ExposeInternalIPV6: true, ConnectorSourceServer: "localhost:8080", Provider: "", ProviderCacheTime: 0, @@ -482,6 +484,7 @@ func App(cfg *Config) *kingpin.Application { app.Flag("traefik-disable-legacy", "Disable listeners on Resources under the traefik.containo.us API Group").Default(strconv.FormatBool(defaultConfig.TraefikDisableLegacy)).BoolVar(&cfg.TraefikDisableLegacy) app.Flag("traefik-disable-new", "Disable listeners on Resources under the traefik.io API Group").Default(strconv.FormatBool(defaultConfig.TraefikDisableNew)).BoolVar(&cfg.TraefikDisableNew) app.Flag("nat64-networks", "Adding an A record for each AAAA record in NAT64-enabled networks; specify multiple times for multiple possible nets (optional)").StringsVar(&cfg.NAT64Networks) + app.Flag("expose-internal-ipv6", "Expose internal IPv6 addresses for services with IPv6 addresses (optional). Default is true.").BoolVar(&cfg.ExposeInternalIPV6) // Flags related to providers providers := []string{"akamai", "alibabacloud", "aws", "aws-sd", "azure", "azure-dns", "azure-private-dns", "civo", "cloudflare", "coredns", "designate", "digitalocean", "dnsimple", "exoscale", "gandi", "godaddy", "google", "ibmcloud", "inmemory", "linode", "ns1", "oci", "ovh", "pdns", "pihole", "plural", "rfc2136", "scaleway", "skydns", "tencentcloud", "transip", "ultradns", "webhook"} diff --git a/source/node.go b/source/node.go index c35b3883e..d241653e9 100644 --- a/source/node.go +++ b/source/node.go @@ -34,15 +34,16 @@ import ( ) type nodeSource struct { - client kubernetes.Interface - annotationFilter string - fqdnTemplate *template.Template - nodeInformer coreinformers.NodeInformer - labelSelector labels.Selector + client kubernetes.Interface + annotationFilter string + fqdnTemplate *template.Template + nodeInformer coreinformers.NodeInformer + exposeInternalIPV6 bool + labelSelector labels.Selector } // NewNodeSource creates a new nodeSource with the given config. -func NewNodeSource(ctx context.Context, kubeClient kubernetes.Interface, annotationFilter, fqdnTemplate string, labelSelector labels.Selector) (Source, error) { +func NewNodeSource(ctx context.Context, kubeClient kubernetes.Interface, annotationFilter, fqdnTemplate string, labelSelector labels.Selector, exposeInternalIPv6 bool) (Source, error) { tmpl, err := parseTemplate(fqdnTemplate) if err != nil { return nil, err @@ -70,11 +71,12 @@ func NewNodeSource(ctx context.Context, kubeClient kubernetes.Interface, annotat } return &nodeSource{ - client: kubeClient, - annotationFilter: annotationFilter, - fqdnTemplate: tmpl, - nodeInformer: nodeInformer, - labelSelector: labelSelector, + client: kubeClient, + annotationFilter: annotationFilter, + fqdnTemplate: tmpl, + nodeInformer: nodeInformer, + labelSelector: labelSelector, + exposeInternalIPV6: exposeInternalIPv6, }, nil } @@ -177,13 +179,19 @@ func (ns *nodeSource) nodeAddresses(node *v1.Node) ([]string, error) { var ipv6Addresses []string for _, addr := range node.Status.Addresses { - addresses[addr.Type] = append(addresses[addr.Type], addr.Address) // IPv6 addresses are labeled as NodeInternalIP despite being usable externally as well. if addr.Type == v1.NodeInternalIP && suitableType(addr.Address) == endpoint.RecordTypeAAAA { - ipv6Addresses = append(ipv6Addresses, addr.Address) + if ns.exposeInternalIPV6 { + addresses[v1.NodeInternalIP] = append(addresses[v1.NodeInternalIP], addr.Address) + ipv6Addresses = append(ipv6Addresses, addr.Address) + } + } else { + addresses[addr.Type] = append(addresses[addr.Type], addr.Address) } } + fmt.Printf("%v\n", addresses) + if len(addresses[v1.NodeExternalIP]) > 0 { return append(addresses[v1.NodeExternalIP], ipv6Addresses...), nil } diff --git a/source/node_test.go b/source/node_test.go index bf047bae8..6adeb283f 100644 --- a/source/node_test.go +++ b/source/node_test.go @@ -18,6 +18,8 @@ package source import ( "context" + "fmt" + "k8s.io/utils/ptr" "testing" "github.com/stretchr/testify/assert" @@ -77,6 +79,7 @@ func testNodeSourceNewNodeSource(t *testing.T) { ti.annotationFilter, ti.fqdnTemplate, labels.Everything(), + true, ) if ti.expectError { @@ -93,17 +96,18 @@ func testNodeSourceEndpoints(t *testing.T) { t.Parallel() for _, tc := range []struct { - title string - annotationFilter string - labelSelector string - fqdnTemplate string - nodeName string - nodeAddresses []v1.NodeAddress - labels map[string]string - annotations map[string]string - unschedulable bool // default to false - expected []*endpoint.Endpoint - expectError bool + title string + annotationFilter string + labelSelector string + fqdnTemplate string + nodeName string + nodeAddresses []v1.NodeAddress + labels map[string]string + annotations map[string]string + exposeInternalIPv6 *bool // default to true for this version. Change later when the next minor version is released. + unschedulable bool // default to false + expected []*endpoint.Endpoint + expectError bool }{ { title: "node with short hostname returns one endpoint", @@ -200,6 +204,15 @@ func testNodeSourceEndpoints(t *testing.T) { {RecordType: "AAAA", DNSName: "node1", Targets: endpoint.Targets{"2001:DB8::8"}}, }, }, + { + title: "node with only internal IPs with expose internal IP as false shouldn't return AAAA endpoints with internal IPs", + nodeName: "node1", + exposeInternalIPv6: ptr.To(false), + nodeAddresses: []v1.NodeAddress{{Type: v1.NodeInternalIP, Address: "2.3.4.5"}, {Type: v1.NodeInternalIP, Address: "2001:DB8::9"}}, + expected: []*endpoint.Endpoint{ + {RecordType: "A", DNSName: "node1", Targets: endpoint.Targets{"2.3.4.5"}}, + }, + }, { title: "node with neither external nor internal IP returns no endpoints", nodeName: "node1", @@ -361,6 +374,13 @@ func testNodeSourceEndpoints(t *testing.T) { _, err := kubernetes.CoreV1().Nodes().Create(context.Background(), node, metav1.CreateOptions{}) require.NoError(t, err) + if tc.exposeInternalIPv6 == nil { + tc.exposeInternalIPv6 = new(bool) + *tc.exposeInternalIPv6 = true + } + + fmt.Printf("node: %v %v\n", tc.nodeName, *tc.exposeInternalIPv6) + // Create our object under test and get the endpoints. client, err := NewNodeSource( context.TODO(), @@ -368,6 +388,7 @@ func testNodeSourceEndpoints(t *testing.T) { tc.annotationFilter, tc.fqdnTemplate, labelSelector, + *tc.exposeInternalIPv6, ) require.NoError(t, err) diff --git a/source/store.go b/source/store.go index e0a8c0a15..d71beb3e8 100644 --- a/source/store.go +++ b/source/store.go @@ -80,6 +80,7 @@ type Config struct { ResolveLoadBalancerHostname bool TraefikDisableLegacy bool TraefikDisableNew bool + ExposeInternalIPv6 bool } // ClientGenerator provides clients @@ -216,7 +217,7 @@ func BuildWithConfig(ctx context.Context, source string, p ClientGenerator, cfg if err != nil { return nil, err } - return NewNodeSource(ctx, client, cfg.AnnotationFilter, cfg.FQDNTemplate, cfg.LabelFilter) + return NewNodeSource(ctx, client, cfg.AnnotationFilter, cfg.FQDNTemplate, cfg.LabelFilter, cfg.ExposeInternalIPv6) case "service": client, err := p.KubeClient() if err != nil {