forked from openshift/origin
-
Notifications
You must be signed in to change notification settings - Fork 0
/
dns.go
137 lines (122 loc) · 5.93 KB
/
dns.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
package pod
import (
"fmt"
"math/rand"
"github.com/miekg/dns"
"github.com/openshift/origin/pkg/diagnostics/types"
"k8s.io/kubernetes/pkg/util/sets"
)
const (
PodCheckDnsName = "PodCheckDns"
txtDP2016 = `
Query to nameserver %s for random subdomain of %s
returned an answer, indicating that this domain provides wildcard DNS.
This domain should not be in the search.
It can cause queries for external domains (e.g. github.com)
to resolve to the wildcard address. Full response:
%v`
)
// PodCheckDns is a Diagnostic to check that DNS within a pod works as expected
type PodCheckDns struct {
}
// Name is part of the Diagnostic interface and just returns name.
func (d PodCheckDns) Name() string {
return PodCheckDnsName
}
// Description is part of the Diagnostic interface and just returns the diagnostic description.
func (d PodCheckDns) Description() string {
return "Check that DNS within a pod works as expected"
}
// CanRun is part of the Diagnostic interface; it determines if the conditions are right to run this diagnostic.
func (d PodCheckDns) CanRun() (bool, error) {
return true, nil
}
// Check is part of the Diagnostic interface; it runs the actual diagnostic logic
func (d PodCheckDns) Check() types.DiagnosticResult {
r := types.NewDiagnosticResult(PodCheckDnsName)
if resolvConf, err := getResolvConf(r); err == nil {
connectAndResolve(resolvConf, r)
resolveSearch(resolvConf, r)
}
return r
}
func connectAndResolve(resolvConf *dns.ClientConfig, r types.DiagnosticResult) {
for serverIndex, server := range resolvConf.Servers {
// put together a DNS query to configured nameservers for kubernetes.default
msg := new(dns.Msg)
msg.SetQuestion("kubernetes.default.svc.cluster.local.", dns.TypeA)
msg.RecursionDesired = false
if result, completed := dnsQueryWithTimeout(msg, server, 2); !completed {
if serverIndex == 0 { // in a pod, master (SkyDNS) IP is injected as first nameserver
r.Warn("DP2009", nil, fmt.Sprintf("A request to the master (SkyDNS) nameserver %s timed out.\nThis could be temporary but could also indicate network or DNS problems.\nThis nameserver is critical for resolving cluster DNS names.", server))
} else {
r.Warn("DP2010", nil, fmt.Sprintf("A request to the nameserver %s timed out.\nThis could be temporary but could also indicate network or DNS problems.", server))
}
} else {
in, err := result.in, result.err
if serverIndex == 0 { // in a pod, master (SkyDNS) IP is injected as first nameserver
if err != nil {
r.Error("DP2003", err, fmt.Sprintf("The first /etc/resolv.conf nameserver %s\ncould not resolve kubernetes.default.svc.cluster.local.\nError: %v\nThis nameserver points to the master's SkyDNS which is critical for\nresolving cluster names, e.g. for Services.", server, err))
} else if len(in.Answer) == 0 {
r.Error("DP2006", err, fmt.Sprintf("The first /etc/resolv.conf nameserver %s\ncould not resolve kubernetes.default.svc.cluster.local.\nReturn code: %v\nThis nameserver points to the master's SkyDNS which is critical for\nresolving cluster names, e.g. for Services.", server, dns.RcodeToString[in.MsgHdr.Rcode]))
} else {
r.Debug("DP2007", fmt.Sprintf("The first /etc/resolv.conf nameserver %s\nresolved kubernetes.default.svc.cluster.local. to:\n %s", server, in.Answer[0]))
}
} else if err != nil {
r.Warn("DP2004", err, fmt.Sprintf("Error querying nameserver %s:\n %v\nThis may indicate a problem with non-cluster DNS.", server, err))
} else {
rcode := in.MsgHdr.Rcode
switch rcode {
case dns.RcodeSuccess, dns.RcodeNameError: // aka NXDOMAIN
r.Debug("DP2005", fmt.Sprintf("Successful query to nameserver %s", server))
default:
r.Warn("DP2008", nil, fmt.Sprintf("Received unexpected return code '%s' from nameserver %s:\nThis may indicate a problem with non-cluster DNS.", dns.RcodeToString[rcode], server))
}
}
}
}
}
const letterBytes = "abcdefghijklmnopqrstuvwxyz0123456789"
func resolveSearch(resolvConf *dns.ClientConfig, r types.DiagnosticResult) {
foundDomain := false
randomString := func() string {
b := make([]byte, 20)
for i := range b {
b[i] = letterBytes[rand.Intn(len(letterBytes))]
}
return string(b)
}()
seenDP2014 := sets.String{}
seenDP2015 := sets.String{}
for _, domain := range resolvConf.Search {
if domain == "svc.cluster.local" {
foundDomain = true // this will make kubernetes.default work
}
// put together a DNS query to configured nameservers for each search domain
msg := new(dns.Msg)
msg.SetQuestion("wildcard."+randomString+"."+domain+".", dns.TypeA)
msg.RecursionDesired = true // otherwise we just get the authority section for the TLD
for _, server := range resolvConf.Servers {
result, completed := dnsQueryWithTimeout(msg, server, 2)
switch {
case !completed:
if !seenDP2014.Has(server) {
r.Warn("DP2014", nil, fmt.Sprintf("A request to the nameserver %s timed out.\nThis could be temporary but could also indicate network or DNS problems.", server))
seenDP2014.Insert(server) // no need to keep warning about the same server for every domain
}
case result.err != nil:
if !seenDP2015.Has(server) {
r.Warn("DP2015", result.err, fmt.Sprintf("Error querying nameserver %s:\n %v\nThis may indicate a problem with DNS.", server, result.err))
seenDP2015.Insert(server) // don't repeat the error for the same nameserver; chances are it's the same error
}
case result.in.Answer == nil, len(result.in.Answer) == 0:
r.Debug("DP2017", fmt.Sprintf("Nameserver %s responded to wildcard with no answer, which is expected.\n%v", server, result.in))
default: // the random domain is not supposed to resolve
r.Error("DP2016", nil, fmt.Sprintf(txtDP2016, server, domain, result.in))
}
}
}
if !foundDomain {
r.Error("DP2019", nil, "Did not find svc.cluster.local among the configured search domains in /etc/resolv.conf.\nThis is likely to cause problems with certain components that expect to use partial cluster addresses.")
}
}