Skip to content

Commit

Permalink
add CNAME support
Browse files Browse the repository at this point in the history
udpate version
  • Loading branch information
gcleroux committed Feb 19, 2025
1 parent 64e6541 commit 30a45b6
Show file tree
Hide file tree
Showing 6 changed files with 124 additions and 36 deletions.
6 changes: 3 additions & 3 deletions charts/k8s-gateway/Chart.yaml
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
apiVersion: v2
name: k8s-gateway
description: A fork of the k8s_gateway CoreDNS plugin to allow TXT records
description: A fork of the k8s_gateway CoreDNS plugin with added functionalities
type: application
version: 3.1.1
appVersion: 0.6.1
version: 3.2.0
appVersion: 0.7.0
maintainers:
- email: [email protected]
name: Guillaume
2 changes: 1 addition & 1 deletion charts/k8s-gateway/values.yaml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
image:
registry: ghcr.io
repository: pinax-network/k8s_gateway
tag: v0.6.1
tag: v0.7.0
pullPolicy: IfNotPresent

# Delegated domain
Expand Down
66 changes: 41 additions & 25 deletions gateway.go
Original file line number Diff line number Diff line change
Expand Up @@ -179,71 +179,65 @@ func (gw *Gateway) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Ms

var ipv4Addrs []netip.Addr
var ipv6Addrs []netip.Addr
var targetDomains []string
var acmeChallengeKeys []string

for _, obj := range objs {
switch v := obj.(type) {

case netip.Addr:

if v.Is4() {
ipv4Addrs = append(ipv4Addrs, v)
}
if v.Is6() {
ipv6Addrs = append(ipv6Addrs, v)
}

case string:

acmeChallengeKeys = append(acmeChallengeKeys, v)

if dns.IsFqdn(v) {
targetDomains = append(targetDomains, v)
} else {
acmeChallengeKeys = append(acmeChallengeKeys, v)
}
default:

log.Errorf("Unexpected type in results: %T", v)
}
}

