aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--middleware/kubernetes/README.md18
-rw-r--r--middleware/kubernetes/kubernetes.go82
-rw-r--r--middleware/kubernetes/setup.go15
-rw-r--r--test/kubernetes_test.go13
4 files changed, 111 insertions, 17 deletions
diff --git a/middleware/kubernetes/README.md b/middleware/kubernetes/README.md
index e2e1fa09a..54dd98e57 100644
--- a/middleware/kubernetes/README.md
+++ b/middleware/kubernetes/README.md
@@ -43,21 +43,36 @@ This is the default kubernetes setup, with everything specified in full:
# Kubernetes data API resync period
# Example values: 60s, 5m, 1h
resyncperiod 5m
+
# Use url for k8s API endpoint
endpoint https://k8sendpoint:8080
+
# The tls cert, key and the CA cert filenames
tls cert key cacert
+
# Assemble k8s record names with the template
template {service}.{namespace}.{type}.{zone}
+
# Only expose the k8s namespace "demo"
namespaces demo
+
# Only expose the records for kubernetes objects
# that match this label selector. The label
# selector syntax is described in the kubernetes
# API documentation: http://kubernetes.io/docs/user-guide/labels/
# Example selector below only exposes objects tagged as
# "application=nginx" in the staging or qa environments.
- labels environment in (staging, qa),application=nginx
+ #labels environment in (staging, qa),application=nginx
+
+ # The mode of responding to pod A record requests.
+ # e.g 1-2-3-4.ns.pod.zone. This option is provided to allow use of
+ # SSL certs when connecting directly to pods.
+ # Valid values: disabled, verified, insecure
+ # disabled: default. ignore pod requests, always returning NXDOMAIN
+ # insecure: Always return an A record with IP from request (without
+ # checking k8s). This option is is vulnerable to abuse if
+ # used maliciously in conjuction with wildcard SSL certs.
+ pods disabled
}
# Perform DNS response caching for the coredns.local zone
# Cache timeout is specified by an integer in seconds
@@ -72,6 +87,7 @@ Defaults:
* The `labels` keyword is only used when filtering results based on kubernetes label selector syntax
is required. The label selector syntax is described in the kubernetes API documentation at:
http://kubernetes.io/docs/user-guide/labels/
+* If the `pods` keyword is omitted, all pod type requests will result in NXDOMAIN
### Template Syntax
Record name templates can be constructed using the symbolic elements:
diff --git a/middleware/kubernetes/kubernetes.go b/middleware/kubernetes/kubernetes.go
index 68086702e..85e4940ac 100644
--- a/middleware/kubernetes/kubernetes.go
+++ b/middleware/kubernetes/kubernetes.go
@@ -42,8 +42,14 @@ type Kubernetes struct {
Namespaces []string
LabelSelector *unversionedapi.LabelSelector
Selector *labels.Selector
+ PodMode string
}
+const (
+ PodModeDisabled = "disabled" // default. pod requests are ignored
+ PodModeInsecure = "insecure" // ALL pod requests are answered without verfying they exist
+)
+
type endpoint struct {
addr api.EndpointAddress
port api.EndpointPort
@@ -57,6 +63,12 @@ type service struct {
endpoints []endpoint
}
+type pod struct {
+ name string
+ namespace string
+ addr string
+}
+
var errNoItems = errors.New("no items found")
var errNsNotExposed = errors.New("namespace is not exposed")
var errInvalidRequest = errors.New("invalid query name")
@@ -221,12 +233,9 @@ func (k *Kubernetes) Records(name string, exact bool) ([]msg.Service, error) {
return nil, errNoItems
}
- // TODO: Implementation above globbed together segments for the serviceName if
- // multiple segments remained. Determine how to do similar globbing using
- // the template-based implementation.
- namespace = k.NameTemplate.NamespaceFromSegmentArray(serviceSegments)
- serviceName = k.NameTemplate.ServiceFromSegmentArray(serviceSegments)
- typeName = k.NameTemplate.TypeFromSegmentArray(serviceSegments)
+ serviceName = serviceSegments[0]
+ namespace = serviceSegments[1]
+ typeName = serviceSegments[2]
if namespace == "" {
err := errors.New("Parsing query string did not produce a namespace value. Assuming wildcard namespace.")
@@ -246,16 +255,16 @@ func (k *Kubernetes) Records(name string, exact bool) ([]msg.Service, error) {
return nil, errNsNotExposed
}
- k8sItems, err := k.Get(namespace, serviceName, endpointname, port, protocol, typeName)
+ services, pods, err := k.Get(namespace, serviceName, endpointname, port, protocol, typeName)
if err != nil {
return nil, err
}
- if len(k8sItems) == 0 {
+ if len(services) == 0 && len(pods) == 0 {
// Did not find item in k8s
return nil, errNoItems
}
- records := k.getRecordsForServiceItems(k8sItems, zone)
+ records := k.getRecordsForK8sItems(services, pods, zone)
return records, nil
}
@@ -272,10 +281,10 @@ func endpointHostname(addr api.EndpointAddress) string {
return ""
}
-func (k *Kubernetes) getRecordsForServiceItems(serviceItems []service, zone string) []msg.Service {
+func (k *Kubernetes) getRecordsForK8sItems(services []service, pods []pod, zone string) []msg.Service {
var records []msg.Service
- for _, svc := range serviceItems {
+ for _, svc := range services {
key := svc.name + "." + svc.namespace + ".svc." + zone
@@ -299,20 +308,61 @@ func (k *Kubernetes) getRecordsForServiceItems(serviceItems []service, zone stri
}
}
+ for _, p := range pods {
+ key := p.name + "." + p.namespace + ".pod." + zone
+ s := msg.Service{
+ Key: msg.Path(strings.ToLower(key), "coredns"),
+ Host: p.addr,
+ }
+ records = append(records, s)
+ }
+
return records
}
-// Get performs the call to the Kubernetes http API.
-func (k *Kubernetes) Get(namespace, servicename, endpointname, port, protocol, typeName string) (services []service, err error) {
+// Get retrieves matching data from the cache.
+func (k *Kubernetes) Get(namespace, servicename, endpointname, port, protocol, typeName string) (services []service, pods []pod, err error) {
switch {
case typeName == "pod":
- return nil, fmt.Errorf("%v not implemented", typeName)
+ pods, err = k.findPods(namespace, servicename)
+ return nil, pods, err
default:
- return k.getServices(namespace, servicename, endpointname, port, protocol)
+ services, err = k.findServices(namespace, servicename, endpointname, port, protocol)
+ return services, nil, err
}
}
-func (k *Kubernetes) getServices(namespace, servicename, endpointname, port, protocol string) ([]service, error) {
+func ipFromPodName(podname string) string {
+ if strings.Count(podname, "-") == 3 && !strings.Contains(podname, "--") {
+ return strings.Replace(podname, "-", ".", -1)
+ }
+ return strings.Replace(podname, "-", ":", -1)
+}
+
+func (k *Kubernetes) findPods(namespace, podname string) (pods []pod, err error) {
+ if k.PodMode == PodModeDisabled {
+ return pods, nil
+ }
+
+ var ip string
+ if strings.Count(podname, "-") == 3 && !strings.Contains(podname, "--") {
+ ip = strings.Replace(podname, "-", ".", -1)
+ } else {
+ ip = strings.Replace(podname, "-", ":", -1)
+ }
+
+ if k.PodMode == PodModeInsecure {
+ s := pod{name: podname, namespace: namespace, addr: ip}
+ pods = append(pods, s)
+ return pods, nil
+ }
+
+ // TODO: implement cache verified pod responses
+ return pods, nil
+
+}
+
+func (k *Kubernetes) findServices(namespace, servicename, endpointname, port, protocol string) ([]service, error) {
serviceList := k.APIConn.ServiceList()
var resultItems []service
diff --git a/middleware/kubernetes/setup.go b/middleware/kubernetes/setup.go
index d2b34b054..8c733a2df 100644
--- a/middleware/kubernetes/setup.go
+++ b/middleware/kubernetes/setup.go
@@ -54,6 +54,7 @@ func kubernetesParse(c *caddy.Controller) (*Kubernetes, error) {
k8s := &Kubernetes{ResyncPeriod: defaultResyncPeriod}
k8s.NameTemplate = new(nametemplate.Template)
k8s.NameTemplate.SetTemplate(defaultNameTemplate)
+ k8s.PodMode = PodModeDisabled
for c.Next() {
if c.Val() == "kubernetes" {
@@ -86,6 +87,19 @@ func kubernetesParse(c *caddy.Controller) (*Kubernetes, error) {
for c.NextBlock() {
switch c.Val() {
+ case "pods":
+ args := c.RemainingArgs()
+ if len(args) == 1 {
+ switch args[0] {
+ case PodModeDisabled, PodModeInsecure:
+ k8s.PodMode = args[0]
+ default:
+ return nil, errors.New("pods must be one of: disabled, insecure")
+ }
+ continue
+ }
+ return nil, c.ArgErr()
+
case "template":
args := c.RemainingArgs()
if len(args) > 0 {
@@ -152,4 +166,5 @@ func kubernetesParse(c *caddy.Controller) (*Kubernetes, error) {
const (
defaultNameTemplate = "{service}.{namespace}.{type}.{zone}"
defaultResyncPeriod = 5 * time.Minute
+ defaultPodMode = PodModeDisabled
)
diff --git a/test/kubernetes_test.go b/test/kubernetes_test.go
index 1d5df37a3..02f731475 100644
--- a/test/kubernetes_test.go
+++ b/test/kubernetes_test.go
@@ -195,6 +195,18 @@ var dnsTestCases = []test.Case{
Answer: []dns.RR{},
},
{
+ Qname: "10-20-0-101.test-1.pod.cluster.local.", Qtype: dns.TypeA,
+ Rcode: dns.RcodeSuccess,
+ Answer: []dns.RR{
+ test.A("10-20-0-101.test-1.pod.cluster.local. 0 IN A 10.20.0.101"),
+ },
+ },
+ {
+ Qname: "10-20-0-101.test-X.pod.cluster.local.", Qtype: dns.TypeA,
+ Rcode: dns.RcodeNameError,
+ Answer: []dns.RR{},
+ },
+ {
Qname: "123.0.0.10.in-addr.arpa.", Qtype: dns.TypePTR,
Rcode: dns.RcodeSuccess,
Answer: []dns.RR{},
@@ -238,6 +250,7 @@ func TestKubernetesIntegration(t *testing.T) {
#tls admin.pem admin-key.pem ca.pem
#tls k8s_auth/client2.crt k8s_auth/client2.key k8s_auth/ca2.crt
namespaces test-1
+ pods insecure
}
`
server, udp := createTestServer(t, corefile)