2
0

client.go 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533
  1. // Copyright (c) Tailscale Inc & AUTHORS
  2. // SPDX-License-Identifier: BSD-3-Clause
  3. //go:build !js
  4. // Package controlhttp implements the Tailscale 2021 control protocol
  5. // base transport over HTTP.
  6. //
  7. // This tunnels the protocol in control/controlbase over HTTP with a
  8. // variety of compatibility fallbacks for handling picky or deep
  9. // inspecting proxies.
  10. //
  11. // In the happy path, a client makes a single cleartext HTTP request
  12. // to the server, the server responds with 101 Switching Protocols,
  13. // and the control base protocol takes place over plain TCP.
  14. //
  15. // In the compatibility path, the client does the above over HTTPS,
  16. // resulting in double encryption (once for the control transport, and
  17. // once for the outer TLS layer).
  18. package controlhttp
  19. import (
  20. "context"
  21. "crypto/tls"
  22. "encoding/base64"
  23. "errors"
  24. "fmt"
  25. "io"
  26. "math"
  27. "net"
  28. "net/http"
  29. "net/http/httptrace"
  30. "net/netip"
  31. "net/url"
  32. "sort"
  33. "sync/atomic"
  34. "time"
  35. "tailscale.com/control/controlbase"
  36. "tailscale.com/envknob"
  37. "tailscale.com/health"
  38. "tailscale.com/net/dnscache"
  39. "tailscale.com/net/dnsfallback"
  40. "tailscale.com/net/netutil"
  41. "tailscale.com/net/sockstats"
  42. "tailscale.com/net/tlsdial"
  43. "tailscale.com/net/tshttpproxy"
  44. "tailscale.com/tailcfg"
  45. "tailscale.com/tstime"
  46. "tailscale.com/util/multierr"
  47. )
  48. var stdDialer net.Dialer
  49. // Dial connects to the HTTP server at this Dialer's Host:HTTPPort, requests to
  50. // switch to the Tailscale control protocol, and returns an established control
  51. // protocol connection.
  52. //
  53. // If Dial fails to connect using HTTP, it also tries to tunnel over TLS to the
  54. // Dialer's Host:HTTPSPort as a compatibility fallback.
  55. //
  56. // The provided ctx is only used for the initial connection, until
  57. // Dial returns. It does not affect the connection once established.
  58. func (a *Dialer) Dial(ctx context.Context) (*ClientConn, error) {
  59. if a.Hostname == "" {
  60. return nil, errors.New("required Dialer.Hostname empty")
  61. }
  62. return a.dial(ctx)
  63. }
  64. func (a *Dialer) logf(format string, args ...any) {
  65. if a.Logf != nil {
  66. a.Logf(format, args...)
  67. }
  68. }
  69. func (a *Dialer) getProxyFunc() func(*http.Request) (*url.URL, error) {
  70. if a.proxyFunc != nil {
  71. return a.proxyFunc
  72. }
  73. return tshttpproxy.ProxyFromEnvironment
  74. }
  75. // httpsFallbackDelay is how long we'll wait for a.HTTPPort to work before
  76. // starting to try a.HTTPSPort.
  77. func (a *Dialer) httpsFallbackDelay() time.Duration {
  78. if forceNoise443() {
  79. return time.Nanosecond
  80. }
  81. if v := a.testFallbackDelay; v != 0 {
  82. return v
  83. }
  84. return 500 * time.Millisecond
  85. }
  86. var _ = envknob.RegisterBool("TS_USE_CONTROL_DIAL_PLAN") // to record at init time whether it's in use
  87. func (a *Dialer) dial(ctx context.Context) (*ClientConn, error) {
  88. // If we don't have a dial plan, just fall back to dialing the single
  89. // host we know about.
  90. useDialPlan := envknob.BoolDefaultTrue("TS_USE_CONTROL_DIAL_PLAN")
  91. if !useDialPlan || a.DialPlan == nil || len(a.DialPlan.Candidates) == 0 {
  92. return a.dialHost(ctx, netip.Addr{})
  93. }
  94. candidates := a.DialPlan.Candidates
  95. // Otherwise, we try dialing per the plan. Store the highest priority
  96. // in the list, so that if we get a connection to one of those
  97. // candidates we can return quickly.
  98. var highestPriority int = math.MinInt
  99. for _, c := range candidates {
  100. if c.Priority > highestPriority {
  101. highestPriority = c.Priority
  102. }
  103. }
  104. // This context allows us to cancel in-flight connections if we get a
  105. // highest-priority connection before we're all done.
  106. ctx, cancel := context.WithCancel(ctx)
  107. defer cancel()
  108. // Now, for each candidate, kick off a dial in parallel.
  109. type dialResult struct {
  110. conn *ClientConn
  111. err error
  112. addr netip.Addr
  113. priority int
  114. }
  115. resultsCh := make(chan dialResult, len(candidates))
  116. var pending atomic.Int32
  117. pending.Store(int32(len(candidates)))
  118. for _, c := range candidates {
  119. go func(ctx context.Context, c tailcfg.ControlIPCandidate) {
  120. var (
  121. conn *ClientConn
  122. err error
  123. )
  124. // Always send results back to our channel.
  125. defer func() {
  126. resultsCh <- dialResult{conn, err, c.IP, c.Priority}
  127. if pending.Add(-1) == 0 {
  128. close(resultsCh)
  129. }
  130. }()
  131. // If non-zero, wait the configured start timeout
  132. // before we do anything.
  133. if c.DialStartDelaySec > 0 {
  134. a.logf("[v2] controlhttp: waiting %.2f seconds before dialing %q @ %v", c.DialStartDelaySec, a.Hostname, c.IP)
  135. if a.Clock == nil {
  136. a.Clock = tstime.StdClock{}
  137. }
  138. tmr, tmrChannel := a.Clock.NewTimer(time.Duration(c.DialStartDelaySec * float64(time.Second)))
  139. defer tmr.Stop()
  140. select {
  141. case <-ctx.Done():
  142. err = ctx.Err()
  143. return
  144. case <-tmrChannel:
  145. }
  146. }
  147. // Now, create a sub-context with the given timeout and
  148. // try dialing the provided host.
  149. ctx, cancel := context.WithTimeout(ctx, time.Duration(c.DialTimeoutSec*float64(time.Second)))
  150. defer cancel()
  151. // This will dial, and the defer above sends it back to our parent.
  152. a.logf("[v2] controlhttp: trying to dial %q @ %v", a.Hostname, c.IP)
  153. conn, err = a.dialHost(ctx, c.IP)
  154. }(ctx, c)
  155. }
  156. var results []dialResult
  157. for res := range resultsCh {
  158. // If we get a response that has the highest priority, we don't
  159. // need to wait for any of the other connections to finish; we
  160. // can just return this connection.
  161. //
  162. // TODO(andrew): we could make this better by keeping track of
  163. // the highest remaining priority dynamically, instead of just
  164. // checking for the highest total
  165. if res.priority == highestPriority && res.conn != nil {
  166. a.logf("[v1] controlhttp: high-priority success dialing %q @ %v from dial plan", a.Hostname, res.addr)
  167. // Drain the channel and any existing connections in
  168. // the background.
  169. go func() {
  170. for _, res := range results {
  171. if res.conn != nil {
  172. res.conn.Close()
  173. }
  174. }
  175. for res := range resultsCh {
  176. if res.conn != nil {
  177. res.conn.Close()
  178. }
  179. }
  180. if a.drainFinished != nil {
  181. close(a.drainFinished)
  182. }
  183. }()
  184. return res.conn, nil
  185. }
  186. // This isn't a highest-priority result, so just store it until
  187. // we're done.
  188. results = append(results, res)
  189. }
  190. // After we finish this function, close any remaining open connections.
  191. defer func() {
  192. for _, result := range results {
  193. // Note: below, we nil out the returned connection (if
  194. // any) in the slice so we don't close it.
  195. if result.conn != nil {
  196. result.conn.Close()
  197. }
  198. }
  199. // We don't drain asynchronously after this point, so notify our
  200. // channel when we return.
  201. if a.drainFinished != nil {
  202. close(a.drainFinished)
  203. }
  204. }()
  205. // Sort by priority, then take the first non-error response.
  206. sort.Slice(results, func(i, j int) bool {
  207. // NOTE: intentionally inverted so that the highest priority
  208. // item comes first
  209. return results[i].priority > results[j].priority
  210. })
  211. var (
  212. conn *ClientConn
  213. errs []error
  214. )
  215. for i, result := range results {
  216. if result.err != nil {
  217. errs = append(errs, result.err)
  218. continue
  219. }
  220. a.logf("[v1] controlhttp: succeeded dialing %q @ %v from dial plan", a.Hostname, result.addr)
  221. conn = result.conn
  222. results[i].conn = nil // so we don't close it in the defer
  223. return conn, nil
  224. }
  225. merr := multierr.New(errs...)
  226. // If we get here, then we didn't get anywhere with our dial plan; fall back to just using DNS.
  227. a.logf("controlhttp: failed dialing using DialPlan, falling back to DNS; errs=%s", merr.Error())
  228. return a.dialHost(ctx, netip.Addr{})
  229. }
  230. // The TS_FORCE_NOISE_443 envknob forces the controlclient noise dialer to
  231. // always use port 443 HTTPS connections to the controlplane and not try the
  232. // port 80 HTTP fast path.
  233. //
  234. // This is currently (2023-01-17) needed for Docker Desktop's "VPNKit" proxy
  235. // that breaks port 80 for us post-Noise-handshake, causing us to never try port
  236. // 443. Until one of Docker's proxy and/or this package's port 443 fallback is
  237. // fixed, this is a workaround. It might also be useful for future debugging.
  238. var forceNoise443 = envknob.RegisterBool("TS_FORCE_NOISE_443")
  239. var debugNoiseDial = envknob.RegisterBool("TS_DEBUG_NOISE_DIAL")
  240. // dialHost connects to the configured Dialer.Hostname and upgrades the
  241. // connection into a controlbase.Conn. If addr is valid, then no DNS is used
  242. // and the connection will be made to the provided address.
  243. func (a *Dialer) dialHost(ctx context.Context, addr netip.Addr) (*ClientConn, error) {
  244. // Create one shared context used by both port 80 and port 443 dials.
  245. // If port 80 is still in flight when 443 returns, this deferred cancel
  246. // will stop the port 80 dial.
  247. ctx, cancel := context.WithCancel(ctx)
  248. defer cancel()
  249. ctx = sockstats.WithSockStats(ctx, sockstats.LabelControlClientDialer, a.logf)
  250. // u80 and u443 are the URLs we'll try to hit over HTTP or HTTPS,
  251. // respectively, in order to do the HTTP upgrade to a net.Conn over which
  252. // we'll speak Noise.
  253. u80 := &url.URL{
  254. Scheme: "http",
  255. Host: net.JoinHostPort(a.Hostname, strDef(a.HTTPPort, "80")),
  256. Path: serverUpgradePath,
  257. }
  258. u443 := &url.URL{
  259. Scheme: "https",
  260. Host: net.JoinHostPort(a.Hostname, strDef(a.HTTPSPort, "443")),
  261. Path: serverUpgradePath,
  262. }
  263. type tryURLRes struct {
  264. u *url.URL // input (the URL conn+err are for/from)
  265. conn *ClientConn // result (mutually exclusive with err)
  266. err error
  267. }
  268. ch := make(chan tryURLRes) // must be unbuffered
  269. try := func(u *url.URL) {
  270. if debugNoiseDial() {
  271. a.logf("trying noise dial (%v, %v) ...", u, addr)
  272. }
  273. cbConn, err := a.dialURL(ctx, u, addr)
  274. if debugNoiseDial() {
  275. a.logf("noise dial (%v, %v) = (%v, %v)", u, addr, cbConn, err)
  276. }
  277. select {
  278. case ch <- tryURLRes{u, cbConn, err}:
  279. case <-ctx.Done():
  280. if cbConn != nil {
  281. cbConn.Close()
  282. }
  283. }
  284. }
  285. // Start the plaintext HTTP attempt first, unless disabled by the envknob.
  286. if !forceNoise443() {
  287. go try(u80)
  288. }
  289. // In case outbound port 80 blocked or MITM'ed poorly, start a backup timer
  290. // to dial port 443 if port 80 doesn't either succeed or fail quickly.
  291. if a.Clock == nil {
  292. a.Clock = tstime.StdClock{}
  293. }
  294. try443Timer := a.Clock.AfterFunc(a.httpsFallbackDelay(), func() { try(u443) })
  295. defer try443Timer.Stop()
  296. var err80, err443 error
  297. for {
  298. select {
  299. case <-ctx.Done():
  300. return nil, fmt.Errorf("connection attempts aborted by context: %w", ctx.Err())
  301. case res := <-ch:
  302. if res.err == nil {
  303. return res.conn, nil
  304. }
  305. switch res.u {
  306. case u80:
  307. // Connecting over plain HTTP failed; assume it's an HTTP proxy
  308. // being difficult and see if we can get through over HTTPS.
  309. err80 = res.err
  310. // Stop the fallback timer and run it immediately. We don't use
  311. // Timer.Reset(0) here because on AfterFuncs, that can run it
  312. // again.
  313. if try443Timer.Stop() {
  314. go try(u443)
  315. } // else we lost the race and it started already which is what we want
  316. case u443:
  317. err443 = res.err
  318. default:
  319. panic("invalid")
  320. }
  321. if err80 != nil && err443 != nil {
  322. return nil, fmt.Errorf("all connection attempts failed (HTTP: %v, HTTPS: %v)", err80, err443)
  323. }
  324. }
  325. }
  326. }
  327. // dialURL attempts to connect to the given URL.
  328. func (a *Dialer) dialURL(ctx context.Context, u *url.URL, addr netip.Addr) (*ClientConn, error) {
  329. init, cont, err := controlbase.ClientDeferred(a.MachineKey, a.ControlKey, a.ProtocolVersion)
  330. if err != nil {
  331. return nil, err
  332. }
  333. netConn, err := a.tryURLUpgrade(ctx, u, addr, init)
  334. if err != nil {
  335. return nil, err
  336. }
  337. cbConn, err := cont(ctx, netConn)
  338. if err != nil {
  339. netConn.Close()
  340. return nil, err
  341. }
  342. return &ClientConn{
  343. Conn: cbConn,
  344. }, nil
  345. }
  346. // resolver returns a.DNSCache if non-nil or a new *dnscache.Resolver
  347. // otherwise.
  348. func (a *Dialer) resolver() *dnscache.Resolver {
  349. if a.DNSCache != nil {
  350. return a.DNSCache
  351. }
  352. return &dnscache.Resolver{
  353. Forward: dnscache.Get().Forward,
  354. LookupIPFallback: dnsfallback.MakeLookupFunc(a.logf, a.NetMon),
  355. UseLastGood: true,
  356. Logf: a.Logf, // not a.logf method; we want to propagate nil-ness
  357. NetMon: a.NetMon,
  358. }
  359. }
  360. // tryURLUpgrade connects to u, and tries to upgrade it to a net.Conn. If addr
  361. // is valid, then no DNS is used and the connection will be made to the
  362. // provided address.
  363. //
  364. // Only the provided ctx is used, not a.ctx.
  365. func (a *Dialer) tryURLUpgrade(ctx context.Context, u *url.URL, addr netip.Addr, init []byte) (net.Conn, error) {
  366. var dns *dnscache.Resolver
  367. // If we were provided an address to dial, then create a resolver that just
  368. // returns that value; otherwise, fall back to DNS.
  369. if addr.IsValid() {
  370. dns = &dnscache.Resolver{
  371. SingleHostStaticResult: []netip.Addr{addr},
  372. SingleHost: u.Hostname(),
  373. Logf: a.Logf, // not a.logf method; we want to propagate nil-ness
  374. NetMon: a.NetMon,
  375. }
  376. } else {
  377. dns = a.resolver()
  378. }
  379. var dialer dnscache.DialContextFunc
  380. if a.Dialer != nil {
  381. dialer = a.Dialer
  382. } else {
  383. dialer = stdDialer.DialContext
  384. }
  385. tr := http.DefaultTransport.(*http.Transport).Clone()
  386. defer tr.CloseIdleConnections()
  387. tr.Proxy = a.getProxyFunc()
  388. tshttpproxy.SetTransportGetProxyConnectHeader(tr)
  389. tr.DialContext = dnscache.Dialer(dialer, dns)
  390. // Disable HTTP2, since h2 can't do protocol switching.
  391. tr.TLSClientConfig.NextProtos = []string{}
  392. tr.TLSNextProto = map[string]func(string, *tls.Conn) http.RoundTripper{}
  393. tr.TLSClientConfig = tlsdial.Config(a.Hostname, health.Global, tr.TLSClientConfig)
  394. if !tr.TLSClientConfig.InsecureSkipVerify {
  395. panic("unexpected") // should be set by tlsdial.Config
  396. }
  397. verify := tr.TLSClientConfig.VerifyConnection
  398. if verify == nil {
  399. panic("unexpected") // should be set by tlsdial.Config
  400. }
  401. // Demote all cert verification errors to log messages. We don't actually
  402. // care about the TLS security (because we just do the Noise crypto atop whatever
  403. // connection we get, including HTTP port 80 plaintext) so this permits
  404. // middleboxes to MITM their users. All they'll see is some Noise.
  405. tr.TLSClientConfig.VerifyConnection = func(cs tls.ConnectionState) error {
  406. if err := verify(cs); err != nil && a.Logf != nil && !a.omitCertErrorLogging {
  407. a.Logf("warning: TLS cert verificication for %q failed: %v", a.Hostname, err)
  408. }
  409. return nil // regardless
  410. }
  411. tr.DialTLSContext = dnscache.TLSDialer(dialer, dns, tr.TLSClientConfig)
  412. tr.DisableCompression = true
  413. // (mis)use httptrace to extract the underlying net.Conn from the
  414. // transport. We make exactly 1 request using this transport, so
  415. // there will be exactly 1 GotConn call. Additionally, the
  416. // transport handles 101 Switching Protocols correctly, such that
  417. // the Conn will not be reused or kept alive by the transport once
  418. // the response has been handed back from RoundTrip.
  419. //
  420. // In theory, the machinery of net/http should make it such that
  421. // the trace callback happens-before we get the response, but
  422. // there's no promise of that. So, to make sure, we use a buffered
  423. // channel as a synchronization step to avoid data races.
  424. //
  425. // Note that even though we're able to extract a net.Conn via this
  426. // mechanism, we must still keep using the eventual resp.Body to
  427. // read from, because it includes a buffer we can't get rid of. If
  428. // the server never sends any data after sending the HTTP
  429. // response, we could get away with it, but violating this
  430. // assumption leads to very mysterious transport errors (lockups,
  431. // unexpected EOFs...), and we're bound to forget someday and
  432. // introduce a protocol optimization at a higher level that starts
  433. // eagerly transmitting from the server.
  434. connCh := make(chan net.Conn, 1)
  435. trace := httptrace.ClientTrace{
  436. GotConn: func(info httptrace.GotConnInfo) {
  437. connCh <- info.Conn
  438. },
  439. }
  440. ctx = httptrace.WithClientTrace(ctx, &trace)
  441. req := &http.Request{
  442. Method: "POST",
  443. URL: u,
  444. Header: http.Header{
  445. "Upgrade": []string{upgradeHeaderValue},
  446. "Connection": []string{"upgrade"},
  447. handshakeHeaderName: []string{base64.StdEncoding.EncodeToString(init)},
  448. },
  449. }
  450. req = req.WithContext(ctx)
  451. resp, err := tr.RoundTrip(req)
  452. if err != nil {
  453. return nil, err
  454. }
  455. if resp.StatusCode != http.StatusSwitchingProtocols {
  456. return nil, fmt.Errorf("unexpected HTTP response: %s", resp.Status)
  457. }
  458. // From here on, the underlying net.Conn is ours to use, but there
  459. // is still a read buffer attached to it within resp.Body. So, we
  460. // must direct I/O through resp.Body, but we can still use the
  461. // underlying net.Conn for stuff like deadlines.
  462. var switchedConn net.Conn
  463. select {
  464. case switchedConn = <-connCh:
  465. default:
  466. }
  467. if switchedConn == nil {
  468. resp.Body.Close()
  469. return nil, fmt.Errorf("httptrace didn't provide a connection")
  470. }
  471. if next := resp.Header.Get("Upgrade"); next != upgradeHeaderValue {
  472. resp.Body.Close()
  473. return nil, fmt.Errorf("server switched to unexpected protocol %q", next)
  474. }
  475. rwc, ok := resp.Body.(io.ReadWriteCloser)
  476. if !ok {
  477. resp.Body.Close()
  478. return nil, errors.New("http Transport did not provide a writable body")
  479. }
  480. return netutil.NewAltReadWriteCloserConn(rwc, switchedConn), nil
  481. }