dns_server_test.go 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429
  1. package nebula
  2. import (
  3. "context"
  4. "log/slog"
  5. "net"
  6. "net/netip"
  7. "strconv"
  8. "testing"
  9. "time"
  10. "github.com/gaissmai/bart"
  11. "github.com/miekg/dns"
  12. "github.com/slackhq/nebula/cert"
  13. "github.com/slackhq/nebula/cert_test"
  14. "github.com/slackhq/nebula/config"
  15. "github.com/stretchr/testify/assert"
  16. "github.com/stretchr/testify/require"
  17. )
  18. type stubDNSWriter struct{}
  19. func (stubDNSWriter) LocalAddr() net.Addr { return &net.UDPAddr{} }
  20. func (stubDNSWriter) RemoteAddr() net.Addr {
  21. return &net.UDPAddr{IP: net.ParseIP("127.0.0.1"), Port: 5353}
  22. }
  23. func (stubDNSWriter) Write([]byte) (int, error) { return 0, nil }
  24. func (stubDNSWriter) WriteMsg(*dns.Msg) error { return nil }
  25. func (stubDNSWriter) Close() error { return nil }
  26. func (stubDNSWriter) TsigStatus() error { return nil }
  27. func (stubDNSWriter) TsigTimersOnly(bool) {}
  28. func (stubDNSWriter) Hijack() {}
  29. func TestParsequery(t *testing.T) {
  30. l := slog.New(slog.DiscardHandler)
  31. hostMap := &HostMap{}
  32. ds := &dnsServer{
  33. l: l,
  34. dnsMap4: make(map[string]netip.Addr),
  35. dnsMap6: make(map[string]netip.Addr),
  36. hostMap: hostMap,
  37. }
  38. ds.enabled.Store(true)
  39. addrs := []netip.Addr{
  40. netip.MustParseAddr("1.2.3.4"),
  41. netip.MustParseAddr("1.2.3.5"),
  42. netip.MustParseAddr("fd01::24"),
  43. netip.MustParseAddr("fd01::25"),
  44. }
  45. ds.Add("test.com.com", addrs)
  46. ds.Add("v4only.com.com", []netip.Addr{netip.MustParseAddr("1.2.3.6")})
  47. ds.Add("v6only.com.com", []netip.Addr{netip.MustParseAddr("fd01::26")})
  48. m := &dns.Msg{}
  49. m.SetQuestion("test.com.com", dns.TypeA)
  50. ds.parseQuery(m, nil)
  51. assert.NotNil(t, m.Answer)
  52. assert.Equal(t, "1.2.3.4", m.Answer[0].(*dns.A).A.String())
  53. assert.Equal(t, dns.RcodeSuccess, m.Rcode)
  54. m = &dns.Msg{}
  55. m.SetQuestion("test.com.com", dns.TypeAAAA)
  56. ds.parseQuery(m, nil)
  57. assert.NotNil(t, m.Answer)
  58. assert.Equal(t, "fd01::24", m.Answer[0].(*dns.AAAA).AAAA.String())
  59. assert.Equal(t, dns.RcodeSuccess, m.Rcode)
  60. // A known name with no record of the requested type should return NODATA
  61. // (NOERROR with empty answer), not NXDOMAIN.
  62. m = &dns.Msg{}
  63. m.SetQuestion("v4only.com.com", dns.TypeAAAA)
  64. ds.parseQuery(m, nil)
  65. assert.Empty(t, m.Answer)
  66. assert.Equal(t, dns.RcodeSuccess, m.Rcode)
  67. m = &dns.Msg{}
  68. m.SetQuestion("v6only.com.com", dns.TypeA)
  69. ds.parseQuery(m, nil)
  70. assert.Empty(t, m.Answer)
  71. assert.Equal(t, dns.RcodeSuccess, m.Rcode)
  72. // An unknown name should still return NXDOMAIN.
  73. m = &dns.Msg{}
  74. m.SetQuestion("unknown.com.com", dns.TypeA)
  75. ds.parseQuery(m, nil)
  76. assert.Empty(t, m.Answer)
  77. assert.Equal(t, dns.RcodeNameError, m.Rcode)
  78. // short lookups should not fail
  79. m = &dns.Msg{}
  80. m.Question = []dns.Question{{Name: "", Qtype: dns.TypeTXT, Qclass: dns.ClassINET}}
  81. ds.parseQuery(m, stubDNSWriter{})
  82. assert.Empty(t, m.Answer)
  83. assert.Equal(t, dns.RcodeNameError, m.Rcode)
  84. m = &dns.Msg{}
  85. m.Question = []dns.Question{{Name: ".", Qtype: dns.TypeTXT, Qclass: dns.ClassINET}}
  86. ds.parseQuery(m, stubDNSWriter{})
  87. assert.Empty(t, m.Answer)
  88. assert.Equal(t, dns.RcodeNameError, m.Rcode)
  89. }
  90. func Test_getDnsServerAddr(t *testing.T) {
  91. c := config.NewC(nil)
  92. c.Settings["lighthouse"] = map[string]any{
  93. "dns": map[string]any{
  94. "host": "0.0.0.0",
  95. "port": "1",
  96. },
  97. }
  98. assert.Equal(t, "0.0.0.0:1", getDnsServerAddr(c))
  99. c.Settings["lighthouse"] = map[string]any{
  100. "dns": map[string]any{
  101. "host": "::",
  102. "port": "1",
  103. },
  104. }
  105. assert.Equal(t, "[::]:1", getDnsServerAddr(c))
  106. c.Settings["lighthouse"] = map[string]any{
  107. "dns": map[string]any{
  108. "host": "[::]",
  109. "port": "1",
  110. },
  111. }
  112. assert.Equal(t, "[::]:1", getDnsServerAddr(c))
  113. // Make sure whitespace doesn't mess us up
  114. c.Settings["lighthouse"] = map[string]any{
  115. "dns": map[string]any{
  116. "host": "[::] ",
  117. "port": "1",
  118. },
  119. }
  120. assert.Equal(t, "[::]:1", getDnsServerAddr(c))
  121. }
  122. func newTestDnsServer(t *testing.T) (*dnsServer, *config.C) {
  123. t.Helper()
  124. sl := slog.New(slog.DiscardHandler)
  125. ds := &dnsServer{
  126. l: sl,
  127. ctx: context.Background(),
  128. dnsMap4: make(map[string]netip.Addr),
  129. dnsMap6: make(map[string]netip.Addr),
  130. hostMap: &HostMap{},
  131. }
  132. ds.mux = dns.NewServeMux()
  133. ds.mux.HandleFunc(".", ds.handleDnsRequest)
  134. return ds, config.NewC(nil)
  135. }
  136. func setDnsConfig(c *config.C, host string, port string, amLighthouse, serveDns bool) {
  137. c.Settings["lighthouse"] = map[string]any{
  138. "am_lighthouse": amLighthouse,
  139. "serve_dns": serveDns,
  140. "dns": map[string]any{
  141. "host": host,
  142. "port": port,
  143. },
  144. }
  145. }
  146. func TestDnsServer_reload_initial_disabled(t *testing.T) {
  147. ds, c := newTestDnsServer(t)
  148. setDnsConfig(c, "127.0.0.1", "0", true, false)
  149. require.NoError(t, ds.reload(c, true))
  150. assert.False(t, ds.enabled.Load())
  151. assert.Equal(t, "127.0.0.1:0", ds.addr)
  152. assert.Nil(t, ds.server)
  153. }
  154. func TestDnsServer_reload_initial_enabled(t *testing.T) {
  155. ds, c := newTestDnsServer(t)
  156. setDnsConfig(c, "127.0.0.1", "0", true, true)
  157. require.NoError(t, ds.reload(c, true))
  158. assert.True(t, ds.enabled.Load())
  159. assert.Equal(t, "127.0.0.1:0", ds.addr)
  160. // initial never starts a runner; that's Control.Start's job
  161. assert.Nil(t, ds.server)
  162. }
  163. func TestDnsServer_reload_initial_serveDnsWithoutLighthouse(t *testing.T) {
  164. ds, c := newTestDnsServer(t)
  165. setDnsConfig(c, "127.0.0.1", "0", false, true)
  166. require.NoError(t, ds.reload(c, true))
  167. // Wants DNS but isn't a lighthouse: gated off, no runner.
  168. assert.False(t, ds.enabled.Load())
  169. }
  170. func TestDnsServer_reload_sameAddr_noOp(t *testing.T) {
  171. ds, c := newTestDnsServer(t)
  172. setDnsConfig(c, "127.0.0.1", "0", true, true)
  173. require.NoError(t, ds.reload(c, true))
  174. // No server running yet, no addr change. Reload should not spawn anything.
  175. require.NoError(t, ds.reload(c, false))
  176. assert.True(t, ds.enabled.Load())
  177. assert.Nil(t, ds.server)
  178. }
  179. func TestDnsServer_StartStop_lifecycle(t *testing.T) {
  180. // Bind to a real (random) UDP port so we exercise the actual
  181. // ListenAndServe + Shutdown plumbing including the started-chan race fix.
  182. port := freeUDPPort(t)
  183. ds, c := newTestDnsServer(t)
  184. setDnsConfig(c, "127.0.0.1", port, true, true)
  185. require.NoError(t, ds.reload(c, true))
  186. done := make(chan struct{})
  187. go func() {
  188. ds.Start()
  189. close(done)
  190. }()
  191. waitFor(t, func() bool {
  192. ds.serverMu.Lock()
  193. started := ds.started
  194. ds.serverMu.Unlock()
  195. if started == nil {
  196. return false
  197. }
  198. select {
  199. case <-started:
  200. return true
  201. default:
  202. return false
  203. }
  204. })
  205. ds.Stop()
  206. select {
  207. case <-done:
  208. case <-time.After(5 * time.Second):
  209. t.Fatal("Start did not return after Stop")
  210. }
  211. }
  212. func TestDnsServer_Stop_beforeBind_doesNotHang(t *testing.T) {
  213. // Stop called immediately after Start should not deadlock even if bind
  214. // hasn't completed yet. This exercises the started-chan close-on-bind-fail
  215. // path: by binding to an obviously bad port (privileged) we get a fast
  216. // bind error before NotifyStartedFunc fires.
  217. ds, c := newTestDnsServer(t)
  218. // Use a port that should fail to bind (negative would be invalid, use a
  219. // host that won't resolve to ensure listenUDP fails quickly).
  220. setDnsConfig(c, "256.256.256.256", "53", true, true)
  221. require.NoError(t, ds.reload(c, true))
  222. done := make(chan struct{})
  223. go func() {
  224. ds.Start()
  225. close(done)
  226. }()
  227. // Give Start a moment to attempt the bind and fail.
  228. select {
  229. case <-done:
  230. // Bind failed and Start returned; Stop should be a no-op.
  231. case <-time.After(time.Second):
  232. t.Fatal("Start did not return after a bad bind")
  233. }
  234. stopped := make(chan struct{})
  235. go func() {
  236. ds.Stop()
  237. close(stopped)
  238. }()
  239. select {
  240. case <-stopped:
  241. case <-time.After(time.Second):
  242. t.Fatal("Stop hung after a failed bind")
  243. }
  244. }
  245. // newTestPKI builds a minimal *PKI with a single v1 cert whose name and
  246. // VPN addresses are caller-provided, suitable for exercising seedSelf and
  247. // QueryCert self handling.
  248. func newTestPKI(t *testing.T, name string, addrs []netip.Addr) *PKI {
  249. t.Helper()
  250. networks := make([]netip.Prefix, 0, len(addrs))
  251. for _, a := range addrs {
  252. bits := 32
  253. if a.Is6() {
  254. bits = 128
  255. }
  256. networks = append(networks, netip.PrefixFrom(a, bits))
  257. }
  258. ca, _, caKey, _ := cert_test.NewTestCaCert(cert.Version2, cert.Curve_CURVE25519, time.Time{}, time.Time{}, nil, nil, nil)
  259. c, _, _, _ := cert_test.NewTestCert(cert.Version2, cert.Curve_CURVE25519, ca, caKey, name, time.Time{}, time.Time{}, networks, nil, nil)
  260. addrsTable := new(bart.Lite)
  261. for _, a := range addrs {
  262. addrsTable.Insert(netip.PrefixFrom(a, a.BitLen()))
  263. }
  264. cs := &CertState{
  265. v2Cert: c,
  266. initiatingVersion: cert.Version2,
  267. myVpnAddrs: addrs,
  268. myVpnAddrsTable: addrsTable,
  269. }
  270. pki := &PKI{}
  271. pki.cs.Store(cs)
  272. return pki
  273. }
  274. func TestDnsServer_seedSelf_addsOwnRecord(t *testing.T) {
  275. ds, c := newTestDnsServer(t)
  276. myV4 := netip.MustParseAddr("10.0.0.1")
  277. myV6 := netip.MustParseAddr("fd00::1")
  278. ds.pki = newTestPKI(t, "lighthouse", []netip.Addr{myV4, myV6})
  279. setDnsConfig(c, "127.0.0.1", "0", true, true)
  280. require.NoError(t, ds.reload(c, true))
  281. ds.seedSelf()
  282. got4, exists := ds.Query(dns.TypeA, "lighthouse.")
  283. assert.True(t, exists)
  284. assert.Equal(t, myV4, got4)
  285. got6, exists := ds.Query(dns.TypeAAAA, "lighthouse.")
  286. assert.True(t, exists)
  287. assert.Equal(t, myV6, got6)
  288. }
  289. func TestDnsServer_seedSelf_disabled_noOp(t *testing.T) {
  290. ds, c := newTestDnsServer(t)
  291. ds.pki = newTestPKI(t, "lighthouse", []netip.Addr{netip.MustParseAddr("10.0.0.1")})
  292. setDnsConfig(c, "127.0.0.1", "0", true, false)
  293. require.NoError(t, ds.reload(c, true))
  294. ds.seedSelf()
  295. _, exists := ds.Query(dns.TypeA, "lighthouse.")
  296. assert.False(t, exists)
  297. }
  298. func TestDnsServer_clearRecords_dropsSelfHost(t *testing.T) {
  299. ds, c := newTestDnsServer(t)
  300. ds.pki = newTestPKI(t, "lighthouse", []netip.Addr{netip.MustParseAddr("10.0.0.1")})
  301. setDnsConfig(c, "127.0.0.1", "0", true, true)
  302. require.NoError(t, ds.reload(c, true))
  303. ds.seedSelf()
  304. require.NotEmpty(t, ds.selfHost)
  305. ds.clearRecords()
  306. assert.Empty(t, ds.selfHost)
  307. _, exists := ds.Query(dns.TypeA, "lighthouse.")
  308. assert.False(t, exists)
  309. }
  310. func TestDnsServer_QueryCert_returnsOwnCert(t *testing.T) {
  311. ds, _ := newTestDnsServer(t)
  312. myV4 := netip.MustParseAddr("10.0.0.1")
  313. ds.pki = newTestPKI(t, "lighthouse", []netip.Addr{myV4})
  314. got := ds.QueryCert(myV4.String() + ".")
  315. assert.NotEmpty(t, got, "TXT lookup of our own VPN address should return our cert")
  316. other := netip.MustParseAddr("10.0.0.99")
  317. assert.Empty(t, ds.QueryCert(other.String()+"."), "unknown peer IP should return nothing")
  318. }
  319. func TestDnsServer_reload_disable_stopsRunningServer(t *testing.T) {
  320. port := freeUDPPort(t)
  321. ds, c := newTestDnsServer(t)
  322. setDnsConfig(c, "127.0.0.1", port, true, true)
  323. require.NoError(t, ds.reload(c, true))
  324. startReturned := make(chan struct{})
  325. go func() {
  326. ds.Start()
  327. close(startReturned)
  328. }()
  329. waitForBind(t, ds)
  330. // Toggle serve_dns off; reload should shut the running server down.
  331. setDnsConfig(c, "127.0.0.1", port, true, false)
  332. require.NoError(t, ds.reload(c, false))
  333. select {
  334. case <-startReturned:
  335. case <-time.After(5 * time.Second):
  336. t.Fatal("Start did not return after reload disabled DNS")
  337. }
  338. assert.False(t, ds.enabled.Load())
  339. }
  340. func freeUDPPort(t *testing.T) string {
  341. t.Helper()
  342. conn, err := net.ListenPacket("udp", "127.0.0.1:0")
  343. require.NoError(t, err)
  344. port := conn.LocalAddr().(*net.UDPAddr).Port
  345. require.NoError(t, conn.Close())
  346. return strconv.Itoa(port)
  347. }
  348. func waitForBind(t *testing.T, ds *dnsServer) {
  349. t.Helper()
  350. waitFor(t, func() bool {
  351. ds.serverMu.Lock()
  352. started := ds.started
  353. ds.serverMu.Unlock()
  354. if started == nil {
  355. return false
  356. }
  357. select {
  358. case <-started:
  359. return true
  360. default:
  361. return false
  362. }
  363. })
  364. }
  365. func waitFor(t *testing.T, cond func() bool) {
  366. t.Helper()
  367. deadline := time.Now().Add(5 * time.Second)
  368. for time.Now().Before(deadline) {
  369. if cond() {
  370. return
  371. }
  372. time.Sleep(5 * time.Millisecond)
  373. }
  374. t.Fatal("timed out waiting for condition")
  375. }