Skip to content

Commit 30a45b6

Browse files
committed
add CNAME support
udpate version
1 parent 64e6541 commit 30a45b6

File tree

6 files changed

+124
-36
lines changed

6 files changed

+124
-36
lines changed

charts/k8s-gateway/Chart.yaml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
apiVersion: v2
22
name: k8s-gateway
3-
description: A fork of the k8s_gateway CoreDNS plugin to allow TXT records
3+
description: A fork of the k8s_gateway CoreDNS plugin with added functionalities
44
type: application
5-
version: 3.1.1
6-
appVersion: 0.6.1
5+
version: 3.2.0
6+
appVersion: 0.7.0
77
maintainers:
88
- email: guillaume@pinax.network
99
name: Guillaume

charts/k8s-gateway/values.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
image:
22
registry: ghcr.io
33
repository: pinax-network/k8s_gateway
4-
tag: v0.6.1
4+
tag: v0.7.0
55
pullPolicy: IfNotPresent
66

77
# Delegated domain

gateway.go

Lines changed: 41 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -179,71 +179,65 @@ func (gw *Gateway) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Ms
179179

180180
var ipv4Addrs []netip.Addr
181181
var ipv6Addrs []netip.Addr
182+
var targetDomains []string
182183
var acmeChallengeKeys []string
183184

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

187188
case netip.Addr:
188-
189189
if v.Is4() {
190190
ipv4Addrs = append(ipv4Addrs, v)
191191
}
192192
if v.Is6() {
193193
ipv6Addrs = append(ipv6Addrs, v)
194194
}
195-
196195
case string:
197-
198-
acmeChallengeKeys = append(acmeChallengeKeys, v)
199-
196+
if dns.IsFqdn(v) {
197+
targetDomains = append(targetDomains, v)
198+
} else {
199+
acmeChallengeKeys = append(acmeChallengeKeys, v)
200+
}
200201
default:
201-
202202
log.Errorf("Unexpected type in results: %T", v)
203203
}
204204
}
205205

