serve.go 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956
  1. // Copyright (c) Tailscale Inc & AUTHORS
  2. // SPDX-License-Identifier: BSD-3-Clause
  3. package ipn
  4. import (
  5. "errors"
  6. "fmt"
  7. "iter"
  8. "net"
  9. "net/netip"
  10. "net/url"
  11. "runtime"
  12. "slices"
  13. "strconv"
  14. "strings"
  15. "tailscale.com/ipn/ipnstate"
  16. "tailscale.com/tailcfg"
  17. "tailscale.com/types/ipproto"
  18. "tailscale.com/util/dnsname"
  19. "tailscale.com/util/mak"
  20. "tailscale.com/util/set"
  21. )
  22. // ServeConfigKey returns a StateKey that stores the
  23. // JSON-encoded ServeConfig for a config profile.
  24. func ServeConfigKey(profileID ProfileID) StateKey {
  25. return StateKey("_serve/" + profileID)
  26. }
  27. // ServiceConfig contains the config information for a single service.
  28. // it contains a bool to indicate if the service is in Tun mode (L3 forwarding).
  29. // If the service is not in Tun mode, the service is configured by the L4 forwarding
  30. // (TCP ports) and/or the L7 forwarding (http handlers) information.
  31. type ServiceConfig struct {
  32. // TCP are the list of TCP port numbers that tailscaled should handle for
  33. // the Tailscale IP addresses. (not subnet routers, etc)
  34. TCP map[uint16]*TCPPortHandler `json:",omitempty"`
  35. // Web maps from "$SNI_NAME:$PORT" to a set of HTTP handlers
  36. // keyed by mount point ("/", "/foo", etc)
  37. Web map[HostPort]*WebServerConfig `json:",omitempty"`
  38. // Tun determines if the service should be using L3 forwarding (Tun mode).
  39. Tun bool `json:",omitempty"`
  40. }
  41. // ServeConfig is the JSON type stored in the StateStore for
  42. // StateKey "_serve/$PROFILE_ID" as returned by ServeConfigKey.
  43. type ServeConfig struct {
  44. // TCP are the list of TCP port numbers that tailscaled should handle for
  45. // the Tailscale IP addresses. (not subnet routers, etc)
  46. TCP map[uint16]*TCPPortHandler `json:",omitempty"`
  47. // Web maps from "$SNI_NAME:$PORT" to a set of HTTP handlers
  48. // keyed by mount point ("/", "/foo", etc)
  49. Web map[HostPort]*WebServerConfig `json:",omitempty"`
  50. // Services maps from service name (in the form "svc:dns-label") to a ServiceConfig.
  51. // Which describes the L3, L4, and L7 forwarding information for the service.
  52. Services map[tailcfg.ServiceName]*ServiceConfig `json:",omitempty"`
  53. // AllowFunnel is the set of SNI:port values for which funnel
  54. // traffic is allowed, from trusted ingress peers.
  55. AllowFunnel map[HostPort]bool `json:",omitempty"`
  56. // Foreground is a map of an IPN Bus session ID to an alternate foreground serve config that's valid for the
  57. // life of that WatchIPNBus session ID. This allows the config to specify ephemeral configs that are used
  58. // in the CLI's foreground mode to ensure ungraceful shutdowns of either the client or the LocalBackend does not
  59. // expose ports that users are not aware of. In practice this contains any serve config set via 'tailscale
  60. // serve' command run without the '--bg' flag. ServeConfig contained by Foreground is not expected itself to contain
  61. // another Foreground block.
  62. Foreground map[string]*ServeConfig `json:",omitempty"`
  63. // ETag is the checksum of the serve config that's populated
  64. // by the LocalClient through the HTTP ETag header during a
  65. // GetServeConfig request and is translated to an If-Match header
  66. // during a SetServeConfig request.
  67. ETag string `json:"-"`
  68. }
  69. // HostPort is an SNI name and port number, joined by a colon.
  70. // There is no implicit port 443. It must contain a colon.
  71. type HostPort string
  72. // Port extracts just the port number from hp.
  73. // An error is reported in the case that the hp does not
  74. // have a valid numeric port ending.
  75. func (hp HostPort) Port() (uint16, error) {
  76. _, port, err := net.SplitHostPort(string(hp))
  77. if err != nil {
  78. return 0, err
  79. }
  80. port16, err := strconv.ParseUint(port, 10, 16)
  81. if err != nil {
  82. return 0, err
  83. }
  84. return uint16(port16), nil
  85. }
  86. // A FunnelConn wraps a net.Conn that is coming over a
  87. // Funnel connection. It can be used to determine further
  88. // information about the connection, like the source address
  89. // and the target SNI name.
  90. type FunnelConn struct {
  91. // Conn is the underlying connection.
  92. net.Conn
  93. // Target is what was presented in the "Tailscale-Ingress-Target"
  94. // HTTP header.
  95. Target HostPort
  96. // Src is the source address of the connection.
  97. // This is the address of the client that initiated the
  98. // connection, not the address of the Tailscale Funnel
  99. // node which is relaying the connection. That address
  100. // can be found in Conn.RemoteAddr.
  101. Src netip.AddrPort
  102. }
  103. // WebServerConfig describes a web server's configuration.
  104. type WebServerConfig struct {
  105. Handlers map[string]*HTTPHandler // mountPoint => handler
  106. }
  107. // TCPPortHandler describes what to do when handling a TCP
  108. // connection.
  109. type TCPPortHandler struct {
  110. // HTTPS, if true, means that tailscaled should handle this connection as an
  111. // HTTPS request as configured by ServeConfig.Web.
  112. //
  113. // It is mutually exclusive with TCPForward.
  114. HTTPS bool `json:",omitempty"`
  115. // HTTP, if true, means that tailscaled should handle this connection as an
  116. // HTTP request as configured by ServeConfig.Web.
  117. //
  118. // It is mutually exclusive with TCPForward.
  119. HTTP bool `json:",omitempty"`
  120. // TCPForward is the IP:port to forward TCP connections to.
  121. // Whether or not TLS is terminated by tailscaled depends on
  122. // TerminateTLS.
  123. //
  124. // It is mutually exclusive with HTTPS.
  125. TCPForward string `json:",omitempty"`
  126. // TerminateTLS, if non-empty, means that tailscaled should terminate the
  127. // TLS connections before forwarding them to TCPForward, permitting only the
  128. // SNI name with this value. It is only used if TCPForward is non-empty.
  129. // (the HTTPS mode uses ServeConfig.Web)
  130. TerminateTLS string `json:",omitempty"`
  131. // ProxyProtocol indicates whether to send a PROXY protocol header
  132. // before forwarding the connection to TCPForward.
  133. //
  134. // This is only valid if TCPForward is non-empty.
  135. ProxyProtocol int `json:",omitzero"`
  136. }
  137. // HTTPHandler is either a path or a proxy to serve.
  138. type HTTPHandler struct {
  139. // Exactly one of the following may be set.
  140. Path string `json:",omitempty"` // absolute path to directory or file to serve
  141. Proxy string `json:",omitempty"` // http://localhost:3000/, localhost:3030, 3030
  142. Text string `json:",omitempty"` // plaintext to serve (primarily for testing)
  143. AcceptAppCaps []tailcfg.PeerCapability `json:",omitempty"` // peer capabilities to forward in grant header, e.g. example.com/cap/mon
  144. // Redirect, if not empty, is the target URL to redirect requests to.
  145. // By default, we redirect with HTTP 302 (Found) status.
  146. // If Redirect starts with '<httpcode>:', then we use that status instead.
  147. //
  148. // The target URL supports the following expansion variables:
  149. // - ${HOST}: replaced with the request's Host header value
  150. // - ${REQUEST_URI}: replaced with the request's full URI (path and query string)
  151. Redirect string `json:",omitempty"`
  152. // TODO(bradfitz): bool to not enumerate directories? TTL on mapping for
  153. // temporary ones? Error codes?
  154. }
  155. // WebHandlerExists reports whether if the ServeConfig Web handler exists for
  156. // the given host:port and mount point.
  157. func (sc *ServeConfig) WebHandlerExists(svcName tailcfg.ServiceName, hp HostPort, mount string) bool {
  158. h := sc.GetWebHandler(svcName, hp, mount)
  159. return h != nil
  160. }
  161. // GetWebHandler returns the HTTPHandler for the given host:port and mount point.
  162. // Returns nil if the handler does not exist.
  163. func (sc *ServeConfig) GetWebHandler(svcName tailcfg.ServiceName, hp HostPort, mount string) *HTTPHandler {
  164. if sc == nil {
  165. return nil
  166. }
  167. if svcName != "" {
  168. if svc, ok := sc.Services[svcName]; ok && svc.Web != nil {
  169. if webCfg, ok := svc.Web[hp]; ok {
  170. return webCfg.Handlers[mount]
  171. }
  172. }
  173. return nil
  174. }
  175. if sc.Web[hp] == nil {
  176. return nil
  177. }
  178. return sc.Web[hp].Handlers[mount]
  179. }
  180. // GetTCPPortHandler returns the TCPPortHandler for the given port. If the port
  181. // is not configured, nil is returned. Parameter svcName can be tailcfg.NoService
  182. // for local serve or a service name for a service hosted on node.
  183. func (sc *ServeConfig) GetTCPPortHandler(port uint16, svcName tailcfg.ServiceName) *TCPPortHandler {
  184. if sc == nil {
  185. return nil
  186. }
  187. if svcName != "" {
  188. if svc, ok := sc.Services[svcName]; ok && svc != nil {
  189. return svc.TCP[port]
  190. }
  191. return nil
  192. }
  193. return sc.TCP[port]
  194. }
  195. // HasPathHandler reports whether if ServeConfig has at least
  196. // one path handler, including foreground configs.
  197. func (sc *ServeConfig) HasPathHandler() bool {
  198. if sc.Web != nil {
  199. for _, webServerConfig := range sc.Web {
  200. for _, httpHandler := range webServerConfig.Handlers {
  201. if httpHandler.Path != "" {
  202. return true
  203. }
  204. }
  205. }
  206. }
  207. if sc.Services != nil {
  208. for _, serviceConfig := range sc.Services {
  209. if serviceConfig.Web != nil {
  210. for _, webServerConfig := range serviceConfig.Web {
  211. for _, httpHandler := range webServerConfig.Handlers {
  212. if httpHandler.Path != "" {
  213. return true
  214. }
  215. }
  216. }
  217. }
  218. }
  219. }
  220. if sc.Foreground != nil {
  221. for _, fgConfig := range sc.Foreground {
  222. if fgConfig.HasPathHandler() {
  223. return true
  224. }
  225. }
  226. }
  227. return false
  228. }
  229. // IsTCPForwardingAny reports whether ServeConfig is currently forwarding in
  230. // TCPForward mode on any port. This is exclusive of Web/HTTPS serving.
  231. func (sc *ServeConfig) IsTCPForwardingAny() bool {
  232. if sc == nil || len(sc.TCP) == 0 {
  233. return false
  234. }
  235. for _, h := range sc.TCP {
  236. if h.TCPForward != "" {
  237. return true
  238. }
  239. }
  240. return false
  241. }
  242. // IsTCPForwardingOnPort reports whether ServeConfig is currently forwarding
  243. // in TCPForward mode on the given port for local or a service. svcName will
  244. // either be noService (empty string) for local serve or a serviceName for service
  245. // hosted on node. Notice TCPForwarding is exclusive with Web/HTTPS serving.
  246. func (sc *ServeConfig) IsTCPForwardingOnPort(port uint16, svcName tailcfg.ServiceName) bool {
  247. if sc == nil {
  248. return false
  249. }
  250. if svcName != "" {
  251. svc, ok := sc.Services[svcName]
  252. if !ok || svc == nil {
  253. return false
  254. }
  255. if svc.TCP[port] == nil {
  256. return false
  257. }
  258. } else if sc.TCP[port] == nil {
  259. return false
  260. }
  261. return !sc.IsServingWeb(port, svcName)
  262. }
  263. // IsServingWeb reports whether ServeConfig is currently serving Web (HTTP/HTTPS)
  264. // on the given port for local or a service. svcName will be either tailcfg.NoService,
  265. // or a serviceName for service hosted on node. This is exclusive with TCPForwarding.
  266. func (sc *ServeConfig) IsServingWeb(port uint16, svcName tailcfg.ServiceName) bool {
  267. return sc.IsServingHTTP(port, svcName) || sc.IsServingHTTPS(port, svcName)
  268. }
  269. // IsServingHTTPS reports whether ServeConfig is currently serving HTTPS on
  270. // the given port for local or a service. svcName will be either tailcfg.NoService
  271. // for local serve, or a serviceName for service hosted on node. This is exclusive
  272. // with HTTP and TCPForwarding.
  273. func (sc *ServeConfig) IsServingHTTPS(port uint16, svcName tailcfg.ServiceName) bool {
  274. if sc == nil {
  275. return false
  276. }
  277. var tcpHandlers map[uint16]*TCPPortHandler
  278. if svcName != "" {
  279. if svc := sc.Services[svcName]; svc != nil {
  280. tcpHandlers = svc.TCP
  281. }
  282. } else {
  283. tcpHandlers = sc.TCP
  284. }
  285. th := tcpHandlers[port]
  286. if th == nil {
  287. return false
  288. }
  289. return th.HTTPS
  290. }
  291. // IsServingHTTP reports whether ServeConfig is currently serving HTTP on the
  292. // given port for local or a service. svcName will be either tailcfg.NoService for
  293. // local serve, or a serviceName for service hosted on node. This is exclusive
  294. // with HTTPS and TCPForwarding.
  295. func (sc *ServeConfig) IsServingHTTP(port uint16, svcName tailcfg.ServiceName) bool {
  296. if sc == nil {
  297. return false
  298. }
  299. if svcName != "" {
  300. if svc := sc.Services[svcName]; svc != nil {
  301. if svc.TCP[port] != nil {
  302. return svc.TCP[port].HTTP
  303. }
  304. }
  305. return false
  306. }
  307. if sc.TCP[port] == nil {
  308. return false
  309. }
  310. return sc.TCP[port].HTTP
  311. }
  312. // FindConfig finds a config that contains the given port, which can be
  313. // the top level background config or an inner foreground one.
  314. // The second result is true if it's foreground.
  315. func (sc *ServeConfig) FindConfig(port uint16) (*ServeConfig, bool) {
  316. if sc == nil {
  317. return nil, false
  318. }
  319. if _, ok := sc.TCP[port]; ok {
  320. return sc, false
  321. }
  322. for _, sc := range sc.Foreground {
  323. if _, ok := sc.TCP[port]; ok {
  324. return sc, true
  325. }
  326. }
  327. return nil, false
  328. }
  329. // SetWebHandler sets the given HTTPHandler at the specified host, port,
  330. // and mount in the serve config. sc.TCP is also updated to reflect web
  331. // serving usage of the given port. The st argument is needed when setting
  332. // a web handler for a service, otherwise it can be nil. mds is the Magic DNS
  333. // suffix, which is used to recreate serve's host.
  334. func (sc *ServeConfig) SetWebHandler(handler *HTTPHandler, host string, port uint16, mount string, useTLS bool, mds string) {
  335. if sc == nil {
  336. sc = new(ServeConfig)
  337. }
  338. tcpMap := &sc.TCP
  339. webServerMap := &sc.Web
  340. hostName := host
  341. if svcName := tailcfg.AsServiceName(host); svcName != "" {
  342. hostName = strings.Join([]string{svcName.WithoutPrefix(), mds}, ".")
  343. svc, ok := sc.Services[svcName]
  344. if !ok {
  345. svc = new(ServiceConfig)
  346. mak.Set(&sc.Services, svcName, svc)
  347. }
  348. tcpMap = &svc.TCP
  349. webServerMap = &svc.Web
  350. }
  351. mak.Set(tcpMap, port, &TCPPortHandler{HTTPS: useTLS, HTTP: !useTLS})
  352. hp := HostPort(net.JoinHostPort(hostName, strconv.Itoa(int(port))))
  353. webCfg, ok := (*webServerMap)[hp]
  354. if !ok {
  355. webCfg = new(WebServerConfig)
  356. mak.Set(webServerMap, hp, webCfg)
  357. }
  358. mak.Set(&webCfg.Handlers, mount, handler)
  359. // TODO(tylersmalley): handle multiple web handlers from foreground mode
  360. for k, v := range webCfg.Handlers {
  361. if v == handler {
  362. continue
  363. }
  364. // If the new mount point ends in / and another mount point
  365. // shares the same prefix, remove the other handler.
  366. // (e.g. /foo/ overwrites /foo)
  367. // The opposite example is also handled.
  368. m1 := strings.TrimSuffix(mount, "/")
  369. m2 := strings.TrimSuffix(k, "/")
  370. if m1 == m2 {
  371. delete(webCfg.Handlers, k)
  372. }
  373. }
  374. }
  375. // SetTCPForwarding sets the fwdAddr (IP:port form) to which to forward
  376. // connections from the given port. If terminateTLS is true, TLS connections
  377. // are terminated with only the given host name permitted before passing them
  378. // to the fwdAddr.
  379. //
  380. // If proxyProtocol is non-zero, the corresponding PROXY protocol version
  381. // header is sent before forwarding the connection.
  382. func (sc *ServeConfig) SetTCPForwarding(port uint16, fwdAddr string, terminateTLS bool, proxyProtocol int, host string) {
  383. if sc == nil {
  384. sc = new(ServeConfig)
  385. }
  386. mak.Set(&sc.TCP, port, &TCPPortHandler{
  387. TCPForward: fwdAddr,
  388. ProxyProtocol: proxyProtocol, // can be 0
  389. })
  390. if terminateTLS {
  391. sc.TCP[port].TerminateTLS = host
  392. }
  393. }
  394. // SetTCPForwardingForService sets the fwdAddr (IP:port form) to which to
  395. // forward connections from the given port on the service. If terminateTLS
  396. // is true, TLS connections are terminated, with only the FQDN that corresponds
  397. // to the given service being permitted, before passing them to the fwdAddr.
  398. func (sc *ServeConfig) SetTCPForwardingForService(port uint16, fwdAddr string, terminateTLS bool, svcName tailcfg.ServiceName, proxyProtocol int, magicDNSSuffix string) {
  399. if sc == nil {
  400. sc = new(ServeConfig)
  401. }
  402. svcConfig, ok := sc.Services[svcName]
  403. if !ok {
  404. svcConfig = new(ServiceConfig)
  405. mak.Set(&sc.Services, svcName, svcConfig)
  406. }
  407. mak.Set(&svcConfig.TCP, port, &TCPPortHandler{
  408. TCPForward: fwdAddr,
  409. ProxyProtocol: proxyProtocol, // can be 0
  410. })
  411. if terminateTLS {
  412. svcConfig.TCP[port].TerminateTLS = fmt.Sprintf("%s.%s", svcName.WithoutPrefix(), magicDNSSuffix)
  413. }
  414. }
  415. // SetFunnel sets the sc.AllowFunnel value for the given host and port.
  416. func (sc *ServeConfig) SetFunnel(host string, port uint16, setOn bool) {
  417. if sc == nil {
  418. sc = new(ServeConfig)
  419. }
  420. hp := HostPort(net.JoinHostPort(host, strconv.Itoa(int(port))))
  421. // TODO(tylersmalley): should ensure there is no other conflicting funnel
  422. // TODO(tylersmalley): add error handling for if toggling for existing sc
  423. if setOn {
  424. mak.Set(&sc.AllowFunnel, hp, true)
  425. } else if _, exists := sc.AllowFunnel[hp]; exists {
  426. delete(sc.AllowFunnel, hp)
  427. // Clear map mostly for testing.
  428. if len(sc.AllowFunnel) == 0 {
  429. sc.AllowFunnel = nil
  430. }
  431. }
  432. }
  433. // RemoveWebHandler deletes the web handlers at all of the given mount points for the
  434. // provided host and port in the serve config for the node (as opposed to a service).
  435. // If cleanupFunnel is true, this also removes the funnel value for this port if no handlers remain.
  436. func (sc *ServeConfig) RemoveWebHandler(host string, port uint16, mounts []string, cleanupFunnel bool) {
  437. hp := HostPort(net.JoinHostPort(host, strconv.Itoa(int(port))))
  438. // Delete existing handler, then cascade delete if empty.
  439. for _, m := range mounts {
  440. delete(sc.Web[hp].Handlers, m)
  441. }
  442. if len(sc.Web[hp].Handlers) == 0 {
  443. delete(sc.Web, hp)
  444. delete(sc.TCP, port)
  445. if cleanupFunnel {
  446. delete(sc.AllowFunnel, hp) // disable funnel if no mounts remain for the port
  447. }
  448. }
  449. // Clear empty maps, mostly for testing.
  450. if len(sc.Web) == 0 {
  451. sc.Web = nil
  452. }
  453. if len(sc.TCP) == 0 {
  454. sc.TCP = nil
  455. }
  456. if len(sc.AllowFunnel) == 0 {
  457. sc.AllowFunnel = nil
  458. }
  459. }
  460. // RemoveServiceWebHandler deletes the web handlers at all of the given mount points
  461. // for the provided host and port in the serve config for the given service.
  462. func (sc *ServeConfig) RemoveServiceWebHandler(svcName tailcfg.ServiceName, hostName string, port uint16, mounts []string) {
  463. hp := HostPort(net.JoinHostPort(hostName, strconv.Itoa(int(port))))
  464. svc, ok := sc.Services[svcName]
  465. if !ok || svc == nil {
  466. return
  467. }
  468. // Delete existing handler, then cascade delete if empty.
  469. for _, m := range mounts {
  470. delete(svc.Web[hp].Handlers, m)
  471. }
  472. if len(svc.Web[hp].Handlers) == 0 {
  473. delete(svc.Web, hp)
  474. delete(svc.TCP, port)
  475. }
  476. if len(svc.Web) == 0 && len(svc.TCP) == 0 {
  477. delete(sc.Services, svcName)
  478. }
  479. if len(sc.Services) == 0 {
  480. sc.Services = nil
  481. }
  482. }
  483. // RemoveTCPForwarding deletes the TCP forwarding configuration for the given
  484. // port from the serve config.
  485. func (sc *ServeConfig) RemoveTCPForwarding(svcName tailcfg.ServiceName, port uint16) {
  486. if svcName != "" {
  487. if svc := sc.Services[svcName]; svc != nil {
  488. delete(svc.TCP, port)
  489. if len(svc.TCP) == 0 {
  490. svc.TCP = nil
  491. }
  492. if len(svc.Web) == 0 && len(svc.TCP) == 0 {
  493. delete(sc.Services, svcName)
  494. }
  495. if len(sc.Services) == 0 {
  496. sc.Services = nil
  497. }
  498. }
  499. return
  500. }
  501. delete(sc.TCP, port)
  502. if len(sc.TCP) == 0 {
  503. sc.TCP = nil
  504. }
  505. }
  506. // IsFunnelOn reports whether if ServeConfig is currently allowing funnel
  507. // traffic for any host:port.
  508. //
  509. // View version of ServeConfig.IsFunnelOn.
  510. func (v ServeConfigView) IsFunnelOn() bool { return v.ж.IsFunnelOn() }
  511. // IsFunnelOn reports whether any funnel endpoint is currently enabled for this node.
  512. func (sc *ServeConfig) IsFunnelOn() bool {
  513. if sc == nil {
  514. return false
  515. }
  516. for _, b := range sc.AllowFunnel {
  517. if b {
  518. return true
  519. }
  520. }
  521. for _, conf := range sc.Foreground {
  522. if conf.IsFunnelOn() {
  523. return true
  524. }
  525. }
  526. return false
  527. }
  528. // CheckFunnelAccess checks whether Funnel access is allowed for the given node
  529. // and port.
  530. // It checks:
  531. // 1. HTTPS is enabled on the tailnet
  532. // 2. the node has the "funnel" nodeAttr
  533. // 3. the port is allowed for Funnel
  534. //
  535. // The node arg should be the ipnstate.Status.Self node.
  536. func CheckFunnelAccess(port uint16, node *ipnstate.PeerStatus) error {
  537. if err := NodeCanFunnel(node); err != nil {
  538. return err
  539. }
  540. return CheckFunnelPort(port, node)
  541. }
  542. // NodeCanFunnel returns an error if the given node is not configured to allow
  543. // for Tailscale Funnel usage.
  544. func NodeCanFunnel(node *ipnstate.PeerStatus) error {
  545. if !node.HasCap(tailcfg.CapabilityHTTPS) {
  546. return errors.New("Funnel not available; HTTPS must be enabled. See https://tailscale.com/s/https.")
  547. }
  548. if !node.HasCap(tailcfg.NodeAttrFunnel) {
  549. return errors.New("Funnel not available; \"funnel\" node attribute not set. See https://tailscale.com/s/no-funnel.")
  550. }
  551. return nil
  552. }
  553. // CheckFunnelPort checks whether the given port is allowed for Funnel.
  554. // It uses the tailcfg.CapabilityFunnelPorts nodeAttr to determine the allowed
  555. // ports.
  556. func CheckFunnelPort(wantedPort uint16, node *ipnstate.PeerStatus) error {
  557. deny := func(allowedPorts string) error {
  558. if allowedPorts == "" {
  559. return fmt.Errorf("port %d is not allowed for funnel", wantedPort)
  560. }
  561. return fmt.Errorf("port %d is not allowed for funnel; allowed ports are: %v", wantedPort, allowedPorts)
  562. }
  563. var portsStr string
  564. parseAttr := func(attr string) (string, error) {
  565. u, err := url.Parse(attr)
  566. if err != nil {
  567. return "", deny("")
  568. }
  569. portsStr := u.Query().Get("ports")
  570. if portsStr == "" {
  571. return "", deny("")
  572. }
  573. u.RawQuery = ""
  574. if u.String() != string(tailcfg.CapabilityFunnelPorts) {
  575. return "", deny("")
  576. }
  577. return portsStr, nil
  578. }
  579. for attr := range node.CapMap {
  580. attr := string(attr)
  581. if !strings.HasPrefix(attr, string(tailcfg.CapabilityFunnelPorts)) {
  582. continue
  583. }
  584. var err error
  585. portsStr, err = parseAttr(attr)
  586. if err != nil {
  587. return err
  588. }
  589. break
  590. }
  591. if portsStr == "" {
  592. for attr := range node.CapMap {
  593. attr := string(attr)
  594. if !strings.HasPrefix(attr, string(tailcfg.CapabilityFunnelPorts)) {
  595. continue
  596. }
  597. var err error
  598. portsStr, err = parseAttr(attr)
  599. if err != nil {
  600. return err
  601. }
  602. break
  603. }
  604. }
  605. if portsStr == "" {
  606. return deny("")
  607. }
  608. wantedPortString := strconv.Itoa(int(wantedPort))
  609. for _, ps := range strings.Split(portsStr, ",") {
  610. if ps == "" {
  611. continue
  612. }
  613. first, last, ok := strings.Cut(ps, "-")
  614. if !ok {
  615. if first == wantedPortString {
  616. return nil
  617. }
  618. continue
  619. }
  620. fp, err := strconv.ParseUint(first, 10, 16)
  621. if err != nil {
  622. continue
  623. }
  624. lp, err := strconv.ParseUint(last, 10, 16)
  625. if err != nil {
  626. continue
  627. }
  628. pr := tailcfg.PortRange{First: uint16(fp), Last: uint16(lp)}
  629. if pr.Contains(wantedPort) {
  630. return nil
  631. }
  632. }
  633. return deny(portsStr)
  634. }
  635. // ExpandProxyTargetValue expands the supported target values to be proxied
  636. // allowing for input values to be a port number, a partial URL, or a full URL
  637. // including a path. If it's for a service, remote addresses are allowed and
  638. // there doesn't have to be a port specified.
  639. //
  640. // examples:
  641. // - 3000
  642. // - localhost:3000
  643. // - tcp://localhost:3000
  644. // - http://localhost:3000
  645. // - https://localhost:3000
  646. // - https-insecure://localhost:3000
  647. // - https-insecure://localhost:3000/foo
  648. // - https://tailscale.com
  649. func ExpandProxyTargetValue(target string, supportedSchemes []string, defaultScheme string) (string, error) {
  650. const host = "127.0.0.1"
  651. // empty target is invalid
  652. if target == "" {
  653. return "", fmt.Errorf("empty target")
  654. }
  655. // support target being a port number
  656. if port, err := strconv.ParseUint(target, 10, 16); err == nil {
  657. return fmt.Sprintf("%s://%s:%d", defaultScheme, host, port), nil
  658. }
  659. // handle unix: scheme specially - it doesn't use standard URL format
  660. if strings.HasPrefix(target, "unix:") {
  661. if !slices.Contains(supportedSchemes, "unix") {
  662. return "", fmt.Errorf("unix sockets are not supported for this target type")
  663. }
  664. if runtime.GOOS == "windows" {
  665. return "", fmt.Errorf("unix socket serve target is not supported on Windows")
  666. }
  667. path := strings.TrimPrefix(target, "unix:")
  668. if path == "" {
  669. return "", fmt.Errorf("unix socket path cannot be empty")
  670. }
  671. return target, nil
  672. }
  673. hasScheme := true
  674. // prepend scheme if not present
  675. if !strings.Contains(target, "://") {
  676. target = defaultScheme + "://" + target
  677. hasScheme = false
  678. }
  679. // make sure we can parse the target
  680. u, err := url.ParseRequestURI(target)
  681. if err != nil {
  682. return "", fmt.Errorf("invalid URL %w", err)
  683. }
  684. // ensure a supported scheme
  685. if !slices.Contains(supportedSchemes, u.Scheme) {
  686. return "", fmt.Errorf("must be a URL starting with one of the supported schemes: %v", supportedSchemes)
  687. }
  688. // validate port according to host.
  689. if u.Hostname() == "localhost" || u.Hostname() == "127.0.0.1" || u.Hostname() == "::1" {
  690. // require port for localhost targets
  691. if u.Port() == "" {
  692. return "", fmt.Errorf("port required for localhost target %q", target)
  693. }
  694. } else {
  695. validHN := dnsname.ValidHostname(u.Hostname()) == nil
  696. validIP := net.ParseIP(u.Hostname()) != nil
  697. if !validHN && !validIP {
  698. return "", fmt.Errorf("invalid hostname or IP address %q", u.Hostname())
  699. }
  700. // require scheme for non-localhost targets
  701. if !hasScheme {
  702. return "", fmt.Errorf("non-localhost target %q must include a scheme", target)
  703. }
  704. }
  705. port, err := strconv.ParseUint(u.Port(), 10, 16)
  706. if err != nil || port == 0 {
  707. if u.Port() == "" {
  708. return u.String(), nil // allow no port for remote destinations
  709. }
  710. return "", fmt.Errorf("invalid port %q", u.Port())
  711. }
  712. u.Host = fmt.Sprintf("%s:%d", u.Hostname(), port)
  713. return u.String(), nil
  714. }
  715. // TCPs returns an iterator over both background and foreground TCP
  716. // listeners.
  717. //
  718. // The key is the port number.
  719. func (v ServeConfigView) TCPs() iter.Seq2[uint16, TCPPortHandlerView] {
  720. return func(yield func(uint16, TCPPortHandlerView) bool) {
  721. for k, v := range v.TCP().All() {
  722. if !yield(k, v) {
  723. return
  724. }
  725. }
  726. for _, conf := range v.Foreground().All() {
  727. for k, v := range conf.TCP().All() {
  728. if !yield(k, v) {
  729. return
  730. }
  731. }
  732. }
  733. }
  734. }
  735. // Webs returns an iterator over both background and foreground Web configurations.
  736. func (v ServeConfigView) Webs() iter.Seq2[HostPort, WebServerConfigView] {
  737. return func(yield func(HostPort, WebServerConfigView) bool) {
  738. for k, v := range v.Web().All() {
  739. if !yield(k, v) {
  740. return
  741. }
  742. }
  743. for _, conf := range v.Foreground().All() {
  744. for k, v := range conf.Web().All() {
  745. if !yield(k, v) {
  746. return
  747. }
  748. }
  749. }
  750. for _, service := range v.Services().All() {
  751. for k, v := range service.Web().All() {
  752. if !yield(k, v) {
  753. return
  754. }
  755. }
  756. }
  757. }
  758. }
  759. // FindServiceTCP return the TCPPortHandlerView for the given service name and port.
  760. func (v ServeConfigView) FindServiceTCP(svcName tailcfg.ServiceName, port uint16) (res TCPPortHandlerView, ok bool) {
  761. svcCfg, ok := v.Services().GetOk(svcName)
  762. if !ok {
  763. return res, ok
  764. }
  765. return svcCfg.TCP().GetOk(port)
  766. }
  767. // FindServiceWeb returns the web handler for the service's host-port.
  768. func (v ServeConfigView) FindServiceWeb(svcName tailcfg.ServiceName, hp HostPort) (res WebServerConfigView, ok bool) {
  769. if svcCfg, ok := v.Services().GetOk(svcName); ok {
  770. if res, ok := svcCfg.Web().GetOk(hp); ok {
  771. return res, ok
  772. }
  773. }
  774. return res, ok
  775. }
  776. // FindTCP returns the first TCP that matches with the given port. It
  777. // prefers a foreground match first followed by a background search if none
  778. // existed.
  779. func (v ServeConfigView) FindTCP(port uint16) (res TCPPortHandlerView, ok bool) {
  780. res, ok = v.FindForegroundTCP(port)
  781. if ok {
  782. return res, ok
  783. }
  784. return v.TCP().GetOk(port)
  785. }
  786. // FindWeb returns the first Web that matches with the given HostPort. It
  787. // prefers a foreground match first followed by a background search if none
  788. // existed.
  789. func (v ServeConfigView) FindWeb(hp HostPort) (res WebServerConfigView, ok bool) {
  790. for _, conf := range v.Foreground().All() {
  791. if res, ok := conf.Web().GetOk(hp); ok {
  792. return res, ok
  793. }
  794. }
  795. return v.Web().GetOk(hp)
  796. }
  797. // FindForegroundTCP returns the first foreground TCP handler matching the input
  798. // port.
  799. func (v ServeConfigView) FindForegroundTCP(port uint16) (res TCPPortHandlerView, ok bool) {
  800. for _, conf := range v.Foreground().All() {
  801. if res, ok := conf.TCP().GetOk(port); ok {
  802. return res, ok
  803. }
  804. }
  805. return res, false
  806. }
  807. // HasAllowFunnel returns whether this config has at least one AllowFunnel
  808. // set in the background or foreground configs.
  809. func (v ServeConfigView) HasAllowFunnel() bool {
  810. if v.AllowFunnel().Len() > 0 {
  811. return true
  812. }
  813. for _, conf := range v.Foreground().All() {
  814. if conf.AllowFunnel().Len() > 0 {
  815. return true
  816. }
  817. }
  818. return false
  819. }
  820. // FindFunnel reports whether target exists in either the background AllowFunnel
  821. // or any of the foreground configs.
  822. func (v ServeConfigView) HasFunnelForTarget(target HostPort) bool {
  823. if v.AllowFunnel().Get(target) {
  824. return true
  825. }
  826. for _, conf := range v.Foreground().All() {
  827. if conf.AllowFunnel().Get(target) {
  828. return true
  829. }
  830. }
  831. return false
  832. }
  833. // ServicePortRange returns the list of tailcfg.ProtoPortRange that represents
  834. // the proto/ports pairs that are being served by the service.
  835. //
  836. // Right now Tun mode is the only thing supports UDP, otherwise serve only supports TCP.
  837. func (v ServiceConfigView) ServicePortRange() []tailcfg.ProtoPortRange {
  838. if v.Tun() {
  839. // If the service is in Tun mode, means service accept TCP/UDP on all ports.
  840. return []tailcfg.ProtoPortRange{{Ports: tailcfg.PortRangeAny}}
  841. }
  842. tcp := int(ipproto.TCP)
  843. // Deduplicate the ports.
  844. servePorts := make(set.Set[uint16])
  845. for port := range v.TCP().All() {
  846. if port > 0 {
  847. servePorts.Add(uint16(port))
  848. }
  849. }
  850. dedupedServePorts := servePorts.Slice()
  851. slices.Sort(dedupedServePorts)
  852. var ranges []tailcfg.ProtoPortRange
  853. for _, p := range dedupedServePorts {
  854. if n := len(ranges); n > 0 && p == ranges[n-1].Ports.Last+1 {
  855. ranges[n-1].Ports.Last = p
  856. continue
  857. }
  858. ranges = append(ranges, tailcfg.ProtoPortRange{
  859. Proto: tcp,
  860. Ports: tailcfg.PortRange{
  861. First: p,
  862. Last: p,
  863. },
  864. })
  865. }
  866. return ranges
  867. }