server.go 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578
  1. // Copyright (c) Tailscale Inc & AUTHORS
  2. // SPDX-License-Identifier: BSD-3-Clause
  3. // Package ipnserver runs the LocalAPI HTTP server that communicates
  4. // with the LocalBackend.
  5. package ipnserver
  6. import (
  7. "context"
  8. "encoding/base64"
  9. "encoding/json"
  10. "errors"
  11. "fmt"
  12. "io"
  13. "net"
  14. "net/http"
  15. "os/user"
  16. "runtime"
  17. "strconv"
  18. "strings"
  19. "sync"
  20. "sync/atomic"
  21. "unicode"
  22. "tailscale.com/client/tailscale/apitype"
  23. "tailscale.com/envknob"
  24. "tailscale.com/feature"
  25. "tailscale.com/feature/buildfeatures"
  26. "tailscale.com/ipn/ipnauth"
  27. "tailscale.com/ipn/ipnlocal"
  28. "tailscale.com/ipn/localapi"
  29. "tailscale.com/net/netmon"
  30. "tailscale.com/types/logger"
  31. "tailscale.com/types/logid"
  32. "tailscale.com/util/eventbus"
  33. "tailscale.com/util/mak"
  34. "tailscale.com/util/set"
  35. "tailscale.com/util/testenv"
  36. )
  37. // Server is an IPN backend and its set of 0 or more active localhost
  38. // TCP or unix socket connections talking to that backend.
  39. type Server struct {
  40. lb atomic.Pointer[ipnlocal.LocalBackend]
  41. logf logger.Logf
  42. bus *eventbus.Bus
  43. netMon *netmon.Monitor // must be non-nil
  44. backendLogID logid.PublicID
  45. // mu guards the fields that follow.
  46. // lock order: mu, then LocalBackend.mu
  47. mu sync.Mutex
  48. activeReqs map[*http.Request]ipnauth.Actor
  49. backendWaiter waiterSet // of LocalBackend waiters
  50. zeroReqWaiter waiterSet // of blockUntilZeroConnections waiters
  51. }
  52. func (s *Server) mustBackend() *ipnlocal.LocalBackend {
  53. lb := s.lb.Load()
  54. if lb == nil {
  55. panic("unexpected: call to mustBackend in path where SetLocalBackend should've been called")
  56. }
  57. return lb
  58. }
  59. // waiterSet is a set of callers waiting on something. Each item (map value) in
  60. // the set is a func that wakes up that waiter's context. The waiter is responsible
  61. // for removing itself from the set when woken up. The (*waiterSet).add method
  62. // returns a cleanup method which does that removal. The caller than defers that
  63. // cleanup.
  64. //
  65. // TODO(bradfitz): this is a generally useful pattern. Move elsewhere?
  66. type waiterSet set.HandleSet[context.CancelFunc]
  67. // add registers a new waiter in the set.
  68. // It acquires mu to add the waiter, and does so again when cleanup is called to remove it.
  69. // ready is closed when the waiter is ready (or ctx is done).
  70. func (s *waiterSet) add(mu *sync.Mutex, ctx context.Context) (ready <-chan struct{}, cleanup func()) {
  71. ctx, cancel := context.WithCancel(ctx)
  72. hs := (*set.HandleSet[context.CancelFunc])(s) // change method set
  73. mu.Lock()
  74. h := hs.Add(cancel)
  75. mu.Unlock()
  76. return ctx.Done(), func() {
  77. mu.Lock()
  78. delete(*hs, h)
  79. mu.Unlock()
  80. cancel()
  81. }
  82. }
  83. // wakeAll wakes up all waiters in the set.
  84. func (w waiterSet) wakeAll() {
  85. for _, cancel := range w {
  86. cancel() // they'll remove themselves
  87. }
  88. }
  89. func (s *Server) awaitBackend(ctx context.Context) (_ *ipnlocal.LocalBackend, ok bool) {
  90. lb := s.lb.Load()
  91. if lb != nil {
  92. return lb, true
  93. }
  94. ready, cleanup := s.backendWaiter.add(&s.mu, ctx)
  95. defer cleanup()
  96. // Try again, now that we've registered, in case there was a
  97. // race.
  98. lb = s.lb.Load()
  99. if lb != nil {
  100. return lb, true
  101. }
  102. <-ready
  103. lb = s.lb.Load()
  104. return lb, lb != nil
  105. }
  106. // serveServerStatus serves the /server-status endpoint which reports whether
  107. // the LocalBackend is up yet.
  108. // This is primarily for the Windows GUI, because wintun can take awhile to
  109. // come up. See https://github.com/tailscale/tailscale/issues/6522.
  110. func (s *Server) serveServerStatus(w http.ResponseWriter, r *http.Request) {
  111. if !buildfeatures.HasDebug && runtime.GOOS != "windows" {
  112. http.Error(w, feature.ErrUnavailable.Error(), http.StatusNotFound)
  113. return
  114. }
  115. ctx := r.Context()
  116. w.Header().Set("Content-Type", "application/json")
  117. var res struct {
  118. Error string `json:"error,omitempty"`
  119. }
  120. lb := s.lb.Load()
  121. if lb == nil {
  122. w.WriteHeader(http.StatusServiceUnavailable)
  123. if wait, _ := strconv.ParseBool(r.FormValue("wait")); wait {
  124. w.(http.Flusher).Flush()
  125. lb, _ = s.awaitBackend(ctx)
  126. }
  127. }
  128. if lb == nil {
  129. res.Error = "backend not ready"
  130. }
  131. json.NewEncoder(w).Encode(res)
  132. }
  133. func (s *Server) serveHTTP(w http.ResponseWriter, r *http.Request) {
  134. ctx := r.Context()
  135. if r.Method == "CONNECT" {
  136. if envknob.GOOS() == "windows" {
  137. // For the GUI client when using an exit node. See docs on handleProxyConnectConn.
  138. s.handleProxyConnectConn(w, r)
  139. } else {
  140. http.Error(w, "bad method for platform", http.StatusMethodNotAllowed)
  141. }
  142. return
  143. }
  144. // Check for this method before the awaitBackend call, as it reports whether
  145. // the backend is available.
  146. if r.Method == "GET" && r.URL.Path == "/server-status" {
  147. s.serveServerStatus(w, r)
  148. return
  149. }
  150. lb, ok := s.awaitBackend(ctx)
  151. if !ok {
  152. // Almost certainly because the context was canceled so the response
  153. // here doesn't really matter. The client is gone.
  154. http.Error(w, "no backend", http.StatusServiceUnavailable)
  155. return
  156. }
  157. ci, err := actorFromContext(r.Context())
  158. if err != nil {
  159. if errors.Is(err, errNoActor) {
  160. http.Error(w, "internal error: "+err.Error(), http.StatusInternalServerError)
  161. } else {
  162. http.Error(w, err.Error(), http.StatusUnauthorized)
  163. }
  164. return
  165. }
  166. onDone, err := s.addActiveHTTPRequest(r, ci)
  167. if err != nil {
  168. if ou, ok := err.(inUseOtherUserError); ok && localapi.InUseOtherUserIPNStream(w, r, ou.Unwrap()) {
  169. w.(http.Flusher).Flush()
  170. s.blockWhileIdentityInUse(ctx, ci)
  171. return
  172. }
  173. http.Error(w, err.Error(), http.StatusUnauthorized)
  174. return
  175. }
  176. defer onDone()
  177. if strings.HasPrefix(r.URL.Path, "/localapi/") {
  178. if actor, ok := ci.(*actor); ok {
  179. reason, err := base64.StdEncoding.DecodeString(r.Header.Get(apitype.RequestReasonHeader))
  180. if err != nil {
  181. http.Error(w, "invalid reason header", http.StatusBadRequest)
  182. return
  183. }
  184. ci = actorWithAccessOverride(actor, string(reason))
  185. }
  186. lah := localapi.NewHandler(localapi.HandlerConfig{
  187. Actor: ci,
  188. Backend: lb,
  189. Logf: s.logf,
  190. LogID: s.backendLogID,
  191. EventBus: lb.Sys().Bus.Get(),
  192. })
  193. if actor, ok := ci.(*actor); ok {
  194. lah.PermitRead, lah.PermitWrite = actor.Permissions(lb.OperatorUserID())
  195. lah.PermitCert = actor.CanFetchCerts()
  196. } else if testenv.InTest() {
  197. lah.PermitRead, lah.PermitWrite = true, true
  198. }
  199. lah.ServeHTTP(w, r)
  200. return
  201. }
  202. if r.URL.Path != "/" {
  203. http.NotFound(w, r)
  204. return
  205. }
  206. if envknob.GOOS() == "windows" {
  207. // TODO(bradfitz): remove this once we moved to named pipes for LocalAPI
  208. // on Windows. This could then move to all platforms instead at
  209. // 100.100.100.100 or something (quad100 handler in LocalAPI)
  210. s.ServeHTMLStatus(w, r)
  211. return
  212. }
  213. io.WriteString(w, "<html><title>Tailscale</title><body><h1>Tailscale</h1>This is the local Tailscale daemon.\n")
  214. }
  215. // inUseOtherUserError is the error type for when the server is in use
  216. // by a different local user.
  217. type inUseOtherUserError struct{ error }
  218. func (e inUseOtherUserError) Unwrap() error { return e.error }
  219. // checkConnIdentityLocked checks whether the provided identity is
  220. // allowed to connect to the server.
  221. //
  222. // The returned error, when non-nil, will be of type inUseOtherUserError.
  223. //
  224. // s.mu must be held.
  225. func (s *Server) checkConnIdentityLocked(ci ipnauth.Actor) error {
  226. // If clients are already connected, verify they're the same user.
  227. // This mostly matters on Windows at the moment.
  228. if len(s.activeReqs) > 0 {
  229. var active ipnauth.Actor
  230. for _, active = range s.activeReqs {
  231. break
  232. }
  233. if active != nil {
  234. // Always allow Windows SYSTEM user to connect,
  235. // even if Tailscale is currently being used by another user.
  236. if ci.IsLocalSystem() {
  237. return nil
  238. }
  239. if ci.UserID() != active.UserID() {
  240. var b strings.Builder
  241. b.WriteString("Tailscale already in use")
  242. if username, err := active.Username(); err == nil {
  243. fmt.Fprintf(&b, " by %s", username)
  244. }
  245. if active, ok := active.(*actor); ok {
  246. fmt.Fprintf(&b, ", pid %d", active.pid())
  247. }
  248. return inUseOtherUserError{errors.New(b.String())}
  249. }
  250. }
  251. }
  252. if err := s.mustBackend().CheckIPNConnectionAllowed(ci); err != nil {
  253. return inUseOtherUserError{err}
  254. }
  255. return nil
  256. }
  257. // blockWhileIdentityInUse blocks while ci can't connect to the server because
  258. // the server is in use by a different user.
  259. //
  260. // This is primarily used for the Windows GUI, to block until one user's done
  261. // controlling the tailscaled process.
  262. func (s *Server) blockWhileIdentityInUse(ctx context.Context, actor ipnauth.Actor) error {
  263. inUse := func() bool {
  264. s.mu.Lock()
  265. defer s.mu.Unlock()
  266. _, ok := s.checkConnIdentityLocked(actor).(inUseOtherUserError)
  267. return ok
  268. }
  269. for inUse() {
  270. // Check whenever the connection count drops down to zero.
  271. ready, cleanup := s.zeroReqWaiter.add(&s.mu, ctx)
  272. if inUse() {
  273. // If the server was in use at the time of the initial check,
  274. // but disconnected and was removed from the activeReqs map
  275. // by the time we registered a waiter, the ready channel
  276. // will never be closed, resulting in a deadlock. To avoid
  277. // this, we can check again after registering the waiter.
  278. //
  279. // This method is planned for complete removal as part of the
  280. // multi-user improvements in tailscale/corp#18342,
  281. // and this approach should be fine as a temporary solution.
  282. <-ready
  283. }
  284. cleanup()
  285. if err := ctx.Err(); err != nil {
  286. return err
  287. }
  288. }
  289. return nil
  290. }
  291. // Permissions returns the actor's permissions for accessing
  292. // the Tailscale local daemon API. The operatorUID is only used on
  293. // Unix-like platforms and specifies the ID of a local user
  294. // (in the os/user.User.Uid string form) who is allowed
  295. // to operate tailscaled without being root or using sudo.
  296. //
  297. // Sandboxed macos clients must directly supply, or be able to read,
  298. // an explicit token. Permission is inferred by validating that
  299. // token. Sandboxed macos clients also don't use ipnserver.actor at all
  300. // (and prior to that, they didn't use ipnauth.ConnIdentity)
  301. //
  302. // See safesocket and safesocket_darwin.
  303. func (a *actor) Permissions(operatorUID string) (read, write bool) {
  304. switch envknob.GOOS() {
  305. case "windows":
  306. // As of 2024-08-27, according to the current permission model,
  307. // Windows users always have read/write access to the local API if
  308. // they're allowed to connect. Whether a user is allowed to connect
  309. // is determined by [Server.checkConnIdentityLocked] when adding a
  310. // new connection in [Server.addActiveHTTPRequest]. Therefore, it's
  311. // acceptable to permit read and write access without any additional
  312. // checks here. Note that this permission model is being changed in
  313. // tailscale/corp#18342.
  314. return true, true
  315. case "js", "plan9":
  316. return true, true
  317. }
  318. if a.ci.IsUnixSock() {
  319. return true, !a.ci.IsReadonlyConn(operatorUID, logger.Discard)
  320. }
  321. return false, false
  322. }
  323. // userIDFromString maps from either a numeric user id in string form
  324. // ("998") or username ("caddy") to its string userid ("998").
  325. // It returns the empty string on error.
  326. func userIDFromString(v string) string {
  327. if v == "" || isAllDigit(v) {
  328. return v
  329. }
  330. u, err := user.Lookup(v)
  331. if err != nil {
  332. return ""
  333. }
  334. return u.Uid
  335. }
  336. func isAllDigit(s string) bool {
  337. for i := range len(s) {
  338. if b := s[i]; b < '0' || b > '9' {
  339. return false
  340. }
  341. }
  342. return true
  343. }
  344. // CanFetchCerts reports whether the actor is allowed to fetch HTTPS
  345. // certs from this server when it wouldn't otherwise be able to.
  346. //
  347. // That is, this reports whether the actor should grant additional
  348. // capabilities over what the actor would otherwise be able to do.
  349. //
  350. // For now this only returns true on Unix machines when
  351. // TS_PERMIT_CERT_UID is set the to the userid of the peer
  352. // connection. It's intended to give your non-root webserver access
  353. // (www-data, caddy, nginx, etc) to certs.
  354. func (a *actor) CanFetchCerts() bool {
  355. if !buildfeatures.HasACME {
  356. return false
  357. }
  358. if a.ci.IsUnixSock() && a.ci.Creds() != nil {
  359. connUID, ok := a.ci.Creds().UserID()
  360. if ok && connUID == userIDFromString(envknob.String("TS_PERMIT_CERT_UID")) {
  361. return true
  362. }
  363. }
  364. return false
  365. }
  366. // addActiveHTTPRequest adds c to the server's list of active HTTP requests.
  367. //
  368. // It returns an error if the specified actor is not allowed to connect.
  369. // The returned error may be of type [inUseOtherUserError].
  370. //
  371. // onDone must be called when the HTTP request is done.
  372. func (s *Server) addActiveHTTPRequest(req *http.Request, actor ipnauth.Actor) (onDone func(), err error) {
  373. if runtime.GOOS != "windows" && !buildfeatures.HasUnixSocketIdentity {
  374. return func() {}, nil
  375. }
  376. if actor == nil {
  377. return nil, errors.New("internal error: nil actor")
  378. }
  379. lb := s.mustBackend()
  380. s.mu.Lock()
  381. defer s.mu.Unlock()
  382. if err := s.checkConnIdentityLocked(actor); err != nil {
  383. return nil, err
  384. }
  385. mak.Set(&s.activeReqs, req, actor)
  386. if len(s.activeReqs) == 1 {
  387. if envknob.GOOS() == "windows" && !actor.IsLocalSystem() {
  388. // Tell the LocalBackend about the identity we're now running as,
  389. // unless its the SYSTEM user. That user is not a real account and
  390. // doesn't have a home directory.
  391. lb.SetCurrentUser(actor)
  392. }
  393. }
  394. onDone = func() {
  395. s.mu.Lock()
  396. defer s.mu.Unlock()
  397. delete(s.activeReqs, req)
  398. if len(s.activeReqs) != 0 {
  399. // The server is not idle yet.
  400. return
  401. }
  402. if envknob.GOOS() == "windows" && !actor.IsLocalSystem() {
  403. lb.SetCurrentUser(nil)
  404. }
  405. // Wake up callers waiting for the server to be idle:
  406. s.zeroReqWaiter.wakeAll()
  407. }
  408. return onDone, nil
  409. }
  410. // New returns a new Server.
  411. //
  412. // To start it, use the Server.Run method.
  413. //
  414. // At some point, either before or after Run, the Server's SetLocalBackend
  415. // method must also be called before Server can do anything useful.
  416. func New(logf logger.Logf, logID logid.PublicID, bus *eventbus.Bus, netMon *netmon.Monitor) *Server {
  417. if netMon == nil {
  418. panic("nil netMon")
  419. }
  420. return &Server{
  421. backendLogID: logID,
  422. logf: logf,
  423. bus: bus,
  424. netMon: netMon,
  425. }
  426. }
  427. // SetLocalBackend sets the server's LocalBackend.
  428. //
  429. // It should only call be called after calling lb.Start.
  430. func (s *Server) SetLocalBackend(lb *ipnlocal.LocalBackend) {
  431. if lb == nil {
  432. panic("nil LocalBackend")
  433. }
  434. if !s.lb.CompareAndSwap(nil, lb) {
  435. panic("already set")
  436. }
  437. s.mu.Lock()
  438. s.backendWaiter.wakeAll()
  439. s.mu.Unlock()
  440. // TODO(bradfitz): send status update to GUI long poller waiter. See
  441. // https://github.com/tailscale/tailscale/issues/6522
  442. }
  443. // Run runs the server, accepting connections from ln forever.
  444. //
  445. // If the context is done, the listener is closed. It is also the base context
  446. // of all HTTP requests.
  447. //
  448. // If the Server's LocalBackend has already been set, Run starts it.
  449. // Otherwise, the next call to SetLocalBackend will start it.
  450. func (s *Server) Run(ctx context.Context, ln net.Listener) error {
  451. defer func() {
  452. if lb := s.lb.Load(); lb != nil {
  453. lb.Shutdown()
  454. }
  455. }()
  456. runDone := make(chan struct{})
  457. defer close(runDone)
  458. ec := s.bus.Client("ipnserver.Server")
  459. defer ec.Close()
  460. shutdownSub := eventbus.Subscribe[localapi.Shutdown](ec)
  461. // When the context is closed, a [localapi.Shutdown] event is received,
  462. // or when we return, whichever is first, close our listener
  463. // and all open connections.
  464. go func() {
  465. select {
  466. case <-shutdownSub.Events():
  467. case <-ctx.Done():
  468. case <-runDone:
  469. }
  470. ln.Close()
  471. }()
  472. if ready, ok := feature.HookSystemdReady.GetOk(); ok {
  473. ready()
  474. }
  475. hs := &http.Server{
  476. Handler: http.HandlerFunc(s.serveHTTP),
  477. BaseContext: func(_ net.Listener) context.Context { return ctx },
  478. ConnContext: func(ctx context.Context, c net.Conn) context.Context {
  479. return contextWithActor(ctx, s.logf, c)
  480. },
  481. ErrorLog: logger.StdLogger(logger.WithPrefix(s.logf, "ipnserver: ")),
  482. }
  483. if err := hs.Serve(ln); err != nil {
  484. if err := ctx.Err(); err != nil {
  485. return err
  486. }
  487. return err
  488. }
  489. return nil
  490. }
  491. // ServeHTMLStatus serves an HTML status page at http://localhost:41112/ for
  492. // Windows and via $DEBUG_LISTENER/debug/ipn when tailscaled's --debug flag
  493. // is used to run a debug server.
  494. func (s *Server) ServeHTMLStatus(w http.ResponseWriter, r *http.Request) {
  495. if !buildfeatures.HasDebug {
  496. http.Error(w, feature.ErrUnavailable.Error(), http.StatusNotFound)
  497. return
  498. }
  499. lb := s.lb.Load()
  500. if lb == nil {
  501. http.Error(w, "no LocalBackend", http.StatusServiceUnavailable)
  502. return
  503. }
  504. // As this is only meant for debug, verify there's no DNS name being used to
  505. // access this.
  506. if !strings.HasPrefix(r.Host, "localhost:") && strings.IndexFunc(r.Host, unicode.IsLetter) != -1 {
  507. http.Error(w, "invalid host", http.StatusForbidden)
  508. return
  509. }
  510. w.Header().Set("Content-Security-Policy", `default-src 'none'; frame-ancestors 'none'; script-src 'none'; script-src-elem 'none'; script-src-attr 'none'`)
  511. w.Header().Set("X-Frame-Options", "DENY")
  512. w.Header().Set("X-Content-Type-Options", "nosniff")
  513. w.Header().Set("Content-Type", "text/html; charset=utf-8")
  514. st := lb.Status()
  515. // TODO(bradfitz): add LogID and opts to st?
  516. st.WriteHTML(w)
  517. }