@@ -18,6 +18,7 @@ import (
1818 "errors"
1919 "expvar"
2020 "fmt"
21+ E "github.com/sagernet/sing/common/exceptions"
2122 "io"
2223 "log"
2324 "math"
@@ -174,9 +175,10 @@ type Server struct {
174175 // server if the clientKey is a known peer in the network, as specified by a
175176 // running tailscaled's client's LocalAPI.
176177 verifyClientsLocalTailscaled bool
177-
178- verifyClientsURL string
179- verifyClientsURLFailOpen bool
178+ verifyClientLocalClient []* tailscale.LocalClient
179+ verifyClientHTTPClient * http.Client
180+ verifyClientsURL []string
181+ verifyClientsURLFailOpen bool
180182
181183 mu sync.Mutex
182184 closed bool
@@ -471,10 +473,18 @@ func (s *Server) SetVerifyClient(v bool) {
471473 s .verifyClientsLocalTailscaled = v
472474}
473475
476+ func (s * Server ) SetVerifyClientLocalClient (localClient []* tailscale.LocalClient ) {
477+ s .verifyClientLocalClient = localClient
478+ }
479+
480+ func (s * Server ) SetVerifyClientHTTPClient (httpClient * http.Client ) {
481+ s .verifyClientHTTPClient = httpClient
482+ }
483+
474484// SetVerifyClientURL sets the admission controller URL to use for verifying clients.
475485// If empty, all clients are accepted (unless restricted by SetVerifyClient checking
476486// against tailscaled).
477- func (s * Server ) SetVerifyClientURL (v string ) {
487+ func (s * Server ) SetVerifyClientURL (v [] string ) {
478488 s .verifyClientsURL = v
479489}
480490
@@ -1352,42 +1362,72 @@ func (s *Server) verifyClient(ctx context.Context, clientKey key.NodePublic, inf
13521362 }
13531363 }
13541364
1365+ var errors []error
1366+ if len (s .verifyClientLocalClient ) > 0 {
1367+ for _ , verifyClient := range s .verifyClientLocalClient {
1368+ _ , err := verifyClient .WhoIsNodeKey (ctx , clientKey )
1369+ if err == tailscale .ErrPeerNotFound {
1370+ errors = append (errors , fmt .Errorf ("peer %v not authorized (not found in local tailscaled)" , clientKey ))
1371+ continue
1372+ }
1373+ if err != nil {
1374+ if strings .Contains (err .Error (), "invalid 'addr' parameter" ) {
1375+ // Issue 12617
1376+ errors = append (errors , E .New ("tailscaled version is too old (out of sync with derper binary)" ))
1377+ continue
1378+ }
1379+ errors = append (errors , fmt .Errorf ("failed to query local tailscaled status for %v: %w" , clientKey , err ))
1380+ continue
1381+ }
1382+ return nil
1383+ }
1384+ }
13551385 // admission controller-based verification:
1356- if s .verifyClientsURL != "" {
1357- ctx , cancel := context .WithTimeout (ctx , 5 * time .Second )
1358- defer cancel ()
1359-
1386+ if len (s .verifyClientsURL ) > 0 {
13601387 jreq , err := json .Marshal (& tailcfg.DERPAdmitClientRequest {
13611388 NodePublic : clientKey ,
13621389 Source : clientIP ,
13631390 })
13641391 if err != nil {
13651392 return err
13661393 }
1367- req , err := http .NewRequestWithContext (ctx , "POST" , s .verifyClientsURL , bytes .NewReader (jreq ))
1368- if err != nil {
1369- return err
1370- }
1371- res , err := http .DefaultClient .Do (req )
1372- if err != nil {
1373- if s .verifyClientsURLFailOpen {
1374- s .logf ("admission controller unreachable; allowing client %v" , clientKey )
1375- return nil
1394+
1395+ for _ , verifyClientsURL := range s .verifyClientsURL {
1396+ ctx , cancel := context .WithTimeout (ctx , 5 * time .Second )
1397+ defer cancel ()
1398+ req , err := http .NewRequestWithContext (ctx , "POST" , verifyClientsURL , bytes .NewReader (jreq ))
1399+ if err != nil {
1400+ return err
13761401 }
1377- return err
1378- }
1379- defer res .Body .Close ()
1380- if res .StatusCode != 200 {
1381- return fmt .Errorf ("admission controller: %v" , res .Status )
1382- }
1383- var jres tailcfg.DERPAdmitClientResponse
1384- if err := json .NewDecoder (io .LimitReader (res .Body , 4 << 10 )).Decode (& jres ); err != nil {
1385- return err
1386- }
1387- if ! jres .Allow {
1388- return fmt .Errorf ("admission controller: %v/%v not allowed" , clientKey , clientIP )
1402+ res , err := s .verifyClientHTTPClient .Do (req )
1403+ if err != nil {
1404+ if s .verifyClientsURLFailOpen {
1405+ s .logf ("admission controller unreachable; allowing client %v" , clientKey )
1406+ continue
1407+ }
1408+ errors = append (errors , err )
1409+ continue
1410+ }
1411+ defer res .Body .Close ()
1412+ if res .StatusCode != 200 {
1413+ errors = append (errors , fmt .Errorf ("admission controller: %v" , res .Status ))
1414+ continue
1415+ }
1416+ var jres tailcfg.DERPAdmitClientResponse
1417+ if err := json .NewDecoder (io .LimitReader (res .Body , 4 << 10 )).Decode (& jres ); err != nil {
1418+ errors = append (errors , err )
1419+ continue
1420+ }
1421+ if ! jres .Allow {
1422+ errors = append (errors , fmt .Errorf ("admission controller: %v/%v not allowed" , clientKey , clientIP ))
1423+ continue
1424+ }
1425+ // TODO(bradfitz): add policy for configurable bandwidth rate per client?
1426+ return nil
13891427 }
1390- // TODO(bradfitz): add policy for configurable bandwidth rate per client?
1428+ }
1429+ if len (errors ) > 0 {
1430+ return E .Errors (errors ... )
13911431 }
13921432 return nil
13931433}
0 commit comments