serve.go 27 KB

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