state_test.go 60 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016
  1. // Copyright (c) Tailscale Inc & AUTHORS
  2. // SPDX-License-Identifier: BSD-3-Clause
  3. package ipnlocal
  4. import (
  5. "context"
  6. "errors"
  7. "fmt"
  8. "math/rand/v2"
  9. "net/netip"
  10. "strings"
  11. "sync"
  12. "sync/atomic"
  13. "testing"
  14. "time"
  15. qt "github.com/frankban/quicktest"
  16. "github.com/google/go-cmp/cmp"
  17. "github.com/google/go-cmp/cmp/cmpopts"
  18. "tailscale.com/control/controlclient"
  19. "tailscale.com/envknob"
  20. "tailscale.com/ipn"
  21. "tailscale.com/ipn/ipnauth"
  22. "tailscale.com/ipn/ipnstate"
  23. "tailscale.com/ipn/store/mem"
  24. "tailscale.com/net/dns"
  25. "tailscale.com/net/netmon"
  26. "tailscale.com/net/packet"
  27. "tailscale.com/net/tsdial"
  28. "tailscale.com/tailcfg"
  29. "tailscale.com/tsd"
  30. "tailscale.com/tstest"
  31. "tailscale.com/types/dnstype"
  32. "tailscale.com/types/key"
  33. "tailscale.com/types/logger"
  34. "tailscale.com/types/logid"
  35. "tailscale.com/types/netmap"
  36. "tailscale.com/types/persist"
  37. "tailscale.com/types/preftype"
  38. "tailscale.com/util/dnsname"
  39. "tailscale.com/util/eventbus/eventbustest"
  40. "tailscale.com/util/mak"
  41. "tailscale.com/util/must"
  42. "tailscale.com/wgengine"
  43. "tailscale.com/wgengine/filter"
  44. "tailscale.com/wgengine/magicsock"
  45. "tailscale.com/wgengine/router"
  46. "tailscale.com/wgengine/wgcfg"
  47. "tailscale.com/wgengine/wgint"
  48. )
  49. // notifyThrottler receives notifications from an ipn.Backend, blocking
  50. // (with eventual timeout and t.Fatal) if there are too many and complaining
  51. // (also with t.Fatal) if they are too few.
  52. type notifyThrottler struct {
  53. t *testing.T
  54. // ch gets replaced frequently. Lock the mutex before getting or
  55. // setting it, but not while waiting on it.
  56. mu sync.Mutex
  57. ch chan ipn.Notify
  58. putErr error // set by put if the channel is full
  59. }
  60. // expect tells the throttler to expect count upcoming notifications.
  61. func (nt *notifyThrottler) expect(count int) {
  62. nt.mu.Lock()
  63. nt.ch = make(chan ipn.Notify, count)
  64. nt.mu.Unlock()
  65. }
  66. // put adds one notification into the throttler's queue.
  67. func (nt *notifyThrottler) put(n ipn.Notify) {
  68. nt.t.Helper()
  69. nt.mu.Lock()
  70. ch := nt.ch
  71. nt.mu.Unlock()
  72. select {
  73. case ch <- n:
  74. return
  75. default:
  76. err := fmt.Errorf("put: channel full: %v", n)
  77. nt.t.Log(err)
  78. nt.mu.Lock()
  79. nt.putErr = err
  80. nt.mu.Unlock()
  81. }
  82. }
  83. // drain pulls the notifications out of the queue, asserting that there are
  84. // exactly count notifications that have been put so far.
  85. func (nt *notifyThrottler) drain(count int) []ipn.Notify {
  86. nt.t.Helper()
  87. nt.mu.Lock()
  88. ch := nt.ch
  89. putErr := nt.putErr
  90. nt.mu.Unlock()
  91. if putErr != nil {
  92. nt.t.Fatalf("drain: previous call to put errored: %s", putErr)
  93. }
  94. nn := []ipn.Notify{}
  95. for i := range count {
  96. select {
  97. case n := <-ch:
  98. nn = append(nn, n)
  99. case <-time.After(6 * time.Second):
  100. nt.t.Fatalf("drain: channel empty after %d/%d", i, count)
  101. }
  102. }
  103. // no more notifications expected
  104. close(ch)
  105. nt.t.Log(nn)
  106. return nn
  107. }
  108. // mockControl is a mock implementation of controlclient.Client.
  109. // Much of the backend state machine depends on callbacks and state
  110. // in the controlclient.Client, so by controlling it, we can check that
  111. // the state machine works as expected.
  112. type mockControl struct {
  113. tb testing.TB
  114. logf logger.Logf
  115. opts controlclient.Options
  116. paused atomic.Bool
  117. controlClientID int64
  118. mu sync.Mutex
  119. persist *persist.Persist
  120. calls []string
  121. authBlocked bool
  122. shutdown chan struct{}
  123. }
  124. func newClient(tb testing.TB, opts controlclient.Options) *mockControl {
  125. return &mockControl{
  126. tb: tb,
  127. authBlocked: true,
  128. logf: opts.Logf,
  129. opts: opts,
  130. shutdown: make(chan struct{}),
  131. persist: opts.Persist.Clone(),
  132. controlClientID: rand.Int64(),
  133. }
  134. }
  135. func (cc *mockControl) assertShutdown(wasPaused bool) {
  136. cc.tb.Helper()
  137. select {
  138. case <-cc.shutdown:
  139. // ok
  140. case <-time.After(500 * time.Millisecond):
  141. cc.tb.Fatalf("timed out waiting for shutdown")
  142. }
  143. if wasPaused {
  144. cc.assertCalls("unpause", "Shutdown")
  145. } else {
  146. cc.assertCalls("Shutdown")
  147. }
  148. }
  149. func (cc *mockControl) populateKeys() (newKeys bool) {
  150. cc.mu.Lock()
  151. defer cc.mu.Unlock()
  152. if cc.persist == nil {
  153. cc.persist = &persist.Persist{}
  154. }
  155. if cc.persist != nil && cc.persist.PrivateNodeKey.IsZero() {
  156. cc.logf("Generating a new nodekey.")
  157. cc.persist.OldPrivateNodeKey = cc.persist.PrivateNodeKey
  158. cc.persist.PrivateNodeKey = key.NewNode()
  159. newKeys = true
  160. }
  161. return newKeys
  162. }
  163. type sendOpt struct {
  164. err error
  165. url string
  166. loginFinished bool
  167. nm *netmap.NetworkMap
  168. }
  169. // send publishes a controlclient.Status notification upstream.
  170. // (In our tests here, upstream is the ipnlocal.Local instance.)
  171. func (cc *mockControl) send(opts sendOpt) {
  172. err, url, loginFinished, nm := opts.err, opts.url, opts.loginFinished, opts.nm
  173. if loginFinished {
  174. cc.mu.Lock()
  175. cc.authBlocked = false
  176. cc.mu.Unlock()
  177. }
  178. if cc.opts.Observer != nil {
  179. s := controlclient.Status{
  180. URL: url,
  181. NetMap: nm,
  182. Persist: cc.persist.View(),
  183. Err: err,
  184. }
  185. if loginFinished {
  186. s.LoggedIn = true
  187. }
  188. cc.opts.Observer.SetControlClientStatus(cc, s)
  189. }
  190. }
  191. func (cc *mockControl) authenticated(nm *netmap.NetworkMap) {
  192. if selfUser, ok := nm.UserProfiles[nm.SelfNode.User()]; ok {
  193. cc.persist.UserProfile = *selfUser.AsStruct()
  194. }
  195. cc.persist.NodeID = nm.SelfNode.StableID()
  196. cc.send(sendOpt{loginFinished: true, nm: nm})
  197. }
  198. func (cc *mockControl) sendAuthURL(nm *netmap.NetworkMap) {
  199. s := controlclient.Status{
  200. URL: "https://example.com/a/foo",
  201. NetMap: nm,
  202. Persist: cc.persist.View(),
  203. }
  204. cc.opts.Observer.SetControlClientStatus(cc, s)
  205. }
  206. // called records that a particular function name was called.
  207. func (cc *mockControl) called(s string) {
  208. cc.mu.Lock()
  209. defer cc.mu.Unlock()
  210. cc.calls = append(cc.calls, s)
  211. }
  212. // assertCalls fails the test if the list of functions that have been called since the
  213. // last time assertCall was run does not match want.
  214. func (cc *mockControl) assertCalls(want ...string) {
  215. cc.tb.Helper()
  216. cc.mu.Lock()
  217. defer cc.mu.Unlock()
  218. qt.Assert(cc.tb, cc.calls, qt.DeepEquals, want)
  219. cc.calls = nil
  220. }
  221. // Shutdown disconnects the client.
  222. func (cc *mockControl) Shutdown() {
  223. cc.logf("Shutdown")
  224. cc.called("Shutdown")
  225. close(cc.shutdown)
  226. }
  227. // Login starts a login process. Note that in this mock, we don't automatically
  228. // generate notifications about the progress of the login operation. You have to
  229. // call send() as required by the test.
  230. func (cc *mockControl) Login(flags controlclient.LoginFlags) {
  231. cc.logf("Login flags=%v", flags)
  232. cc.called("Login")
  233. newKeys := cc.populateKeys()
  234. interact := (flags & controlclient.LoginInteractive) != 0
  235. cc.logf("Login: interact=%v newKeys=%v", interact, newKeys)
  236. cc.mu.Lock()
  237. defer cc.mu.Unlock()
  238. cc.authBlocked = interact || newKeys
  239. }
  240. func (cc *mockControl) Logout(ctx context.Context) error {
  241. cc.logf("Logout")
  242. cc.called("Logout")
  243. return nil
  244. }
  245. func (cc *mockControl) SetPaused(paused bool) {
  246. was := cc.paused.Swap(paused)
  247. if was == paused {
  248. return
  249. }
  250. cc.logf("SetPaused=%v", paused)
  251. if paused {
  252. cc.called("pause")
  253. } else {
  254. cc.called("unpause")
  255. }
  256. }
  257. func (cc *mockControl) AuthCantContinue() bool {
  258. cc.mu.Lock()
  259. defer cc.mu.Unlock()
  260. return cc.authBlocked
  261. }
  262. func (cc *mockControl) SetHostinfo(hi *tailcfg.Hostinfo) {
  263. cc.logf("SetHostinfo: %v", *hi)
  264. cc.called("SetHostinfo")
  265. }
  266. func (cc *mockControl) SetNetInfo(ni *tailcfg.NetInfo) {
  267. cc.called("SetNetinfo")
  268. cc.logf("SetNetInfo: %v", *ni)
  269. cc.called("SetNetInfo")
  270. }
  271. func (cc *mockControl) SetTKAHead(head string) {
  272. cc.logf("SetTKAHead: %s", head)
  273. }
  274. func (cc *mockControl) UpdateEndpoints(endpoints []tailcfg.Endpoint) {
  275. // validate endpoint information here?
  276. cc.logf("UpdateEndpoints: ep=%v", endpoints)
  277. cc.called("UpdateEndpoints")
  278. }
  279. func (cc *mockControl) SetDiscoPublicKey(key key.DiscoPublic) {
  280. cc.logf("SetDiscoPublicKey: %v", key)
  281. cc.called("SetDiscoPublicKey")
  282. }
  283. func (cc *mockControl) ClientID() int64 {
  284. return cc.controlClientID
  285. }
  286. func (b *LocalBackend) nonInteractiveLoginForStateTest() {
  287. b.mu.Lock()
  288. if b.cc == nil {
  289. panic("LocalBackend.assertClient: b.cc == nil")
  290. }
  291. cc := b.cc
  292. b.mu.Unlock()
  293. cc.Login(b.loginFlags | controlclient.LoginInteractive)
  294. }
  295. // A very precise test of the sequence of function calls generated by
  296. // ipnlocal.Local into its controlclient instance, and the events it
  297. // produces upstream into the UI.
  298. //
  299. // [apenwarr] Normally I'm not a fan of "mock" style tests, but the precise
  300. // sequence of this state machine is so important for writing our multiple
  301. // frontends, that it's worth validating it all in one place.
  302. //
  303. // Any changes that affect this test will most likely require carefully
  304. // re-testing all our GUIs (and the CLI) to make sure we didn't break
  305. // anything.
  306. //
  307. // Note also that this test doesn't have any timers, goroutines, or duplicate
  308. // detection. It expects messages to be produced in exactly the right order,
  309. // with no duplicates, without doing network activity (other than through
  310. // controlclient, which we fake, so there's no network activity there either).
  311. //
  312. // TODO: A few messages that depend on magicsock (which actually might have
  313. // network delays) are just ignored for now, which makes the test
  314. // predictable, but maybe a bit less thorough. This is more of an overall
  315. // state machine test than a test of the wgengine+magicsock integration.
  316. func TestStateMachine(t *testing.T) {
  317. runTestStateMachine(t, false)
  318. }
  319. func TestStateMachineSeamless(t *testing.T) {
  320. runTestStateMachine(t, true)
  321. }
  322. func runTestStateMachine(t *testing.T, seamless bool) {
  323. envknob.Setenv("TAILSCALE_USE_WIP_CODE", "1")
  324. defer envknob.Setenv("TAILSCALE_USE_WIP_CODE", "")
  325. c := qt.New(t)
  326. logf := tstest.WhileTestRunningLogger(t)
  327. sys := tsd.NewSystem()
  328. store := new(testStateStorage)
  329. sys.Set(store)
  330. e, err := wgengine.NewFakeUserspaceEngine(logf, sys.Set, sys.HealthTracker.Get(), sys.UserMetricsRegistry(), sys.Bus.Get())
  331. if err != nil {
  332. t.Fatalf("NewFakeUserspaceEngine: %v", err)
  333. }
  334. t.Cleanup(e.Close)
  335. sys.Set(e)
  336. b, err := NewLocalBackend(logf, logid.PublicID{}, sys, 0)
  337. if err != nil {
  338. t.Fatalf("NewLocalBackend: %v", err)
  339. }
  340. t.Cleanup(b.Shutdown)
  341. var cc, previousCC *mockControl
  342. b.SetControlClientGetterForTesting(func(opts controlclient.Options) (controlclient.Client, error) {
  343. previousCC = cc
  344. cc = newClient(t, opts)
  345. t.Logf("ccGen: new mockControl.")
  346. cc.called("New")
  347. return cc, nil
  348. })
  349. notifies := &notifyThrottler{t: t}
  350. notifies.expect(0)
  351. b.SetNotifyCallback(func(n ipn.Notify) {
  352. if n.State != nil ||
  353. (n.Prefs != nil && n.Prefs.Valid()) ||
  354. n.BrowseToURL != nil ||
  355. n.LoginFinished != nil {
  356. logf("%+v\n\n", n)
  357. notifies.put(n)
  358. } else {
  359. logf("(ignored) %v\n\n", n)
  360. }
  361. })
  362. // Check that it hasn't called us right away.
  363. // The state machine should be idle until we call Start().
  364. c.Assert(cc, qt.IsNil)
  365. // Start the state machine.
  366. // Since !WantRunning by default, it'll create a controlclient,
  367. // but not ask it to do anything yet.
  368. t.Logf("\n\nStart")
  369. notifies.expect(2)
  370. c.Assert(b.Start(ipn.Options{}), qt.IsNil)
  371. {
  372. // BUG: strictly, it should pause, not unpause, here, since !WantRunning.
  373. cc.assertCalls("New")
  374. nn := notifies.drain(2)
  375. cc.assertCalls()
  376. c.Assert(nn[0].Prefs, qt.IsNotNil)
  377. c.Assert(nn[1].State, qt.IsNotNil)
  378. prefs := nn[0].Prefs
  379. // Note: a totally fresh system has Prefs.LoggedOut=false by
  380. // default. We are logged out, but not because the user asked
  381. // for it, so it doesn't count as Prefs.LoggedOut==true.
  382. c.Assert(prefs.LoggedOut(), qt.IsTrue)
  383. c.Assert(prefs.WantRunning(), qt.IsFalse)
  384. // Verify notification indicates we need login (prefs show logged out)
  385. c.Assert(nn[1].Prefs == nil || nn[1].Prefs.LoggedOut(), qt.IsTrue)
  386. // Verify the actual facts about our state
  387. c.Assert(needsLogin(b), qt.IsTrue)
  388. c.Assert(hasValidNetMap(b), qt.IsFalse)
  389. }
  390. // Restart the state machine.
  391. // It's designed to handle frontends coming and going sporadically.
  392. // Make the sure the restart not only works, but generates the same
  393. // events as the first time, so UIs always know what to expect.
  394. t.Logf("\n\nStart2")
  395. notifies.expect(2)
  396. c.Assert(b.Start(ipn.Options{}), qt.IsNil)
  397. {
  398. previousCC.assertShutdown(false)
  399. cc.assertCalls("New")
  400. nn := notifies.drain(2)
  401. cc.assertCalls()
  402. c.Assert(nn[0].Prefs, qt.IsNotNil)
  403. c.Assert(nn[1].State, qt.IsNotNil)
  404. c.Assert(nn[0].Prefs.LoggedOut(), qt.IsTrue)
  405. c.Assert(nn[0].Prefs.WantRunning(), qt.IsFalse)
  406. // Verify notification indicates we need login
  407. c.Assert(nn[1].Prefs == nil || nn[1].Prefs.LoggedOut(), qt.IsTrue)
  408. // Verify the actual facts about our state
  409. c.Assert(needsLogin(b), qt.IsTrue)
  410. c.Assert(hasValidNetMap(b), qt.IsFalse)
  411. }
  412. // Start non-interactive login with no token.
  413. // This will ask controlclient to start its own Login() process,
  414. // then wait for us to respond.
  415. t.Logf("\n\nLogin (noninteractive)")
  416. notifies.expect(0)
  417. b.nonInteractiveLoginForStateTest()
  418. {
  419. cc.assertCalls("Login")
  420. notifies.drain(0)
  421. // Note: WantRunning isn't true yet. It'll switch to true
  422. // after a successful login finishes.
  423. // (This behaviour is needed so that b.Login() won't
  424. // start connecting to an old account right away, if one
  425. // exists when you launch another login.)
  426. // Verify we still need login
  427. c.Assert(needsLogin(b), qt.IsTrue)
  428. }
  429. // Attempted non-interactive login with no key; indicate that
  430. // the user needs to visit a login URL.
  431. t.Logf("\n\nLogin (url response)")
  432. notifies.expect(3)
  433. b.EditPrefs(&ipn.MaskedPrefs{
  434. ControlURLSet: true,
  435. Prefs: ipn.Prefs{
  436. ControlURL: "https://localhost:1/",
  437. },
  438. })
  439. url1 := "https://localhost:1/1"
  440. cc.send(sendOpt{url: url1})
  441. {
  442. cc.assertCalls()
  443. // ...but backend eats that notification, because the user
  444. // didn't explicitly request interactive login yet, and
  445. // we're already in NeedsLogin state.
  446. nn := notifies.drain(3)
  447. c.Assert(nn[1].Prefs, qt.IsNotNil)
  448. c.Assert(nn[1].Prefs.LoggedOut(), qt.IsTrue)
  449. c.Assert(nn[1].Prefs.WantRunning(), qt.IsFalse)
  450. // Verify we need URL visit
  451. c.Assert(hasAuthURL(b), qt.IsTrue)
  452. c.Assert(nn[2].BrowseToURL, qt.IsNotNil)
  453. c.Assert(url1, qt.Equals, *nn[2].BrowseToURL)
  454. c.Assert(isFullyAuthenticated(b), qt.IsFalse)
  455. }
  456. // Now we'll try an interactive login.
  457. // Since we provided an interactive URL earlier, this shouldn't
  458. // ask control to do anything. Instead backend will emit an event
  459. // indicating that the UI should browse to the given URL.
  460. t.Logf("\n\nLogin (interactive)")
  461. notifies.expect(1)
  462. b.StartLoginInteractive(context.Background())
  463. {
  464. nn := notifies.drain(1)
  465. cc.assertCalls()
  466. c.Assert(nn[0].BrowseToURL, qt.IsNotNil)
  467. c.Assert(url1, qt.Equals, *nn[0].BrowseToURL)
  468. // Verify we still need to complete login
  469. c.Assert(needsLogin(b), qt.IsTrue)
  470. }
  471. // Sometimes users press the Login button again, in the middle of
  472. // a login sequence. For example, they might have closed their
  473. // browser window without logging in, or they waited too long and
  474. // the login URL expired. If they start another interactive login,
  475. // we must always get a *new* login URL first.
  476. t.Logf("\n\nLogin2 (interactive)")
  477. b.authURLTime = time.Now().Add(-time.Hour * 24 * 7) // simulate URL expiration
  478. notifies.expect(0)
  479. b.StartLoginInteractive(context.Background())
  480. {
  481. notifies.drain(0)
  482. // backend asks control for another login sequence
  483. cc.assertCalls("Login")
  484. // Verify we still need login
  485. c.Assert(needsLogin(b), qt.IsTrue)
  486. }
  487. // Provide a new interactive login URL.
  488. t.Logf("\n\nLogin2 (url response)")
  489. notifies.expect(1)
  490. url2 := "https://localhost:1/2"
  491. cc.send(sendOpt{url: url2})
  492. {
  493. cc.assertCalls()
  494. // This time, backend should emit it to the UI right away,
  495. // because the UI is anxiously awaiting a new URL to visit.
  496. nn := notifies.drain(1)
  497. c.Assert(nn[0].BrowseToURL, qt.IsNotNil)
  498. c.Assert(url2, qt.Equals, *nn[0].BrowseToURL)
  499. // Verify we still need to complete login
  500. c.Assert(needsLogin(b), qt.IsTrue)
  501. }
  502. // Pretend that the interactive login actually happened.
  503. // Controlclient always sends the netmap and LoginFinished at the
  504. // same time.
  505. // The backend should propagate this upward for the UI.
  506. t.Logf("\n\nLoginFinished")
  507. notifies.expect(3)
  508. cc.persist.UserProfile.LoginName = "user1"
  509. cc.persist.NodeID = "node1"
  510. // even if seamless is being enabled by default rather than by policy, this is
  511. // the point where it will first get enabled.
  512. if seamless {
  513. sys.ControlKnobs().SeamlessKeyRenewal.Store(true)
  514. }
  515. cc.send(sendOpt{loginFinished: true, nm: &netmap.NetworkMap{}})
  516. {
  517. nn := notifies.drain(3)
  518. // Arguably it makes sense to unpause now, since the machine
  519. // authorization status is part of the netmap.
  520. //
  521. // BUG: backend unblocks wgengine at this point, even though
  522. // our machine key is not authorized. It probably should
  523. // wait until it gets into Starting.
  524. // TODO: (Currently this test doesn't detect that bug, but
  525. // it's visible in the logs)
  526. cc.assertCalls()
  527. c.Assert(nn[0].LoginFinished, qt.IsNotNil)
  528. c.Assert(nn[1].Prefs, qt.IsNotNil)
  529. c.Assert(nn[1].Prefs.Persist().UserProfile().LoginName, qt.Equals, "user1")
  530. // nn[2] is a state notification after login
  531. // Verify login finished but need machine auth using backend state
  532. c.Assert(isFullyAuthenticated(b), qt.IsTrue)
  533. c.Assert(needsMachineAuth(b), qt.IsTrue)
  534. nm := b.NetMap()
  535. c.Assert(nm, qt.IsNotNil)
  536. // For an empty netmap (after initial login), SelfNode may not be valid yet.
  537. // In this case, we can't check MachineAuthorized, but needsMachineAuth already verified the state.
  538. if nm.SelfNode.Valid() {
  539. c.Assert(nm.SelfNode.MachineAuthorized(), qt.IsFalse)
  540. }
  541. }
  542. // Pretend that the administrator has authorized our machine.
  543. t.Logf("\n\nMachineAuthorized")
  544. notifies.expect(1)
  545. // BUG: the real controlclient sends LoginFinished with every
  546. // notification while it's in StateAuthenticated, but not StateSynced.
  547. // It should send it exactly once, or every time we're authenticated,
  548. // but the current code is brittle.
  549. // (ie. I suspect it would be better to change false->true in send()
  550. // below, and do the same in the real controlclient.)
  551. cc.send(sendOpt{nm: &netmap.NetworkMap{
  552. SelfNode: (&tailcfg.Node{MachineAuthorized: true}).View(),
  553. }})
  554. {
  555. nn := notifies.drain(1)
  556. cc.assertCalls()
  557. // nn[0] is a state notification after machine auth granted
  558. c.Assert(len(nn), qt.Equals, 1)
  559. // Verify machine authorized using backend state
  560. nm := b.NetMap()
  561. c.Assert(nm, qt.IsNotNil)
  562. c.Assert(nm.SelfNode.Valid(), qt.IsTrue)
  563. c.Assert(nm.SelfNode.MachineAuthorized(), qt.IsTrue)
  564. }
  565. // TODO: add a fake DERP server to our fake netmap, so we can
  566. // transition to the Running state here.
  567. // TODO: test what happens when the admin forcibly deletes our key.
  568. // (ie. unsolicited logout)
  569. // TODO: test what happens when our key expires, client side.
  570. // (and when it gets close to expiring)
  571. // The user changes their preference to !WantRunning.
  572. t.Logf("\n\nWantRunning -> false")
  573. notifies.expect(2)
  574. b.EditPrefs(&ipn.MaskedPrefs{
  575. WantRunningSet: true,
  576. Prefs: ipn.Prefs{WantRunning: false},
  577. })
  578. {
  579. nn := notifies.drain(2)
  580. cc.assertCalls("pause")
  581. // BUG: I would expect Prefs to change first, and state after.
  582. // nn[0] is state notification, nn[1] is prefs notification
  583. c.Assert(nn[1].Prefs, qt.IsNotNil)
  584. c.Assert(nn[1].Prefs.WantRunning(), qt.IsFalse)
  585. }
  586. // The user changes their preference to WantRunning after all.
  587. t.Logf("\n\nWantRunning -> true")
  588. store.awaitWrite()
  589. notifies.expect(2)
  590. b.EditPrefs(&ipn.MaskedPrefs{
  591. WantRunningSet: true,
  592. Prefs: ipn.Prefs{WantRunning: true},
  593. })
  594. {
  595. nn := notifies.drain(2)
  596. // BUG: Login isn't needed here. We never logged out.
  597. cc.assertCalls("Login", "unpause")
  598. // BUG: I would expect Prefs to change first, and state after.
  599. // nn[0] is state notification, nn[1] is prefs notification
  600. c.Assert(nn[1].Prefs, qt.IsNotNil)
  601. c.Assert(nn[1].Prefs.WantRunning(), qt.IsTrue)
  602. c.Assert(store.sawWrite(), qt.IsTrue)
  603. }
  604. // User wants to logout.
  605. store.awaitWrite()
  606. t.Logf("\n\nLogout")
  607. notifies.expect(5)
  608. b.Logout(context.Background(), ipnauth.Self)
  609. {
  610. nn := notifies.drain(5)
  611. previousCC.assertCalls("pause", "Logout", "unpause", "Shutdown")
  612. // nn[0] is state notification (Stopped)
  613. c.Assert(nn[0].State, qt.IsNotNil)
  614. c.Assert(*nn[0].State, qt.Equals, ipn.Stopped)
  615. // nn[1] is prefs notification after logout
  616. c.Assert(nn[1].Prefs, qt.IsNotNil)
  617. c.Assert(nn[1].Prefs.LoggedOut(), qt.IsTrue)
  618. c.Assert(nn[1].Prefs.WantRunning(), qt.IsFalse)
  619. cc.assertCalls("New")
  620. // nn[2] is the initial state notification after New (NoState)
  621. // nn[3] is prefs notification with emptyPrefs
  622. c.Assert(nn[3].Prefs, qt.IsNotNil)
  623. c.Assert(nn[3].Prefs.LoggedOut(), qt.IsTrue)
  624. c.Assert(nn[3].Prefs.WantRunning(), qt.IsFalse)
  625. c.Assert(store.sawWrite(), qt.IsTrue)
  626. // nn[4] is state notification (NeedsLogin)
  627. // Verify logged out and needs new login using backend state
  628. c.Assert(needsLogin(b), qt.IsTrue)
  629. c.Assert(hasValidNetMap(b), qt.IsFalse)
  630. }
  631. // A second logout should be a no-op as we are in the NeedsLogin state.
  632. t.Logf("\n\nLogout2")
  633. notifies.expect(0)
  634. b.Logout(context.Background(), ipnauth.Self)
  635. {
  636. notifies.drain(0)
  637. cc.assertCalls()
  638. c.Assert(b.Prefs().LoggedOut(), qt.IsTrue)
  639. c.Assert(b.Prefs().WantRunning(), qt.IsFalse)
  640. // Verify still needs login
  641. c.Assert(needsLogin(b), qt.IsTrue)
  642. }
  643. // A third logout should also be a no-op as the cc should be in
  644. // AuthCantContinue state.
  645. t.Logf("\n\nLogout3")
  646. notifies.expect(3)
  647. b.Logout(context.Background(), ipnauth.Self)
  648. {
  649. notifies.drain(0)
  650. cc.assertCalls()
  651. c.Assert(b.Prefs().LoggedOut(), qt.IsTrue)
  652. c.Assert(b.Prefs().WantRunning(), qt.IsFalse)
  653. // Verify still needs login
  654. c.Assert(needsLogin(b), qt.IsTrue)
  655. }
  656. // Oh, you thought we were done? Ha! Now we have to test what
  657. // happens if the user exits and restarts while logged out.
  658. // Note that it's explicitly okay to call b.Start() over and over
  659. // again, every time the frontend reconnects.
  660. // TODO: test user switching between statekeys.
  661. // The frontend restarts!
  662. t.Logf("\n\nStart3")
  663. notifies.expect(2)
  664. c.Assert(b.Start(ipn.Options{}), qt.IsNil)
  665. {
  666. previousCC.assertShutdown(false)
  667. // BUG: We already called Shutdown(), no need to do it again.
  668. // BUG: don't unpause because we're not logged in.
  669. cc.assertCalls("New")
  670. nn := notifies.drain(2)
  671. cc.assertCalls()
  672. c.Assert(nn[0].Prefs, qt.IsNotNil)
  673. c.Assert(nn[0].Prefs.LoggedOut(), qt.IsTrue)
  674. c.Assert(nn[0].Prefs.WantRunning(), qt.IsFalse)
  675. // Verify notification indicates we need login
  676. c.Assert(nn[1].Prefs == nil || nn[1].Prefs.LoggedOut(), qt.IsTrue)
  677. // Verify we need login after restart
  678. c.Assert(needsLogin(b), qt.IsTrue)
  679. c.Assert(hasValidNetMap(b), qt.IsFalse)
  680. }
  681. // Explicitly set the ControlURL to avoid defaulting to [ipn.DefaultControlURL].
  682. // This prevents [LocalBackend] from using the production control server during tests
  683. // and ensures that [LocalBackend.validPopBrowserURL] returns true for the
  684. // fake interactive login URLs used below. Otherwise, we won't be receiving
  685. // BrowseToURL notifications as expected.
  686. // See tailscale/tailscale#11393.
  687. notifies.expect(1)
  688. b.EditPrefs(&ipn.MaskedPrefs{
  689. ControlURLSet: true,
  690. Prefs: ipn.Prefs{
  691. ControlURL: "https://localhost:1/",
  692. },
  693. })
  694. notifies.drain(1)
  695. t.Logf("\n\nStartLoginInteractive3")
  696. b.StartLoginInteractive(context.Background())
  697. // We've been logged out, and the previously created profile is now deleted.
  698. // We're attempting an interactive login for the first time with the new profile,
  699. // this should result in a call to the control server, which in turn should provide
  700. // an interactive login URL to visit.
  701. notifies.expect(2)
  702. url3 := "https://localhost:1/3"
  703. cc.send(sendOpt{url: url3})
  704. {
  705. nn := notifies.drain(2)
  706. cc.assertCalls("Login")
  707. c.Assert(nn[1].BrowseToURL, qt.IsNotNil)
  708. c.Assert(*nn[1].BrowseToURL, qt.Equals, url3)
  709. }
  710. t.Logf("%q visited", url3)
  711. notifies.expect(3)
  712. cc.persist.UserProfile.LoginName = "user2"
  713. cc.persist.NodeID = "node2"
  714. cc.send(sendOpt{loginFinished: true, nm: &netmap.NetworkMap{
  715. SelfNode: (&tailcfg.Node{MachineAuthorized: true}).View(),
  716. }})
  717. t.Logf("\n\nLoginFinished3")
  718. {
  719. nn := notifies.drain(3)
  720. c.Assert(nn[0].LoginFinished, qt.IsNotNil)
  721. c.Assert(nn[1].Prefs, qt.IsNotNil)
  722. c.Assert(nn[1].Prefs.Persist(), qt.IsNotNil)
  723. // Prefs after finishing the login, so LoginName updated.
  724. c.Assert(nn[1].Prefs.Persist().UserProfile().LoginName, qt.Equals, "user2")
  725. c.Assert(nn[1].Prefs.LoggedOut(), qt.IsFalse)
  726. // If a user initiates an interactive login, they also expect WantRunning to become true.
  727. c.Assert(nn[1].Prefs.WantRunning(), qt.IsTrue)
  728. // nn[2] is state notification (Starting) - verify using backend state
  729. c.Assert(isWantRunning(b), qt.IsTrue)
  730. c.Assert(isLoggedIn(b), qt.IsTrue)
  731. }
  732. // Now we've logged in successfully. Let's disconnect.
  733. t.Logf("\n\nWantRunning -> false")
  734. notifies.expect(2)
  735. b.EditPrefs(&ipn.MaskedPrefs{
  736. WantRunningSet: true,
  737. Prefs: ipn.Prefs{WantRunning: false},
  738. })
  739. {
  740. nn := notifies.drain(2)
  741. cc.assertCalls("pause")
  742. // BUG: I would expect Prefs to change first, and state after.
  743. // nn[0] is state notification (Stopped), nn[1] is prefs notification
  744. c.Assert(nn[1].Prefs, qt.IsNotNil)
  745. c.Assert(nn[1].Prefs.WantRunning(), qt.IsFalse)
  746. c.Assert(nn[1].Prefs.LoggedOut(), qt.IsFalse)
  747. }
  748. // One more restart, this time with a valid key, but WantRunning=false.
  749. t.Logf("\n\nStart4")
  750. notifies.expect(2)
  751. c.Assert(b.Start(ipn.Options{}), qt.IsNil)
  752. {
  753. // NOTE: cc.Shutdown() is correct here, since we didn't call
  754. // b.Shutdown() explicitly ourselves.
  755. previousCC.assertShutdown(false)
  756. nn := notifies.drain(2)
  757. // We already have a netmap for this node,
  758. // and WantRunning is false, so cc should be paused.
  759. cc.assertCalls("New", "Login", "pause")
  760. c.Assert(nn[0].Prefs, qt.IsNotNil)
  761. c.Assert(nn[0].Prefs.WantRunning(), qt.IsFalse)
  762. c.Assert(nn[0].Prefs.LoggedOut(), qt.IsFalse)
  763. // nn[1] is state notification (Stopped)
  764. // Verify backend shows we're not wanting to run
  765. c.Assert(isWantRunning(b), qt.IsFalse)
  766. }
  767. // When logged in but !WantRunning, ipn leaves us unpaused to retrieve
  768. // the first netmap. Simulate that netmap being received, after which
  769. // it should pause us, to avoid wasting CPU retrieving unnecessarily
  770. // additional netmap updates. Since our LocalBackend instance already
  771. // has a netmap, we will reset it to nil to simulate the first netmap
  772. // retrieval.
  773. b.setNetMapLocked(nil)
  774. cc.assertCalls("unpause")
  775. //
  776. // TODO: really the various GUIs and prefs should be refactored to
  777. // not require the netmap structure at all when starting while
  778. // !WantRunning. That would remove the need for this (or contacting
  779. // the control server at all when stopped).
  780. t.Logf("\n\nStart4 -> netmap")
  781. notifies.expect(0)
  782. cc.send(sendOpt{loginFinished: true, nm: &netmap.NetworkMap{
  783. SelfNode: (&tailcfg.Node{MachineAuthorized: true}).View(),
  784. }})
  785. {
  786. notifies.drain(0)
  787. cc.assertCalls("pause")
  788. }
  789. // Request connection.
  790. // The state machine didn't call Login() earlier, so now it needs to.
  791. t.Logf("\n\nWantRunning4 -> true")
  792. notifies.expect(2)
  793. b.EditPrefs(&ipn.MaskedPrefs{
  794. WantRunningSet: true,
  795. Prefs: ipn.Prefs{WantRunning: true},
  796. })
  797. {
  798. nn := notifies.drain(2)
  799. cc.assertCalls("Login", "unpause")
  800. // BUG: I would expect Prefs to change first, and state after.
  801. // nn[0] is state notification (Starting), nn[1] is prefs notification
  802. c.Assert(nn[1].Prefs, qt.IsNotNil)
  803. c.Assert(nn[1].Prefs.WantRunning(), qt.IsTrue)
  804. }
  805. // Disconnect.
  806. t.Logf("\n\nStop")
  807. notifies.expect(2)
  808. b.EditPrefs(&ipn.MaskedPrefs{
  809. WantRunningSet: true,
  810. Prefs: ipn.Prefs{WantRunning: false},
  811. })
  812. {
  813. nn := notifies.drain(2)
  814. cc.assertCalls("pause")
  815. // BUG: I would expect Prefs to change first, and state after.
  816. // nn[0] is state notification (Stopped), nn[1] is prefs notification
  817. c.Assert(nn[1].Prefs, qt.IsNotNil)
  818. c.Assert(nn[1].Prefs.WantRunning(), qt.IsFalse)
  819. }
  820. // We want to try logging in as a different user, while Stopped.
  821. // First, start the login process (without logging out first).
  822. t.Logf("\n\nLoginDifferent")
  823. notifies.expect(1)
  824. b.StartLoginInteractive(context.Background())
  825. url4 := "https://localhost:1/4"
  826. cc.send(sendOpt{url: url4})
  827. {
  828. nn := notifies.drain(1)
  829. // It might seem like WantRunning should switch to true here,
  830. // but that would be risky since we already have a valid
  831. // user account. It might try to reconnect to the old account
  832. // before the new one is ready. So no change yet.
  833. //
  834. // Because the login hasn't yet completed, the old login
  835. // is still valid, so it's correct that we stay paused.
  836. cc.assertCalls("Login")
  837. c.Assert(nn[0].BrowseToURL, qt.IsNotNil)
  838. c.Assert(*nn[0].BrowseToURL, qt.Equals, url4)
  839. }
  840. // Now, let's complete the interactive login, using a different
  841. // user account than before. WantRunning changes to true after an
  842. // interactive login, so we end up unpaused.
  843. t.Logf("\n\nLoginDifferent URL visited")
  844. notifies.expect(3)
  845. cc.persist.UserProfile.LoginName = "user3"
  846. cc.persist.NodeID = "node3"
  847. cc.send(sendOpt{loginFinished: true, nm: &netmap.NetworkMap{
  848. SelfNode: (&tailcfg.Node{MachineAuthorized: true}).View(),
  849. }})
  850. {
  851. nn := notifies.drain(3)
  852. // BUG: pause() being called here is a bad sign.
  853. // It means that either the state machine ran at least once
  854. // with the old netmap, or it ran with the new login+netmap
  855. // and !WantRunning. But since it's a fresh and successful
  856. // new login, WantRunning is true, so there was never a
  857. // reason to pause().
  858. cc.assertCalls("unpause")
  859. c.Assert(nn[0].LoginFinished, qt.IsNotNil)
  860. c.Assert(nn[1].Prefs, qt.IsNotNil)
  861. // Prefs after finishing the login, so LoginName updated.
  862. c.Assert(nn[1].Prefs.Persist().UserProfile().LoginName, qt.Equals, "user3")
  863. c.Assert(nn[1].Prefs.LoggedOut(), qt.IsFalse)
  864. c.Assert(nn[1].Prefs.WantRunning(), qt.IsTrue)
  865. // nn[2] is state notification (Starting) - verify using backend state
  866. c.Assert(isWantRunning(b), qt.IsTrue)
  867. c.Assert(isLoggedIn(b), qt.IsTrue)
  868. }
  869. // The last test case is the most common one: restarting when both
  870. // logged in and WantRunning.
  871. t.Logf("\n\nStart5")
  872. notifies.expect(2)
  873. c.Assert(b.Start(ipn.Options{}), qt.IsNil)
  874. {
  875. // NOTE: cc.Shutdown() is correct here, since we didn't call
  876. // b.Shutdown() ourselves.
  877. previousCC.assertShutdown(false)
  878. cc.assertCalls("New", "Login")
  879. nn := notifies.drain(2)
  880. cc.assertCalls()
  881. c.Assert(nn[0].Prefs, qt.IsNotNil)
  882. c.Assert(nn[0].Prefs.LoggedOut(), qt.IsFalse)
  883. c.Assert(nn[0].Prefs.WantRunning(), qt.IsTrue)
  884. // nn[1] is state notification (Starting)
  885. // Verify we're authenticated with valid netmap using backend state
  886. c.Assert(isFullyAuthenticated(b), qt.IsTrue)
  887. c.Assert(hasValidNetMap(b), qt.IsTrue)
  888. }
  889. // Control server accepts our valid key from before.
  890. t.Logf("\n\nLoginFinished5")
  891. notifies.expect(0)
  892. cc.send(sendOpt{loginFinished: true, nm: &netmap.NetworkMap{
  893. SelfNode: (&tailcfg.Node{MachineAuthorized: true}).View(),
  894. }})
  895. {
  896. notifies.drain(0)
  897. cc.assertCalls()
  898. // NOTE: No LoginFinished message since no interactive
  899. // login was needed.
  900. // NOTE: No prefs change this time. WantRunning stays true.
  901. // We were in Starting in the first place, so that doesn't
  902. // change either, so we don't expect any notifications.
  903. // Verify we're still authenticated with valid netmap
  904. c.Assert(isFullyAuthenticated(b), qt.IsTrue)
  905. c.Assert(hasValidNetMap(b), qt.IsTrue)
  906. }
  907. t.Logf("\n\nExpireKey")
  908. notifies.expect(1)
  909. cc.send(sendOpt{nm: &netmap.NetworkMap{
  910. SelfNode: (&tailcfg.Node{
  911. KeyExpiry: time.Now().Add(-time.Minute),
  912. MachineAuthorized: true,
  913. }).View(),
  914. }})
  915. {
  916. nn := notifies.drain(1)
  917. cc.assertCalls()
  918. // nn[0] is state notification (NeedsLogin) due to key expiry
  919. c.Assert(len(nn), qt.Equals, 1)
  920. // Verify key expired, need new login using backend state
  921. c.Assert(needsLogin(b), qt.IsTrue)
  922. c.Assert(b.isEngineBlocked(), qt.IsTrue)
  923. }
  924. t.Logf("\n\nExtendKey")
  925. notifies.expect(1)
  926. cc.send(sendOpt{nm: &netmap.NetworkMap{
  927. SelfNode: (&tailcfg.Node{
  928. MachineAuthorized: true,
  929. KeyExpiry: time.Now().Add(time.Minute),
  930. }).View(),
  931. }})
  932. {
  933. nn := notifies.drain(1)
  934. cc.assertCalls()
  935. // nn[0] is state notification (Starting) after key extension
  936. c.Assert(len(nn), qt.Equals, 1)
  937. // Verify key extended, authenticated again using backend state
  938. c.Assert(isFullyAuthenticated(b), qt.IsTrue)
  939. c.Assert(hasValidNetMap(b), qt.IsTrue)
  940. c.Assert(b.isEngineBlocked(), qt.IsFalse)
  941. }
  942. notifies.expect(1)
  943. // Fake a DERP connection.
  944. b.setWgengineStatus(&wgengine.Status{DERPs: 1, AsOf: time.Now()}, nil)
  945. {
  946. nn := notifies.drain(1)
  947. cc.assertCalls()
  948. // nn[0] is state notification (Running) after DERP connection
  949. c.Assert(len(nn), qt.Equals, 1)
  950. // Verify we can route traffic using backend state
  951. c.Assert(canRouteTraffic(b), qt.IsTrue)
  952. }
  953. }
  954. func TestEditPrefsHasNoKeys(t *testing.T) {
  955. logf := tstest.WhileTestRunningLogger(t)
  956. sys := tsd.NewSystem()
  957. sys.Set(new(mem.Store))
  958. e, err := wgengine.NewFakeUserspaceEngine(logf, sys.Set, sys.HealthTracker.Get(), sys.UserMetricsRegistry(), sys.Bus.Get())
  959. if err != nil {
  960. t.Fatalf("NewFakeUserspaceEngine: %v", err)
  961. }
  962. t.Cleanup(e.Close)
  963. sys.Set(e)
  964. b, err := NewLocalBackend(logf, logid.PublicID{}, sys, 0)
  965. if err != nil {
  966. t.Fatalf("NewLocalBackend: %v", err)
  967. }
  968. t.Cleanup(b.Shutdown)
  969. b.hostinfo = &tailcfg.Hostinfo{OS: "testos"}
  970. b.pm.SetPrefs((&ipn.Prefs{
  971. Persist: &persist.Persist{
  972. PrivateNodeKey: key.NewNode(),
  973. OldPrivateNodeKey: key.NewNode(),
  974. },
  975. }).View(), ipn.NetworkProfile{})
  976. if p := b.pm.CurrentPrefs().Persist(); !p.Valid() || p.PrivateNodeKey().IsZero() {
  977. t.Fatalf("PrivateNodeKey not set")
  978. }
  979. p, err := b.EditPrefs(&ipn.MaskedPrefs{
  980. Prefs: ipn.Prefs{
  981. Hostname: "foo",
  982. },
  983. HostnameSet: true,
  984. })
  985. if err != nil {
  986. t.Fatalf("EditPrefs: %v", err)
  987. }
  988. if p.Hostname() != "foo" {
  989. t.Errorf("Hostname = %q; want foo", p.Hostname())
  990. }
  991. if !p.Persist().PrivateNodeKey().IsZero() {
  992. t.Errorf("PrivateNodeKey = %v; want zero", p.Persist().PrivateNodeKey())
  993. }
  994. if !p.Persist().OldPrivateNodeKey().IsZero() {
  995. t.Errorf("OldPrivateNodeKey = %v; want zero", p.Persist().OldPrivateNodeKey())
  996. }
  997. if !p.Persist().NetworkLockKey().IsZero() {
  998. t.Errorf("NetworkLockKey= %v; want zero", p.Persist().NetworkLockKey())
  999. }
  1000. }
  1001. type testStateStorage struct {
  1002. mem.Store
  1003. written atomic.Bool
  1004. }
  1005. func (s *testStateStorage) WriteState(id ipn.StateKey, bs []byte) error {
  1006. s.written.Store(true)
  1007. return s.Store.WriteState(id, bs)
  1008. }
  1009. // awaitWrite clears the "I've seen writes" bit, in prep for a future
  1010. // call to sawWrite to see if a write arrived.
  1011. func (s *testStateStorage) awaitWrite() { s.written.Store(false) }
  1012. // sawWrite reports whether there's been a WriteState call since the most
  1013. // recent awaitWrite call.
  1014. func (s *testStateStorage) sawWrite() bool {
  1015. v := s.written.Load()
  1016. s.awaitWrite()
  1017. return v
  1018. }
  1019. func TestWGEngineStatusRace(t *testing.T) {
  1020. t.Skip("test fails")
  1021. c := qt.New(t)
  1022. logf := tstest.WhileTestRunningLogger(t)
  1023. sys := tsd.NewSystem()
  1024. sys.Set(new(mem.Store))
  1025. eng, err := wgengine.NewFakeUserspaceEngine(logf, sys.Set, sys.Bus.Get())
  1026. c.Assert(err, qt.IsNil)
  1027. t.Cleanup(eng.Close)
  1028. sys.Set(eng)
  1029. b, err := NewLocalBackend(logf, logid.PublicID{}, sys, 0)
  1030. c.Assert(err, qt.IsNil)
  1031. t.Cleanup(b.Shutdown)
  1032. var cc *mockControl
  1033. b.SetControlClientGetterForTesting(func(opts controlclient.Options) (controlclient.Client, error) {
  1034. cc = newClient(t, opts)
  1035. return cc, nil
  1036. })
  1037. var state ipn.State
  1038. b.SetNotifyCallback(func(n ipn.Notify) {
  1039. if n.State != nil {
  1040. state = *n.State
  1041. }
  1042. })
  1043. wantState := func(want ipn.State) {
  1044. c.Assert(want, qt.Equals, state)
  1045. }
  1046. // Start with the zero value.
  1047. wantState(ipn.NoState)
  1048. // Start the backend.
  1049. err = b.Start(ipn.Options{})
  1050. c.Assert(err, qt.IsNil)
  1051. wantState(ipn.NeedsLogin)
  1052. // Assert that we are logged in and authorized.
  1053. cc.send(sendOpt{loginFinished: true, nm: &netmap.NetworkMap{
  1054. SelfNode: (&tailcfg.Node{MachineAuthorized: true}).View(),
  1055. }})
  1056. wantState(ipn.Starting)
  1057. // Simulate multiple concurrent callbacks from wgengine.
  1058. // Any single callback with DERPS > 0 is enough to transition
  1059. // from Starting to Running, at which point we stay there.
  1060. // Thus if these callbacks occurred serially, in any order,
  1061. // we would end up in state ipn.Running.
  1062. // The same should thus be true if these callbacks occur concurrently.
  1063. var wg sync.WaitGroup
  1064. for i := range 100 {
  1065. wg.Add(1)
  1066. go func(i int) {
  1067. defer wg.Done()
  1068. n := 0
  1069. if i == 0 {
  1070. n = 1
  1071. }
  1072. b.setWgengineStatus(&wgengine.Status{AsOf: time.Now(), DERPs: n}, nil)
  1073. }(i)
  1074. }
  1075. wg.Wait()
  1076. wantState(ipn.Running)
  1077. }
  1078. // TestEngineReconfigOnStateChange verifies that wgengine is properly reconfigured
  1079. // when the LocalBackend's state changes, such as when the user logs in, switches
  1080. // profiles, or disconnects from Tailscale.
  1081. func TestEngineReconfigOnStateChange(t *testing.T) {
  1082. enableLogging := false
  1083. connect := &ipn.MaskedPrefs{Prefs: ipn.Prefs{WantRunning: true}, WantRunningSet: true}
  1084. disconnect := &ipn.MaskedPrefs{Prefs: ipn.Prefs{WantRunning: false}, WantRunningSet: true}
  1085. node1 := buildNetmapWithPeers(
  1086. makePeer(1, withName("node-1"), withAddresses(netip.MustParsePrefix("100.64.1.1/32"))),
  1087. )
  1088. node2 := buildNetmapWithPeers(
  1089. makePeer(2, withName("node-2"), withAddresses(netip.MustParsePrefix("100.64.1.2/32"))),
  1090. )
  1091. node3 := buildNetmapWithPeers(
  1092. makePeer(3, withName("node-3"), withAddresses(netip.MustParsePrefix("100.64.1.3/32"))),
  1093. node1.SelfNode,
  1094. node2.SelfNode,
  1095. )
  1096. routesWithQuad100 := func(extra ...netip.Prefix) []netip.Prefix {
  1097. return append(extra, netip.MustParsePrefix("100.100.100.100/32"))
  1098. }
  1099. hostsFor := func(nm *netmap.NetworkMap) map[dnsname.FQDN][]netip.Addr {
  1100. var hosts map[dnsname.FQDN][]netip.Addr
  1101. appendNode := func(n tailcfg.NodeView) {
  1102. addrs := make([]netip.Addr, 0, n.Addresses().Len())
  1103. for _, addr := range n.Addresses().All() {
  1104. addrs = append(addrs, addr.Addr())
  1105. }
  1106. mak.Set(&hosts, must.Get(dnsname.ToFQDN(n.Name())), addrs)
  1107. }
  1108. if nm != nil && nm.SelfNode.Valid() {
  1109. appendNode(nm.SelfNode)
  1110. }
  1111. for _, n := range nm.Peers {
  1112. appendNode(n)
  1113. }
  1114. return hosts
  1115. }
  1116. tests := []struct {
  1117. name string
  1118. steps func(*testing.T, *LocalBackend, func() *mockControl)
  1119. wantState ipn.State
  1120. wantCfg *wgcfg.Config
  1121. wantRouterCfg *router.Config
  1122. wantDNSCfg *dns.Config
  1123. }{
  1124. {
  1125. name: "Initial",
  1126. // The configs are nil until the the LocalBackend is started.
  1127. wantState: ipn.NoState,
  1128. wantCfg: nil,
  1129. wantRouterCfg: nil,
  1130. wantDNSCfg: nil,
  1131. },
  1132. {
  1133. name: "Start",
  1134. steps: func(t *testing.T, lb *LocalBackend, _ func() *mockControl) {
  1135. mustDo(t)(lb.Start(ipn.Options{}))
  1136. },
  1137. // Once started, all configs must be reset and have their zero values.
  1138. wantState: ipn.NeedsLogin,
  1139. wantCfg: &wgcfg.Config{},
  1140. wantRouterCfg: &router.Config{},
  1141. wantDNSCfg: &dns.Config{},
  1142. },
  1143. {
  1144. name: "Start/Connect",
  1145. steps: func(t *testing.T, lb *LocalBackend, _ func() *mockControl) {
  1146. mustDo(t)(lb.Start(ipn.Options{}))
  1147. mustDo2(t)(lb.EditPrefs(connect))
  1148. },
  1149. // Same if WantRunning is true, but the auth is not completed yet.
  1150. wantState: ipn.NeedsLogin,
  1151. wantCfg: &wgcfg.Config{},
  1152. wantRouterCfg: &router.Config{},
  1153. wantDNSCfg: &dns.Config{},
  1154. },
  1155. {
  1156. name: "Start/Connect/Login",
  1157. steps: func(t *testing.T, lb *LocalBackend, cc func() *mockControl) {
  1158. mustDo(t)(lb.Start(ipn.Options{}))
  1159. mustDo2(t)(lb.EditPrefs(connect))
  1160. cc().authenticated(node1)
  1161. },
  1162. // After the auth is completed, the configs must be updated to reflect the node's netmap.
  1163. wantState: ipn.Starting,
  1164. wantCfg: &wgcfg.Config{
  1165. Peers: []wgcfg.Peer{},
  1166. Addresses: node1.SelfNode.Addresses().AsSlice(),
  1167. },
  1168. wantRouterCfg: &router.Config{
  1169. SNATSubnetRoutes: true,
  1170. NetfilterMode: preftype.NetfilterOn,
  1171. LocalAddrs: node1.SelfNode.Addresses().AsSlice(),
  1172. Routes: routesWithQuad100(),
  1173. },
  1174. wantDNSCfg: &dns.Config{
  1175. Routes: map[dnsname.FQDN][]*dnstype.Resolver{},
  1176. Hosts: hostsFor(node1),
  1177. },
  1178. },
  1179. {
  1180. name: "Start/Connect/Login/Disconnect",
  1181. steps: func(t *testing.T, lb *LocalBackend, cc func() *mockControl) {
  1182. mustDo(t)(lb.Start(ipn.Options{}))
  1183. mustDo2(t)(lb.EditPrefs(connect))
  1184. cc().authenticated(node1)
  1185. mustDo2(t)(lb.EditPrefs(disconnect))
  1186. },
  1187. // After disconnecting, all configs must be reset and have their zero values.
  1188. wantState: ipn.Stopped,
  1189. wantCfg: &wgcfg.Config{},
  1190. wantRouterCfg: &router.Config{},
  1191. wantDNSCfg: &dns.Config{},
  1192. },
  1193. {
  1194. name: "Start/Connect/Login/NewProfile",
  1195. steps: func(t *testing.T, lb *LocalBackend, cc func() *mockControl) {
  1196. mustDo(t)(lb.Start(ipn.Options{}))
  1197. mustDo2(t)(lb.EditPrefs(connect))
  1198. cc().authenticated(node1)
  1199. mustDo(t)(lb.NewProfile())
  1200. },
  1201. // After switching to a new, empty profile, all configs should be reset
  1202. // and have their zero values until the auth is completed.
  1203. wantState: ipn.NeedsLogin,
  1204. wantCfg: &wgcfg.Config{},
  1205. wantRouterCfg: &router.Config{},
  1206. wantDNSCfg: &dns.Config{},
  1207. },
  1208. {
  1209. name: "Start/Connect/Login/NewProfile/Login",
  1210. steps: func(t *testing.T, lb *LocalBackend, cc func() *mockControl) {
  1211. mustDo(t)(lb.Start(ipn.Options{}))
  1212. mustDo2(t)(lb.EditPrefs(connect))
  1213. cc().authenticated(node1)
  1214. mustDo(t)(lb.NewProfile())
  1215. mustDo2(t)(lb.EditPrefs(connect))
  1216. cc().authenticated(node2)
  1217. },
  1218. // Once the auth is completed, the configs must be updated to reflect the node's netmap.
  1219. wantState: ipn.Starting,
  1220. wantCfg: &wgcfg.Config{
  1221. Peers: []wgcfg.Peer{},
  1222. Addresses: node2.SelfNode.Addresses().AsSlice(),
  1223. },
  1224. wantRouterCfg: &router.Config{
  1225. SNATSubnetRoutes: true,
  1226. NetfilterMode: preftype.NetfilterOn,
  1227. LocalAddrs: node2.SelfNode.Addresses().AsSlice(),
  1228. Routes: routesWithQuad100(),
  1229. },
  1230. wantDNSCfg: &dns.Config{
  1231. Routes: map[dnsname.FQDN][]*dnstype.Resolver{},
  1232. Hosts: hostsFor(node2),
  1233. },
  1234. },
  1235. {
  1236. name: "Start/Connect/Login/SwitchProfile",
  1237. steps: func(t *testing.T, lb *LocalBackend, cc func() *mockControl) {
  1238. mustDo(t)(lb.Start(ipn.Options{}))
  1239. mustDo2(t)(lb.EditPrefs(connect))
  1240. cc().authenticated(node1)
  1241. profileID := lb.CurrentProfile().ID()
  1242. mustDo(t)(lb.NewProfile())
  1243. cc().authenticated(node2)
  1244. mustDo(t)(lb.SwitchProfile(profileID))
  1245. },
  1246. // After switching to an existing profile, all configs must be reset
  1247. // and have their zero values until the (non-interactive) login is completed.
  1248. wantState: ipn.NoState,
  1249. wantCfg: &wgcfg.Config{},
  1250. wantRouterCfg: &router.Config{},
  1251. wantDNSCfg: &dns.Config{},
  1252. },
  1253. {
  1254. name: "Start/Connect/Login/SwitchProfile/NonInteractiveLogin",
  1255. steps: func(t *testing.T, lb *LocalBackend, cc func() *mockControl) {
  1256. mustDo(t)(lb.Start(ipn.Options{}))
  1257. mustDo2(t)(lb.EditPrefs(connect))
  1258. cc().authenticated(node1)
  1259. profileID := lb.CurrentProfile().ID()
  1260. mustDo(t)(lb.NewProfile())
  1261. cc().authenticated(node2)
  1262. mustDo(t)(lb.SwitchProfile(profileID))
  1263. cc().authenticated(node1) // complete the login
  1264. },
  1265. // After switching profiles and completing the auth, the configs
  1266. // must be updated to reflect the node's netmap.
  1267. wantState: ipn.Starting,
  1268. wantCfg: &wgcfg.Config{
  1269. Peers: []wgcfg.Peer{},
  1270. Addresses: node1.SelfNode.Addresses().AsSlice(),
  1271. },
  1272. wantRouterCfg: &router.Config{
  1273. SNATSubnetRoutes: true,
  1274. NetfilterMode: preftype.NetfilterOn,
  1275. LocalAddrs: node1.SelfNode.Addresses().AsSlice(),
  1276. Routes: routesWithQuad100(),
  1277. },
  1278. wantDNSCfg: &dns.Config{
  1279. Routes: map[dnsname.FQDN][]*dnstype.Resolver{},
  1280. Hosts: hostsFor(node1),
  1281. },
  1282. },
  1283. {
  1284. name: "Start/Connect/Login/WithPeers",
  1285. steps: func(t *testing.T, lb *LocalBackend, cc func() *mockControl) {
  1286. mustDo(t)(lb.Start(ipn.Options{}))
  1287. mustDo2(t)(lb.EditPrefs(connect))
  1288. cc().authenticated(node3)
  1289. },
  1290. wantState: ipn.Starting,
  1291. wantCfg: &wgcfg.Config{
  1292. Peers: []wgcfg.Peer{
  1293. {
  1294. PublicKey: node1.SelfNode.Key(),
  1295. DiscoKey: node1.SelfNode.DiscoKey(),
  1296. },
  1297. {
  1298. PublicKey: node2.SelfNode.Key(),
  1299. DiscoKey: node2.SelfNode.DiscoKey(),
  1300. },
  1301. },
  1302. Addresses: node3.SelfNode.Addresses().AsSlice(),
  1303. },
  1304. wantRouterCfg: &router.Config{
  1305. SNATSubnetRoutes: true,
  1306. NetfilterMode: preftype.NetfilterOn,
  1307. LocalAddrs: node3.SelfNode.Addresses().AsSlice(),
  1308. Routes: routesWithQuad100(),
  1309. },
  1310. wantDNSCfg: &dns.Config{
  1311. Routes: map[dnsname.FQDN][]*dnstype.Resolver{},
  1312. Hosts: hostsFor(node3),
  1313. },
  1314. },
  1315. {
  1316. name: "Start/Connect/Login/Expire",
  1317. steps: func(t *testing.T, lb *LocalBackend, cc func() *mockControl) {
  1318. mustDo(t)(lb.Start(ipn.Options{}))
  1319. mustDo2(t)(lb.EditPrefs(connect))
  1320. cc().authenticated(node1)
  1321. cc().send(sendOpt{nm: &netmap.NetworkMap{
  1322. SelfNode: (&tailcfg.Node{
  1323. KeyExpiry: time.Now().Add(-time.Minute),
  1324. }).View(),
  1325. }})
  1326. },
  1327. wantState: ipn.NeedsLogin,
  1328. wantCfg: &wgcfg.Config{},
  1329. wantRouterCfg: &router.Config{},
  1330. wantDNSCfg: &dns.Config{},
  1331. },
  1332. {
  1333. name: "Start/Connect/Login/InitReauth",
  1334. steps: func(t *testing.T, lb *LocalBackend, cc func() *mockControl) {
  1335. mustDo(t)(lb.Start(ipn.Options{}))
  1336. mustDo2(t)(lb.EditPrefs(connect))
  1337. cc().authenticated(node1)
  1338. // Start the re-auth process:
  1339. lb.StartLoginInteractive(context.Background())
  1340. cc().sendAuthURL(node1)
  1341. },
  1342. // Without seamless renewal, even starting a reauth tears down everything:
  1343. wantState: ipn.Starting,
  1344. wantCfg: &wgcfg.Config{},
  1345. wantRouterCfg: &router.Config{},
  1346. wantDNSCfg: &dns.Config{},
  1347. },
  1348. {
  1349. name: "Start/Connect/Login/InitReauth/Login",
  1350. steps: func(t *testing.T, lb *LocalBackend, cc func() *mockControl) {
  1351. mustDo(t)(lb.Start(ipn.Options{}))
  1352. mustDo2(t)(lb.EditPrefs(connect))
  1353. cc().authenticated(node1)
  1354. // Start the re-auth process:
  1355. lb.StartLoginInteractive(context.Background())
  1356. cc().sendAuthURL(node1)
  1357. // Complete the re-auth process:
  1358. cc().authenticated(node1)
  1359. },
  1360. wantState: ipn.Starting,
  1361. wantCfg: &wgcfg.Config{
  1362. Peers: []wgcfg.Peer{},
  1363. Addresses: node1.SelfNode.Addresses().AsSlice(),
  1364. },
  1365. wantRouterCfg: &router.Config{
  1366. SNATSubnetRoutes: true,
  1367. NetfilterMode: preftype.NetfilterOn,
  1368. LocalAddrs: node1.SelfNode.Addresses().AsSlice(),
  1369. Routes: routesWithQuad100(),
  1370. },
  1371. wantDNSCfg: &dns.Config{
  1372. Routes: map[dnsname.FQDN][]*dnstype.Resolver{},
  1373. Hosts: hostsFor(node1),
  1374. },
  1375. },
  1376. {
  1377. name: "Seamless/Start/Connect/Login/InitReauth",
  1378. steps: func(t *testing.T, lb *LocalBackend, cc func() *mockControl) {
  1379. lb.ControlKnobs().SeamlessKeyRenewal.Store(true)
  1380. mustDo(t)(lb.Start(ipn.Options{}))
  1381. mustDo2(t)(lb.EditPrefs(connect))
  1382. cc().authenticated(node1)
  1383. // Start the re-auth process:
  1384. lb.StartLoginInteractive(context.Background())
  1385. cc().sendAuthURL(node1)
  1386. },
  1387. // With seamless renewal, starting a reauth should leave everything up:
  1388. wantState: ipn.Starting,
  1389. wantCfg: &wgcfg.Config{
  1390. Peers: []wgcfg.Peer{},
  1391. Addresses: node1.SelfNode.Addresses().AsSlice(),
  1392. },
  1393. wantRouterCfg: &router.Config{
  1394. SNATSubnetRoutes: true,
  1395. NetfilterMode: preftype.NetfilterOn,
  1396. LocalAddrs: node1.SelfNode.Addresses().AsSlice(),
  1397. Routes: routesWithQuad100(),
  1398. },
  1399. wantDNSCfg: &dns.Config{
  1400. Routes: map[dnsname.FQDN][]*dnstype.Resolver{},
  1401. Hosts: hostsFor(node1),
  1402. },
  1403. },
  1404. {
  1405. name: "Seamless/Start/Connect/Login/InitReauth/Login",
  1406. steps: func(t *testing.T, lb *LocalBackend, cc func() *mockControl) {
  1407. lb.ControlKnobs().SeamlessKeyRenewal.Store(true)
  1408. mustDo(t)(lb.Start(ipn.Options{}))
  1409. mustDo2(t)(lb.EditPrefs(connect))
  1410. cc().authenticated(node1)
  1411. // Start the re-auth process:
  1412. lb.StartLoginInteractive(context.Background())
  1413. cc().sendAuthURL(node1)
  1414. // Complete the re-auth process:
  1415. cc().authenticated(node1)
  1416. },
  1417. wantState: ipn.Starting,
  1418. wantCfg: &wgcfg.Config{
  1419. Peers: []wgcfg.Peer{},
  1420. Addresses: node1.SelfNode.Addresses().AsSlice(),
  1421. },
  1422. wantRouterCfg: &router.Config{
  1423. SNATSubnetRoutes: true,
  1424. NetfilterMode: preftype.NetfilterOn,
  1425. LocalAddrs: node1.SelfNode.Addresses().AsSlice(),
  1426. Routes: routesWithQuad100(),
  1427. },
  1428. wantDNSCfg: &dns.Config{
  1429. Routes: map[dnsname.FQDN][]*dnstype.Resolver{},
  1430. Hosts: hostsFor(node1),
  1431. },
  1432. },
  1433. {
  1434. name: "Seamless/Start/Connect/Login/Expire",
  1435. steps: func(t *testing.T, lb *LocalBackend, cc func() *mockControl) {
  1436. lb.ControlKnobs().SeamlessKeyRenewal.Store(true)
  1437. mustDo(t)(lb.Start(ipn.Options{}))
  1438. mustDo2(t)(lb.EditPrefs(connect))
  1439. cc().authenticated(node1)
  1440. cc().send(sendOpt{nm: &netmap.NetworkMap{
  1441. SelfNode: (&tailcfg.Node{
  1442. KeyExpiry: time.Now().Add(-time.Minute),
  1443. }).View(),
  1444. }})
  1445. },
  1446. // Even with seamless, if the key we are using expires, we want to disconnect:
  1447. wantState: ipn.NeedsLogin,
  1448. wantCfg: &wgcfg.Config{},
  1449. wantRouterCfg: &router.Config{},
  1450. wantDNSCfg: &dns.Config{},
  1451. },
  1452. }
  1453. for _, tt := range tests {
  1454. t.Run(tt.name, func(t *testing.T) {
  1455. lb, engine, cc := newLocalBackendWithMockEngineAndControl(t, enableLogging)
  1456. if tt.steps != nil {
  1457. tt.steps(t, lb, cc)
  1458. }
  1459. // TODO(bradfitz): this whole event bus settling thing
  1460. // should be unnecessary once the bogus uses of eventbus
  1461. // are removed. (https://github.com/tailscale/tailscale/issues/16369)
  1462. lb.settleEventBus()
  1463. if gotState := lb.State(); gotState != tt.wantState {
  1464. t.Errorf("State: got %v; want %v", gotState, tt.wantState)
  1465. }
  1466. if engine.Config() != nil {
  1467. for _, p := range engine.Config().Peers {
  1468. pKey := p.PublicKey.UntypedHexString()
  1469. _, err := lb.MagicConn().ParseEndpoint(pKey)
  1470. if err != nil {
  1471. t.Errorf("ParseEndpoint(%q) failed: %v", pKey, err)
  1472. }
  1473. }
  1474. }
  1475. opts := []cmp.Option{
  1476. cmpopts.EquateComparable(key.NodePublic{}, key.DiscoPublic{}, netip.Addr{}, netip.Prefix{}),
  1477. }
  1478. if diff := cmp.Diff(tt.wantCfg, engine.Config(), opts...); diff != "" {
  1479. t.Errorf("wgcfg.Config(+got -want): %v", diff)
  1480. }
  1481. if diff := cmp.Diff(tt.wantRouterCfg, engine.RouterConfig(), opts...); diff != "" {
  1482. t.Errorf("router.Config(+got -want): %v", diff)
  1483. }
  1484. if diff := cmp.Diff(tt.wantDNSCfg, engine.DNSConfig(), opts...); diff != "" {
  1485. t.Errorf("dns.Config(+got -want): %v", diff)
  1486. }
  1487. })
  1488. }
  1489. }
  1490. // TestSendPreservesAuthURL tests that wgengine updates arriving in the middle of
  1491. // processing an auth URL doesn't result in the auth URL being cleared.
  1492. func TestSendPreservesAuthURL(t *testing.T) {
  1493. runTestSendPreservesAuthURL(t, false)
  1494. }
  1495. func TestSendPreservesAuthURLSeamless(t *testing.T) {
  1496. runTestSendPreservesAuthURL(t, true)
  1497. }
  1498. func runTestSendPreservesAuthURL(t *testing.T, seamless bool) {
  1499. var cc *mockControl
  1500. b := newLocalBackendWithTestControl(t, true, func(tb testing.TB, opts controlclient.Options) controlclient.Client {
  1501. cc = newClient(t, opts)
  1502. return cc
  1503. })
  1504. t.Logf("Start")
  1505. b.Start(ipn.Options{
  1506. UpdatePrefs: &ipn.Prefs{
  1507. WantRunning: true,
  1508. ControlURL: "https://localhost:1/",
  1509. },
  1510. })
  1511. t.Logf("LoginFinished")
  1512. cc.persist.UserProfile.LoginName = "user1"
  1513. cc.persist.NodeID = "node1"
  1514. if seamless {
  1515. b.sys.ControlKnobs().SeamlessKeyRenewal.Store(true)
  1516. }
  1517. cc.send(sendOpt{loginFinished: true, nm: &netmap.NetworkMap{
  1518. SelfNode: (&tailcfg.Node{MachineAuthorized: true}).View(),
  1519. }})
  1520. t.Logf("Running")
  1521. b.setWgengineStatus(&wgengine.Status{AsOf: time.Now(), DERPs: 1}, nil)
  1522. t.Logf("Re-auth (StartLoginInteractive)")
  1523. b.StartLoginInteractive(t.Context())
  1524. t.Logf("Re-auth (receive URL)")
  1525. url1 := "https://localhost:1/1"
  1526. cc.send(sendOpt{url: url1})
  1527. // Don't need to wait on anything else - once .send completes, authURL should
  1528. // be set, and once .send has completed, any opportunities for a WG engine
  1529. // status update to trample it have ended as well.
  1530. if b.authURL == "" {
  1531. t.Fatalf("expected authURL to be set")
  1532. } else {
  1533. t.Log("authURL was set")
  1534. }
  1535. }
  1536. func buildNetmapWithPeers(self tailcfg.NodeView, peers ...tailcfg.NodeView) *netmap.NetworkMap {
  1537. const (
  1538. firstAutoUserID = tailcfg.UserID(10000)
  1539. domain = "example.com"
  1540. magicDNSSuffix = ".test.ts.net"
  1541. )
  1542. users := make(map[tailcfg.UserID]tailcfg.UserProfileView)
  1543. makeUserForNode := func(n *tailcfg.Node) {
  1544. var user *tailcfg.UserProfile
  1545. if n.User == 0 {
  1546. n.User = firstAutoUserID + tailcfg.UserID(n.ID)
  1547. user = &tailcfg.UserProfile{
  1548. DisplayName: n.Name,
  1549. LoginName: n.Name,
  1550. }
  1551. } else if _, ok := users[n.User]; !ok {
  1552. user = &tailcfg.UserProfile{
  1553. DisplayName: fmt.Sprintf("User %d", n.User),
  1554. LoginName: fmt.Sprintf("user-%d", n.User),
  1555. }
  1556. }
  1557. if user != nil {
  1558. user.ID = n.User
  1559. user.LoginName = strings.Join([]string{user.LoginName, domain}, "@")
  1560. users[n.User] = user.View()
  1561. }
  1562. }
  1563. derpmap := &tailcfg.DERPMap{
  1564. Regions: make(map[int]*tailcfg.DERPRegion),
  1565. }
  1566. makeDERPRegionForNode := func(n *tailcfg.Node) {
  1567. if n.HomeDERP == 0 {
  1568. return // no DERP region
  1569. }
  1570. if _, ok := derpmap.Regions[n.HomeDERP]; !ok {
  1571. r := &tailcfg.DERPRegion{
  1572. RegionID: n.HomeDERP,
  1573. RegionName: fmt.Sprintf("Region %d", n.HomeDERP),
  1574. }
  1575. r.Nodes = append(r.Nodes, &tailcfg.DERPNode{
  1576. Name: fmt.Sprintf("%da", n.HomeDERP),
  1577. RegionID: n.HomeDERP,
  1578. })
  1579. derpmap.Regions[n.HomeDERP] = r
  1580. }
  1581. }
  1582. updateNode := func(n tailcfg.NodeView) tailcfg.NodeView {
  1583. mut := n.AsStruct()
  1584. makeUserForNode(mut)
  1585. makeDERPRegionForNode(mut)
  1586. mut.Name = mut.Name + magicDNSSuffix
  1587. return mut.View()
  1588. }
  1589. self = updateNode(self)
  1590. for i := range peers {
  1591. peers[i] = updateNode(peers[i])
  1592. }
  1593. return &netmap.NetworkMap{
  1594. SelfNode: self,
  1595. Domain: domain,
  1596. Peers: peers,
  1597. UserProfiles: users,
  1598. DERPMap: derpmap,
  1599. }
  1600. }
  1601. func mustDo(t *testing.T) func(error) {
  1602. t.Helper()
  1603. return func(err error) {
  1604. t.Helper()
  1605. if err != nil {
  1606. t.Fatal(err)
  1607. }
  1608. }
  1609. }
  1610. func mustDo2(t *testing.T) func(any, error) {
  1611. t.Helper()
  1612. return func(_ any, err error) {
  1613. t.Helper()
  1614. if err != nil {
  1615. t.Fatal(err)
  1616. }
  1617. }
  1618. }
  1619. func newLocalBackendWithMockEngineAndControl(t *testing.T, enableLogging bool) (*LocalBackend, *mockEngine, func() *mockControl) {
  1620. t.Helper()
  1621. logf := logger.Discard
  1622. if enableLogging {
  1623. logf = tstest.WhileTestRunningLogger(t)
  1624. }
  1625. dialer := &tsdial.Dialer{Logf: logf}
  1626. dialer.SetNetMon(netmon.NewStatic())
  1627. bus := eventbustest.NewBus(t)
  1628. sys := tsd.NewSystemWithBus(bus)
  1629. sys.Set(dialer)
  1630. sys.Set(dialer.NetMon())
  1631. dialer.SetBus(bus)
  1632. magicConn, err := magicsock.NewConn(magicsock.Options{
  1633. Logf: logf,
  1634. EventBus: sys.Bus.Get(),
  1635. NetMon: dialer.NetMon(),
  1636. Metrics: sys.UserMetricsRegistry(),
  1637. HealthTracker: sys.HealthTracker.Get(),
  1638. DisablePortMapper: true,
  1639. })
  1640. if err != nil {
  1641. t.Fatalf("NewConn failed: %v", err)
  1642. }
  1643. magicConn.SetNetworkUp(dialer.NetMon().InterfaceState().AnyInterfaceUp())
  1644. sys.Set(magicConn)
  1645. engine := newMockEngine()
  1646. sys.Set(engine)
  1647. t.Cleanup(func() {
  1648. engine.Close()
  1649. <-engine.Done()
  1650. })
  1651. lb := newLocalBackendWithSysAndTestControl(t, enableLogging, sys, func(tb testing.TB, opts controlclient.Options) controlclient.Client {
  1652. return newClient(tb, opts)
  1653. })
  1654. return lb, engine, func() *mockControl { return lb.cc.(*mockControl) }
  1655. }
  1656. var _ wgengine.Engine = (*mockEngine)(nil)
  1657. // mockEngine implements [wgengine.Engine].
  1658. type mockEngine struct {
  1659. done chan struct{} // closed when Close is called
  1660. mu sync.Mutex // protects all following fields
  1661. closed bool
  1662. cfg *wgcfg.Config
  1663. routerCfg *router.Config
  1664. dnsCfg *dns.Config
  1665. filter, jailedFilter *filter.Filter
  1666. statusCb wgengine.StatusCallback
  1667. }
  1668. func newMockEngine() *mockEngine {
  1669. return &mockEngine{
  1670. done: make(chan struct{}),
  1671. }
  1672. }
  1673. func (e *mockEngine) Reconfig(cfg *wgcfg.Config, routerCfg *router.Config, dnsCfg *dns.Config) error {
  1674. e.mu.Lock()
  1675. defer e.mu.Unlock()
  1676. if e.closed {
  1677. return errors.New("engine closed")
  1678. }
  1679. e.cfg = cfg
  1680. e.routerCfg = routerCfg
  1681. e.dnsCfg = dnsCfg
  1682. return nil
  1683. }
  1684. func (e *mockEngine) Config() *wgcfg.Config {
  1685. e.mu.Lock()
  1686. defer e.mu.Unlock()
  1687. return e.cfg
  1688. }
  1689. func (e *mockEngine) RouterConfig() *router.Config {
  1690. e.mu.Lock()
  1691. defer e.mu.Unlock()
  1692. return e.routerCfg
  1693. }
  1694. func (e *mockEngine) DNSConfig() *dns.Config {
  1695. e.mu.Lock()
  1696. defer e.mu.Unlock()
  1697. return e.dnsCfg
  1698. }
  1699. func (e *mockEngine) PeerForIP(netip.Addr) (_ wgengine.PeerForIP, ok bool) {
  1700. return wgengine.PeerForIP{}, false
  1701. }
  1702. func (e *mockEngine) GetFilter() *filter.Filter {
  1703. e.mu.Lock()
  1704. defer e.mu.Unlock()
  1705. return e.filter
  1706. }
  1707. func (e *mockEngine) SetFilter(f *filter.Filter) {
  1708. e.mu.Lock()
  1709. e.filter = f
  1710. e.mu.Unlock()
  1711. }
  1712. func (e *mockEngine) GetJailedFilter() *filter.Filter {
  1713. e.mu.Lock()
  1714. defer e.mu.Unlock()
  1715. return e.jailedFilter
  1716. }
  1717. func (e *mockEngine) SetJailedFilter(f *filter.Filter) {
  1718. e.mu.Lock()
  1719. e.jailedFilter = f
  1720. e.mu.Unlock()
  1721. }
  1722. func (e *mockEngine) SetStatusCallback(cb wgengine.StatusCallback) {
  1723. e.mu.Lock()
  1724. e.statusCb = cb
  1725. e.mu.Unlock()
  1726. }
  1727. func (e *mockEngine) RequestStatus() {
  1728. e.mu.Lock()
  1729. cb := e.statusCb
  1730. e.mu.Unlock()
  1731. if cb != nil {
  1732. cb(&wgengine.Status{AsOf: time.Now()}, nil)
  1733. }
  1734. }
  1735. func (e *mockEngine) ResetAndStop() (*wgengine.Status, error) {
  1736. err := e.Reconfig(&wgcfg.Config{}, &router.Config{}, &dns.Config{})
  1737. if err != nil {
  1738. return nil, err
  1739. }
  1740. return &wgengine.Status{AsOf: time.Now()}, nil
  1741. }
  1742. func (e *mockEngine) PeerByKey(key.NodePublic) (_ wgint.Peer, ok bool) {
  1743. return wgint.Peer{}, false
  1744. }
  1745. func (e *mockEngine) SetNetworkMap(*netmap.NetworkMap) {}
  1746. func (e *mockEngine) UpdateStatus(*ipnstate.StatusBuilder) {}
  1747. func (e *mockEngine) Ping(ip netip.Addr, pingType tailcfg.PingType, size int, cb func(*ipnstate.PingResult)) {
  1748. cb(&ipnstate.PingResult{IP: ip.String(), Err: "not implemented"})
  1749. }
  1750. func (e *mockEngine) InstallCaptureHook(packet.CaptureCallback) {}
  1751. func (e *mockEngine) Close() {
  1752. e.mu.Lock()
  1753. defer e.mu.Unlock()
  1754. if e.closed {
  1755. return
  1756. }
  1757. e.closed = true
  1758. close(e.done)
  1759. }
  1760. func (e *mockEngine) Done() <-chan struct{} {
  1761. return e.done
  1762. }
  1763. // hasValidNetMap returns true if the backend has a valid network map with a valid self node.
  1764. func hasValidNetMap(b *LocalBackend) bool {
  1765. nm := b.NetMap()
  1766. return nm != nil && nm.SelfNode.Valid()
  1767. }
  1768. // needsLogin returns true if the backend needs user login action.
  1769. // This is true when logged out, when an auth URL is present (interactive login in progress),
  1770. // or when the node key has expired.
  1771. func needsLogin(b *LocalBackend) bool {
  1772. // Note: b.Prefs() handles its own locking, so we lock only for authURL and keyExpired access
  1773. b.mu.Lock()
  1774. authURL := b.authURL
  1775. keyExpired := b.keyExpired
  1776. b.mu.Unlock()
  1777. return b.Prefs().LoggedOut() || authURL != "" || keyExpired
  1778. }
  1779. // needsMachineAuth returns true if the user has logged in but the machine is not yet authorized.
  1780. // This includes the case where we have a netmap but no valid SelfNode yet (empty netmap after initial login).
  1781. func needsMachineAuth(b *LocalBackend) bool {
  1782. // Note: b.NetMap() and b.Prefs() handle their own locking
  1783. nm := b.NetMap()
  1784. prefs := b.Prefs()
  1785. if prefs.LoggedOut() || nm == nil {
  1786. return false
  1787. }
  1788. // If we have a valid SelfNode, check its MachineAuthorized status
  1789. if nm.SelfNode.Valid() {
  1790. return !nm.SelfNode.MachineAuthorized()
  1791. }
  1792. // Empty netmap (no SelfNode yet) after login also means we need machine auth
  1793. return true
  1794. }
  1795. // hasAuthURL returns true if an authentication URL is present (user needs to visit a URL).
  1796. func hasAuthURL(b *LocalBackend) bool {
  1797. b.mu.Lock()
  1798. authURL := b.authURL
  1799. b.mu.Unlock()
  1800. return authURL != ""
  1801. }
  1802. // canRouteTraffic returns true if the backend is capable of routing traffic.
  1803. // This requires a valid netmap, machine authorization, and WantRunning preference.
  1804. func canRouteTraffic(b *LocalBackend) bool {
  1805. // Note: b.NetMap() and b.Prefs() handle their own locking
  1806. nm := b.NetMap()
  1807. prefs := b.Prefs()
  1808. return nm != nil &&
  1809. nm.SelfNode.Valid() &&
  1810. nm.SelfNode.MachineAuthorized() &&
  1811. prefs.WantRunning()
  1812. }
  1813. // isFullyAuthenticated returns true if the user has completed login and no auth URL is pending.
  1814. func isFullyAuthenticated(b *LocalBackend) bool {
  1815. // Note: b.Prefs() handles its own locking, so we lock only for authURL access
  1816. b.mu.Lock()
  1817. authURL := b.authURL
  1818. b.mu.Unlock()
  1819. return !b.Prefs().LoggedOut() && authURL == ""
  1820. }
  1821. // isWantRunning returns true if the WantRunning preference is set.
  1822. func isWantRunning(b *LocalBackend) bool {
  1823. return b.Prefs().WantRunning()
  1824. }
  1825. // isLoggedIn returns true if the user is logged in (not logged out).
  1826. func isLoggedIn(b *LocalBackend) bool {
  1827. return !b.Prefs().LoggedOut()
  1828. }