mirror of
https://github.com/tailscale/tailscale.git
synced 2025-05-06 07:37:38 +00:00
client/web,cmd/tailscale: add prefix flag for web command
We already had a path on the web client server struct, but hadn't plumbed it through to the CLI. Add that now and use it for Synology and QNAP instead of hard-coding the path. (Adding flag for QNAP is tailscale/tailscale-qpkg#112) This will allow supporting other environments (like unraid) without additional changes to the client/web package. Also fix a small bug in unraid handling to only include the csrf token on POST requests. Updates tailscale/corp#13775 Signed-off-by: Will Norris <will@tailscale.com>
This commit is contained in:
parent
34e3450734
commit
9a3bc9049c
@ -16,8 +16,6 @@ import (
|
|||||||
"net/url"
|
"net/url"
|
||||||
)
|
)
|
||||||
|
|
||||||
const qnapPrefix = "/cgi-bin/qpkg/Tailscale/index.cgi/"
|
|
||||||
|
|
||||||
// authorizeQNAP authenticates the logged-in QNAP user and verifies
|
// authorizeQNAP authenticates the logged-in QNAP user and verifies
|
||||||
// that they are authorized to use the web client. It returns true if the
|
// that they are authorized to use the web client. It returns true if the
|
||||||
// request was handled and no further processing is required.
|
// request was handled and no further processing is required.
|
||||||
|
@ -23,7 +23,7 @@ export function apiFetch(
|
|||||||
const url = `api${endpoint}${search ? `?${search}` : ""}`
|
const url = `api${endpoint}${search ? `?${search}` : ""}`
|
||||||
|
|
||||||
var contentType: string
|
var contentType: string
|
||||||
if (unraidCsrfToken) {
|
if (unraidCsrfToken && method === "POST") {
|
||||||
const params = new URLSearchParams()
|
const params = new URLSearchParams()
|
||||||
params.append("csrf_token", unraidCsrfToken)
|
params.append("csrf_token", unraidCsrfToken)
|
||||||
if (body) {
|
if (body) {
|
||||||
|
@ -15,8 +15,6 @@ import (
|
|||||||
"tailscale.com/util/groupmember"
|
"tailscale.com/util/groupmember"
|
||||||
)
|
)
|
||||||
|
|
||||||
const synologyPrefix = "/webman/3rdparty/Tailscale/index.cgi/"
|
|
||||||
|
|
||||||
// authorizeSynology authenticates the logged-in Synology user and verifies
|
// authorizeSynology authenticates the logged-in Synology user and verifies
|
||||||
// that they are authorized to use the web client. It returns true if the
|
// that they are authorized to use the web client. It returns true if the
|
||||||
// request was handled and no further processing is required.
|
// request was handled and no further processing is required.
|
||||||
|
@ -58,7 +58,7 @@ type Server struct {
|
|||||||
devProxy *httputil.ReverseProxy // only filled when devMode is on
|
devProxy *httputil.ReverseProxy // only filled when devMode is on
|
||||||
|
|
||||||
cgiMode bool
|
cgiMode bool
|
||||||
cgiPath string
|
pathPrefix string
|
||||||
apiHandler http.Handler // csrf-protected api handler
|
apiHandler http.Handler // csrf-protected api handler
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -69,8 +69,8 @@ type ServerOpts struct {
|
|||||||
// CGIMode indicates if the server is running as a CGI script.
|
// CGIMode indicates if the server is running as a CGI script.
|
||||||
CGIMode bool
|
CGIMode bool
|
||||||
|
|
||||||
// If running in CGIMode, CGIPath is the URL path prefix to the CGI script.
|
// PathPrefix is the URL prefix added to requests by CGI or reverse proxy.
|
||||||
CGIPath string
|
PathPrefix string
|
||||||
|
|
||||||
// LocalClient is the tailscale.LocalClient to use for this web server.
|
// LocalClient is the tailscale.LocalClient to use for this web server.
|
||||||
// If nil, a new one will be created.
|
// If nil, a new one will be created.
|
||||||
@ -87,7 +87,7 @@ func NewServer(ctx context.Context, opts ServerOpts) (s *Server, cleanup func())
|
|||||||
devMode: opts.DevMode,
|
devMode: opts.DevMode,
|
||||||
lc: opts.LocalClient,
|
lc: opts.LocalClient,
|
||||||
cgiMode: opts.CGIMode,
|
cgiMode: opts.CGIMode,
|
||||||
cgiPath: opts.CGIPath,
|
pathPrefix: opts.PathPrefix,
|
||||||
}
|
}
|
||||||
cleanup = func() {}
|
cleanup = func() {}
|
||||||
if s.devMode {
|
if s.devMode {
|
||||||
@ -116,20 +116,9 @@ func init() {
|
|||||||
func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||||
handler := s.serve
|
handler := s.serve
|
||||||
|
|
||||||
// if running in cgi mode, strip the cgi path prefix
|
// if path prefix is defined, strip it from requests.
|
||||||
if s.cgiMode {
|
if s.pathPrefix != "" {
|
||||||
prefix := s.cgiPath
|
handler = enforcePrefix(s.pathPrefix, handler)
|
||||||
if prefix == "" {
|
|
||||||
switch distro.Get() {
|
|
||||||
case distro.Synology:
|
|
||||||
prefix = synologyPrefix
|
|
||||||
case distro.QNAP:
|
|
||||||
prefix = qnapPrefix
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if prefix != "" {
|
|
||||||
handler = enforcePrefix(prefix, handler)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
handler(w, r)
|
handler(w, r)
|
||||||
@ -334,7 +323,6 @@ func (s *Server) servePostNodeUpdate(w http.ResponseWriter, r *http.Request) {
|
|||||||
} else {
|
} else {
|
||||||
io.WriteString(w, "{}")
|
io.WriteString(w, "{}")
|
||||||
}
|
}
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) tailscaleUp(ctx context.Context, st *ipnstate.Status, postData nodeUpdate) (authURL string, retErr error) {
|
func (s *Server) tailscaleUp(ctx context.Context, st *ipnstate.Status, postData nodeUpdate) (authURL string, retErr error) {
|
||||||
@ -487,6 +475,19 @@ func (s *Server) csrfKey() []byte {
|
|||||||
// Unlike http.StripPrefix, it does not return a 404 if the prefix is not present.
|
// Unlike http.StripPrefix, it does not return a 404 if the prefix is not present.
|
||||||
// Instead, it returns a redirect to the prefix path.
|
// Instead, it returns a redirect to the prefix path.
|
||||||
func enforcePrefix(prefix string, h http.HandlerFunc) http.HandlerFunc {
|
func enforcePrefix(prefix string, h http.HandlerFunc) http.HandlerFunc {
|
||||||
|
if prefix == "" {
|
||||||
|
return h
|
||||||
|
}
|
||||||
|
|
||||||
|
// ensure that prefix always has both a leading and trailing slash so
|
||||||
|
// that relative links for JS and CSS assets work correctly.
|
||||||
|
if !strings.HasPrefix(prefix, "/") {
|
||||||
|
prefix = "/" + prefix
|
||||||
|
}
|
||||||
|
if !strings.HasSuffix(prefix, "/") {
|
||||||
|
prefix += "/"
|
||||||
|
}
|
||||||
|
|
||||||
return func(w http.ResponseWriter, r *http.Request) {
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
if !strings.HasPrefix(r.URL.Path, prefix) {
|
if !strings.HasPrefix(r.URL.Path, prefix) {
|
||||||
http.Redirect(w, r, prefix, http.StatusFound)
|
http.Redirect(w, r, prefix, http.StatusFound)
|
||||||
|
@ -39,6 +39,7 @@ Tailscale, as opposed to a CLI or a native app.
|
|||||||
webf.StringVar(&webArgs.listen, "listen", "localhost:8088", "listen address; use port 0 for automatic")
|
webf.StringVar(&webArgs.listen, "listen", "localhost:8088", "listen address; use port 0 for automatic")
|
||||||
webf.BoolVar(&webArgs.cgi, "cgi", false, "run as CGI script")
|
webf.BoolVar(&webArgs.cgi, "cgi", false, "run as CGI script")
|
||||||
webf.BoolVar(&webArgs.dev, "dev", false, "run web client in developer mode [this flag is in development, use is unsupported]")
|
webf.BoolVar(&webArgs.dev, "dev", false, "run web client in developer mode [this flag is in development, use is unsupported]")
|
||||||
|
webf.StringVar(&webArgs.prefix, "prefix", "", "URL prefix added to requests (for cgi or reverse proxies)")
|
||||||
return webf
|
return webf
|
||||||
})(),
|
})(),
|
||||||
Exec: runWeb,
|
Exec: runWeb,
|
||||||
@ -48,6 +49,7 @@ var webArgs struct {
|
|||||||
listen string
|
listen string
|
||||||
cgi bool
|
cgi bool
|
||||||
dev bool
|
dev bool
|
||||||
|
prefix string
|
||||||
}
|
}
|
||||||
|
|
||||||
func tlsConfigFromEnvironment() *tls.Config {
|
func tlsConfigFromEnvironment() *tls.Config {
|
||||||
@ -81,6 +83,7 @@ func runWeb(ctx context.Context, args []string) error {
|
|||||||
webServer, cleanup := web.NewServer(ctx, web.ServerOpts{
|
webServer, cleanup := web.NewServer(ctx, web.ServerOpts{
|
||||||
DevMode: webArgs.dev,
|
DevMode: webArgs.dev,
|
||||||
CGIMode: webArgs.cgi,
|
CGIMode: webArgs.cgi,
|
||||||
|
PathPrefix: webArgs.prefix,
|
||||||
LocalClient: &localClient,
|
LocalClient: &localClient,
|
||||||
})
|
})
|
||||||
defer cleanup()
|
defer cleanup()
|
||||||
|
2
release/dist/synology/files/index.cgi
vendored
2
release/dist/synology/files/index.cgi
vendored
@ -1,2 +1,2 @@
|
|||||||
#! /bin/sh
|
#! /bin/sh
|
||||||
exec /var/packages/Tailscale/target/bin/tailscale web -cgi
|
exec /var/packages/Tailscale/target/bin/tailscale web -cgi -prefix="/webman/3rdparty/Tailscale/index.cgi/"
|
||||||
|
Loading…
x
Reference in New Issue
Block a user