mirror of
https://github.com/tailscale/tailscale.git
synced 2024-11-29 04:55:31 +00:00
tsnet: run the LocalAPI handler
Signed-off-by: Maisem Ali <maisem@tailscale.com>
This commit is contained in:
parent
830f641c6b
commit
52be1c0c78
@ -21,6 +21,7 @@
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"go4.org/mem"
|
||||
@ -33,30 +34,39 @@
|
||||
"tailscale.com/version"
|
||||
)
|
||||
|
||||
// TailscaledSocket is the tailscaled Unix socket.
|
||||
var TailscaledSocket = paths.DefaultTailscaledSocket()
|
||||
var (
|
||||
// TailscaledSocket is the tailscaled Unix socket. It's used by the TailscaledDialer.
|
||||
TailscaledSocket = paths.DefaultTailscaledSocket()
|
||||
|
||||
// tsClient does HTTP requests to the local Tailscale daemon.
|
||||
var tsClient = &http.Client{
|
||||
Transport: &http.Transport{
|
||||
DialContext: func(ctx context.Context, network, addr string) (net.Conn, error) {
|
||||
if addr != "local-tailscaled.sock:80" {
|
||||
return nil, fmt.Errorf("unexpected URL address %q", addr)
|
||||
}
|
||||
if TailscaledSocket == paths.DefaultTailscaledSocket() {
|
||||
// On macOS, when dialing from non-sandboxed program to sandboxed GUI running
|
||||
// a TCP server on a random port, find the random port. For HTTP connections,
|
||||
// we don't send the token. It gets added in an HTTP Basic-Auth header.
|
||||
if port, _, err := safesocket.LocalTCPPortAndToken(); err == nil {
|
||||
var d net.Dialer
|
||||
return d.DialContext(ctx, "tcp", "localhost:"+strconv.Itoa(port))
|
||||
}
|
||||
}
|
||||
return safesocket.Connect(TailscaledSocket, 41112)
|
||||
},
|
||||
},
|
||||
// TailscaledDialer is the DialContext func that connects to the local machine's
|
||||
// tailscaled or equivalent.
|
||||
TailscaledDialer = defaultDialer
|
||||
)
|
||||
|
||||
func defaultDialer(ctx context.Context, network, addr string) (net.Conn, error) {
|
||||
if addr != "local-tailscaled.sock:80" {
|
||||
return nil, fmt.Errorf("unexpected URL address %q", addr)
|
||||
}
|
||||
if TailscaledSocket == paths.DefaultTailscaledSocket() {
|
||||
// On macOS, when dialing from non-sandboxed program to sandboxed GUI running
|
||||
// a TCP server on a random port, find the random port. For HTTP connections,
|
||||
// we don't send the token. It gets added in an HTTP Basic-Auth header.
|
||||
if port, _, err := safesocket.LocalTCPPortAndToken(); err == nil {
|
||||
var d net.Dialer
|
||||
return d.DialContext(ctx, "tcp", "localhost:"+strconv.Itoa(port))
|
||||
}
|
||||
}
|
||||
return safesocket.Connect(TailscaledSocket, 41112)
|
||||
}
|
||||
|
||||
var (
|
||||
// tsClient does HTTP requests to the local Tailscale daemon.
|
||||
// We lazily initialize the client in case the caller wants to
|
||||
// override TailscaledDialer.
|
||||
tsClient *http.Client
|
||||
tsClientOnce sync.Once
|
||||
)
|
||||
|
||||
// DoLocalRequest makes an HTTP request to the local machine's Tailscale daemon.
|
||||
//
|
||||
// URLs are of the form http://local-tailscaled.sock/localapi/v0/whois?ip=1.2.3.4.
|
||||
@ -67,6 +77,13 @@
|
||||
//
|
||||
// DoLocalRequest may mutate the request to add Authorization headers.
|
||||
func DoLocalRequest(req *http.Request) (*http.Response, error) {
|
||||
tsClientOnce.Do(func() {
|
||||
tsClient = &http.Client{
|
||||
Transport: &http.Transport{
|
||||
DialContext: TailscaledDialer,
|
||||
},
|
||||
}
|
||||
})
|
||||
if _, token, err := safesocket.LocalTCPPortAndToken(); err == nil {
|
||||
req.SetBasicAuth("", token)
|
||||
}
|
||||
|
@ -12,6 +12,7 @@
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"tailscale.com/client/tailscale"
|
||||
"tailscale.com/tsnet"
|
||||
)
|
||||
|
||||
@ -22,9 +23,9 @@ func main() {
|
||||
log.Fatal(err)
|
||||
}
|
||||
log.Fatal(http.Serve(ln, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
who, ok := s.WhoIs(r.RemoteAddr)
|
||||
if !ok {
|
||||
http.Error(w, "WhoIs failed", 500)
|
||||
who, err := tailscale.WhoIs(r.Context(), r.RemoteAddr)
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), 500)
|
||||
return
|
||||
}
|
||||
fmt.Fprintf(w, "<html><body><h1>Hello, world!</h1>\n")
|
||||
|
@ -12,6 +12,7 @@
|
||||
"fmt"
|
||||
"log"
|
||||
"net"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
@ -19,11 +20,12 @@
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"inet.af/netaddr"
|
||||
"tailscale.com/client/tailscale/apitype"
|
||||
"tailscale.com/client/tailscale"
|
||||
"tailscale.com/control/controlclient"
|
||||
"tailscale.com/ipn"
|
||||
"tailscale.com/ipn/ipnlocal"
|
||||
"tailscale.com/ipn/localapi"
|
||||
"tailscale.com/net/nettest"
|
||||
"tailscale.com/smallzstd"
|
||||
"tailscale.com/types/logger"
|
||||
"tailscale.com/wgengine"
|
||||
@ -60,28 +62,6 @@ type Server struct {
|
||||
listeners map[listenKey]*listener
|
||||
}
|
||||
|
||||
// WhoIs reports the node and user who owns the node with the given
|
||||
// address. The addr may be an ip:port (as from an
|
||||
// http.Request.RemoteAddr) or just an IP address.
|
||||
func (s *Server) WhoIs(addr string) (w *apitype.WhoIsResponse, ok bool) {
|
||||
ipp, err := netaddr.ParseIPPort(addr)
|
||||
if err != nil {
|
||||
ip, err := netaddr.ParseIP(addr)
|
||||
if err != nil {
|
||||
return nil, false
|
||||
}
|
||||
ipp = ipp.WithIP(ip)
|
||||
}
|
||||
n, up, ok := s.lb.WhoIs(ipp)
|
||||
if !ok {
|
||||
return nil, false
|
||||
}
|
||||
return &apitype.WhoIsResponse{
|
||||
Node: n,
|
||||
UserProfile: &up,
|
||||
}, true
|
||||
}
|
||||
|
||||
func (s *Server) doInit() {
|
||||
if err := s.start(); err != nil {
|
||||
s.initErr = fmt.Errorf("tsnet: %w", err)
|
||||
@ -185,6 +165,24 @@ func (s *Server) start() error {
|
||||
if os.Getenv("TS_LOGIN") == "1" || os.Getenv("TS_AUTHKEY") != "" {
|
||||
s.lb.StartLoginInteractive()
|
||||
}
|
||||
|
||||
// Run the localapi handler, to allow fetching LetsEncrypt certs.
|
||||
lah := localapi.NewHandler(lb, logf, logid)
|
||||
lah.PermitWrite = true
|
||||
lah.PermitRead = true
|
||||
|
||||
// Create an in-process listener.
|
||||
// nettest.Listen provides a in-memory pipe based implementation for net.Conn.
|
||||
// TODO(maisem): Rename nettest package to remove "test".
|
||||
lal := nettest.Listen("local-tailscaled.sock:80")
|
||||
|
||||
// Override the Tailscale client to use the in-process listener.
|
||||
tailscale.TailscaledDialer = lal.Dial
|
||||
go func() {
|
||||
if err := http.Serve(lal, lah); err != nil {
|
||||
logf("localapi serve error: %v", err)
|
||||
}
|
||||
}()
|
||||
return nil
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user