parent
36189e2704
commit
fdac0387a7
@ -0,0 +1,95 @@ |
||||
// Copyright (c) 2021 Tailscale Inc & AUTHORS All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Package localapi contains the HTTP server handlers for tailscaled's API server.
|
||||
package localapi |
||||
|
||||
import ( |
||||
"encoding/json" |
||||
"io" |
||||
"net/http" |
||||
|
||||
"inet.af/netaddr" |
||||
"tailscale.com/ipn/ipnlocal" |
||||
"tailscale.com/tailcfg" |
||||
) |
||||
|
||||
func NewHandler(b *ipnlocal.LocalBackend) *Handler { |
||||
return &Handler{b: b} |
||||
} |
||||
|
||||
type Handler struct { |
||||
// RequiredPassword, if non-empty, forces all HTTP
|
||||
// requests to have HTTP basic auth with this password.
|
||||
// It's used by the sandboxed macOS sameuserproof GUI auth mechanism.
|
||||
RequiredPassword string |
||||
|
||||
// PermitRead is whether read-only HTTP handlers are allowed.
|
||||
PermitRead bool |
||||
|
||||
// PermitWrite is whether mutating HTTP handlers are allowed.
|
||||
PermitWrite bool |
||||
|
||||
b *ipnlocal.LocalBackend |
||||
} |
||||
|
||||
func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) { |
||||
if h.b == nil { |
||||
http.Error(w, "server has no local backend", http.StatusInternalServerError) |
||||
return |
||||
} |
||||
if h.RequiredPassword != "" { |
||||
_, pass, ok := r.BasicAuth() |
||||
if !ok { |
||||
http.Error(w, "auth required", http.StatusUnauthorized) |
||||
return |
||||
} |
||||
if pass != h.RequiredPassword { |
||||
http.Error(w, "bad password", http.StatusForbidden) |
||||
return |
||||
} |
||||
} |
||||
switch r.URL.Path { |
||||
case "/localapi/v0/whois": |
||||
h.serveWhoIs(w, r) |
||||
default: |
||||
io.WriteString(w, "tailscaled\n") |
||||
} |
||||
} |
||||
|
||||
func (h *Handler) serveWhoIs(w http.ResponseWriter, r *http.Request) { |
||||
if !h.PermitRead { |
||||
http.Error(w, "whois access denied", http.StatusForbidden) |
||||
return |
||||
} |
||||
b := h.b |
||||
var ip netaddr.IP |
||||
if v := r.FormValue("ip"); v != "" { |
||||
var err error |
||||
ip, err = netaddr.ParseIP(r.FormValue("ip")) |
||||
if err != nil { |
||||
http.Error(w, "invalid 'ip' parameter", 400) |
||||
return |
||||
} |
||||
} else { |
||||
http.Error(w, "missing 'ip' parameter", 400) |
||||
return |
||||
} |
||||
n, u, ok := b.WhoIs(ip) |
||||
if !ok { |
||||
http.Error(w, "no match for IP", 404) |
||||
return |
||||
} |
||||
res := &tailcfg.WhoIsResponse{ |
||||
Node: n, |
||||
UserProfile: &u, |
||||
} |
||||
j, err := json.MarshalIndent(res, "", "\t") |
||||
if err != nil { |
||||
http.Error(w, "JSON encoding error", 500) |
||||
return |
||||
} |
||||
w.Header().Set("Content-Type", "application/json") |
||||
w.Write(j) |
||||
} |
||||
Loading…
Reference in new issue