switch state.QType() {
case dns.TypeA:

if len(ipv4Addrs) == 0 {

// Returning CNAME record
if len(targetDomains) != 0 {
m.Answer = gw.CNAME(state.Name(), targetDomains)
} else if len(ipv4Addrs) != 0 {
m.Answer = gw.A(state.Name(), ipv4Addrs)
} else {
if !isRootZoneQuery {
// No match, return NXDOMAIN
m.Rcode = dns.RcodeNameError
}

m.Ns = []dns.RR{gw.soa(state)}

} else {
m.Answer = gw.A(state.Name(), ipv4Addrs)
}
case dns.TypeAAAA:

if len(ipv6Addrs) == 0 {

// Returning CNAME record
if len(targetDomains) != 0 {
m.Answer = gw.CNAME(state.Name(), targetDomains)
} else if len(ipv6Addrs) != 0 {
m.Answer = gw.AAAA(state.Name(), ipv6Addrs)
} else {
if !isRootZoneQuery {
// No match, return NXDOMAIN
m.Rcode = dns.RcodeNameError
}

// as per rfc4074 #3
if len(ipv4Addrs) > 0 {
m.Rcode = dns.RcodeSuccess
}

m.Ns = []dns.RR{gw.soa(state)}

} else {
m.Answer = gw.AAAA(state.Name(), ipv6Addrs)
}

case dns.TypeSOA:

m.Answer = []dns.RR{gw.soa(state)}

case dns.TypeNS:

if isRootZoneQuery {
m.Answer = gw.nameservers(state)

Expand All @@ -255,7 +249,6 @@ func (gw *Gateway) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Ms
} else {
m.Ns = []dns.RR{gw.soa(state)}
}

case dns.TypeTXT:
if len(acmeChallengeKeys) == 0 {

Expand Down Expand Up @@ -333,6 +326,29 @@ func (gw *Gateway) AAAA(name string, results []netip.Addr) (records []dns.RR) {
return records
}

func (gw *Gateway) CNAME(name string, results []string) (records []dns.RR) {
dup := make(map[string]struct{})
for _, result := range results {
if _, ok := dup[result]; !ok {
dup[result] = struct{}{}
records = append(
records,
&dns.CNAME{
Hdr: dns.RR_Header{
Name: name,
Rrtype: dns.TypeCNAME,
Class: dns.ClassINET,
Ttl: gw.ttlLow,
},
Target: result,
},
)
}
}

return records
}

func (gw *Gateway) TXT(name string, results []string) (records []dns.RR) {
dup := make(map[string]struct{})
for _, result := range results {
Expand Down
48 changes: 44 additions & 4 deletions kubernetes.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ const (
challengeHostnameIndex = "challengeHostname"
virtualServerHostnameIndex = "virtualServerHostname"
hostnameAnnotationKey = "coredns.io/hostname"
targetAnnotationKey = "coredns.io/target"
externalDnsHostnameAnnotationKey = "external-dns.alpha.kubernetes.io/hostname"
)

Expand Down Expand Up @@ -533,9 +534,9 @@ func serviceHostnameIndexFunc(obj interface{}) ([]string, error) {
}

hostname := service.Name + "." + service.Namespace
if annotation, exists := checkServiceAnnotation(hostnameAnnotationKey, service); exists {
if annotation, exists := checkServiceHostnameAnnotation(hostnameAnnotationKey, service); exists {
hostname = annotation
} else if annotation, exists := checkServiceAnnotation(externalDnsHostnameAnnotationKey, service); exists {
} else if annotation, exists := checkServiceHostnameAnnotation(externalDnsHostnameAnnotationKey, service); exists {
hostname = annotation
}

Expand Down Expand Up @@ -572,7 +573,7 @@ func challengeHostnameIndexFunc(obj interface{}) ([]string, error) {
return []string{host}, nil
}

func checkServiceAnnotation(annotation string, service *core.Service) (string, bool) {
func checkServiceHostnameAnnotation(annotation string, service *core.Service) (string, bool) {
if annotationValue, exists := service.Annotations[annotation]; exists {
// checking the hostname length limits
if _, ok := dns.IsDomainName(annotationValue); ok {
Expand All @@ -590,6 +591,30 @@ func checkServiceAnnotation(annotation string, service *core.Service) (string, b
return "", false
}

func checkServiceTargetAnnotation(annotation string, service *core.Service) (string, bool) {
if annotationValue, exists := service.Annotations[annotation]; exists {
if dns.IsFqdn(annotationValue) {
return strings.ToLower(annotationValue), true
} else {
log.Infof("Invalid FQDN: %s", annotationValue)
}
}

return "", false
}

func checkIngressTargetAnnotation(annotation string, ingress *networking.Ingress) (string, bool) {
if annotationValue, exists := ingress.Annotations[annotation]; exists {
if dns.IsFqdn(annotationValue) {
return strings.ToLower(annotationValue), true
} else {
log.Infof("Invalid FQDN: %s", annotationValue)
}
}

return "", false
}

func virtualServerHostnameIndexFunc(obj interface{}) ([]string, error) {
virtualServer, ok := obj.(*nginx_v1.VirtualServer)
if !ok {
Expand All @@ -612,6 +637,14 @@ func lookupServiceIndex(ctrl cache.SharedIndexInformer) func([]string) []interfa
for _, obj := range objs {
service, _ := obj.(*core.Service)

// Check if we should return a CNAME record
if annotation, exists := checkServiceTargetAnnotation(targetAnnotationKey, service); exists {
log.Debugf("Service has coredns.io/target: %s", annotation)
result = append(result, annotation)
// in case target is defined, ignoring other fields completely
return
}

if len(service.Spec.ExternalIPs) > 0 {
for _, ip := range service.Spec.ExternalIPs {
result = append(result, netip.MustParseAddr(ip))
Expand Down Expand Up @@ -759,11 +792,18 @@ func lookupIngressIndex(ctrl cache.SharedIndexInformer) func([]string) []interfa
for _, obj := range objs {
ingress, _ := obj.(*networking.Ingress)

// Check if we should return a CNAME record
if annotation, exists := checkIngressTargetAnnotation(targetAnnotationKey, ingress); exists {
result = append(result, annotation)
// in case target is defined, ignoring other fields completely
return
}

result = append(
result,
fetchIngressLoadBalancerIPs(ingress.Status.LoadBalancer.Ingress)...)
}

}
return
}
}
Expand Down
18 changes: 15 additions & 3 deletions test/dual-stack/ingress-services.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,18 @@
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: ingress-target
namespace: default
annotations:
coredns.io/target: redirect.here.
spec:
ingressClassName: nginx
rules:
- host: ingress.target.foo.org
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: ingress-myservicea
namespace: default
Expand Down Expand Up @@ -39,9 +51,9 @@ spec:
port:
number: 80
tls:
- hosts:
- "*.myservice.foo.org"
secretName: ingress-wildcard-cert
- hosts:
- "*.myservice.foo.org"
secretName: ingress-wildcard-cert
---
apiVersion: v1
kind: Service
Expand Down
20 changes: 20 additions & 0 deletions test/dual-stack/service-annotation.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,26 @@ spec:
---
apiVersion: v1
kind: Service
metadata:
name: target-annotation-good
namespace: default
annotations:
"coredns.io/hostname": "target.ok"
"coredns.io/target": "redirect.here."
spec:
ipFamilyPolicy: RequireDualStack
ports:
- name: 80-80
port: 80
protocol: TCP
targetPort: 80
selector:
app: backend
sessionAffinity: None
type: LoadBalancer
---
apiVersion: v1
kind: Service
metadata:
name: annotation-bad
namespace: default
Expand Down

0 comments on commit 30a45b6

Please sign in to comment.