mirror of
https://github.com/tailscale/tailscale.git
synced 2025-11-16 19:07:42 +00:00
all: add ts_omit_serve, start making tailscale serve/funnel be modular
tailscaled tailscale combined (linux/amd64)
29853147 17384418 31412596 omitting everything
+ 621570 + 219277 + 554256 .. add serve
Updates #17128
Change-Id: I87c2c6c3d3fc2dc026c3de8ef7000a813b41d31c
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
This commit is contained in:
committed by
Brad Fitzpatrick
parent
5b5ae2b2ee
commit
4cca9f7c67
@@ -8,8 +8,6 @@ import (
|
||||
"bytes"
|
||||
"cmp"
|
||||
"context"
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
@@ -112,7 +110,6 @@ var handler = map[string]LocalAPIHandler{
|
||||
"query-feature": (*Handler).serveQueryFeature,
|
||||
"reload-config": (*Handler).reloadConfig,
|
||||
"reset-auth": (*Handler).serveResetAuth,
|
||||
"serve-config": (*Handler).serveServeConfig,
|
||||
"set-dns": (*Handler).serveSetDNS,
|
||||
"set-expiry-sooner": (*Handler).serveSetExpirySooner,
|
||||
"set-gui-visible": (*Handler).serveSetGUIVisible,
|
||||
@@ -1209,89 +1206,6 @@ func (h *Handler) serveResetAuth(w http.ResponseWriter, r *http.Request) {
|
||||
w.WriteHeader(http.StatusNoContent)
|
||||
}
|
||||
|
||||
func (h *Handler) serveServeConfig(w http.ResponseWriter, r *http.Request) {
|
||||
switch r.Method {
|
||||
case httpm.GET:
|
||||
if !h.PermitRead {
|
||||
http.Error(w, "serve config denied", http.StatusForbidden)
|
||||
return
|
||||
}
|
||||
config := h.b.ServeConfig()
|
||||
bts, err := json.Marshal(config)
|
||||
if err != nil {
|
||||
http.Error(w, "error encoding config: "+err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
sum := sha256.Sum256(bts)
|
||||
etag := hex.EncodeToString(sum[:])
|
||||
w.Header().Set("Etag", etag)
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.Write(bts)
|
||||
case httpm.POST:
|
||||
if !h.PermitWrite {
|
||||
http.Error(w, "serve config denied", http.StatusForbidden)
|
||||
return
|
||||
}
|
||||
configIn := new(ipn.ServeConfig)
|
||||
if err := json.NewDecoder(r.Body).Decode(configIn); err != nil {
|
||||
WriteErrorJSON(w, fmt.Errorf("decoding config: %w", err))
|
||||
return
|
||||
}
|
||||
|
||||
// require a local admin when setting a path handler
|
||||
// TODO: roll-up this Windows-specific check into either PermitWrite
|
||||
// or a global admin escalation check.
|
||||
if err := authorizeServeConfigForGOOSAndUserContext(runtime.GOOS, configIn, h); err != nil {
|
||||
http.Error(w, err.Error(), http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
|
||||
etag := r.Header.Get("If-Match")
|
||||
if err := h.b.SetServeConfig(configIn, etag); err != nil {
|
||||
if errors.Is(err, ipnlocal.ErrETagMismatch) {
|
||||
http.Error(w, err.Error(), http.StatusPreconditionFailed)
|
||||
return
|
||||
}
|
||||
WriteErrorJSON(w, fmt.Errorf("updating config: %w", err))
|
||||
return
|
||||
}
|
||||
w.WriteHeader(http.StatusOK)
|
||||
default:
|
||||
http.Error(w, "method not allowed", http.StatusMethodNotAllowed)
|
||||
}
|
||||
}
|
||||
|
||||
func authorizeServeConfigForGOOSAndUserContext(goos string, configIn *ipn.ServeConfig, h *Handler) error {
|
||||
switch goos {
|
||||
case "windows", "linux", "darwin", "illumos", "solaris":
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
// Only check for local admin on tailscaled-on-mac (based on "sudo"
|
||||
// permissions). On sandboxed variants (MacSys and AppStore), tailscaled
|
||||
// cannot serve files outside of the sandbox and this check is not
|
||||
// relevant.
|
||||
if goos == "darwin" && version.IsSandboxedMacOS() {
|
||||
return nil
|
||||
}
|
||||
if !configIn.HasPathHandler() {
|
||||
return nil
|
||||
}
|
||||
if h.Actor.IsLocalAdmin(h.b.OperatorUserID()) {
|
||||
return nil
|
||||
}
|
||||
switch goos {
|
||||
case "windows":
|
||||
return errors.New("must be a Windows local admin to serve a path")
|
||||
case "linux", "darwin", "illumos", "solaris":
|
||||
return errors.New("must be root, or be an operator and able to run 'sudo tailscale' to serve a path")
|
||||
default:
|
||||
// We filter goos at the start of the func, this default case
|
||||
// should never happen.
|
||||
panic("unreachable")
|
||||
}
|
||||
}
|
||||
|
||||
func (h *Handler) serveCheckIPForwarding(w http.ResponseWriter, r *http.Request) {
|
||||
if !h.PermitRead {
|
||||
http.Error(w, "IP forwarding check access denied", http.StatusForbidden)
|
||||
|
||||
Reference in New Issue
Block a user