mirror of
https://github.com/tailscale/tailscale.git
synced 2025-08-11 21:27:31 +00:00
tsnet: add a LocalAPI listener on loopback, with basic auth
This is for use by LocalAPI clients written in other languages that don't appear to be able to talk HTTP over a socket (e.g. java.net.http.HttpClient). Signed-off-by: David Crawshaw <crawshaw@tailscale.com>
This commit is contained in:

committed by
David Crawshaw

parent
e3211ff88b
commit
768df4ff7a
@@ -150,7 +150,7 @@ 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) {
|
||||
if r.Referer() != "" || r.Header.Get("Origin") != "" || !h.validHost(r.Host) {
|
||||
metricInvalidRequests.Add(1)
|
||||
http.Error(w, "invalid localapi request", http.StatusForbidden)
|
||||
return
|
||||
@@ -180,21 +180,20 @@ func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
}
|
||||
|
||||
// validLocalHost allows either localhost or loopback IP hosts on platforms
|
||||
// that use token security.
|
||||
var validLocalHost = runtime.GOOS == "darwin" || runtime.GOOS == "ios" || runtime.GOOS == "android"
|
||||
// validLocalHostForTesting allows loopback handlers without RequiredPassword for testing.
|
||||
var validLocalHostForTesting = false
|
||||
|
||||
// validHost reports whether h is a valid Host header value for a LocalAPI request.
|
||||
func validHost(h string) bool {
|
||||
func (h *Handler) validHost(hostname string) bool {
|
||||
// The client code sends a hostname of "local-tailscaled.sock".
|
||||
switch h {
|
||||
switch hostname {
|
||||
case "", apitype.LocalAPIHost:
|
||||
return true
|
||||
}
|
||||
if !validLocalHost {
|
||||
return false
|
||||
if !validLocalHostForTesting && h.RequiredPassword == "" {
|
||||
return false // only allow localhost with basic auth or in tests
|
||||
}
|
||||
host, _, err := net.SplitHostPort(h)
|
||||
host, _, err := net.SplitHostPort(hostname)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
@@ -23,9 +23,9 @@ func TestValidHost(t *testing.T) {
|
||||
}{
|
||||
{"", true},
|
||||
{apitype.LocalAPIHost, true},
|
||||
{"localhost:9109", validLocalHost},
|
||||
{"127.0.0.1:9110", validLocalHost},
|
||||
{"[::1]:9111", validLocalHost},
|
||||
{"localhost:9109", false},
|
||||
{"127.0.0.1:9110", false},
|
||||
{"[::1]:9111", false},
|
||||
{"100.100.100.100:41112", false},
|
||||
{"10.0.0.1:41112", false},
|
||||
{"37.16.9.210:41112", false},
|
||||
@@ -33,7 +33,8 @@ func TestValidHost(t *testing.T) {
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.host, func(t *testing.T) {
|
||||
if got := validHost(test.host); got != test.valid {
|
||||
h := &Handler{}
|
||||
if got := h.validHost(test.host); got != test.valid {
|
||||
t.Errorf("validHost(%q)=%v, want %v", test.host, got, test.valid)
|
||||
}
|
||||
})
|
||||
@@ -41,10 +42,9 @@ func TestValidHost(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestSetPushDeviceToken(t *testing.T) {
|
||||
origValidLocalHost := validLocalHost
|
||||
validLocalHost = true
|
||||
validLocalHostForTesting = true
|
||||
defer func() {
|
||||
validLocalHost = origValidLocalHost
|
||||
validLocalHostForTesting = false
|
||||
}()
|
||||
|
||||
h := &Handler{
|
||||
|
Reference in New Issue
Block a user