|
|
@@ -71,36 +71,20 @@ var handler = map[string]LocalAPIHandler{
|
|
|
|
|
|
// The other /localapi/v0/NAME handlers are exact matches and contain only NAME
|
|
|
// without a trailing slash:
|
|
|
- "alpha-set-device-attrs": (*Handler).serveSetDeviceAttrs, // see tailscale/corp#24690
|
|
|
- "check-prefs": (*Handler).serveCheckPrefs,
|
|
|
- "check-reverse-path-filtering": (*Handler).serveCheckReversePathFiltering,
|
|
|
- "check-udp-gro-forwarding": (*Handler).serveCheckUDPGROForwarding,
|
|
|
- "derpmap": (*Handler).serveDERPMap,
|
|
|
- "dial": (*Handler).serveDial,
|
|
|
- "disconnect-control": (*Handler).disconnectControl,
|
|
|
- "goroutines": (*Handler).serveGoroutines,
|
|
|
- "handle-push-message": (*Handler).serveHandlePushMessage,
|
|
|
- "id-token": (*Handler).serveIDToken,
|
|
|
- "login-interactive": (*Handler).serveLoginInteractive,
|
|
|
- "logout": (*Handler).serveLogout,
|
|
|
- "logtap": (*Handler).serveLogTap,
|
|
|
- "metrics": (*Handler).serveMetrics,
|
|
|
- "ping": (*Handler).servePing,
|
|
|
- "prefs": (*Handler).servePrefs,
|
|
|
- "query-feature": (*Handler).serveQueryFeature,
|
|
|
- "reload-config": (*Handler).reloadConfig,
|
|
|
- "reset-auth": (*Handler).serveResetAuth,
|
|
|
- "set-expiry-sooner": (*Handler).serveSetExpirySooner,
|
|
|
- "set-gui-visible": (*Handler).serveSetGUIVisible,
|
|
|
- "set-push-device-token": (*Handler).serveSetPushDeviceToken,
|
|
|
- "set-udp-gro-forwarding": (*Handler).serveSetUDPGROForwarding,
|
|
|
- "shutdown": (*Handler).serveShutdown,
|
|
|
- "start": (*Handler).serveStart,
|
|
|
- "status": (*Handler).serveStatus,
|
|
|
- "update/check": (*Handler).serveUpdateCheck,
|
|
|
- "upload-client-metrics": (*Handler).serveUploadClientMetrics,
|
|
|
- "watch-ipn-bus": (*Handler).serveWatchIPNBus,
|
|
|
- "whois": (*Handler).serveWhoIs,
|
|
|
+ "check-prefs": (*Handler).serveCheckPrefs,
|
|
|
+ "derpmap": (*Handler).serveDERPMap,
|
|
|
+ "goroutines": (*Handler).serveGoroutines,
|
|
|
+ "login-interactive": (*Handler).serveLoginInteractive,
|
|
|
+ "logout": (*Handler).serveLogout,
|
|
|
+ "ping": (*Handler).servePing,
|
|
|
+ "prefs": (*Handler).servePrefs,
|
|
|
+ "reload-config": (*Handler).reloadConfig,
|
|
|
+ "reset-auth": (*Handler).serveResetAuth,
|
|
|
+ "set-expiry-sooner": (*Handler).serveSetExpirySooner,
|
|
|
+ "shutdown": (*Handler).serveShutdown,
|
|
|
+ "start": (*Handler).serveStart,
|
|
|
+ "status": (*Handler).serveStatus,
|
|
|
+ "whois": (*Handler).serveWhoIs,
|
|
|
}
|
|
|
|
|
|
func init() {
|
|
|
@@ -109,6 +93,17 @@ func init() {
|
|
|
}
|
|
|
if buildfeatures.HasAdvertiseRoutes {
|
|
|
Register("check-ip-forwarding", (*Handler).serveCheckIPForwarding)
|
|
|
+ Register("check-udp-gro-forwarding", (*Handler).serveCheckUDPGROForwarding)
|
|
|
+ Register("set-udp-gro-forwarding", (*Handler).serveSetUDPGROForwarding)
|
|
|
+ }
|
|
|
+ if buildfeatures.HasUseExitNode && runtime.GOOS == "linux" {
|
|
|
+ Register("check-reverse-path-filtering", (*Handler).serveCheckReversePathFiltering)
|
|
|
+ }
|
|
|
+ if buildfeatures.HasClientMetrics {
|
|
|
+ Register("upload-client-metrics", (*Handler).serveUploadClientMetrics)
|
|
|
+ }
|
|
|
+ if buildfeatures.HasClientUpdate {
|
|
|
+ Register("update/check", (*Handler).serveUpdateCheck)
|
|
|
}
|
|
|
if buildfeatures.HasUseExitNode {
|
|
|
Register("suggest-exit-node", (*Handler).serveSuggestExitNode)
|
|
|
@@ -121,6 +116,9 @@ func init() {
|
|
|
Register("bugreport", (*Handler).serveBugReport)
|
|
|
Register("pprof", (*Handler).servePprof)
|
|
|
}
|
|
|
+ if buildfeatures.HasDebug || buildfeatures.HasServe {
|
|
|
+ Register("watch-ipn-bus", (*Handler).serveWatchIPNBus)
|
|
|
+ }
|
|
|
if buildfeatures.HasDNS {
|
|
|
Register("dns-osconfig", (*Handler).serveDNSOSConfig)
|
|
|
Register("dns-query", (*Handler).serveDNSQuery)
|
|
|
@@ -128,6 +126,36 @@ func init() {
|
|
|
if buildfeatures.HasUserMetrics {
|
|
|
Register("usermetrics", (*Handler).serveUserMetrics)
|
|
|
}
|
|
|
+ if buildfeatures.HasServe {
|
|
|
+ Register("query-feature", (*Handler).serveQueryFeature)
|
|
|
+ }
|
|
|
+ if buildfeatures.HasOutboundProxy || buildfeatures.HasSSH {
|
|
|
+ Register("dial", (*Handler).serveDial)
|
|
|
+ }
|
|
|
+ if buildfeatures.HasClientMetrics || buildfeatures.HasDebug {
|
|
|
+ Register("metrics", (*Handler).serveMetrics)
|
|
|
+ }
|
|
|
+ if buildfeatures.HasDebug || buildfeatures.HasAdvertiseRoutes {
|
|
|
+ Register("disconnect-control", (*Handler).disconnectControl)
|
|
|
+ }
|
|
|
+ // Alpha/experimental/debug features. These should be moved to
|
|
|
+ // their own features if/when they graduate.
|
|
|
+ if buildfeatures.HasDebug {
|
|
|
+ Register("id-token", (*Handler).serveIDToken)
|
|
|
+ Register("alpha-set-device-attrs", (*Handler).serveSetDeviceAttrs) // see tailscale/corp#24690
|
|
|
+ Register("handle-push-message", (*Handler).serveHandlePushMessage)
|
|
|
+ Register("set-push-device-token", (*Handler).serveSetPushDeviceToken)
|
|
|
+ }
|
|
|
+ if buildfeatures.HasDebug || runtime.GOOS == "windows" || runtime.GOOS == "darwin" {
|
|
|
+ Register("set-gui-visible", (*Handler).serveSetGUIVisible)
|
|
|
+ }
|
|
|
+ if buildfeatures.HasLogTail {
|
|
|
+ // TODO(bradfitz): separate out logtail tap functionality from upload
|
|
|
+ // functionality to make this possible? But seems unlikely people would
|
|
|
+ // want just this. They could "tail -f" or "journalctl -f" their logs
|
|
|
+ // themselves.
|
|
|
+ Register("logtap", (*Handler).serveLogTap)
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
// Register registers a new LocalAPI handler for the given name.
|
|
|
@@ -580,15 +608,6 @@ func (h *Handler) serveGoroutines(w http.ResponseWriter, r *http.Request) {
|
|
|
func (h *Handler) serveLogTap(w http.ResponseWriter, r *http.Request) {
|
|
|
ctx := r.Context()
|
|
|
|
|
|
- if !buildfeatures.HasLogTail {
|
|
|
- // TODO(bradfitz): separate out logtail tap functionality from upload
|
|
|
- // functionality to make this possible? But seems unlikely people would
|
|
|
- // want just this. They could "tail -f" or "journalctl -f" their logs
|
|
|
- // themselves.
|
|
|
- http.Error(w, "logtap not supported in this build", http.StatusNotImplemented)
|
|
|
- return
|
|
|
- }
|
|
|
-
|
|
|
// Require write access (~root) as the logs could contain something
|
|
|
// sensitive.
|
|
|
if !h.PermitWrite {
|
|
|
@@ -662,7 +681,7 @@ func (h *Handler) servePprof(w http.ResponseWriter, r *http.Request) {
|
|
|
|
|
|
// disconnectControl is the handler for local API /disconnect-control endpoint that shuts down control client, so that
|
|
|
// node no longer communicates with control. Doing this makes control consider this node inactive. This can be used
|
|
|
-// before shutting down a replica of HA subnet router or app connector deployments to ensure that control tells the
|
|
|
+// before shutting down a replica of HA subnet router or app connector deployments to ensure that control tells the
|
|
|
// peers to switch over to another replica whilst still maintaining th existing peer connections.
|
|
|
func (h *Handler) disconnectControl(w http.ResponseWriter, r *http.Request) {
|
|
|
if !h.PermitWrite {
|
|
|
@@ -1230,11 +1249,6 @@ func (h *Handler) serveHandlePushMessage(w http.ResponseWriter, r *http.Request)
|
|
|
}
|
|
|
|
|
|
func (h *Handler) serveUploadClientMetrics(w http.ResponseWriter, r *http.Request) {
|
|
|
- if !buildfeatures.HasClientMetrics {
|
|
|
- w.Header().Set("Content-Type", "application/json")
|
|
|
- json.NewEncoder(w).Encode(struct{}{})
|
|
|
- return
|
|
|
- }
|
|
|
if r.Method != httpm.POST {
|
|
|
http.Error(w, "unsupported method", http.StatusMethodNotAllowed)
|
|
|
return
|
|
|
@@ -1498,13 +1512,6 @@ func (h *Handler) serveUpdateCheck(w http.ResponseWriter, r *http.Request) {
|
|
|
http.Error(w, "only GET allowed", http.StatusMethodNotAllowed)
|
|
|
return
|
|
|
}
|
|
|
-
|
|
|
- if !feature.CanAutoUpdate() {
|
|
|
- // if we don't support auto-update, just say that we're up to date
|
|
|
- json.NewEncoder(w).Encode(tailcfg.ClientVersion{RunningLatest: true})
|
|
|
- return
|
|
|
- }
|
|
|
-
|
|
|
cv := h.b.StatusWithoutPeers().ClientVersion
|
|
|
// ipnstate.Status documentation notes that ClientVersion may be nil on some
|
|
|
// platforms where this information is unavailable. In that case, return a
|