206206
switch state.QType() {
207207
case dns.TypeA:
208-
209-
if len(ipv4Addrs) == 0 {
210-
208+
// Returning CNAME record
209+
if len(targetDomains) != 0 {
210+
m.Answer = gw.CNAME(state.Name(), targetDomains)
211+
} else if len(ipv4Addrs) != 0 {
212+
m.Answer = gw.A(state.Name(), ipv4Addrs)
213+
} else {
211214
if !isRootZoneQuery {
212215
// No match, return NXDOMAIN
213216
m.Rcode = dns.RcodeNameError
214217
}
215-
216218
m.Ns = []dns.RR{gw.soa(state)}
217-
218-
} else {
219-
m.Answer = gw.A(state.Name(), ipv4Addrs)
220219
}
221220
case dns.TypeAAAA:
222-
223-
if len(ipv6Addrs) == 0 {
224-
221+
// Returning CNAME record
222+
if len(targetDomains) != 0 {
223+
m.Answer = gw.CNAME(state.Name(), targetDomains)
224+
} else if len(ipv6Addrs) != 0 {
225+
m.Answer = gw.AAAA(state.Name(), ipv6Addrs)
226+
} else {
225227
if !isRootZoneQuery {
226228
// No match, return NXDOMAIN
227229
m.Rcode = dns.RcodeNameError
228230
}
229-
230231
// as per rfc4074 #3
231232
if len(ipv4Addrs) > 0 {
232233
m.Rcode = dns.RcodeSuccess
233234
}
234-
235235
m.Ns = []dns.RR{gw.soa(state)}
236-
237-
} else {
238-
m.Answer = gw.AAAA(state.Name(), ipv6Addrs)
239236
}
240-
241237
case dns.TypeSOA:
242-
243238
m.Answer = []dns.RR{gw.soa(state)}
244239

245240
case dns.TypeNS:
246-
247241
if isRootZoneQuery {
248242
m.Answer = gw.nameservers(state)
249243

@@ -255,7 +249,6 @@ func (gw *Gateway) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Ms
255249
} else {
256250
m.Ns = []dns.RR{gw.soa(state)}
257251
}
258-
259252
case dns.TypeTXT:
260253
if len(acmeChallengeKeys) == 0 {
261254

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

329+
func (gw *Gateway) CNAME(name string, results []string) (records []dns.RR) {
330+
dup := make(map[string]struct{})
331+
for _, result := range results {
332+
if _, ok := dup[result]; !ok {
333+
dup[result] = struct{}{}
334+
records = append(
335+
records,
336+
&dns.CNAME{
337+
Hdr: dns.RR_Header{
338+
Name: name,
339+
Rrtype: dns.TypeCNAME,
340+
Class: dns.ClassINET,
341+
Ttl: gw.ttlLow,
342+
},
343+
Target: result,
344+
},
345+
)
346+
}
347+
}
348+
349+
return records
350+
}
351+
336352
func (gw *Gateway) TXT(name string, results []string) (records []dns.RR) {
337353
dup := make(map[string]struct{})
338354
for _, result := range results {

kubernetes.go

Lines changed: 44 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ const (
4040
challengeHostnameIndex = "challengeHostname"
4141
virtualServerHostnameIndex = "virtualServerHostname"
4242
hostnameAnnotationKey = "coredns.io/hostname"
43+
targetAnnotationKey = "coredns.io/target"
4344
externalDnsHostnameAnnotationKey = "external-dns.alpha.kubernetes.io/hostname"
4445
)
4546

@@ -533,9 +534,9 @@ func serviceHostnameIndexFunc(obj interface{}) ([]string, error) {
533534
}
534535

535536
hostname := service.Name + "." + service.Namespace
536-
if annotation, exists := checkServiceAnnotation(hostnameAnnotationKey, service); exists {
537+
if annotation, exists := checkServiceHostnameAnnotation(hostnameAnnotationKey, service); exists {
537538
hostname = annotation
538-
} else if annotation, exists := checkServiceAnnotation(externalDnsHostnameAnnotationKey, service); exists {
539+
} else if annotation, exists := checkServiceHostnameAnnotation(externalDnsHostnameAnnotationKey, service); exists {
539540
hostname = annotation
540541
}
541542

@@ -572,7 +573,7 @@ func challengeHostnameIndexFunc(obj interface{}) ([]string, error) {
572573
return []string{host}, nil
573574
}
574575

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

594+
func checkServiceTargetAnnotation(annotation string, service *core.Service) (string, bool) {
595+
if annotationValue, exists := service.Annotations[annotation]; exists {
596+
if dns.IsFqdn(annotationValue) {
597+
return strings.ToLower(annotationValue), true
598+
} else {
599+
log.Infof("Invalid FQDN: %s", annotationValue)
600+
}
601+
}
602+
603+
return "", false
604+
}
605+
606+
func checkIngressTargetAnnotation(annotation string, ingress *networking.Ingress) (string, bool) {
607+
if annotationValue, exists := ingress.Annotations[annotation]; exists {
608+
if dns.IsFqdn(annotationValue) {
609+
return strings.ToLower(annotationValue), true
610+
} else {
611+
log.Infof("Invalid FQDN: %s", annotationValue)
612+
}
613+
}
614+
615+
return "", false
616+
}
617+
593618
func virtualServerHostnameIndexFunc(obj interface{}) ([]string, error) {
594619
virtualServer, ok := obj.(*nginx_v1.VirtualServer)
595620
if !ok {
@@ -612,6 +637,14 @@ func lookupServiceIndex(ctrl cache.SharedIndexInformer) func([]string) []interfa
612637
for _, obj := range objs {
613638
service, _ := obj.(*core.Service)
614639

640+
// Check if we should return a CNAME record
641+
if annotation, exists := checkServiceTargetAnnotation(targetAnnotationKey, service); exists {
642+
log.Debugf("Service has coredns.io/target: %s", annotation)
643+
result = append(result, annotation)
644+
// in case target is defined, ignoring other fields completely
645+
return
646+
}
647+
615648
if len(service.Spec.ExternalIPs) > 0 {
616649
for _, ip := range service.Spec.ExternalIPs {
617650
result = append(result, netip.MustParseAddr(ip))
@@ -759,11 +792,18 @@ func lookupIngressIndex(ctrl cache.SharedIndexInformer) func([]string) []interfa
759792
for _, obj := range objs {
760793
ingress, _ := obj.(*networking.Ingress)
761794

795+
// Check if we should return a CNAME record
796+
if annotation, exists := checkIngressTargetAnnotation(targetAnnotationKey, ingress); exists {
797+
result = append(result, annotation)
798+
// in case target is defined, ignoring other fields completely
799+
return
800+
}
801+
762802
result = append(
763803
result,
764804
fetchIngressLoadBalancerIPs(ingress.Status.LoadBalancer.Ingress)...)
765-
}
766805

806+
}
767807
return
768808
}
769809
}

test/dual-stack/ingress-services.yml

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,18 @@
11
---
22
apiVersion: networking.k8s.io/v1
33
kind: Ingress
4+
metadata:
5+
name: ingress-target
6+
namespace: default
7+
annotations:
8+
coredns.io/target: redirect.here.
9+
spec:
10+
ingressClassName: nginx
11+
rules:
12+
- host: ingress.target.foo.org
13+
---
14+
apiVersion: networking.k8s.io/v1
15+
kind: Ingress
416
metadata:
517
name: ingress-myservicea
618
namespace: default
@@ -39,9 +51,9 @@ spec:
3951
port:
4052
number: 80
4153
tls:
42-
- hosts:
43-
- "*.myservice.foo.org"
44-
secretName: ingress-wildcard-cert
54+
- hosts:
55+
- "*.myservice.foo.org"
56+
secretName: ingress-wildcard-cert
4557
---
4658
apiVersion: v1
4759
kind: Service

test/dual-stack/service-annotation.yml

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,26 @@ spec:
2020
---
2121
apiVersion: v1
2222
kind: Service
23+
metadata:
24+
name: target-annotation-good
25+
namespace: default
26+
annotations:
27+
"coredns.io/hostname": "target.ok"
28+
"coredns.io/target": "redirect.here."
29+
spec:
30+
ipFamilyPolicy: RequireDualStack
31+
ports:
32+
- name: 80-80
33+
port: 80
34+
protocol: TCP
35+
targetPort: 80
36+
selector:
37+
app: backend
38+
sessionAffinity: None
39+
type: LoadBalancer
40+
---
41+
apiVersion: v1
42+
kind: Service
2343
metadata:
2444
name: annotation-bad
2545
namespace: default

0 commit comments

Comments
 (0)