client/web: add Sec-Fetch-Site CSRF protection (#16046)

RELNOTE=Fix CSRF errors in the client Web UI

Replace gorilla/csrf with a Sec-Fetch-Site based CSRF protection
middleware that falls back to comparing the Host & Origin headers if no
SFS value is passed by the client.

Add an -origin override to the web CLI that allows callers to specify
the origin at which the web UI will be available if it is hosted behind
a reverse proxy or within another application via CGI.

Updates #14872
Updates #15065

Signed-off-by: Patrick O'Doherty <patrick@tailscale.com>
This commit is contained in:
Patrick O'Doherty
2025-05-22 12:26:02 -07:00
committed by GitHub
parent 3ee4c60ff0
commit a05924a9e5
8 changed files with 184 additions and 169 deletions

View File

@@ -144,8 +144,6 @@ tailscale.com/cmd/k8s-operator dependencies: (generated by github.com/tailscale/
L github.com/google/nftables/internal/parseexprfunc from github.com/google/nftables+
L github.com/google/nftables/xt from github.com/google/nftables/expr+
github.com/google/uuid from github.com/prometheus-community/pro-bing+
github.com/gorilla/csrf from tailscale.com/client/web
github.com/gorilla/securecookie from github.com/gorilla/csrf
github.com/hdevalence/ed25519consensus from tailscale.com/clientupdate/distsign+
L 💣 github.com/illarion/gonotify/v3 from tailscale.com/net/dns
L github.com/illarion/gonotify/v3/syscallf from github.com/illarion/gonotify/v3
@@ -1112,13 +1110,12 @@ tailscale.com/cmd/k8s-operator dependencies: (generated by github.com/tailscale/
W debug/dwarf from debug/pe
W debug/pe from github.com/dblohm7/wingoes/pe
embed from github.com/tailscale/web-client-prebuilt+
encoding from encoding/gob+
encoding from encoding/json+
encoding/asn1 from crypto/x509+
encoding/base32 from github.com/fxamacker/cbor/v2+
encoding/base64 from encoding/json+
encoding/binary from compress/gzip+
encoding/csv from github.com/spf13/pflag
encoding/gob from github.com/gorilla/securecookie
encoding/hex from crypto/x509+
encoding/json from expvar+
encoding/pem from crypto/tls+
@@ -1140,7 +1137,7 @@ tailscale.com/cmd/k8s-operator dependencies: (generated by github.com/tailscale/
hash/fnv from google.golang.org/protobuf/internal/detrand
hash/maphash from go4.org/mem
html from html/template+
html/template from github.com/gorilla/csrf+
html/template from tailscale.com/util/eventbus
internal/abi from crypto/x509/internal/macos+
internal/asan from internal/runtime/maps+
internal/bisect from internal/godebug
@@ -1172,7 +1169,7 @@ tailscale.com/cmd/k8s-operator dependencies: (generated by github.com/tailscale/
internal/runtime/math from internal/runtime/maps+
internal/runtime/sys from crypto/subtle+
L internal/runtime/syscall from runtime+
internal/saferio from debug/pe+
W internal/saferio from debug/pe
internal/singleflight from net
internal/stringslite from embed+
internal/sync from sync+

View File

@@ -43,6 +43,7 @@ Tailscale, as opposed to a CLI or a native app.
webf.BoolVar(&webArgs.cgi, "cgi", false, "run as CGI script")
webf.StringVar(&webArgs.prefix, "prefix", "", "URL prefix added to requests (for cgi or reverse proxies)")
webf.BoolVar(&webArgs.readonly, "readonly", false, "run web UI in read-only mode")
webf.StringVar(&webArgs.origin, "origin", "", "origin at which the web UI is served (if behind a reverse proxy or used with cgi)")
return webf
})(),
Exec: runWeb,
@@ -53,6 +54,7 @@ var webArgs struct {
cgi bool
prefix string
readonly bool
origin string
}
func tlsConfigFromEnvironment() *tls.Config {
@@ -115,6 +117,9 @@ func runWeb(ctx context.Context, args []string) error {
if webArgs.readonly {
opts.Mode = web.ReadOnlyServerMode
}
if webArgs.origin != "" {
opts.OriginOverride = webArgs.origin
}
webServer, err := web.NewServer(opts)
if err != nil {
log.Printf("tailscale.web: %v", err)

View File

@@ -27,8 +27,6 @@ tailscale.com/cmd/tailscale dependencies: (generated by github.com/tailscale/dep
L github.com/google/nftables/internal/parseexprfunc from github.com/google/nftables+
L github.com/google/nftables/xt from github.com/google/nftables/expr+
DW github.com/google/uuid from tailscale.com/clientupdate+
github.com/gorilla/csrf from tailscale.com/client/web
github.com/gorilla/securecookie from github.com/gorilla/csrf
github.com/hdevalence/ed25519consensus from tailscale.com/clientupdate/distsign+
L 💣 github.com/jsimonetti/rtnetlink from tailscale.com/net/netmon
L github.com/jsimonetti/rtnetlink/internal/unix from github.com/jsimonetti/rtnetlink
@@ -319,12 +317,11 @@ tailscale.com/cmd/tailscale dependencies: (generated by github.com/tailscale/dep
W debug/dwarf from debug/pe
W debug/pe from github.com/dblohm7/wingoes/pe
embed from github.com/peterbourgon/ff/v3+
encoding from encoding/gob+
encoding from encoding/json+
encoding/asn1 from crypto/x509+
encoding/base32 from github.com/fxamacker/cbor/v2+
encoding/base64 from encoding/json+
encoding/binary from compress/gzip+
encoding/gob from github.com/gorilla/securecookie
encoding/hex from crypto/x509+
encoding/json from expvar+
encoding/pem from crypto/tls+
@@ -338,7 +335,7 @@ tailscale.com/cmd/tailscale dependencies: (generated by github.com/tailscale/dep
hash/crc32 from compress/gzip+
hash/maphash from go4.org/mem
html from html/template+
html/template from github.com/gorilla/csrf+
html/template from tailscale.com/util/eventbus
image from github.com/skip2/go-qrcode+
image/color from github.com/skip2/go-qrcode+
image/png from github.com/skip2/go-qrcode
@@ -372,7 +369,7 @@ tailscale.com/cmd/tailscale dependencies: (generated by github.com/tailscale/dep
internal/runtime/math from internal/runtime/maps+
internal/runtime/sys from crypto/subtle+
L internal/runtime/syscall from runtime+
internal/saferio from debug/pe+
W internal/saferio from debug/pe
internal/singleflight from net
internal/stringslite from embed+
internal/sync from sync+

View File

@@ -123,8 +123,6 @@ tailscale.com/cmd/tailscaled dependencies: (generated by github.com/tailscale/de
L github.com/google/nftables/internal/parseexprfunc from github.com/google/nftables+
L github.com/google/nftables/xt from github.com/google/nftables/expr+
DW github.com/google/uuid from tailscale.com/clientupdate+
github.com/gorilla/csrf from tailscale.com/client/web
github.com/gorilla/securecookie from github.com/gorilla/csrf
github.com/hdevalence/ed25519consensus from tailscale.com/clientupdate/distsign+
L 💣 github.com/illarion/gonotify/v3 from tailscale.com/net/dns
L github.com/illarion/gonotify/v3/syscallf from github.com/illarion/gonotify/v3
@@ -590,12 +588,11 @@ tailscale.com/cmd/tailscaled dependencies: (generated by github.com/tailscale/de
W debug/dwarf from debug/pe
W debug/pe from github.com/dblohm7/wingoes/pe
embed from github.com/tailscale/web-client-prebuilt+
encoding from encoding/gob+
encoding from encoding/json+
encoding/asn1 from crypto/x509+
encoding/base32 from github.com/fxamacker/cbor/v2+
encoding/base64 from encoding/json+
encoding/binary from compress/gzip+
encoding/gob from github.com/gorilla/securecookie
encoding/hex from crypto/x509+
encoding/json from expvar+
encoding/pem from crypto/tls+
@@ -609,7 +606,7 @@ tailscale.com/cmd/tailscaled dependencies: (generated by github.com/tailscale/de
hash/crc32 from compress/gzip+
hash/maphash from go4.org/mem
html from html/template+
html/template from github.com/gorilla/csrf+
html/template from tailscale.com/util/eventbus
internal/abi from crypto/x509/internal/macos+
internal/asan from internal/runtime/maps+
internal/bisect from internal/godebug
@@ -640,7 +637,7 @@ tailscale.com/cmd/tailscaled dependencies: (generated by github.com/tailscale/de
internal/runtime/math from internal/runtime/maps+
internal/runtime/sys from crypto/subtle+
L internal/runtime/syscall from runtime+
internal/saferio from debug/pe+
W internal/saferio from debug/pe
internal/singleflight from net
internal/stringslite from embed+
internal/sync from sync+