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:
Brad Fitzpatrick
2025-09-30 20:53:47 -07:00
committed by Brad Fitzpatrick
parent c2f37c891c
commit 05a4c8e839
7 changed files with 0 additions and 164 deletions

View File

@@ -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"

View File

@@ -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)
}

View File

@@ -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) {

View File

@@ -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 {

View File

@@ -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 {

View File

@@ -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) {

View File

@@ -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.
//