server_test.go 9.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329
  1. // Copyright (c) Tailscale Inc & AUTHORS
  2. // SPDX-License-Identifier: BSD-3-Clause
  3. package ipnserver_test
  4. import (
  5. "context"
  6. "errors"
  7. "runtime"
  8. "strconv"
  9. "sync"
  10. "testing"
  11. "tailscale.com/client/local"
  12. "tailscale.com/envknob"
  13. "tailscale.com/ipn"
  14. "tailscale.com/ipn/lapitest"
  15. "tailscale.com/tsd"
  16. "tailscale.com/types/ptr"
  17. "tailscale.com/util/syspolicy/pkey"
  18. "tailscale.com/util/syspolicy/policytest"
  19. )
  20. func TestUserConnectDisconnectNonWindows(t *testing.T) {
  21. enableLogging := false
  22. if runtime.GOOS == "windows" {
  23. setGOOSForTest(t, "linux")
  24. }
  25. ctx := context.Background()
  26. server := lapitest.NewServer(t, lapitest.WithLogging(enableLogging))
  27. // UserA connects and starts watching the IPN bus.
  28. clientA := server.ClientWithName("UserA")
  29. watcherA, _ := clientA.WatchIPNBus(ctx, 0)
  30. // The concept of "current user" is only relevant on Windows
  31. // and it should not be set on non-Windows platforms.
  32. server.CheckCurrentUser(nil)
  33. // Additionally, a different user should be able to connect and use the LocalAPI.
  34. clientB := server.ClientWithName("UserB")
  35. if _, gotErr := clientB.Status(ctx); gotErr != nil {
  36. t.Fatalf("Status(%q): want nil; got %v", clientB.Username(), gotErr)
  37. }
  38. // Watching the IPN bus should also work for UserB.
  39. watcherB, _ := clientB.WatchIPNBus(ctx, 0)
  40. // And if we send a notification, both users should receive it.
  41. wantErrMessage := "test error"
  42. testNotify := ipn.Notify{ErrMessage: ptr.To(wantErrMessage)}
  43. server.Backend().DebugNotify(testNotify)
  44. if n, err := watcherA.Next(); err != nil {
  45. t.Fatalf("IPNBusWatcher.Next(%q): %v", clientA.Username(), err)
  46. } else if gotErrMessage := n.ErrMessage; gotErrMessage == nil || *gotErrMessage != wantErrMessage {
  47. t.Fatalf("IPNBusWatcher.Next(%q): want %v; got %v", clientA.Username(), wantErrMessage, gotErrMessage)
  48. }
  49. if n, err := watcherB.Next(); err != nil {
  50. t.Fatalf("IPNBusWatcher.Next(%q): %v", clientB.Username(), err)
  51. } else if gotErrMessage := n.ErrMessage; gotErrMessage == nil || *gotErrMessage != wantErrMessage {
  52. t.Fatalf("IPNBusWatcher.Next(%q): want %v; got %v", clientB.Username(), wantErrMessage, gotErrMessage)
  53. }
  54. }
  55. func TestUserConnectDisconnectOnWindows(t *testing.T) {
  56. enableLogging := false
  57. setGOOSForTest(t, "windows")
  58. ctx := context.Background()
  59. server := lapitest.NewServer(t, lapitest.WithLogging(enableLogging))
  60. client := server.ClientWithName("User")
  61. _, cancelWatcher := client.WatchIPNBus(ctx, 0)
  62. // On Windows, however, the current user should be set to the user that connected.
  63. server.CheckCurrentUser(client.Actor)
  64. // Cancel the IPN bus watcher request and wait for the server to unblock.
  65. cancelWatcher()
  66. server.BlockWhileInUse(ctx)
  67. // The current user should not be set after a disconnect, as no one is
  68. // currently using the server.
  69. server.CheckCurrentUser(nil)
  70. }
  71. func TestIPNAlreadyInUseOnWindows(t *testing.T) {
  72. enableLogging := false
  73. setGOOSForTest(t, "windows")
  74. ctx := context.Background()
  75. server := lapitest.NewServer(t, lapitest.WithLogging(enableLogging))
  76. // UserA connects and starts watching the IPN bus.
  77. clientA := server.ClientWithName("UserA")
  78. clientA.WatchIPNBus(ctx, 0)
  79. // While UserA is connected, UserB should not be able to connect.
  80. clientB := server.ClientWithName("UserB")
  81. if _, gotErr := clientB.Status(ctx); gotErr == nil {
  82. t.Fatalf("Status(%q): want error; got nil", clientB.Username())
  83. } else if wantError := "401 Unauthorized: Tailscale already in use by UserA"; gotErr.Error() != wantError {
  84. t.Fatalf("Status(%q): want %q; got %q", clientB.Username(), wantError, gotErr.Error())
  85. }
  86. // Current user should still be UserA.
  87. server.CheckCurrentUser(clientA.Actor)
  88. }
  89. func TestSequentialOSUserSwitchingOnWindows(t *testing.T) {
  90. enableLogging := false
  91. setGOOSForTest(t, "windows")
  92. ctx := context.Background()
  93. server := lapitest.NewServer(t, lapitest.WithLogging(enableLogging))
  94. connectDisconnectAsUser := func(name string) {
  95. // User connects and starts watching the IPN bus.
  96. client := server.ClientWithName(name)
  97. watcher, cancelWatcher := client.WatchIPNBus(ctx, 0)
  98. defer cancelWatcher()
  99. go pumpIPNBus(watcher)
  100. // It should be the current user from the LocalBackend's perspective...
  101. server.CheckCurrentUser(client.Actor)
  102. // until it disconnects.
  103. cancelWatcher()
  104. server.BlockWhileInUse(ctx)
  105. // Now, the current user should be unset.
  106. server.CheckCurrentUser(nil)
  107. }
  108. // UserA logs in, uses Tailscale for a bit, then logs out.
  109. connectDisconnectAsUser("UserA")
  110. // Same for UserB.
  111. connectDisconnectAsUser("UserB")
  112. }
  113. func TestConcurrentOSUserSwitchingOnWindows(t *testing.T) {
  114. enableLogging := false
  115. setGOOSForTest(t, "windows")
  116. ctx := context.Background()
  117. server := lapitest.NewServer(t, lapitest.WithLogging(enableLogging))
  118. connectDisconnectAsUser := func(name string) {
  119. // User connects and starts watching the IPN bus.
  120. client := server.ClientWithName(name)
  121. watcher, cancelWatcher := client.WatchIPNBus(ctx, ipn.NotifyInitialState)
  122. defer cancelWatcher()
  123. runtime.Gosched()
  124. // Get the current user from the LocalBackend's perspective
  125. // as soon as we're connected.
  126. gotUID, gotActor := server.Backend().CurrentUserForTest()
  127. // Wait for the first notification to arrive.
  128. // It will either be the initial state we've requested via [ipn.NotifyInitialState],
  129. // returned by an actual handler, or a "fake" notification sent by the server
  130. // itself to indicate that it is being used by someone else.
  131. n, err := watcher.Next()
  132. if err != nil {
  133. t.Fatal(err)
  134. }
  135. // If our user lost the race and the IPN is in use by another user,
  136. // we should just return. For the sake of this test, we're not
  137. // interested in waiting for the server to become idle.
  138. if n.State != nil && *n.State == ipn.InUseOtherUser {
  139. return
  140. }
  141. // Otherwise, our user should have been the current user since the time we connected.
  142. if gotUID != client.Actor.UserID() {
  143. t.Errorf("CurrentUser(Initial): got UID %q; want %q", gotUID, client.Actor.UserID())
  144. return
  145. }
  146. if hasActor := gotActor != nil; !hasActor || gotActor != client.Actor {
  147. t.Errorf("CurrentUser(Initial): got %v; want %v", gotActor, client.Actor)
  148. return
  149. }
  150. // And should still be the current user (as they're still connected)...
  151. server.CheckCurrentUser(client.Actor)
  152. }
  153. numIterations := 10
  154. for range numIterations {
  155. numGoRoutines := 100
  156. var wg sync.WaitGroup
  157. wg.Add(numGoRoutines)
  158. for i := range numGoRoutines {
  159. // User logs in, uses Tailscale for a bit, then logs out
  160. // in parallel with other users doing the same.
  161. go func() {
  162. defer wg.Done()
  163. connectDisconnectAsUser("User-" + strconv.Itoa(i))
  164. }()
  165. }
  166. wg.Wait()
  167. if err := server.BlockWhileInUse(ctx); err != nil {
  168. t.Fatalf("BlockUntilIdle: %v", err)
  169. }
  170. server.CheckCurrentUser(nil)
  171. }
  172. }
  173. func TestBlockWhileIdentityInUse(t *testing.T) {
  174. enableLogging := false
  175. setGOOSForTest(t, "windows")
  176. ctx := context.Background()
  177. server := lapitest.NewServer(t, lapitest.WithLogging(enableLogging))
  178. // connectWaitDisconnectAsUser connects as a user with the specified name
  179. // and keeps the IPN bus watcher alive until the context is canceled.
  180. // It returns a channel that is closed when done.
  181. connectWaitDisconnectAsUser := func(ctx context.Context, name string) <-chan struct{} {
  182. client := server.ClientWithName(name)
  183. watcher, cancelWatcher := client.WatchIPNBus(ctx, 0)
  184. done := make(chan struct{})
  185. go func() {
  186. defer cancelWatcher()
  187. defer close(done)
  188. for {
  189. _, err := watcher.Next()
  190. if err != nil {
  191. // There's either an error or the request has been canceled.
  192. break
  193. }
  194. }
  195. }()
  196. return done
  197. }
  198. for range 100 {
  199. // Connect as UserA, and keep the connection alive
  200. // until disconnectUserA is called.
  201. userAContext, disconnectUserA := context.WithCancel(ctx)
  202. userADone := connectWaitDisconnectAsUser(userAContext, "UserA")
  203. disconnectUserA()
  204. // Check if userB can connect. Calling it directly increases
  205. // the likelihood of triggering a deadlock due to a race condition
  206. // in blockWhileIdentityInUse. But the issue also occurs during
  207. // the normal execution path when UserB connects to the IPN server
  208. // while UserA is disconnecting.
  209. userB := server.MakeTestActor("UserB", "ClientB")
  210. server.BlockWhileInUseByOther(ctx, userB)
  211. <-userADone
  212. }
  213. }
  214. func TestShutdownViaLocalAPI(t *testing.T) {
  215. t.Parallel()
  216. errAccessDeniedByPolicy := errors.New("Access denied: shutdown access denied by policy")
  217. tests := []struct {
  218. name string
  219. allowTailscaledRestart *bool
  220. wantErr error
  221. }{
  222. {
  223. name: "AllowTailscaledRestart/NotConfigured",
  224. allowTailscaledRestart: nil,
  225. wantErr: errAccessDeniedByPolicy,
  226. },
  227. {
  228. name: "AllowTailscaledRestart/False",
  229. allowTailscaledRestart: ptr.To(false),
  230. wantErr: errAccessDeniedByPolicy,
  231. },
  232. {
  233. name: "AllowTailscaledRestart/True",
  234. allowTailscaledRestart: ptr.To(true),
  235. wantErr: nil, // shutdown should be allowed
  236. },
  237. }
  238. for _, tt := range tests {
  239. t.Run(tt.name, func(t *testing.T) {
  240. t.Parallel()
  241. sys := tsd.NewSystem()
  242. var pol policytest.Config
  243. if tt.allowTailscaledRestart != nil {
  244. pol.Set(pkey.AllowTailscaledRestart, *tt.allowTailscaledRestart)
  245. }
  246. sys.Set(pol)
  247. server := lapitest.NewServer(t, lapitest.WithSys(sys))
  248. lc := server.ClientWithName("User")
  249. err := lc.ShutdownTailscaled(t.Context())
  250. checkError(t, err, tt.wantErr)
  251. })
  252. }
  253. }
  254. func checkError(tb testing.TB, got, want error) {
  255. tb.Helper()
  256. if (want == nil) != (got == nil) ||
  257. (want != nil && got != nil && want.Error() != got.Error() && !errors.Is(got, want)) {
  258. tb.Fatalf("gotErr: %v; wantErr: %v", got, want)
  259. }
  260. }
  261. func setGOOSForTest(tb testing.TB, goos string) {
  262. tb.Helper()
  263. envknob.Setenv("TS_DEBUG_FAKE_GOOS", goos)
  264. tb.Cleanup(func() { envknob.Setenv("TS_DEBUG_FAKE_GOOS", "") })
  265. }
  266. func pumpIPNBus(watcher *local.IPNBusWatcher) {
  267. for {
  268. _, err := watcher.Next()
  269. if err != nil {
  270. break
  271. }
  272. }
  273. }