dns_server.go 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486
  1. package nebula
  2. import (
  3. "context"
  4. "fmt"
  5. "log/slog"
  6. "net"
  7. "net/netip"
  8. "strconv"
  9. "strings"
  10. "sync"
  11. "sync/atomic"
  12. "github.com/miekg/dns"
  13. "github.com/slackhq/nebula/config"
  14. )
  15. type dnsServer struct {
  16. sync.RWMutex
  17. l *slog.Logger
  18. ctx context.Context
  19. dnsMap4 map[string]netip.Addr
  20. dnsMap6 map[string]netip.Addr
  21. hostMap *HostMap
  22. pki *PKI
  23. // selfHost is the cached FQDN we last seeded for ourselves
  24. selfHost string
  25. mux *dns.ServeMux
  26. // enabled mirrors `lighthouse.serve_dns && lighthouse.am_lighthouse`.
  27. // Start, Add, and reload consult it so callers don't need to know the
  28. // gating rules. When it toggles off via reload, accumulated records are
  29. // cleared so a later re-enable starts with a fresh map populated from
  30. // new handshakes.
  31. enabled atomic.Bool
  32. serverMu sync.Mutex
  33. server *dns.Server
  34. // started is closed once `server` is serving (or after serve returns
  35. // without ever starting). Stop waits on it before
  36. // calling Shutdown to avoid the miekg/dns "server not started" race
  37. // where a Shutdown that arrives before bind completes is silently
  38. // ignored, leaving the listener running forever.
  39. started chan struct{}
  40. addr string
  41. }
  42. // newDnsServerFromConfig builds a dnsServer, applies the initial config, and
  43. // registers a reload callback. The reload callback is registered before the
  44. // initial config is applied, so a SIGHUP can later enable, fix, or disable
  45. // DNS even if the initial application failed.
  46. //
  47. // The dnsServer internally gates on `lighthouse.serve_dns &&
  48. // lighthouse.am_lighthouse`. Start and Add are safe to call unconditionally,
  49. // they no-op when DNS isn't enabled. Each Start invocation owns a ctx-cancel
  50. // watcher that tears the listener down on nebula shutdown. The returned
  51. // pointer is always non-nil, even on error.
  52. func newDnsServerFromConfig(ctx context.Context, l *slog.Logger, pki *PKI, hostMap *HostMap, c *config.C) (*dnsServer, error) {
  53. ds := &dnsServer{
  54. l: l,
  55. ctx: ctx,
  56. dnsMap4: make(map[string]netip.Addr),
  57. dnsMap6: make(map[string]netip.Addr),
  58. hostMap: hostMap,
  59. pki: pki,
  60. }
  61. ds.mux = dns.NewServeMux()
  62. ds.mux.HandleFunc(".", ds.handleDnsRequest)
  63. c.RegisterReloadCallback(func(c *config.C, r *config.Reload) {
  64. if err := ds.reload(c, false); err != nil {
  65. ds.l.Error("Failed to reload DNS responder from config", "error", err)
  66. r.Failed()
  67. }
  68. })
  69. if err := ds.reload(c, true); err != nil {
  70. return ds, err
  71. }
  72. ds.seedSelf()
  73. return ds, nil
  74. }
  75. // reload applies the latest config and reconciles the running state with it:
  76. // - enabled toggled on -> bind, then serve in the background
  77. // - enabled toggled off -> stop the runner
  78. // - listen address changed (while running) -> restart on the new address
  79. // - everything else -> no-op
  80. //
  81. // On the initial call it only records configuration; Control.Start is what
  82. // launches the first runner via dnsStart.
  83. func (d *dnsServer) reload(c *config.C, initial bool) error {
  84. wantsDns := c.GetBool("lighthouse.serve_dns", false)
  85. amLighthouse := c.GetBool("lighthouse.am_lighthouse", false)
  86. enabled := wantsDns && amLighthouse
  87. newAddr := getDnsServerAddr(c)
  88. d.serverMu.Lock()
  89. running := d.server != nil
  90. sameAddr := d.addr == newAddr
  91. d.addr = newAddr
  92. d.enabled.Store(enabled)
  93. d.serverMu.Unlock()
  94. if initial {
  95. if wantsDns && !amLighthouse {
  96. d.l.Warn("DNS server refusing to run because this host is not a lighthouse.")
  97. }
  98. return nil
  99. }
  100. if !enabled {
  101. if running {
  102. d.Stop()
  103. }
  104. // Drop any records that accumulated while enabled; a later re-enable
  105. // will repopulate from fresh handshakes and a fresh seedSelf.
  106. d.clearRecords()
  107. return nil
  108. }
  109. if running && !sameAddr {
  110. // Stop clears the slot before shutting down, otherwise the listen below can find the dying server and refuse
  111. d.Stop()
  112. running = false
  113. }
  114. if !running {
  115. // Bind here rather than in the background, so a reload that can't listen says so
  116. server, started, err := d.listen()
  117. if err != nil {
  118. return err
  119. }
  120. if server != nil {
  121. go d.serve(server, started)
  122. }
  123. }
  124. // Refresh the self entry every enabled reload so cert renewals that change our name or VPN addresses are picked up.
  125. d.seedSelf()
  126. return nil
  127. }
  128. // shutdownServer waits for the server to finish binding (so Shutdown actually
  129. // stops it rather than no-oping) and then shuts it down.
  130. func (d *dnsServer) shutdownServer(srv *dns.Server, started chan struct{}, reason string) {
  131. if srv == nil {
  132. return
  133. }
  134. if started != nil {
  135. <-started
  136. }
  137. if err := srv.Shutdown(); err != nil {
  138. d.l.Warn("Failed to shut down the DNS responder", "reason", reason, "error", err)
  139. }
  140. }
  141. // Start binds and serves the DNS responder. Blocks until Stop is called or
  142. // the listener errors. Safe to call when DNS is disabled (returns
  143. // immediately). This is what Control.dnsStart points at.
  144. //
  145. // Must be invoked after the tun device is active so that lighthouse.dns.host
  146. // may bind to a nebula IP.
  147. func (d *dnsServer) Start() {
  148. server, started, err := d.listen()
  149. if err != nil {
  150. d.l.Warn("Failed to run the DNS responder", "error", err)
  151. return
  152. }
  153. if server != nil {
  154. d.serve(server, started)
  155. }
  156. }
  157. // listen binds the responder and claims the server slot for serve. It returns a nil server, and no error, when DNS is
  158. // disabled or a server is already running.
  159. func (d *dnsServer) listen() (*dns.Server, chan struct{}, error) {
  160. if !d.enabled.Load() {
  161. return nil, nil, nil
  162. }
  163. started := make(chan struct{})
  164. d.serverMu.Lock()
  165. defer d.serverMu.Unlock()
  166. // Re-check enabled under the lock, a disable that raced our check above snapshots the slot under it too.
  167. // A reload and Control.Start can both get here, the loser would orphan the live listener past Stop
  168. if d.ctx.Err() != nil || d.server != nil || !d.enabled.Load() {
  169. return nil, nil, nil
  170. }
  171. pc, err := net.ListenPacket("udp", d.addr)
  172. if err != nil {
  173. return nil, nil, err
  174. }
  175. server := &dns.Server{
  176. Addr: d.addr,
  177. PacketConn: pc,
  178. Handler: d.mux,
  179. NotifyStartedFunc: func() { close(started) },
  180. }
  181. d.server = server
  182. d.started = started
  183. d.l.Info("Starting DNS responder", "dnsListener", pc.LocalAddr())
  184. return server, started, nil
  185. }
  186. // serve answers queries on a server from listen until Stop or the context ends it.
  187. func (d *dnsServer) serve(server *dns.Server, started chan struct{}) {
  188. // Per-invocation ctx watcher. Exits when serve does, so we don't leak a
  189. // watcher per reload-driven restart.
  190. done := make(chan struct{})
  191. go func() {
  192. select {
  193. case <-d.ctx.Done():
  194. d.shutdownServer(server, started, "shutdown")
  195. case <-done:
  196. }
  197. }()
  198. err := server.ActivateAndServe()
  199. close(done)
  200. // If serving failed before it started NotifyStartedFunc never fires, so close started here to release any Stop
  201. // caller waiting on it. ActivateAndServe doesn't close the socket on that path either
  202. select {
  203. case <-started:
  204. default:
  205. close(started)
  206. _ = server.PacketConn.Close()
  207. }
  208. // Release our slot, unless a reload already replaced us, so a dead listener can't block a future Start
  209. d.serverMu.Lock()
  210. if d.server == server {
  211. d.server = nil
  212. d.started = nil
  213. }
  214. d.serverMu.Unlock()
  215. if err != nil {
  216. d.l.Warn("Failed to run the DNS responder", "error", err)
  217. }
  218. }
  219. // Stop shuts down the active server, if any. Idempotent.
  220. func (d *dnsServer) Stop() {
  221. d.serverMu.Lock()
  222. srv := d.server
  223. started := d.started
  224. d.server = nil
  225. d.started = nil
  226. d.serverMu.Unlock()
  227. d.shutdownServer(srv, started, "stop")
  228. }
  229. // Query returns the address for the given name and query type. The second
  230. // return value reports whether the name is known at all (in either A or AAAA),
  231. // which lets callers distinguish NODATA from NXDOMAIN.
  232. func (d *dnsServer) Query(q uint16, data string) (netip.Addr, bool) {
  233. data = strings.ToLower(data)
  234. d.RLock()
  235. defer d.RUnlock()
  236. addr4, haveV4 := d.dnsMap4[data]
  237. addr6, haveV6 := d.dnsMap6[data]
  238. nameExists := haveV4 || haveV6
  239. switch q {
  240. case dns.TypeA:
  241. if haveV4 {
  242. return addr4, nameExists
  243. }
  244. case dns.TypeAAAA:
  245. if haveV6 {
  246. return addr6, nameExists
  247. }
  248. }
  249. return netip.Addr{}, nameExists
  250. }
  251. func (d *dnsServer) QueryCert(data string) string {
  252. if len(data) < 2 {
  253. return ""
  254. }
  255. ip, err := netip.ParseAddr(data[:len(data)-1])
  256. if err != nil {
  257. return ""
  258. }
  259. // The hostmap only ever contains peers we have handshaked with, so it never carries an entry for ourselves.
  260. // Answer self lookups straight from the local cert state.
  261. if cs := d.certState(); cs != nil && cs.myVpnAddrsTable != nil && cs.myVpnAddrsTable.Contains(ip) {
  262. c := cs.GetDefaultCertificate()
  263. if c == nil {
  264. return ""
  265. }
  266. b, err := c.MarshalJSON()
  267. if err != nil {
  268. return ""
  269. }
  270. return string(b)
  271. }
  272. hostinfo := d.hostMap.QueryVpnAddr(ip)
  273. if hostinfo == nil {
  274. return ""
  275. }
  276. q := hostinfo.GetCert()
  277. if q == nil {
  278. return ""
  279. }
  280. b, err := q.Certificate.MarshalJSON()
  281. if err != nil {
  282. return ""
  283. }
  284. return string(b)
  285. }
  286. // clearRecords drops all DNS records, including the self entry.
  287. func (d *dnsServer) clearRecords() {
  288. d.Lock()
  289. defer d.Unlock()
  290. clear(d.dnsMap4)
  291. clear(d.dnsMap6)
  292. d.selfHost = ""
  293. }
  294. // seedSelf inserts (or refreshes) a record for our own cert name pointing at our VPN addresses,
  295. // so a single-lighthouse network can resolve the lighthouse's own hostname without the two-process workaround.
  296. func (d *dnsServer) seedSelf() {
  297. if !d.enabled.Load() {
  298. return
  299. }
  300. cs := d.certState()
  301. if cs == nil {
  302. return
  303. }
  304. c := cs.GetDefaultCertificate()
  305. if c == nil {
  306. return
  307. }
  308. newHost := strings.ToLower(c.Name()) + "."
  309. d.Lock()
  310. defer d.Unlock()
  311. if d.selfHost != "" && d.selfHost != newHost {
  312. delete(d.dnsMap4, d.selfHost)
  313. delete(d.dnsMap6, d.selfHost)
  314. }
  315. d.selfHost = newHost
  316. delete(d.dnsMap4, newHost)
  317. delete(d.dnsMap6, newHost)
  318. haveV4, haveV6 := false, false
  319. for _, addr := range cs.myVpnAddrs {
  320. if addr.Is4() && !haveV4 {
  321. d.dnsMap4[newHost] = addr
  322. haveV4 = true
  323. } else if addr.Is6() && !haveV6 {
  324. d.dnsMap6[newHost] = addr
  325. haveV6 = true
  326. }
  327. if haveV4 && haveV6 {
  328. break
  329. }
  330. }
  331. }
  332. func (d *dnsServer) certState() *CertState {
  333. if d.pki == nil {
  334. return nil
  335. }
  336. return d.pki.getCertState()
  337. }
  338. // Add adds the first IPv4 and IPv6 address that appears in `addresses` as the record for `host`
  339. func (d *dnsServer) Add(host string, addresses []netip.Addr) {
  340. if !d.enabled.Load() {
  341. return
  342. }
  343. host = strings.ToLower(host)
  344. d.Lock()
  345. defer d.Unlock()
  346. haveV4 := false
  347. haveV6 := false
  348. for _, addr := range addresses {
  349. if addr.Is4() && !haveV4 {
  350. d.dnsMap4[host] = addr
  351. haveV4 = true
  352. } else if addr.Is6() && !haveV6 {
  353. d.dnsMap6[host] = addr
  354. haveV6 = true
  355. }
  356. if haveV4 && haveV6 {
  357. break
  358. }
  359. }
  360. }
  361. func (d *dnsServer) isSelfNebulaOrLocalhost(addr string) bool {
  362. a, _, _ := net.SplitHostPort(addr)
  363. b, err := netip.ParseAddr(a)
  364. if err != nil {
  365. return false
  366. }
  367. if b.IsLoopback() {
  368. return true
  369. }
  370. cs := d.certState()
  371. if cs == nil || cs.myVpnAddrsTable == nil {
  372. return false
  373. }
  374. //if we found it in this table, it's good
  375. return cs.myVpnAddrsTable.Contains(b)
  376. }
  377. func (d *dnsServer) parseQuery(m *dns.Msg, w dns.ResponseWriter) {
  378. debugEnabled := d.l.Enabled(context.Background(), slog.LevelDebug)
  379. // Per RFC 2308 §2.2, a name that exists but has no record of the requested
  380. // type must be answered with NOERROR and an empty answer section (NODATA),
  381. // not NXDOMAIN (RFC 2308 §2.1), which is reserved for names that do not
  382. // exist at all.
  383. anyNameExists := false
  384. for _, q := range m.Question {
  385. switch q.Qtype {
  386. case dns.TypeA, dns.TypeAAAA:
  387. qType := dns.TypeToString[q.Qtype]
  388. if debugEnabled {
  389. d.l.Debug("DNS query", "type", qType, "name", q.Name)
  390. }
  391. ip, nameExists := d.Query(q.Qtype, q.Name)
  392. if nameExists {
  393. anyNameExists = true
  394. }
  395. if ip.IsValid() {
  396. rr, err := dns.NewRR(fmt.Sprintf("%s %s %s", q.Name, qType, ip))
  397. if err == nil {
  398. m.Answer = append(m.Answer, rr)
  399. }
  400. }
  401. case dns.TypeTXT:
  402. // We only answer these queries from nebula nodes or localhost
  403. if !d.isSelfNebulaOrLocalhost(w.RemoteAddr().String()) {
  404. return
  405. }
  406. if debugEnabled {
  407. d.l.Debug("DNS query", "type", "TXT", "name", q.Name)
  408. }
  409. ip := d.QueryCert(q.Name)
  410. if ip != "" {
  411. rr, err := dns.NewRR(fmt.Sprintf("%s TXT %s", q.Name, ip))
  412. if err == nil {
  413. m.Answer = append(m.Answer, rr)
  414. }
  415. }
  416. }
  417. }
  418. if len(m.Answer) == 0 && !anyNameExists {
  419. m.Rcode = dns.RcodeNameError
  420. }
  421. }
  422. func (d *dnsServer) handleDnsRequest(w dns.ResponseWriter, r *dns.Msg) {
  423. m := new(dns.Msg)
  424. m.SetReply(r)
  425. m.Compress = false
  426. switch r.Opcode {
  427. case dns.OpcodeQuery:
  428. d.parseQuery(m, w)
  429. }
  430. w.WriteMsg(m)
  431. }
  432. func getDnsServerAddr(c *config.C) string {
  433. dnsHost := strings.TrimSpace(c.GetString("lighthouse.dns.host", ""))
  434. // Old guidance was to provide the literal `[::]` in `lighthouse.dns.host` but that won't resolve.
  435. if dnsHost == "[::]" {
  436. dnsHost = "::"
  437. }
  438. return net.JoinHostPort(dnsHost, strconv.Itoa(c.GetInt("lighthouse.dns.port", 53)))
  439. }