@ -100,48 +100,25 @@ func init() {
tmpls = template . Must ( template . New ( "" ) . ParseFS ( embeddedFS , "*" ) )
tmpls = template . Must ( template . New ( "" ) . ParseFS ( embeddedFS , "*" ) )
}
}
// authorize returns the name of the user accessing the web UI after verifying
// ServeHTTP processes all requests for the Tailscale web client.
// whether the user has access to the web UI. The function will write the
func ( s * Server ) ServeHTTP ( w http . ResponseWriter , r * http . Request ) {
// error to the provided http.ResponseWriter.
// some platforms where the client runs have their own authentication
// Note: This is different from a tailscale user, and is typically the local
// and authorization mechanisms we need to work with. Do those checks first.
// user on the node.
func authorize ( w http . ResponseWriter , r * http . Request ) ( string , error ) {
switch distro . Get ( ) {
switch distro . Get ( ) {
case distro . Synology :
case distro . Synology :
user , err := synoAuthn ( )
if authorizeSynology ( w , r ) {
if err != nil {
return
http . Error ( w , err . Error ( ) , http . StatusUnauthorized )
return "" , err
}
if err := authorizeSynology ( user ) ; err != nil {
http . Error ( w , err . Error ( ) , http . StatusForbidden )
return "" , err
}
}
return user , nil
case distro . QNAP :
case distro . QNAP :
user , resp , err := qnapAuthn ( r )
if authorizeQNAP ( w , r ) {
if err != nil {
return
http . Error ( w , err . Error ( ) , http . StatusUnauthorized )
return "" , err
}
if resp . IsAdmin == 0 {
http . Error ( w , err . Error ( ) , http . StatusForbidden )
return "" , err
}
}
return user , nil
}
}
return "" , nil
}
func authRedirect ( w http . ResponseWriter , r * http . Request ) bool {
s . serve ( w , r )
if distro . Get ( ) == distro . Synology {
return synoTokenRedirect ( w , r )
}
return false
}
}
// ServeHTTP processes all requests for the Tailscale web client.
func ( s * Server ) serve ( w http . ResponseWriter , r * http . Request ) {
func ( s * Server ) ServeHTTP ( w http . ResponseWriter , r * http . Request ) {
if s . devMode {
if s . devMode {
if strings . HasPrefix ( r . URL . Path , "/api/" ) {
if strings . HasPrefix ( r . URL . Path , "/api/" ) {
// Pass through to other handlers via CSRF protection.
// Pass through to other handlers via CSRF protection.
@ -153,29 +130,19 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
return
return
}
}
if authRedirect ( w , r ) {
return
}
user , err := authorize ( w , r )
if err != nil {
return
}
switch {
switch {
case r . Method == "POST" :
case r . Method == "POST" :
s . servePostNodeUpdate ( w , r )
s . servePostNodeUpdate ( w , r )
return
return
default :
default :
s . lc . IncrementCounter ( context . Background ( ) , "web_client_page_load" , 1 )
s . lc . IncrementCounter ( context . Background ( ) , "web_client_page_load" , 1 )
s . serveGetNodeData ( w , r , user )
s . serveGetNodeData ( w , r )
return
return
}
}
}
}
type nodeData struct {
type nodeData struct {
Profile tailcfg . UserProfile
Profile tailcfg . UserProfile
SynologyUser string
Status string
Status string
DeviceName string
DeviceName string
IP string
IP string
@ -190,7 +157,7 @@ type nodeData struct {
IPNVersion string
IPNVersion string
}
}
func ( s * Server ) getNodeData ( ctx context . Context , user string ) ( * nodeData , error ) {
func ( s * Server ) getNodeData ( ctx context . Context ) ( * nodeData , error ) {
st , err := s . lc . Status ( ctx )
st , err := s . lc . Status ( ctx )
if err != nil {
if err != nil {
return nil , err
return nil , err
@ -203,17 +170,16 @@ func (s *Server) getNodeData(ctx context.Context, user string) (*nodeData, error
deviceName := strings . Split ( st . Self . DNSName , "." ) [ 0 ]
deviceName := strings . Split ( st . Self . DNSName , "." ) [ 0 ]
versionShort := strings . Split ( st . Version , "-" ) [ 0 ]
versionShort := strings . Split ( st . Version , "-" ) [ 0 ]
data := & nodeData {
data := & nodeData {
SynologyUser : user ,
Profile : profile ,
Profile : profile ,
Status : st . BackendState ,
Status : st . BackendState ,
DeviceName : deviceName ,
DeviceName : deviceName ,
LicensesURL : licenses . LicensesURL ( ) ,
LicensesURL : licenses . LicensesURL ( ) ,
TUNMode : st . TUN ,
TUNMode : st . TUN ,
IsSynology : distro . Get ( ) == distro . Synology || envknob . Bool ( "TS_FAKE_SYNOLOGY" ) ,
IsSynology : distro . Get ( ) == distro . Synology || envknob . Bool ( "TS_FAKE_SYNOLOGY" ) ,
DSMVersion : distro . DSMVersion ( ) ,
DSMVersion : distro . DSMVersion ( ) ,
IsUnraid : distro . Get ( ) == distro . Unraid ,
IsUnraid : distro . Get ( ) == distro . Unraid ,
UnraidToken : os . Getenv ( "UNRAID_CSRF_TOKEN" ) ,
UnraidToken : os . Getenv ( "UNRAID_CSRF_TOKEN" ) ,
IPNVersion : versionShort ,
IPNVersion : versionShort ,
}
}
exitNodeRouteV4 := netip . MustParsePrefix ( "0.0.0.0/0" )
exitNodeRouteV4 := netip . MustParsePrefix ( "0.0.0.0/0" )
exitNodeRouteV6 := netip . MustParsePrefix ( "::/0" )
exitNodeRouteV6 := netip . MustParsePrefix ( "::/0" )
@ -233,8 +199,8 @@ func (s *Server) getNodeData(ctx context.Context, user string) (*nodeData, error
return data , nil
return data , nil
}
}
func ( s * Server ) serveGetNodeData ( w http . ResponseWriter , r * http . Request , user string ) {
func ( s * Server ) serveGetNodeData ( w http . ResponseWriter , r * http . Request ) {
data , err := s . getNodeData ( r . Context ( ) , user )
data , err := s . getNodeData ( r . Context ( ) )
if err != nil {
if err != nil {
http . Error ( w , err . Error ( ) , http . StatusInternalServerError )
http . Error ( w , err . Error ( ) , http . StatusInternalServerError )
return
return
@ -247,8 +213,8 @@ func (s *Server) serveGetNodeData(w http.ResponseWriter, r *http.Request, user s
w . Write ( buf . Bytes ( ) )
w . Write ( buf . Bytes ( ) )
}
}
func ( s * Server ) serveGetNodeDataJSON ( w http . ResponseWriter , r * http . Request , user string ) {
func ( s * Server ) serveGetNodeDataJSON ( w http . ResponseWriter , r * http . Request ) {
data , err := s . getNodeData ( r . Context ( ) , user )
data , err := s . getNodeData ( r . Context ( ) )
if err != nil {
if err != nil {
http . Error ( w , err . Error ( ) , http . StatusInternalServerError )
http . Error ( w , err . Error ( ) , http . StatusInternalServerError )
return
return