-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathproxyauth.go
More file actions
89 lines (73 loc) · 3.03 KB
/
Copy pathproxyauth.go
File metadata and controls
89 lines (73 loc) · 3.03 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
package groxy
import (
"crypto/sha256"
"crypto/subtle"
"errors"
)
// ProxyBasicAuth returns middleware that requires HTTP Basic proxy
// authentication with a static username and password for HTTP requests and
// CONNECT tunnels.
//
// The credentials are read from the Proxy-Authorization header. Static
// credentials are compared in constant time. Basic authentication is not
// encrypted by itself, so only use it when the client-to-proxy connection is
// otherwise protected or trusted.
func ProxyBasicAuth(username, password string) Middleware {
wantUsername := sha256.Sum256([]byte(username))
wantPassword := sha256.Sum256([]byte(password))
return proxyBasicAuthMiddleware("ProxyBasicAuth", defaultProxyAuthRealm, func(gotUsername, gotPassword string) bool {
gotUsernameHash := sha256.Sum256([]byte(gotUsername))
gotPasswordHash := sha256.Sum256([]byte(gotPassword))
usernameOK := subtle.ConstantTimeCompare(gotUsernameHash[:], wantUsername[:]) == 1
passwordOK := subtle.ConstantTimeCompare(gotPasswordHash[:], wantPassword[:]) == 1
return usernameOK && passwordOK
})
}
// ProxyBasicAuthFunc returns middleware that requires HTTP Basic proxy
// authentication for HTTP requests and CONNECT tunnels using validate to check
// username and password pairs.
//
// The credentials are read from the Proxy-Authorization header. If validate is
// nil or returns false, Groxy rejects the request with 407 Proxy Authentication
// Required. Basic authentication is not encrypted by itself, so only use it when
// the client-to-proxy connection is otherwise protected or trusted.
func ProxyBasicAuthFunc(validate func(username, password string) bool) Middleware {
return proxyBasicAuthMiddleware("ProxyBasicAuthFunc", defaultProxyAuthRealm, validate)
}
func proxyBasicAuthMiddleware(name, realm string, validate func(username, password string) bool) Middleware {
auth := &proxyBasicAuthenticator{realm: realm, validate: validate}
return Middleware{name: name, requestHook: auth.onRequest, connectHook: auth.onConnect}
}
type proxyBasicAuthenticator struct {
realm string
validate func(username, password string) bool
}
func (auth *proxyBasicAuthenticator) onRequest(ctx *RequestContext) error {
if proxyAuthAlreadyChecked(ctx.Request) {
return nil
}
return auth.authenticate(ctx.Request.Header.Get("Proxy-Authorization"))
}
func (auth *proxyBasicAuthenticator) onConnect(ctx *ConnectContext) error {
return auth.authenticate(ctx.Request.Header.Get("Proxy-Authorization"))
}
func (auth *proxyBasicAuthenticator) authenticate(header string) error {
username, password, ok := parseProxyBasicAuth(header)
if !ok || auth.validate == nil || !auth.validate(username, password) {
return &proxyAuthRequiredError{realm: auth.realm}
}
return nil
}
type proxyAuthRequiredError struct {
realm string
}
func (e proxyAuthRequiredError) Error() string {
return "proxy authentication required"
}
func proxyAuthRequired(err error) (*proxyAuthRequiredError, bool) {
var auth *proxyAuthRequiredError
if errors.As(err, &auth) {
return auth, true
}
return nil, false
}