| 
									
										
										
										
											2018-12-14 09:41:51 +00:00
										 |  |  | package kubernetes
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import (
 | 
					
						
							|  |  |  | 	"strings"
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	"github.com/coredns/coredns/plugin/etcd/msg"
 | 
					
						
							|  |  |  | 	"github.com/coredns/coredns/plugin/kubernetes/object"
 | 
					
						
							|  |  |  | 	"github.com/coredns/coredns/plugin/pkg/dnsutil"
 | 
					
						
							|  |  |  | 	"github.com/coredns/coredns/request"
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	"github.com/miekg/dns"
 | 
					
						
							|  |  |  | )
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // External implements the ExternalFunc call from the external plugin.
 | 
					
						
							|  |  |  | // It returns any services matching in the services' ExternalIPs.
 | 
					
						
							|  |  |  | func (k *Kubernetes) External(state request.Request) ([]msg.Service, int) {
 | 
					
						
							| 
									
										
										
										
											2022-07-06 13:55:15 -04:00
										 |  |  | 	if state.QType() == dns.TypePTR {
 | 
					
						
							|  |  |  | 		ip := dnsutil.ExtractAddressFromReverse(state.Name())
 | 
					
						
							|  |  |  | 		if ip != "" {
 | 
					
						
							|  |  |  | 			svcs, err := k.ExternalReverse(ip)
 | 
					
						
							|  |  |  | 			if err != nil {
 | 
					
						
							|  |  |  | 				return nil, dns.RcodeNameError
 | 
					
						
							|  |  |  | 			}
 | 
					
						
							|  |  |  | 			return svcs, dns.RcodeSuccess
 | 
					
						
							|  |  |  | 		}
 | 
					
						
							|  |  |  | 		// for invalid reverse names, fall through to determine proper nxdomain/nodata response
 | 
					
						
							|  |  |  | 	}
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-12-14 09:41:51 +00:00
										 |  |  | 	base, _ := dnsutil.TrimZone(state.Name(), state.Zone)
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	segs := dns.SplitDomainName(base)
 | 
					
						
							|  |  |  | 	last := len(segs) - 1
 | 
					
						
							|  |  |  | 	if last < 0 {
 | 
					
						
							|  |  |  | 		return nil, dns.RcodeServerFailure
 | 
					
						
							|  |  |  | 	}
 | 
					
						
							| 
									
										
										
										
											2019-08-21 16:08:55 -04:00
										 |  |  | 	// We are dealing with a fairly normal domain name here, but we still need to have the service
 | 
					
						
							| 
									
										
										
										
											2018-12-14 09:41:51 +00:00
										 |  |  | 	// and the namespace:
 | 
					
						
							|  |  |  | 	// service.namespace.<base>
 | 
					
						
							| 
									
										
										
										
											2022-02-09 09:25:10 -05:00
										 |  |  | 	var port, protocol string
 | 
					
						
							| 
									
										
										
										
											2018-12-14 09:41:51 +00:00
										 |  |  | 	namespace := segs[last]
 | 
					
						
							| 
									
										
										
										
											2019-03-22 08:32:40 -06:00
										 |  |  | 	if !k.namespaceExposed(namespace) {
 | 
					
						
							| 
									
										
										
										
											2018-12-14 09:41:51 +00:00
										 |  |  | 		return nil, dns.RcodeNameError
 | 
					
						
							|  |  |  | 	}
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	last--
 | 
					
						
							|  |  |  | 	if last < 0 {
 | 
					
						
							|  |  |  | 		return nil, dns.RcodeSuccess
 | 
					
						
							|  |  |  | 	}
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	service := segs[last]
 | 
					
						
							|  |  |  | 	last--
 | 
					
						
							|  |  |  | 	if last == 1 {
 | 
					
						
							|  |  |  | 		protocol = stripUnderscore(segs[last])
 | 
					
						
							|  |  |  | 		port = stripUnderscore(segs[last-1])
 | 
					
						
							|  |  |  | 		last -= 2
 | 
					
						
							|  |  |  | 	}
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if last != -1 {
 | 
					
						
							|  |  |  | 		// too long
 | 
					
						
							|  |  |  | 		return nil, dns.RcodeNameError
 | 
					
						
							|  |  |  | 	}
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	idx := object.ServiceKey(service, namespace)
 | 
					
						
							|  |  |  | 	serviceList := k.APIConn.SvcIndex(idx)
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	services := []msg.Service{}
 | 
					
						
							|  |  |  | 	zonePath := msg.Path(state.Zone, coredns)
 | 
					
						
							|  |  |  | 	rcode := dns.RcodeNameError
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	for _, svc := range serviceList {
 | 
					
						
							|  |  |  | 		if namespace != svc.Namespace {
 | 
					
						
							|  |  |  | 			continue
 | 
					
						
							|  |  |  | 		}
 | 
					
						
							|  |  |  | 		if service != svc.Name {
 | 
					
						
							|  |  |  | 			continue
 | 
					
						
							|  |  |  | 		}
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		for _, ip := range svc.ExternalIPs {
 | 
					
						
							|  |  |  | 			for _, p := range svc.Ports {
 | 
					
						
							| 
									
										
										
										
											2022-02-09 09:25:10 -05:00
										 |  |  | 				if !(matchPortAndProtocol(port, p.Name, protocol, string(p.Protocol))) {
 | 
					
						
							| 
									
										
										
										
											2018-12-14 09:41:51 +00:00
										 |  |  | 					continue
 | 
					
						
							|  |  |  | 				}
 | 
					
						
							|  |  |  | 				rcode = dns.RcodeSuccess
 | 
					
						
							|  |  |  | 				s := msg.Service{Host: ip, Port: int(p.Port), TTL: k.ttl}
 | 
					
						
							|  |  |  | 				s.Key = strings.Join([]string{zonePath, svc.Namespace, svc.Name}, "/")
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 				services = append(services, s)
 | 
					
						
							|  |  |  | 			}
 | 
					
						
							|  |  |  | 		}
 | 
					
						
							|  |  |  | 	}
 | 
					
						
							| 
									
										
										
										
											2022-07-06 13:55:15 -04:00
										 |  |  | 	if state.QType() == dns.TypePTR {
 | 
					
						
							|  |  |  | 		// if this was a PTR request, return empty service list, but retain rcode for proper nxdomain/nodata response
 | 
					
						
							|  |  |  | 		return nil, rcode
 | 
					
						
							|  |  |  | 	}
 | 
					
						
							| 
									
										
										
										
											2018-12-14 09:41:51 +00:00
										 |  |  | 	return services, rcode
 | 
					
						
							|  |  |  | }
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // ExternalAddress returns the external service address(es) for the CoreDNS service.
 | 
					
						
							|  |  |  | func (k *Kubernetes) ExternalAddress(state request.Request) []dns.RR {
 | 
					
						
							| 
									
										
										
										
											2019-08-23 12:54:06 -04:00
										 |  |  | 	// If CoreDNS is running inside the Kubernetes cluster: k.nsAddrs() will return the external IPs of the services
 | 
					
						
							|  |  |  | 	// targeting the CoreDNS Pod.
 | 
					
						
							|  |  |  | 	// If CoreDNS is running outside of the Kubernetes cluster: k.nsAddrs() will return the first non-loopback IP
 | 
					
						
							|  |  |  | 	// address seen on the local system it is running on. This could be the wrong answer if coredns is using the *bind*
 | 
					
						
							|  |  |  | 	// plugin to bind to a different IP address.
 | 
					
						
							|  |  |  | 	return k.nsAddrs(true, state.Zone)
 | 
					
						
							| 
									
										
										
										
											2018-12-14 09:41:51 +00:00
										 |  |  | }
 | 
					
						
							| 
									
										
										
										
											2022-03-07 12:16:24 -05:00
										 |  |  | 
 | 
					
						
							|  |  |  | // ExternalServices returns all services with external IPs
 | 
					
						
							|  |  |  | func (k *Kubernetes) ExternalServices(zone string) (services []msg.Service) {
 | 
					
						
							|  |  |  | 	zonePath := msg.Path(zone, coredns)
 | 
					
						
							|  |  |  | 	for _, svc := range k.APIConn.ServiceList() {
 | 
					
						
							|  |  |  | 		for _, ip := range svc.ExternalIPs {
 | 
					
						
							|  |  |  | 			for _, p := range svc.Ports {
 | 
					
						
							|  |  |  | 				s := msg.Service{Host: ip, Port: int(p.Port), TTL: k.ttl}
 | 
					
						
							|  |  |  | 				s.Key = strings.Join([]string{zonePath, svc.Namespace, svc.Name}, "/")
 | 
					
						
							|  |  |  | 				services = append(services, s)
 | 
					
						
							|  |  |  | 				s.Key = strings.Join(append([]string{zonePath, svc.Namespace, svc.Name}, strings.ToLower("_"+string(p.Protocol)), strings.ToLower("_"+string(p.Name))), "/")
 | 
					
						
							|  |  |  | 				s.TargetStrip = 2
 | 
					
						
							|  |  |  | 				services = append(services, s)
 | 
					
						
							|  |  |  | 			}
 | 
					
						
							|  |  |  | 		}
 | 
					
						
							|  |  |  | 	}
 | 
					
						
							|  |  |  | 	return services
 | 
					
						
							|  |  |  | }
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | //ExternalSerial returns the serial of the external zone
 | 
					
						
							|  |  |  | func (k *Kubernetes) ExternalSerial(string) uint32 {
 | 
					
						
							|  |  |  | 	return uint32(k.APIConn.Modified(true))
 | 
					
						
							|  |  |  | }
 | 
					
						
							| 
									
										
										
										
											2022-07-06 13:55:15 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  | // ExternalReverse does a reverse lookup for the external IPs
 | 
					
						
							|  |  |  | func (k *Kubernetes) ExternalReverse(ip string) ([]msg.Service, error) {
 | 
					
						
							|  |  |  | 	records := k.serviceRecordForExternalIP(ip)
 | 
					
						
							|  |  |  | 	if len(records) == 0 {
 | 
					
						
							|  |  |  | 		return records, errNoItems
 | 
					
						
							|  |  |  | 	}
 | 
					
						
							|  |  |  | 	return records, nil
 | 
					
						
							|  |  |  | }
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (k *Kubernetes) serviceRecordForExternalIP(ip string) []msg.Service {
 | 
					
						
							|  |  |  | 	var svcs []msg.Service
 | 
					
						
							|  |  |  | 	for _, service := range k.APIConn.SvcExtIndexReverse(ip) {
 | 
					
						
							|  |  |  | 		if len(k.Namespaces) > 0 && !k.namespaceExposed(service.Namespace) {
 | 
					
						
							|  |  |  | 			continue
 | 
					
						
							|  |  |  | 		}
 | 
					
						
							|  |  |  | 		domain := strings.Join([]string{service.Name, service.Namespace}, ".")
 | 
					
						
							|  |  |  | 		svcs = append(svcs, msg.Service{Host: domain, TTL: k.ttl})
 | 
					
						
							|  |  |  | 	}
 | 
					
						
							|  |  |  | 	return svcs
 | 
					
						
							|  |  |  | }
 |