authorization_test.go 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230
  1. // Copyright (c) Tailscale Inc & AUTHORS
  2. // SPDX-License-Identifier: BSD-3-Clause
  3. package tsconsensus
  4. import (
  5. "context"
  6. "fmt"
  7. "net/netip"
  8. "testing"
  9. "tailscale.com/ipn"
  10. "tailscale.com/ipn/ipnstate"
  11. "tailscale.com/tailcfg"
  12. "tailscale.com/types/key"
  13. "tailscale.com/types/views"
  14. )
  15. type testStatusGetter struct {
  16. status *ipnstate.Status
  17. }
  18. func (sg testStatusGetter) getStatus(ctx context.Context) (*ipnstate.Status, error) {
  19. return sg.status, nil
  20. }
  21. const testTag string = "tag:clusterTag"
  22. func makeAuthTestPeer(i int, tags views.Slice[string]) *ipnstate.PeerStatus {
  23. return &ipnstate.PeerStatus{
  24. ID: tailcfg.StableNodeID(fmt.Sprintf("%d", i)),
  25. Tags: &tags,
  26. TailscaleIPs: []netip.Addr{
  27. netip.AddrFrom4([4]byte{100, 0, 0, byte(i)}),
  28. netip.MustParseAddr(fmt.Sprintf("fd7a:115c:a1e0:0::%d", i)),
  29. },
  30. }
  31. }
  32. func makeAuthTestPeers(tags [][]string) []*ipnstate.PeerStatus {
  33. peers := make([]*ipnstate.PeerStatus, len(tags))
  34. for i, ts := range tags {
  35. peers[i] = makeAuthTestPeer(i, views.SliceOf(ts))
  36. }
  37. return peers
  38. }
  39. func authForStatus(s *ipnstate.Status) *authorization {
  40. return &authorization{
  41. sg: testStatusGetter{
  42. status: s,
  43. },
  44. tag: testTag,
  45. }
  46. }
  47. func authForPeers(self *ipnstate.PeerStatus, peers []*ipnstate.PeerStatus) *authorization {
  48. s := &ipnstate.Status{
  49. BackendState: ipn.Running.String(),
  50. Self: self,
  51. Peer: map[key.NodePublic]*ipnstate.PeerStatus{},
  52. }
  53. for _, p := range peers {
  54. s.Peer[key.NewNode().Public()] = p
  55. }
  56. return authForStatus(s)
  57. }
  58. func TestAuthRefreshErrorsNotRunning(t *testing.T) {
  59. tests := []struct {
  60. in *ipnstate.Status
  61. expected string
  62. }{
  63. {
  64. in: nil,
  65. expected: "no status",
  66. },
  67. {
  68. in: &ipnstate.Status{
  69. BackendState: "NeedsMachineAuth",
  70. },
  71. expected: "ts Server is not running",
  72. },
  73. }
  74. for _, tt := range tests {
  75. t.Run(tt.expected, func(t *testing.T) {
  76. ctx := t.Context()
  77. a := authForStatus(tt.in)
  78. err := a.Refresh(ctx)
  79. if err == nil {
  80. t.Fatalf("expected err to be non-nil")
  81. }
  82. if err.Error() != tt.expected {
  83. t.Fatalf("expected: %s, got: %s", tt.expected, err.Error())
  84. }
  85. })
  86. }
  87. }
  88. func TestAuthUnrefreshed(t *testing.T) {
  89. a := authForStatus(nil)
  90. if a.AllowsHost(netip.MustParseAddr("100.0.0.1")) {
  91. t.Fatalf("never refreshed authorization, allowsHost: expected false, got true")
  92. }
  93. gotAllowedPeers := a.AllowedPeers()
  94. if gotAllowedPeers.Len() != 0 {
  95. t.Fatalf("never refreshed authorization, allowedPeers: expected [], got %v", gotAllowedPeers)
  96. }
  97. if a.SelfAllowed() != false {
  98. t.Fatalf("never refreshed authorization, selfAllowed: expected false got true")
  99. }
  100. }
  101. func TestAuthAllowsHost(t *testing.T) {
  102. peerTags := [][]string{
  103. {"woo"},
  104. nil,
  105. {"woo", testTag},
  106. {testTag},
  107. }
  108. peers := makeAuthTestPeers(peerTags)
  109. tests := []struct {
  110. name string
  111. peerStatus *ipnstate.PeerStatus
  112. expected bool
  113. }{
  114. {
  115. name: "tagged with different tag",
  116. peerStatus: peers[0],
  117. expected: false,
  118. },
  119. {
  120. name: "not tagged",
  121. peerStatus: peers[1],
  122. expected: false,
  123. },
  124. {
  125. name: "tags includes testTag",
  126. peerStatus: peers[2],
  127. expected: true,
  128. },
  129. {
  130. name: "only tag is testTag",
  131. peerStatus: peers[3],
  132. expected: true,
  133. },
  134. }
  135. a := authForPeers(nil, peers)
  136. err := a.Refresh(t.Context())
  137. if err != nil {
  138. t.Fatal(err)
  139. }
  140. for _, tt := range tests {
  141. t.Run(tt.name, func(t *testing.T) {
  142. // test we get the expected result for any of the peers TailscaleIPs
  143. for _, addr := range tt.peerStatus.TailscaleIPs {
  144. got := a.AllowsHost(addr)
  145. if got != tt.expected {
  146. t.Fatalf("allowed for peer with tags: %v, expected: %t, got %t", tt.peerStatus.Tags, tt.expected, got)
  147. }
  148. }
  149. })
  150. }
  151. }
  152. func TestAuthAllowedPeers(t *testing.T) {
  153. ctx := t.Context()
  154. peerTags := [][]string{
  155. {"woo"},
  156. nil,
  157. {"woo", testTag},
  158. {testTag},
  159. }
  160. peers := makeAuthTestPeers(peerTags)
  161. a := authForPeers(nil, peers)
  162. err := a.Refresh(ctx)
  163. if err != nil {
  164. t.Fatal(err)
  165. }
  166. ps := a.AllowedPeers()
  167. if ps.Len() != 2 {
  168. t.Fatalf("expected: 2, got: %d", ps.Len())
  169. }
  170. for _, i := range []int{2, 3} {
  171. if !ps.ContainsFunc(func(p *ipnstate.PeerStatus) bool {
  172. return p.ID == peers[i].ID
  173. }) {
  174. t.Fatalf("expected peers[%d] to be in AllowedPeers because it is tagged with testTag", i)
  175. }
  176. }
  177. }
  178. func TestAuthSelfAllowed(t *testing.T) {
  179. tests := []struct {
  180. name string
  181. in []string
  182. expected bool
  183. }{
  184. {
  185. name: "self has different tag",
  186. in: []string{"woo"},
  187. expected: false,
  188. },
  189. {
  190. name: "selfs tags include testTag",
  191. in: []string{"woo", testTag},
  192. expected: true,
  193. },
  194. }
  195. for _, tt := range tests {
  196. t.Run(tt.name, func(t *testing.T) {
  197. ctx := t.Context()
  198. self := makeAuthTestPeer(0, views.SliceOf(tt.in))
  199. a := authForPeers(self, nil)
  200. err := a.Refresh(ctx)
  201. if err != nil {
  202. t.Fatal(err)
  203. }
  204. got := a.SelfAllowed()
  205. if got != tt.expected {
  206. t.Fatalf("expected: %t, got: %t", tt.expected, got)
  207. }
  208. })
  209. }
  210. }