Skip to content

Commit e1d91b8

Browse files
authored
Merge pull request #202 from SenseUnit/tlssess_hidden_domain
Auth fixes and improvements
2 parents 3b9c000 + 9326de2 commit e1d91b8

5 files changed

Lines changed: 69 additions & 21 deletions

File tree

README.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -302,10 +302,11 @@ Authentication parameters are passed as URI via `-auth` parameter. Scheme of URI
302302
* `code` - optional parameter specifying HTTP response code. Default is 403.
303303
* `body` - optional parameter specifying file with response body.
304304
* `headers` - optional parameter specifying file with response headers. It uses format identical to request header file format used by `curl` program.
305-
* `tlscookie` - (EXPERIMENTAL) auth provider which grants access to whitelisted TLS session IDs. Whitelist is checked by query of another auth provider (provided as URL in `lookup` query parameter) with session ID as username and empty password. Example of auth parameter: `-auth tlscookie://?lookup=basicfile%3A%2F%2F%3Fpath%3D%2Fetc%2Fdumbproxy%2Fsessions`. Parameters of this scheme are:
305+
* `tlscookie` - (EXPERIMENTAL) auth provider which grants access to whitelisted TLS session IDs. Whitelist is either checked by query of another auth provider (provided as URL in `lookup` query parameter) with session ID as username and empty password, or checked against list of sessions which once requested some secret domain name through proxy. Example of auth parameter: `-auth tlscookie://?lookup=basicfile%3A%2F%2F%3Fpath%3D%2Fetc%2Fdumbproxy%2Fsessions`. Parameters of this scheme are:
306+
* `hidden_domain` - if specified and is not an empty string, authorize every session ID which requested this secret domain.
306307
* `next` - optional URL specifying the next auth provider to chain to, if authentication succeeded.
307308
* `else` - optional URL specifying the next auth provider to chain to, if authentication failed.
308-
* `lookup` - mandatory URL specifying another auth provider queried for session validity (typically `basicfile` or some Redis-backed password auth). Queries to this lookup provider ask for validity of session providing hexadecimal session ID as username and empty string as password.
309+
* `lookup` - optional URL specifying another auth provider queried for session validity (typically `basicfile` or some Redis-backed password auth). Queries to this lookup provider ask for validity of session providing hexadecimal session ID as username and empty string as password.
309310
310311
## Scripting
311312

auth/basic.go

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ import (
77
"fmt"
88
"net/http"
99
"net/url"
10-
"strconv"
1110
"strings"
1211
"sync"
1312
"sync/atomic"
@@ -64,10 +63,10 @@ func NewBasicFileAuth(param_url *url.URL, logger *clog.CondLogger) (*BasicAuth,
6463
if reloadInterval > 0 {
6564
go auth.reloadLoop(reloadInterval)
6665
}
67-
6866
if nextAuth := values.Get("else"); nextAuth != "" {
6967
nap, err := NewAuth(nextAuth, logger)
7068
if err != nil {
69+
defer auth.Close()
7170
return nil, fmt.Errorf("chained auth provider construction failed: %w", err)
7271
}
7372
auth.next = nap
@@ -157,13 +156,11 @@ func (auth *BasicAuth) Validate(ctx context.Context, wr http.ResponseWriter, req
157156

158157
if pwFile.Match(login, password) {
159158
if auth.hiddenDomain != "" &&
160-
(req.Host == auth.hiddenDomain || req.URL.Host == auth.hiddenDomain) {
161-
wr.Header().Set("Content-Length", strconv.Itoa(len([]byte(AUTH_TRIGGERED_MSG))))
159+
(matchHiddenDomain(req.Host, auth.hiddenDomain) || matchHiddenDomain(req.URL.Host, auth.hiddenDomain)) {
162160
wr.Header().Set("Pragma", "no-cache")
163161
wr.Header().Set("Cache-Control", "no-cache, no-store, must-revalidate")
164162
wr.Header().Set("Expires", EPOCH_EXPIRE)
165-
wr.Header()["Date"] = nil
166-
wr.WriteHeader(http.StatusOK)
163+
wr.WriteHeader(http.StatusBadRequest)
167164
wr.Write([]byte(AUTH_TRIGGERED_MSG))
168165
return "", false
169166
} else {

auth/cert.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,13 +68,15 @@ func NewCertAuth(param_url *url.URL, logger *clog.CondLogger) (*CertAuth, error)
6868
if nextAuth := values.Get("next"); nextAuth != "" {
6969
nap, err := NewAuth(nextAuth, logger)
7070
if err != nil {
71+
defer auth.Close()
7172
return nil, fmt.Errorf("chained auth provider construction failed: %w", err)
7273
}
7374
auth.next = nap
7475
}
7576
if nextAuth := values.Get("else"); nextAuth != "" {
7677
nap, err := NewAuth(nextAuth, logger)
7778
if err != nil {
79+
defer auth.Close()
7880
return nil, fmt.Errorf("chained auth provider construction failed: %w", err)
7981
}
8082
auth.reject = nap

auth/redis.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ func NewRedisAuth(param_url *url.URL, cluster bool, logger *clog.CondLogger) (*R
5858
if nextAuth := values.Get("else"); nextAuth != "" {
5959
nap, err := NewAuth(nextAuth, logger)
6060
if err != nil {
61+
defer auth.Close()
6162
return nil, fmt.Errorf("chained auth provider construction failed: %w", err)
6263
}
6364
auth.next = nap

auth/tlscookie.go

Lines changed: 60 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,10 @@ package auth
33
import (
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

1818
type sessionValidator interface {
1919
Valid(sessionID, _, userAddr string) bool
20+
Close() error
2021
}
2122

2223
type 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

3034
func 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+
6889
func (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
92134
func (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

Comments
 (0)