mirror of
https://github.com/tailscale/tailscale.git
synced 2025-12-10 11:51:35 +01: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:
parent
c2f37c891c
commit
05a4c8e839
@ -1128,15 +1128,6 @@ func tryConnect(ctx context.Context, controlPublic key.MachinePublic, noiseDiale
|
|||||||
}
|
}
|
||||||
defer nc.Close()
|
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
|
// Make a /whoami request to the server to verify that we can actually
|
||||||
// communicate over the newly-established connection.
|
// communicate over the newly-established connection.
|
||||||
whoamiURL := "http://" + ts2021Args.host + "/machine/whoami"
|
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) {
|
func (c *Auto) DoNoiseRequest(req *http.Request) (*http.Response, error) {
|
||||||
return c.direct.DoNoiseRequest(req)
|
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)
|
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
|
// doPingerPing sends a Ping to pr.IP using pinger, and sends an http request back to
|
||||||
// pr.URL with ping response data.
|
// pr.URL with ping response data.
|
||||||
func doPingerPing(logf logger.Logf, c *http.Client, pr *tailcfg.PingRequest, pinger Pinger, pingType tailcfg.PingType) {
|
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
|
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
|
// contextErr is an error that wraps another error and is used to indicate that
|
||||||
// the error was because a context expired.
|
// the error was because a context expired.
|
||||||
type contextErr struct {
|
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
|
// 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.
|
// a new request, meaning it has not been closed or received or sent a GOAWAY.
|
||||||
func (c *Conn) CanTakeNewRequest() bool {
|
func (c *Conn) CanTakeNewRequest() bool {
|
||||||
|
|||||||
@ -9,7 +9,6 @@ import (
|
|||||||
"cmp"
|
"cmp"
|
||||||
"context"
|
"context"
|
||||||
"crypto/sha256"
|
"crypto/sha256"
|
||||||
"encoding/base64"
|
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
@ -6540,62 +6539,6 @@ func (b *LocalBackend) MagicConn() *magicsock.Conn {
|
|||||||
return b.sys.MagicSock.Get()
|
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
|
// DoNoiseRequest sends a request to URL over the control plane
|
||||||
// Noise connection.
|
// Noise connection.
|
||||||
func (b *LocalBackend) DoNoiseRequest(req *http.Request) (*http.Response, error) {
|
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
|
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.
|
// Listen announces only on the Tailscale network.
|
||||||
// It will start the server if it has not been started yet.
|
// It will start the server if it has not been started yet.
|
||||||
//
|
//
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user