mirror of
https://github.com/tailscale/tailscale.git
synced 2025-10-27 11:41:14 +00:00
tsnet: remove AuthenticatedAPITransport (API-over-noise) support
It never launched and I've lost hope of it launching and it's in my way now, so I guess it's time to say goodbye. Updates tailscale/corp#4383 Updates #17305 Change-Id: I2eb551d49f2fb062979cc307f284df4b3dfa5956 Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
This commit is contained in:
committed by
Brad Fitzpatrick
parent
c2f37c891c
commit
05a4c8e839
@@ -1128,15 +1128,6 @@ func tryConnect(ctx context.Context, controlPublic key.MachinePublic, noiseDiale
|
||||
}
|
||||
defer nc.Close()
|
||||
|
||||
// Reserve a RoundTrip for the whoami request.
|
||||
ok, _, err := nc.ReserveNewRequest(ctx)
|
||||
if err != nil {
|
||||
return fmt.Errorf("ReserveNewRequest: %w", err)
|
||||
}
|
||||
if !ok {
|
||||
return errors.New("ReserveNewRequest failed")
|
||||
}
|
||||
|
||||
// Make a /whoami request to the server to verify that we can actually
|
||||
// communicate over the newly-established connection.
|
||||
whoamiURL := "http://" + ts2021Args.host + "/machine/whoami"
|
||||
|
||||
@@ -845,13 +845,3 @@ func (c *Auto) SetDNS(ctx context.Context, req *tailcfg.SetDNSRequest) error {
|
||||
func (c *Auto) DoNoiseRequest(req *http.Request) (*http.Response, error) {
|
||||
return c.direct.DoNoiseRequest(req)
|
||||
}
|
||||
|
||||
// GetSingleUseNoiseRoundTripper returns a RoundTripper that can be only be used
|
||||
// once (and must be used once) to make a single HTTP request over the noise
|
||||
// channel to the coordination server.
|
||||
//
|
||||
// In addition to the RoundTripper, it returns the HTTP/2 channel's early noise
|
||||
// payload, if any.
|
||||
func (c *Auto) GetSingleUseNoiseRoundTripper(ctx context.Context) (http.RoundTripper, *tailcfg.EarlyNoise, error) {
|
||||
return c.direct.GetSingleUseNoiseRoundTripper(ctx)
|
||||
}
|
||||
|
||||
@@ -1606,20 +1606,6 @@ func (c *Direct) DoNoiseRequest(req *http.Request) (*http.Response, error) {
|
||||
return nc.Do(req)
|
||||
}
|
||||
|
||||
// GetSingleUseNoiseRoundTripper returns a RoundTripper that can be only be used
|
||||
// once (and must be used once) to make a single HTTP request over the noise
|
||||
// channel to the coordination server.
|
||||
//
|
||||
// In addition to the RoundTripper, it returns the HTTP/2 channel's early noise
|
||||
// payload, if any.
|
||||
func (c *Direct) GetSingleUseNoiseRoundTripper(ctx context.Context) (http.RoundTripper, *tailcfg.EarlyNoise, error) {
|
||||
nc, err := c.getNoiseClient()
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
return nc.GetSingleUseRoundTripper(ctx)
|
||||
}
|
||||
|
||||
// doPingerPing sends a Ping to pr.IP using pinger, and sends an http request back to
|
||||
// pr.URL with ping response data.
|
||||
func doPingerPing(logf logger.Logf, c *http.Client, pr *tailcfg.PingRequest, pinger Pinger, pingType tailcfg.PingType) {
|
||||
|
||||
@@ -181,29 +181,6 @@ func NewNoiseClient(opts NoiseOpts) (*NoiseClient, error) {
|
||||
return np, nil
|
||||
}
|
||||
|
||||
// GetSingleUseRoundTripper returns a RoundTripper that can be only be used once
|
||||
// (and must be used once) to make a single HTTP request over the noise channel
|
||||
// to the coordination server.
|
||||
//
|
||||
// In addition to the RoundTripper, it returns the HTTP/2 channel's early noise
|
||||
// payload, if any.
|
||||
func (nc *NoiseClient) GetSingleUseRoundTripper(ctx context.Context) (http.RoundTripper, *tailcfg.EarlyNoise, error) {
|
||||
for tries := 0; tries < 3; tries++ {
|
||||
conn, err := nc.getConn(ctx)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
ok, earlyPayloadMaybeNil, err := conn.ReserveNewRequest(ctx)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
if ok {
|
||||
return conn, earlyPayloadMaybeNil, nil
|
||||
}
|
||||
}
|
||||
return nil, nil, errors.New("[unexpected] failed to reserve a request on a connection")
|
||||
}
|
||||
|
||||
// contextErr is an error that wraps another error and is used to indicate that
|
||||
// the error was because a context expired.
|
||||
type contextErr struct {
|
||||
|
||||
@@ -84,22 +84,6 @@ func (c *Conn) GetEarlyPayload(ctx context.Context) (*tailcfg.EarlyNoise, error)
|
||||
}
|
||||
}
|
||||
|
||||
// ReserveNewRequest will reserve a new concurrent request on the connection.
|
||||
//
|
||||
// It returns whether the reservation was successful, and any early Noise
|
||||
// payload if present. If a reservation was not successful, it will return
|
||||
// false and nil for the early payload.
|
||||
func (c *Conn) ReserveNewRequest(ctx context.Context) (bool, *tailcfg.EarlyNoise, error) {
|
||||
earlyPayloadMaybeNil, err := c.GetEarlyPayload(ctx)
|
||||
if err != nil {
|
||||
return false, nil, err
|
||||
}
|
||||
if c.h2cc.ReserveNewRequest() {
|
||||
return true, earlyPayloadMaybeNil, nil
|
||||
}
|
||||
return false, nil, nil
|
||||
}
|
||||
|
||||
// CanTakeNewRequest reports whether the underlying HTTP/2 connection can take
|
||||
// a new request, meaning it has not been closed or received or sent a GOAWAY.
|
||||
func (c *Conn) CanTakeNewRequest() bool {
|
||||
|
||||
@@ -9,7 +9,6 @@ import (
|
||||
"cmp"
|
||||
"context"
|
||||
"crypto/sha256"
|
||||
"encoding/base64"
|
||||
"encoding/binary"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
@@ -6540,62 +6539,6 @@ func (b *LocalBackend) MagicConn() *magicsock.Conn {
|
||||
return b.sys.MagicSock.Get()
|
||||
}
|
||||
|
||||
type keyProvingNoiseRoundTripper struct {
|
||||
b *LocalBackend
|
||||
}
|
||||
|
||||
func (n keyProvingNoiseRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) {
|
||||
b := n.b
|
||||
|
||||
var priv key.NodePrivate
|
||||
|
||||
b.mu.Lock()
|
||||
cc := b.ccAuto
|
||||
if nm := b.NetMap(); nm != nil {
|
||||
priv = nm.PrivateKey
|
||||
}
|
||||
b.mu.Unlock()
|
||||
if cc == nil {
|
||||
return nil, errors.New("no client")
|
||||
}
|
||||
if priv.IsZero() {
|
||||
return nil, errors.New("no netmap or private key")
|
||||
}
|
||||
rt, ep, err := cc.GetSingleUseNoiseRoundTripper(req.Context())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if ep == nil || ep.NodeKeyChallenge.IsZero() {
|
||||
go rt.RoundTrip(new(http.Request)) // return our reservation with a bogus request
|
||||
return nil, errors.New("this coordination server does not support API calls over the Noise channel")
|
||||
}
|
||||
|
||||
// QueryEscape the node key since it has a colon in it.
|
||||
nk := url.QueryEscape(priv.Public().String())
|
||||
req.SetBasicAuth(nk, "")
|
||||
|
||||
// genNodeProofHeaderValue returns the Tailscale-Node-Proof header's value to prove
|
||||
// to chalPub that we control claimedPrivate.
|
||||
genNodeProofHeaderValue := func(claimedPrivate key.NodePrivate, chalPub key.ChallengePublic) string {
|
||||
// TODO(bradfitz): cache this somewhere?
|
||||
box := claimedPrivate.SealToChallenge(chalPub, []byte(chalPub.String()))
|
||||
return claimedPrivate.Public().String() + " " + base64.StdEncoding.EncodeToString(box)
|
||||
}
|
||||
|
||||
// And prove we have the private key corresponding to the public key sent
|
||||
// tin the basic auth username.
|
||||
req.Header.Set("Tailscale-Node-Proof", genNodeProofHeaderValue(priv, ep.NodeKeyChallenge))
|
||||
|
||||
return rt.RoundTrip(req)
|
||||
}
|
||||
|
||||
// KeyProvingNoiseRoundTripper returns an http.RoundTripper that uses the LocalBackend's
|
||||
// DoNoiseRequest method and mutates the request to add an authorization header
|
||||
// to prove the client's nodekey.
|
||||
func (b *LocalBackend) KeyProvingNoiseRoundTripper() http.RoundTripper {
|
||||
return keyProvingNoiseRoundTripper{b}
|
||||
}
|
||||
|
||||
// DoNoiseRequest sends a request to URL over the control plane
|
||||
// Noise connection.
|
||||
func (b *LocalBackend) DoNoiseRequest(req *http.Request) (*http.Response, error) {
|
||||
|
||||
@@ -931,41 +931,6 @@ func (s *Server) getUDPHandlerForFlow(src, dst netip.AddrPort) (handler func(net
|
||||
return func(c nettype.ConnPacketConn) { ln.handle(c) }, true
|
||||
}
|
||||
|
||||
// I_Acknowledge_This_API_Is_Experimental must be set true to use AuthenticatedAPITransport()
|
||||
// for now.
|
||||
var I_Acknowledge_This_API_Is_Experimental = false
|
||||
|
||||
// AuthenticatedAPITransport provides an HTTP transport that can be used with
|
||||
// the control server API without needing additional authentication details. It
|
||||
// authenticates using the current client's nodekey.
|
||||
//
|
||||
// It requires the user to set I_Acknowledge_This_API_Is_Experimental.
|
||||
//
|
||||
// For example:
|
||||
//
|
||||
// import "net/http"
|
||||
// import "tailscale.com/client/tailscale/v2"
|
||||
// import "tailscale.com/tsnet"
|
||||
//
|
||||
// var s *tsnet.Server
|
||||
// ...
|
||||
// rt, err := s.AuthenticatedAPITransport()
|
||||
// // handler err ...
|
||||
// var client tailscale.Client{HTTP: http.Client{
|
||||
// Timeout: 1*time.Minute,
|
||||
// UserAgent: "your-useragent-here",
|
||||
// Transport: rt,
|
||||
// }}
|
||||
func (s *Server) AuthenticatedAPITransport() (http.RoundTripper, error) {
|
||||
if !I_Acknowledge_This_API_Is_Experimental {
|
||||
return nil, errors.New("use of AuthenticatedAPITransport without setting I_Acknowledge_This_API_Is_Experimental")
|
||||
}
|
||||
if err := s.Start(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return s.lb.KeyProvingNoiseRoundTripper(), nil
|
||||
}
|
||||
|
||||
// Listen announces only on the Tailscale network.
|
||||
// It will start the server if it has not been started yet.
|
||||
//
|
||||
|
||||
Reference in New Issue
Block a user