client/tailscale/apitype: add LocalAPIHost const, use it

Removes duplication.

Updates tailcale/corp#7948

Change-Id: I564c912ecfde31ba2293124bb1316e433c2a10f1
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
pull/6347/head
Brad Fitzpatrick 2 years ago committed by Brad Fitzpatrick
parent 97319a1970
commit 976e88d430

@ -7,6 +7,9 @@ package apitype
import "tailscale.com/tailcfg"
// LocalAPIHost is the Host header value used by the LocalAPI.
const LocalAPIHost = "local-tailscaled.sock"
// WhoIsResponse is the JSON type returned by tailscaled debug server's /whois?ip=$IP handler.
type WhoIsResponse struct {
Node *tailcfg.Node

@ -200,7 +200,7 @@ func (lc *LocalClient) send(ctx context.Context, method, path string, wantStatus
if jr, ok := body.(jsonReader); ok && jr.err != nil {
return nil, jr.err // fail early if there was a JSON marshaling error
}
req, err := http.NewRequestWithContext(ctx, method, "http://local-tailscaled.sock"+path, body)
req, err := http.NewRequestWithContext(ctx, method, "http://"+apitype.LocalAPIHost+path, body)
if err != nil {
return nil, err
}
@ -440,7 +440,7 @@ func (lc *LocalClient) DeleteWaitingFile(ctx context.Context, baseName string) e
}
func (lc *LocalClient) GetWaitingFile(ctx context.Context, baseName string) (rc io.ReadCloser, size int64, err error) {
req, err := http.NewRequestWithContext(ctx, "GET", "http://local-tailscaled.sock/localapi/v0/files/"+url.PathEscape(baseName), nil)
req, err := http.NewRequestWithContext(ctx, "GET", "http://"+apitype.LocalAPIHost+"/localapi/v0/files/"+url.PathEscape(baseName), nil)
if err != nil {
return nil, 0, err
}
@ -473,7 +473,7 @@ func (lc *LocalClient) FileTargets(ctx context.Context) ([]apitype.FileTarget, e
// A size of -1 means unknown.
// The name parameter is the original filename, not escaped.
func (lc *LocalClient) PushFile(ctx context.Context, target tailcfg.StableNodeID, size int64, name string, r io.Reader) error {
req, err := http.NewRequestWithContext(ctx, "PUT", "http://local-tailscaled.sock/localapi/v0/file-put/"+string(target)+"/"+url.PathEscape(name), r)
req, err := http.NewRequestWithContext(ctx, "PUT", "http://"+apitype.LocalAPIHost+"/localapi/v0/file-put/"+string(target)+"/"+url.PathEscape(name), r)
if err != nil {
return err
}
@ -584,7 +584,7 @@ func (lc *LocalClient) DialTCP(ctx context.Context, host string, port uint16) (n
},
}
ctx = httptrace.WithClientTrace(ctx, &trace)
req, err := http.NewRequestWithContext(ctx, "POST", "http://local-tailscaled.sock/localapi/v0/dial", nil)
req, err := http.NewRequestWithContext(ctx, "POST", "http://"+apitype.LocalAPIHost+"/localapi/v0/dial", nil)
if err != nil {
return nil, err
}

@ -34,6 +34,7 @@ import (
"tailscale.com/ipn/ipnlocal"
"tailscale.com/ipn/ipnstate"
"tailscale.com/net/netutil"
"tailscale.com/safesocket"
"tailscale.com/tailcfg"
"tailscale.com/tka"
"tailscale.com/types/key"
@ -137,6 +138,10 @@ func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
http.Error(w, "server has no local backend", http.StatusInternalServerError)
return
}
if r.Referer() != "" || r.Header.Get("Origin") != "" || !validHost(r.Host) {
http.Error(w, "invalid localapi request", http.StatusForbidden)
return
}
w.Header().Set("Tailscale-Version", version.Long)
if h.RequiredPassword != "" {
_, pass, ok := r.BasicAuth()
@ -156,6 +161,24 @@ func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
}
}
// validHost reports whether h is a valid Host header value for a LocalAPI request.
func validHost(h string) bool {
// The client code sends a hostname of "local-tailscaled.sock".
switch h {
case "", apitype.LocalAPIHost:
return true
}
// Otherwise, any Host header we see should at most be an ip:port.
ap, err := netip.ParseAddrPort(h)
if err != nil {
return false
}
if runtime.GOOS == "windows" && ap.Port() != safesocket.WindowsLocalPort {
return false
}
return ap.Addr().IsLoopback()
}
// handlerForPath returns the LocalAPI handler for the provided Request.URI.Path.
// (the path doesn't include any query parameters)
func handlerForPath(urlPath string) (h localAPIHandler, ok bool) {

Loading…
Cancel
Save