summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorDenton Gentry <dgentry@tailscale.com>2023-01-29 07:30:40 -0800
committerDenton Gentry <dgentry@tailscale.com>2023-03-01 18:00:58 -0800
commit066193c18df3d36f0f168abbc25ce694b8f4159d (patch)
tree684fc841b49f5fd93ccfc272a3883cee0a881451
parent0438c67e2517c78feeaf0d9f61ea2a6303dd875c (diff)
downloadtailscale-release-branch/1.36.tar.xz
tailscale-release-branch/1.36.zip
cmd/tailscale: use request Schema+Host for QNAP authLogin.cgirelease-branch/1.36
QNAP allows users to set the port number for the management WebUI, which includes authLogin.cgi. If they do, then connecting to localhost:8080 fails. https://github.com/tailscale/tailscale-qpkg/issues/74#issuecomment-1407486911 Fixes https://github.com/tailscale/tailscale/issues/7108 Signed-off-by: Denton Gentry <dgentry@tailscale.com> (cherry picked from commit 51288221cee5b4b8d6d2160ed2c90de31b22dfc0)
-rw-r--r--cmd/tailscale/cli/web.go45
-rw-r--r--cmd/tailscale/cli/web_test.go67
2 files changed, 93 insertions, 19 deletions
diff --git a/cmd/tailscale/cli/web.go b/cmd/tailscale/cli/web.go
index 81f8b13e4..1cbd12d5e 100644
--- a/cmd/tailscale/cli/web.go
+++ b/cmd/tailscale/cli/web.go
@@ -220,33 +220,48 @@ func qnapAuthn(r *http.Request) (string, *qnapAuthResponse, error) {
return "", nil, fmt.Errorf("not authenticated by any mechanism")
}
-func qnapAuthnQtoken(r *http.Request, user, token string) (string, *qnapAuthResponse, error) {
- query := url.Values{
- "qtoken": []string{token},
- "user": []string{user},
+// qnapAuthnURL returns the auth URL to use by inferring where the UI is
+// running based on the request URL. This is necessary because QNAP has so
+// many options, see https://github.com/tailscale/tailscale/issues/7108
+// and https://github.com/tailscale/tailscale/issues/6903
+func qnapAuthnURL(requestUrl string, query url.Values) string {
+ in, err := url.Parse(requestUrl)
+ scheme := ""
+ host := ""
+ if err != nil || in.Scheme == "" {
+ log.Printf("Cannot parse QNAP login URL %v", err)
+
+ // try localhost and hope for the best
+ scheme = "http"
+ host = "localhost"
+ } else {
+ scheme = in.Scheme
+ host = in.Host
}
+
u := url.URL{
- Scheme: "http",
- Host: "127.0.0.1:8080",
+ Scheme: scheme,
+ Host: host,
Path: "/cgi-bin/authLogin.cgi",
RawQuery: query.Encode(),
}
- return qnapAuthnFinish(user, u.String())
+ return u.String()
+}
+
+func qnapAuthnQtoken(r *http.Request, user, token string) (string, *qnapAuthResponse, error) {
+ query := url.Values{
+ "qtoken": []string{token},
+ "user": []string{user},
+ }
+ return qnapAuthnFinish(user, qnapAuthnURL(r.URL.String(), query))
}
func qnapAuthnSid(r *http.Request, user, sid string) (string, *qnapAuthResponse, error) {
query := url.Values{
"sid": []string{sid},
}
- u := url.URL{
- Scheme: "http",
- Host: "127.0.0.1:8080",
- Path: "/cgi-bin/authLogin.cgi",
- RawQuery: query.Encode(),
- }
-
- return qnapAuthnFinish(user, u.String())
+ return qnapAuthnFinish(user, qnapAuthnURL(r.URL.String(), query))
}
func qnapAuthnFinish(user, url string) (string, *qnapAuthResponse, error) {
diff --git a/cmd/tailscale/cli/web_test.go b/cmd/tailscale/cli/web_test.go
index 5a48625b8..12e153828 100644
--- a/cmd/tailscale/cli/web_test.go
+++ b/cmd/tailscale/cli/web_test.go
@@ -4,7 +4,10 @@
package cli
-import "testing"
+import (
+ "net/url"
+ "testing"
+)
func TestUrlOfListenAddr(t *testing.T) {
tests := []struct {
@@ -35,9 +38,65 @@ func TestUrlOfListenAddr(t *testing.T) {
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
- url := urlOfListenAddr(tt.in)
- if url != tt.want {
- t.Errorf("expected url: %q, got: %q", tt.want, url)
+ u := urlOfListenAddr(tt.in)
+ if u != tt.want {
+ t.Errorf("expected url: %q, got: %q", tt.want, u)
+ }
+ })
+ }
+}
+
+func TestQnapAuthnURL(t *testing.T) {
+ query := url.Values{
+ "qtoken": []string{"token"},
+ }
+ tests := []struct {
+ name string
+ in string
+ want string
+ }{
+ {
+ name: "localhost http",
+ in: "http://localhost:8088/",
+ want: "http://localhost:8088/cgi-bin/authLogin.cgi?qtoken=token",
+ },
+ {
+ name: "localhost https",
+ in: "https://localhost:5000/",
+ want: "https://localhost:5000/cgi-bin/authLogin.cgi?qtoken=token",
+ },
+ {
+ name: "IP http",
+ in: "http://10.1.20.4:80/",
+ want: "http://10.1.20.4:80/cgi-bin/authLogin.cgi?qtoken=token",
+ },
+ {
+ name: "IP6 https",
+ in: "https://[ff7d:0:1:2::1]/",
+ want: "https://[ff7d:0:1:2::1]/cgi-bin/authLogin.cgi?qtoken=token",
+ },
+ {
+ name: "hostname https",
+ in: "https://qnap.example.com/",
+ want: "https://qnap.example.com/cgi-bin/authLogin.cgi?qtoken=token",
+ },
+ {
+ name: "invalid URL",
+ in: "This is not a URL, it is a really really really really really really really really really really really really long string to exercise the URL truncation code in the error path.",
+ want: "http://localhost/cgi-bin/authLogin.cgi?qtoken=token",
+ },
+ {
+ name: "err != nil",
+ in: "http://192.168.0.%31/",
+ want: "http://localhost/cgi-bin/authLogin.cgi?qtoken=token",
+ },
+
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ u := qnapAuthnURL(tt.in, query)
+ if u != tt.want {
+ t.Errorf("expected url: %q, got: %q", tt.want, u)
}
})
}