@@ -3,10 +3,10 @@ package auth
33import (
44 "context"
55 "encoding/hex"
6- "errors"
76 "fmt"
87 "net/http"
98 "net/url"
9+ "strings"
1010 "sync"
1111
1212 "github.com/hashicorp/go-multierror"
@@ -17,14 +17,18 @@ import (
1717
1818type sessionValidator interface {
1919 Valid (sessionID , _ , userAddr string ) bool
20+ Close () error
2021}
2122
2223type TLSCookieAuth struct {
23- logger * clog.CondLogger
24- stopOnce sync.Once
25- next Auth
26- reject Auth
27- lookup sessionValidator
24+ logger * clog.CondLogger
25+ stopOnce sync.Once
26+ next Auth
27+ reject Auth
28+ lookup sessionValidator
29+ hiddenDomain string
30+ lsMux sync.RWMutex
31+ learnedSessions map [tlsutil.TLSSessionID ]struct {}
2832}
2933
3034func NewTLSCookieAuth (param_url * url.URL , logger * clog.CondLogger ) (* TLSCookieAuth , error ) {
@@ -33,48 +37,86 @@ func NewTLSCookieAuth(param_url *url.URL, logger *clog.CondLogger) (*TLSCookieAu
3337 return nil , err
3438 }
3539 auth := & TLSCookieAuth {
36- logger : logger ,
40+ logger : logger ,
41+ hiddenDomain : strings .ToLower (values .Get ("hidden_domain" )),
42+ learnedSessions : make (map [tlsutil.TLSSessionID ]struct {}),
3743 }
38- if lookupURL := values .Get ("lookup" ); lookupURL == "" {
39- return nil , errors .New ("\" lookup\" parameter is mandatory for TLS cookie auth provider" )
40- } else {
44+ if lookupURL := values .Get ("lookup" ); lookupURL != "" {
4145 lookupAuth , err := NewAuth (lookupURL , logger )
4246 if err != nil {
4347 return nil , fmt .Errorf ("unable to construct lookup provider for TLS cookie auth provider: %w" , err )
4448 }
4549 lookup , ok := lookupAuth .(sessionValidator )
4650 if ! ok {
51+ lookupAuth .Close ()
52+ defer auth .Close ()
4753 return nil , fmt .Errorf ("unable to construct TLS cookie auth provider: provided lookup provider %q is not suitable for session validation" , lookupURL )
4854 }
4955 auth .lookup = lookup
5056 }
5157 if nextAuth := values .Get ("next" ); nextAuth != "" {
5258 nap , err := NewAuth (nextAuth , logger )
5359 if err != nil {
60+ defer auth .Close ()
5461 return nil , fmt .Errorf ("chained auth provider construction failed: %w" , err )
5562 }
5663 auth .next = nap
5764 }
5865 if nextAuth := values .Get ("else" ); nextAuth != "" {
5966 nap , err := NewAuth (nextAuth , logger )
6067 if err != nil {
68+ defer auth .Close ()
6169 return nil , fmt .Errorf ("chained auth provider construction failed: %w" , err )
6270 }
6371 auth .reject = nap
6472 }
6573 return auth , nil
6674}
6775
76+ func (auth * TLSCookieAuth ) checkLearned (sessionID tlsutil.TLSSessionID ) bool {
77+ auth .lsMux .RLock ()
78+ defer auth .lsMux .RUnlock ()
79+ _ , ok := auth .learnedSessions [sessionID ]
80+ return ok
81+ }
82+
83+ func (auth * TLSCookieAuth ) addLearned (sessionID tlsutil.TLSSessionID ) {
84+ auth .lsMux .Lock ()
85+ defer auth .lsMux .Unlock ()
86+ auth .learnedSessions [sessionID ] = struct {}{}
87+ }
88+
6889func (auth * TLSCookieAuth ) Validate (ctx context.Context , wr http.ResponseWriter , req * http.Request ) (string , bool ) {
6990 sessionID , ok := tlsutil .TLSSessionIDFromContext (ctx )
7091 if ! ok {
7192 auth .logger .Debug ("tlscookie: no session extracted for %s" , req .RemoteAddr )
7293 return auth .handleReject (ctx , wr , req )
7394 }
74- if ! auth .lookup .Valid (hex .EncodeToString (sessionID [:]), "" , req .RemoteAddr ) {
75- auth .logger .Info ("tlscookie: session ID %x from %s is not permitted" , sessionID , req .RemoteAddr )
76- return auth .handleReject (ctx , wr , req )
95+ if auth .hiddenDomain != "" {
96+ if matchHiddenDomain (req .Host , auth .hiddenDomain ) || matchHiddenDomain (req .URL .Host , auth .hiddenDomain ) {
97+ auth .logger .Debug ("tlscookie: session %x from %s requested magic domain" , sessionID , req .RemoteAddr )
98+ auth .addLearned (sessionID )
99+ wr .Header ().Set ("Pragma" , "no-cache" )
100+ wr .Header ().Set ("Cache-Control" , "no-cache, no-store, must-revalidate" )
101+ wr .Header ().Set ("Expires" , EPOCH_EXPIRE )
102+ wr .WriteHeader (http .StatusBadRequest )
103+ wr .Write ([]byte (AUTH_TRIGGERED_MSG ))
104+ return "" , false
105+ }
106+ if auth .checkLearned (sessionID ) {
107+ auth .logger .Debug ("tlscookie: session %x from %s passed because it is in learned set" , sessionID , req .RemoteAddr )
108+ return auth .handleSuccess (ctx , wr , req , sessionID )
109+ }
77110 }
111+ if auth .lookup != nil && auth .lookup .Valid (hex .EncodeToString (sessionID [:]), "" , req .RemoteAddr ) {
112+ auth .logger .Debug ("tlscookie: session %x from %s passed because external lookup said yay" , sessionID , req .RemoteAddr )
113+ return auth .handleSuccess (ctx , wr , req , sessionID )
114+ }
115+ auth .logger .Info ("tlscookie: session ID %x from %s is not permitted" , sessionID , req .RemoteAddr )
116+ return auth .handleReject (ctx , wr , req )
117+ }
118+
119+ func (auth * TLSCookieAuth ) handleSuccess (ctx context.Context , wr http.ResponseWriter , req * http.Request , sessionID tlsutil.TLSSessionID ) (string , bool ) {
78120 if auth .next != nil {
79121 return auth .next .Validate (ctx , wr , req )
80122 }
@@ -92,6 +134,11 @@ func (auth *TLSCookieAuth) handleReject(ctx context.Context, wr http.ResponseWri
92134func (auth * TLSCookieAuth ) Close () error {
93135 var err error
94136 auth .stopOnce .Do (func () {
137+ if auth .lookup != nil {
138+ if closeErr := auth .lookup .Close (); closeErr != nil {
139+ err = multierror .Append (err , closeErr )
140+ }
141+ }
95142 if auth .next != nil {
96143 if closeErr := auth .next .Close (); closeErr != nil {
97144 err = multierror .Append (err , closeErr )
0 commit comments