resolver.go 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555
  1. package libnetwork
  2. import (
  3. "context"
  4. "fmt"
  5. "math/rand"
  6. "net"
  7. "strings"
  8. "sync"
  9. "time"
  10. "github.com/containerd/containerd/log"
  11. "github.com/docker/docker/libnetwork/types"
  12. "github.com/miekg/dns"
  13. "github.com/sirupsen/logrus"
  14. "golang.org/x/sync/semaphore"
  15. "golang.org/x/time/rate"
  16. )
  17. // DNSBackend represents a backend DNS resolver used for DNS name
  18. // resolution. All the queries to the resolver are forwarded to the
  19. // backend resolver.
  20. type DNSBackend interface {
  21. // ResolveName resolves a service name to an IPv4 or IPv6 address by searching
  22. // the networks the sandbox is connected to. For IPv6 queries, second return
  23. // value will be true if the name exists in docker domain but doesn't have an
  24. // IPv6 address. Such queries shouldn't be forwarded to external nameservers.
  25. ResolveName(name string, iplen int) ([]net.IP, bool)
  26. // ResolveIP returns the service name for the passed in IP. IP is in reverse dotted
  27. // notation; the format used for DNS PTR records
  28. ResolveIP(name string) string
  29. // ResolveService returns all the backend details about the containers or hosts
  30. // backing a service. Its purpose is to satisfy an SRV query
  31. ResolveService(name string) ([]*net.SRV, []net.IP)
  32. // ExecFunc allows a function to be executed in the context of the backend
  33. // on behalf of the resolver.
  34. ExecFunc(f func()) error
  35. // NdotsSet queries the backends ndots dns option settings
  36. NdotsSet() bool
  37. // HandleQueryResp passes the name & IP from a response to the backend. backend
  38. // can use it to maintain any required state about the resolution
  39. HandleQueryResp(name string, ip net.IP)
  40. }
  41. const (
  42. dnsPort = "53"
  43. ptrIPv4domain = ".in-addr.arpa."
  44. ptrIPv6domain = ".ip6.arpa."
  45. respTTL = 600
  46. maxExtDNS = 3 // max number of external servers to try
  47. extIOTimeout = 4 * time.Second
  48. maxConcurrent = 1024
  49. logInterval = 2 * time.Second
  50. )
  51. type extDNSEntry struct {
  52. IPStr string
  53. port uint16 // for testing
  54. HostLoopback bool
  55. }
  56. // Resolver is the embedded DNS server in Docker. It operates by listening on
  57. // the container's loopback interface for DNS queries.
  58. type Resolver struct {
  59. backend DNSBackend
  60. extDNSList [maxExtDNS]extDNSEntry
  61. server *dns.Server
  62. conn *net.UDPConn
  63. tcpServer *dns.Server
  64. tcpListen *net.TCPListener
  65. err error
  66. listenAddress string
  67. proxyDNS bool
  68. startCh chan struct{}
  69. logger *logrus.Logger
  70. fwdSem *semaphore.Weighted // Limit the number of concurrent external DNS requests in-flight
  71. logInverval rate.Sometimes // Rate-limit logging about hitting the fwdSem limit
  72. }
  73. // NewResolver creates a new instance of the Resolver
  74. func NewResolver(address string, proxyDNS bool, backend DNSBackend) *Resolver {
  75. return &Resolver{
  76. backend: backend,
  77. proxyDNS: proxyDNS,
  78. listenAddress: address,
  79. err: fmt.Errorf("setup not done yet"),
  80. startCh: make(chan struct{}, 1),
  81. fwdSem: semaphore.NewWeighted(maxConcurrent),
  82. logInverval: rate.Sometimes{Interval: logInterval},
  83. }
  84. }
  85. func (r *Resolver) log() *logrus.Logger {
  86. if r.logger == nil {
  87. return log.G(context.TODO()).Logger
  88. }
  89. return r.logger
  90. }
  91. // SetupFunc returns the setup function that should be run in the container's
  92. // network namespace.
  93. func (r *Resolver) SetupFunc(port int) func() {
  94. return func() {
  95. var err error
  96. // DNS operates primarily on UDP
  97. addr := &net.UDPAddr{
  98. IP: net.ParseIP(r.listenAddress),
  99. Port: port,
  100. }
  101. r.conn, err = net.ListenUDP("udp", addr)
  102. if err != nil {
  103. r.err = fmt.Errorf("error in opening name server socket %v", err)
  104. return
  105. }
  106. // Listen on a TCP as well
  107. tcpaddr := &net.TCPAddr{
  108. IP: net.ParseIP(r.listenAddress),
  109. Port: port,
  110. }
  111. r.tcpListen, err = net.ListenTCP("tcp", tcpaddr)
  112. if err != nil {
  113. r.err = fmt.Errorf("error in opening name TCP server socket %v", err)
  114. return
  115. }
  116. r.err = nil
  117. }
  118. }
  119. // Start starts the name server for the container.
  120. func (r *Resolver) Start() error {
  121. r.startCh <- struct{}{}
  122. defer func() { <-r.startCh }()
  123. // make sure the resolver has been setup before starting
  124. if r.err != nil {
  125. return r.err
  126. }
  127. if err := r.setupIPTable(); err != nil {
  128. return fmt.Errorf("setting up IP table rules failed: %v", err)
  129. }
  130. s := &dns.Server{Handler: dns.HandlerFunc(r.serveDNS), PacketConn: r.conn}
  131. r.server = s
  132. go func() {
  133. if err := s.ActivateAndServe(); err != nil {
  134. r.log().WithError(err).Error("[resolver] failed to start PacketConn DNS server")
  135. }
  136. }()
  137. tcpServer := &dns.Server{Handler: dns.HandlerFunc(r.serveDNS), Listener: r.tcpListen}
  138. r.tcpServer = tcpServer
  139. go func() {
  140. if err := tcpServer.ActivateAndServe(); err != nil {
  141. r.log().WithError(err).Error("[resolver] failed to start TCP DNS server")
  142. }
  143. }()
  144. return nil
  145. }
  146. // Stop stops the name server for the container. A stopped resolver can be
  147. // reused after running the SetupFunc again.
  148. func (r *Resolver) Stop() {
  149. r.startCh <- struct{}{}
  150. defer func() { <-r.startCh }()
  151. if r.server != nil {
  152. r.server.Shutdown() //nolint:errcheck
  153. }
  154. if r.tcpServer != nil {
  155. r.tcpServer.Shutdown() //nolint:errcheck
  156. }
  157. r.conn = nil
  158. r.tcpServer = nil
  159. r.err = fmt.Errorf("setup not done yet")
  160. r.fwdSem = semaphore.NewWeighted(maxConcurrent)
  161. }
  162. // SetExtServers configures the external nameservers the resolver should use
  163. // when forwarding queries.
  164. func (r *Resolver) SetExtServers(extDNS []extDNSEntry) {
  165. l := len(extDNS)
  166. if l > maxExtDNS {
  167. l = maxExtDNS
  168. }
  169. for i := 0; i < l; i++ {
  170. r.extDNSList[i] = extDNS[i]
  171. }
  172. }
  173. // NameServer returns the IP of the DNS resolver for the containers.
  174. func (r *Resolver) NameServer() string {
  175. return r.listenAddress
  176. }
  177. // ResolverOptions returns resolv.conf options that should be set.
  178. func (r *Resolver) ResolverOptions() []string {
  179. return []string{"ndots:0"}
  180. }
  181. func setCommonFlags(msg *dns.Msg) {
  182. msg.RecursionAvailable = true
  183. }
  184. //nolint:gosec // The RNG is not used in a security-sensitive context.
  185. var (
  186. shuffleRNG = rand.New(rand.NewSource(time.Now().Unix()))
  187. shuffleRNGMu sync.Mutex
  188. )
  189. func shuffleAddr(addr []net.IP) []net.IP {
  190. shuffleRNGMu.Lock()
  191. defer shuffleRNGMu.Unlock()
  192. for i := len(addr) - 1; i > 0; i-- {
  193. r := shuffleRNG.Intn(i + 1) //nolint:gosec // gosec complains about the use of rand here. It should be fine.
  194. addr[i], addr[r] = addr[r], addr[i]
  195. }
  196. return addr
  197. }
  198. func createRespMsg(query *dns.Msg) *dns.Msg {
  199. resp := new(dns.Msg)
  200. resp.SetReply(query)
  201. setCommonFlags(resp)
  202. return resp
  203. }
  204. func (r *Resolver) handleMXQuery(query *dns.Msg) (*dns.Msg, error) {
  205. name := query.Question[0].Name
  206. addrv4, _ := r.backend.ResolveName(name, types.IPv4)
  207. addrv6, _ := r.backend.ResolveName(name, types.IPv6)
  208. if addrv4 == nil && addrv6 == nil {
  209. return nil, nil
  210. }
  211. // We were able to resolve the name. Respond with an empty list with
  212. // RcodeSuccess/NOERROR so that email clients can treat it as "implicit MX"
  213. // [RFC 5321 Section-5.1] and issue a Type A/AAAA query for the name.
  214. resp := createRespMsg(query)
  215. return resp, nil
  216. }
  217. func (r *Resolver) handleIPQuery(query *dns.Msg, ipType int) (*dns.Msg, error) {
  218. var (
  219. addr []net.IP
  220. ipv6Miss bool
  221. name = query.Question[0].Name
  222. )
  223. addr, ipv6Miss = r.backend.ResolveName(name, ipType)
  224. if addr == nil && ipv6Miss {
  225. // Send a reply without any Answer sections
  226. r.log().Debugf("[resolver] lookup name %s present without IPv6 address", name)
  227. resp := createRespMsg(query)
  228. return resp, nil
  229. }
  230. if addr == nil {
  231. return nil, nil
  232. }
  233. r.log().Debugf("[resolver] lookup for %s: IP %v", name, addr)
  234. resp := createRespMsg(query)
  235. if len(addr) > 1 {
  236. addr = shuffleAddr(addr)
  237. }
  238. if ipType == types.IPv4 {
  239. for _, ip := range addr {
  240. rr := new(dns.A)
  241. rr.Hdr = dns.RR_Header{Name: name, Rrtype: dns.TypeA, Class: dns.ClassINET, Ttl: respTTL}
  242. rr.A = ip
  243. resp.Answer = append(resp.Answer, rr)
  244. }
  245. } else {
  246. for _, ip := range addr {
  247. rr := new(dns.AAAA)
  248. rr.Hdr = dns.RR_Header{Name: name, Rrtype: dns.TypeAAAA, Class: dns.ClassINET, Ttl: respTTL}
  249. rr.AAAA = ip
  250. resp.Answer = append(resp.Answer, rr)
  251. }
  252. }
  253. return resp, nil
  254. }
  255. func (r *Resolver) handlePTRQuery(query *dns.Msg) (*dns.Msg, error) {
  256. ptr := query.Question[0].Name
  257. name, after, found := strings.Cut(ptr, ptrIPv4domain)
  258. if !found || after != "" {
  259. name, after, found = strings.Cut(ptr, ptrIPv6domain)
  260. }
  261. if !found || after != "" {
  262. // Not a known IPv4 or IPv6 PTR domain.
  263. // Maybe the external DNS servers know what to do with the query?
  264. return nil, nil
  265. }
  266. host := r.backend.ResolveIP(name)
  267. if host == "" {
  268. return nil, nil
  269. }
  270. r.log().Debugf("[resolver] lookup for IP %s: name %s", name, host)
  271. fqdn := dns.Fqdn(host)
  272. resp := new(dns.Msg)
  273. resp.SetReply(query)
  274. setCommonFlags(resp)
  275. rr := new(dns.PTR)
  276. rr.Hdr = dns.RR_Header{Name: ptr, Rrtype: dns.TypePTR, Class: dns.ClassINET, Ttl: respTTL}
  277. rr.Ptr = fqdn
  278. resp.Answer = append(resp.Answer, rr)
  279. return resp, nil
  280. }
  281. func (r *Resolver) handleSRVQuery(query *dns.Msg) (*dns.Msg, error) {
  282. svc := query.Question[0].Name
  283. srv, ip := r.backend.ResolveService(svc)
  284. if len(srv) == 0 {
  285. return nil, nil
  286. }
  287. if len(srv) != len(ip) {
  288. return nil, fmt.Errorf("invalid reply for SRV query %s", svc)
  289. }
  290. resp := createRespMsg(query)
  291. for i, r := range srv {
  292. rr := new(dns.SRV)
  293. rr.Hdr = dns.RR_Header{Name: svc, Rrtype: dns.TypePTR, Class: dns.ClassINET, Ttl: respTTL}
  294. rr.Port = r.Port
  295. rr.Target = r.Target
  296. resp.Answer = append(resp.Answer, rr)
  297. rr1 := new(dns.A)
  298. rr1.Hdr = dns.RR_Header{Name: r.Target, Rrtype: dns.TypeA, Class: dns.ClassINET, Ttl: respTTL}
  299. rr1.A = ip[i]
  300. resp.Extra = append(resp.Extra, rr1)
  301. }
  302. return resp, nil
  303. }
  304. func (r *Resolver) serveDNS(w dns.ResponseWriter, query *dns.Msg) {
  305. var (
  306. resp *dns.Msg
  307. err error
  308. )
  309. if query == nil || len(query.Question) == 0 {
  310. return
  311. }
  312. queryName := query.Question[0].Name
  313. queryType := query.Question[0].Qtype
  314. switch queryType {
  315. case dns.TypeA:
  316. resp, err = r.handleIPQuery(query, types.IPv4)
  317. case dns.TypeAAAA:
  318. resp, err = r.handleIPQuery(query, types.IPv6)
  319. case dns.TypeMX:
  320. resp, err = r.handleMXQuery(query)
  321. case dns.TypePTR:
  322. resp, err = r.handlePTRQuery(query)
  323. case dns.TypeSRV:
  324. resp, err = r.handleSRVQuery(query)
  325. default:
  326. r.log().Debugf("[resolver] query type %s is not supported by the embedded DNS and will be forwarded to external DNS", dns.TypeToString[queryType])
  327. }
  328. reply := func(msg *dns.Msg) {
  329. if err = w.WriteMsg(msg); err != nil {
  330. r.log().WithError(err).Errorf("[resolver] failed to write response")
  331. }
  332. }
  333. if err != nil {
  334. r.log().WithError(err).Errorf("[resolver] failed to handle query: %s (%s)", queryName, dns.TypeToString[queryType])
  335. reply(new(dns.Msg).SetRcode(query, dns.RcodeServerFailure))
  336. return
  337. }
  338. if resp != nil {
  339. // We are the authoritative DNS server for this request so it's
  340. // on us to truncate the response message to the size limit
  341. // negotiated by the client.
  342. maxSize := dns.MinMsgSize
  343. if w.LocalAddr().Network() == "tcp" {
  344. maxSize = dns.MaxMsgSize
  345. } else {
  346. if optRR := query.IsEdns0(); optRR != nil {
  347. if udpsize := int(optRR.UDPSize()); udpsize > maxSize {
  348. maxSize = udpsize
  349. }
  350. }
  351. }
  352. resp.Truncate(maxSize)
  353. reply(resp)
  354. return
  355. }
  356. if r.proxyDNS {
  357. // If the user sets ndots > 0 explicitly and the query is
  358. // in the root domain don't forward it out. We will return
  359. // failure and let the client retry with the search domain
  360. // attached.
  361. if (queryType == dns.TypeA || queryType == dns.TypeAAAA) && r.backend.NdotsSet() &&
  362. !strings.Contains(strings.TrimSuffix(queryName, "."), ".") {
  363. resp = createRespMsg(query)
  364. } else {
  365. resp = r.forwardExtDNS(w.LocalAddr().Network(), query)
  366. }
  367. }
  368. if resp == nil {
  369. // We were unable to get an answer from any of the upstream DNS
  370. // servers or the backend doesn't support proxying DNS requests.
  371. resp = new(dns.Msg).SetRcode(query, dns.RcodeServerFailure)
  372. }
  373. reply(resp)
  374. }
  375. func (r *Resolver) dialExtDNS(proto string, server extDNSEntry) (net.Conn, error) {
  376. var (
  377. extConn net.Conn
  378. dialErr error
  379. )
  380. extConnect := func() {
  381. if server.port == 0 {
  382. server.port = 53
  383. }
  384. addr := fmt.Sprintf("%s:%d", server.IPStr, server.port)
  385. extConn, dialErr = net.DialTimeout(proto, addr, extIOTimeout)
  386. }
  387. if server.HostLoopback {
  388. extConnect()
  389. } else {
  390. execErr := r.backend.ExecFunc(extConnect)
  391. if execErr != nil {
  392. return nil, execErr
  393. }
  394. }
  395. if dialErr != nil {
  396. return nil, dialErr
  397. }
  398. return extConn, nil
  399. }
  400. func (r *Resolver) forwardExtDNS(proto string, query *dns.Msg) *dns.Msg {
  401. for _, extDNS := range r.extDNSList {
  402. if extDNS.IPStr == "" {
  403. break
  404. }
  405. // limits the number of outstanding concurrent queries.
  406. ctx, cancel := context.WithTimeout(context.Background(), extIOTimeout)
  407. err := r.fwdSem.Acquire(ctx, 1)
  408. cancel()
  409. if err != nil {
  410. r.logInverval.Do(func() {
  411. r.log().Errorf("[resolver] more than %v concurrent queries", maxConcurrent)
  412. })
  413. return new(dns.Msg).SetRcode(query, dns.RcodeRefused)
  414. }
  415. resp := func() *dns.Msg {
  416. defer r.fwdSem.Release(1)
  417. return r.exchange(proto, extDNS, query)
  418. }()
  419. if resp == nil {
  420. continue
  421. }
  422. switch resp.Rcode {
  423. case dns.RcodeServerFailure, dns.RcodeRefused:
  424. // Server returned FAILURE: continue with the next external DNS server
  425. // Server returned REFUSED: this can be a transitional status, so continue with the next external DNS server
  426. r.log().Debugf("[resolver] external DNS %s:%s returned failure:\n%s", proto, extDNS.IPStr, resp)
  427. continue
  428. }
  429. answers := 0
  430. for _, rr := range resp.Answer {
  431. h := rr.Header()
  432. switch h.Rrtype {
  433. case dns.TypeA:
  434. answers++
  435. ip := rr.(*dns.A).A
  436. r.log().Debugf("[resolver] received A record %q for %q from %s:%s", ip, h.Name, proto, extDNS.IPStr)
  437. r.backend.HandleQueryResp(h.Name, ip)
  438. case dns.TypeAAAA:
  439. answers++
  440. ip := rr.(*dns.AAAA).AAAA
  441. r.log().Debugf("[resolver] received AAAA record %q for %q from %s:%s", ip, h.Name, proto, extDNS.IPStr)
  442. r.backend.HandleQueryResp(h.Name, ip)
  443. }
  444. }
  445. if len(resp.Answer) == 0 {
  446. r.log().Debugf("[resolver] external DNS %s:%s returned response with no answers:\n%s", proto, extDNS.IPStr, resp)
  447. }
  448. resp.Compress = true
  449. return resp
  450. }
  451. return nil
  452. }
  453. func (r *Resolver) exchange(proto string, extDNS extDNSEntry, query *dns.Msg) *dns.Msg {
  454. extConn, err := r.dialExtDNS(proto, extDNS)
  455. if err != nil {
  456. r.log().WithError(err).Warn("[resolver] connect failed")
  457. return nil
  458. }
  459. defer extConn.Close()
  460. log := r.log().WithFields(logrus.Fields{
  461. "dns-server": extConn.RemoteAddr().Network() + ":" + extConn.RemoteAddr().String(),
  462. "client-addr": extConn.LocalAddr().Network() + ":" + extConn.LocalAddr().String(),
  463. "question": query.Question[0].String(),
  464. })
  465. log.Debug("[resolver] forwarding query")
  466. resp, _, err := (&dns.Client{
  467. Timeout: extIOTimeout,
  468. // Following the robustness principle, make a best-effort
  469. // attempt to receive oversized response messages without
  470. // truncating them on our end to forward verbatim to the client.
  471. // Some DNS servers (e.g. Mikrotik RouterOS) don't support
  472. // EDNS(0) and may send replies over UDP longer than 512 bytes
  473. // regardless of what size limit, if any, was advertized in the
  474. // query message. Note that ExchangeWithConn will override this
  475. // value if it detects an EDNS OPT record in query so only
  476. // oversized replies to non-EDNS queries will benefit.
  477. UDPSize: dns.MaxMsgSize,
  478. }).ExchangeWithConn(query, &dns.Conn{Conn: extConn})
  479. if err != nil {
  480. r.log().WithError(err).Errorf("[resolver] failed to query DNS server: %s, query: %s", extConn.RemoteAddr().String(), query.Question[0].String())
  481. return nil
  482. }
  483. if resp == nil {
  484. // Should be impossible, so make noise if it happens anyway.
  485. log.Error("[resolver] external DNS returned empty response")
  486. }
  487. return resp
  488. }