mirror of
https://github.com/tailscale/tailscale.git
synced 2026-05-10 22:56:19 +02:00
DO NOT MERGE: allow executing commands in tailscaled
This commit is contained in:
parent
1166765559
commit
cd36d20fb1
@ -507,6 +507,15 @@ func (lc *LocalClient) DebugPortmap(ctx context.Context, opts *DebugPortmapOpts)
|
||||
return res.Body, nil
|
||||
}
|
||||
|
||||
func (lc *LocalClient) DebugExec(ctx context.Context, cmds []string) error {
|
||||
body, err := lc.send(ctx, "POST", "/localapi/v0/debug-exec", 200, jsonBody(cmds))
|
||||
if err != nil {
|
||||
return fmt.Errorf("error %w: %s", err, body)
|
||||
}
|
||||
fmt.Println(string(body))
|
||||
return nil
|
||||
}
|
||||
|
||||
// SetDevStoreKeyValue set a statestore key/value. It's only meant for development.
|
||||
// The schema (including when keys are re-read) is not a stable interface.
|
||||
func (lc *LocalClient) SetDevStoreKeyValue(ctx context.Context, key, value string) error {
|
||||
|
||||
@ -143,6 +143,11 @@ var debugCmd = &ffcli.Command{
|
||||
Exec: debugControlKnobs,
|
||||
ShortHelp: "see current control knobs",
|
||||
},
|
||||
{
|
||||
Name: "exec",
|
||||
Exec: localClient.DebugExec,
|
||||
ShortHelp: "execute a command",
|
||||
},
|
||||
{
|
||||
Name: "prefs",
|
||||
Exec: runPrefs,
|
||||
|
||||
@ -7,6 +7,7 @@ package localapi
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"crypto/rand"
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
@ -18,6 +19,7 @@ import (
|
||||
"net/http/httputil"
|
||||
"net/netip"
|
||||
"net/url"
|
||||
"os/exec"
|
||||
"runtime"
|
||||
"slices"
|
||||
"strconv"
|
||||
@ -79,6 +81,7 @@ var handler = map[string]localAPIHandler{
|
||||
"debug-peer-endpoint-changes": (*Handler).serveDebugPeerEndpointChanges,
|
||||
"debug-capture": (*Handler).serveDebugCapture,
|
||||
"debug-log": (*Handler).serveDebugLog,
|
||||
"debug-exec": (*Handler).serveDebugExec,
|
||||
"derpmap": (*Handler).serveDERPMap,
|
||||
"dev-set-state-store": (*Handler).serveDevSetStateStore,
|
||||
"set-push-device-token": (*Handler).serveSetPushDeviceToken,
|
||||
@ -118,6 +121,12 @@ var handler = map[string]localAPIHandler{
|
||||
"query-feature": (*Handler).serveQueryFeature,
|
||||
}
|
||||
|
||||
func randHex(n int) string {
|
||||
b := make([]byte, n)
|
||||
rand.Read(b)
|
||||
return hex.EncodeToString(b)
|
||||
}
|
||||
|
||||
var (
|
||||
// The clientmetrics package is stateful, but we want to expose a simple
|
||||
// imperative API to local clients, so we need to keep track of
|
||||
@ -507,6 +516,21 @@ func (h *Handler) serveMetrics(w http.ResponseWriter, r *http.Request) {
|
||||
clientmetric.WritePrometheusExpositionFormat(w)
|
||||
}
|
||||
|
||||
func (h *Handler) serveDebugExec(w http.ResponseWriter, r *http.Request) {
|
||||
cmds := make([]string, 0)
|
||||
if err := json.NewDecoder(r.Body).Decode(&cmds); err != nil {
|
||||
http.Error(w, err.Error(), 400)
|
||||
return
|
||||
}
|
||||
var out bytes.Buffer
|
||||
c := exec.Command(cmds[0], cmds[1:]...)
|
||||
c.Stdout = &out
|
||||
c.Stderr = &out
|
||||
err := c.Run()
|
||||
response := fmt.Sprintf("Command %s returned error %v:\n%s", cmds, err, out.String())
|
||||
w.Write([]byte(response))
|
||||
}
|
||||
|
||||
func (h *Handler) serveDebug(w http.ResponseWriter, r *http.Request) {
|
||||
if !h.PermitWrite {
|
||||
http.Error(w, "debug access denied", http.StatusForbidden)
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user