interface_test.go 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140
  1. package nebula
  2. import (
  3. "net/netip"
  4. "testing"
  5. "github.com/slackhq/nebula/cert"
  6. "github.com/slackhq/nebula/config"
  7. "github.com/slackhq/nebula/test"
  8. "github.com/slackhq/nebula/util"
  9. "github.com/stretchr/testify/assert"
  10. "github.com/stretchr/testify/require"
  11. )
  12. // TestReloadFirewall_CertUnsafeNetworksChanged verifies that reloadFirewall
  13. // rebuilds the firewall when only the certificate's UnsafeNetworks have changed,
  14. // even if the firewall section of the YAML has not.
  15. func TestReloadFirewall_CertUnsafeNetworksChanged(t *testing.T) {
  16. l := test.NewLogger()
  17. vpnNet := netip.MustParsePrefix("10.0.0.1/24")
  18. initialUnsafe := []netip.Prefix{netip.MustParsePrefix("198.51.100.0/24")}
  19. // dummyCert avoids dragging the real signing pipeline into a unit test.
  20. c1 := &dummyCert{
  21. version: cert.Version2,
  22. networks: []netip.Prefix{vpnNet},
  23. unsafeNetworks: initialUnsafe,
  24. }
  25. pki := &PKI{}
  26. pki.cs.Store(&CertState{v2Cert: c1, initiatingVersion: cert.Version2})
  27. rawYAML := `firewall:
  28. outbound:
  29. - port: any
  30. proto: any
  31. host: any
  32. inbound:
  33. - port: any
  34. proto: any
  35. host: any
  36. `
  37. cfg := config.NewC(l)
  38. require.NoError(t, cfg.LoadString(rawYAML))
  39. fw, err := NewFirewallFromConfig(l, pki.getCertState(), cfg)
  40. require.NoError(t, err)
  41. require.Equal(t, initialUnsafe, fw.unsafeNetworks)
  42. f := &Interface{
  43. pki: pki,
  44. firewall: fw,
  45. l: l,
  46. }
  47. // Swap the cert with a different UnsafeNetworks set.
  48. newUnsafe := []netip.Prefix{
  49. netip.MustParsePrefix("198.51.100.0/24"),
  50. netip.MustParsePrefix("203.0.113.0/24"),
  51. }
  52. c2 := &dummyCert{
  53. version: cert.Version2,
  54. networks: []netip.Prefix{vpnNet},
  55. unsafeNetworks: newUnsafe,
  56. }
  57. pki.cs.Store(&CertState{v2Cert: c2, initiatingVersion: cert.Version2})
  58. // Reload with the same YAML so HasChanged("firewall") reports false.
  59. require.NoError(t, cfg.ReloadConfigString(rawYAML))
  60. require.False(t, cfg.HasChanged("firewall"))
  61. f.reloadFirewall(cfg)
  62. assert.NotSame(t, fw, f.firewall, "firewall pointer should have been replaced")
  63. assert.Equal(t, newUnsafe, f.firewall.unsafeNetworks)
  64. assert.True(t, f.firewall.routableNetworks.Contains(netip.MustParseAddr("203.0.113.5")))
  65. }
  66. // TestReloadFirewall_NoChange verifies that reloadFirewall is a no-op when
  67. // neither the firewall config nor the cert's UnsafeNetworks have changed.
  68. func TestReloadFirewall_NoChange(t *testing.T) {
  69. l := test.NewLogger()
  70. vpnNet := netip.MustParsePrefix("10.0.0.1/24")
  71. unsafe := []netip.Prefix{netip.MustParsePrefix("198.51.100.0/24")}
  72. c1 := &dummyCert{
  73. version: cert.Version2,
  74. networks: []netip.Prefix{vpnNet},
  75. unsafeNetworks: unsafe,
  76. }
  77. pki := &PKI{}
  78. pki.cs.Store(&CertState{v2Cert: c1, initiatingVersion: cert.Version2})
  79. rawYAML := `firewall:
  80. outbound:
  81. - port: any
  82. proto: any
  83. host: any
  84. inbound:
  85. - port: any
  86. proto: any
  87. host: any
  88. `
  89. cfg := config.NewC(l)
  90. require.NoError(t, cfg.LoadString(rawYAML))
  91. fw, err := NewFirewallFromConfig(l, pki.getCertState(), cfg)
  92. require.NoError(t, err)
  93. f := &Interface{
  94. pki: pki,
  95. firewall: fw,
  96. l: l,
  97. }
  98. require.NoError(t, cfg.ReloadConfigString(rawYAML))
  99. f.reloadFirewall(cfg)
  100. assert.Same(t, fw, f.firewall, "firewall should not have been replaced")
  101. }
  102. // TestPinThisThreadFallbackSkipsAvoided covers the single reader, which never goes through cpupick: its fallback
  103. // spread over the allowed set must still skip tun.avoid_cpus.
  104. func TestPinThisThreadFallbackSkipsAvoided(t *testing.T) {
  105. allowed, err := util.AllowedCPUs()
  106. require.NoError(t, err)
  107. if len(allowed) < 2 {
  108. t.Skip("requires at least two allowed CPUs")
  109. }
  110. f := &Interface{l: test.NewLogger(), pinThreads: true, avoidCPUs: []int{allowed[0]}}
  111. got := make(chan []int)
  112. go func() {
  113. // The pin leaves this goroutine locked to its thread, so the runtime discards the thread when it returns.
  114. f.pinThisThread(0)
  115. cpus, _ := util.AllowedCPUs()
  116. got <- cpus
  117. }()
  118. assert.Equal(t, []int{allowed[1]}, <-got)
  119. }