mirror of
https://github.com/tailscale/tailscale.git
synced 2024-11-29 13:05:46 +00:00
9a3bc9049c
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>
77 lines
1.9 KiB
Go
77 lines
1.9 KiB
Go
// Copyright (c) Tailscale Inc & AUTHORS
|
|
// SPDX-License-Identifier: BSD-3-Clause
|
|
|
|
// synology.go contains handlers and logic, such as authentication,
|
|
// that is specific to running the web client on Synology.
|
|
|
|
package web
|
|
|
|
import (
|
|
"fmt"
|
|
"net/http"
|
|
"os/exec"
|
|
"strings"
|
|
|
|
"tailscale.com/util/groupmember"
|
|
)
|
|
|
|
// authorizeSynology authenticates the logged-in Synology user and verifies
|
|
// that they are authorized to use the web client. It returns true if the
|
|
// request was handled and no further processing is required.
|
|
func authorizeSynology(w http.ResponseWriter, r *http.Request) (handled bool) {
|
|
if synoTokenRedirect(w, r) {
|
|
return true
|
|
}
|
|
|
|
// authenticate the Synology user
|
|
cmd := exec.Command("/usr/syno/synoman/webman/modules/authenticate.cgi")
|
|
out, err := cmd.CombinedOutput()
|
|
if err != nil {
|
|
http.Error(w, fmt.Sprintf("auth: %v: %s", err, out), http.StatusUnauthorized)
|
|
return true
|
|
}
|
|
user := strings.TrimSpace(string(out))
|
|
|
|
// check if the user is in the administrators group
|
|
isAdmin, err := groupmember.IsMemberOfGroup("administrators", user)
|
|
if err != nil {
|
|
http.Error(w, err.Error(), http.StatusForbidden)
|
|
return true
|
|
}
|
|
if !isAdmin {
|
|
http.Error(w, "not a member of administrators group", http.StatusForbidden)
|
|
return true
|
|
}
|
|
|
|
return false
|
|
}
|
|
|
|
func synoTokenRedirect(w http.ResponseWriter, r *http.Request) bool {
|
|
if r.Header.Get("X-Syno-Token") != "" {
|
|
return false
|
|
}
|
|
if r.URL.Query().Get("SynoToken") != "" {
|
|
return false
|
|
}
|
|
if r.Method == "POST" && r.FormValue("SynoToken") != "" {
|
|
return false
|
|
}
|
|
// We need a SynoToken for authenticate.cgi.
|
|
// So we tell the client to get one.
|
|
_, _ = fmt.Fprint(w, synoTokenRedirectHTML)
|
|
return true
|
|
}
|
|
|
|
const synoTokenRedirectHTML = `<html>
|
|
Redirecting with session token...
|
|
<script>
|
|
fetch("/webman/login.cgi")
|
|
.then(r => r.json())
|
|
.then(data => {
|
|
u = new URL(window.location)
|
|
u.searchParams.set("SynoToken", data.SynoToken)
|
|
document.location = u
|
|
})
|
|
</script>
|
|
`
|