-
Notifications
You must be signed in to change notification settings - Fork 4
/
proxy.go
152 lines (125 loc) · 3.85 KB
/
proxy.go
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
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
package jwtpxy
import (
"crypto/rsa"
"net/http"
"net/http/httputil"
"net/url"
"strings"
"time"
"github.com/prometheus/client_golang/prometheus"
"go.uber.org/zap"
)
const HeadersHeader = "JwtPxy-Headers"
const StatusHeader = "JwtPxy-Token-Status"
const RequireTokenModeHeader = "JwtPxy-Require-Token-Mode"
const SignatureHeader = "JwtPxy-Signature"
const VersionHeader = "JwtPxy-Version"
const TokenLocationHeader = "JwtPxy-Token-Location"
const CookieNameHeader = "JwtPxy-Cookie-Name"
type Proxy struct {
Target *url.URL
Proxy *httputil.ReverseProxy
Logger *zap.Logger
JWTConfig JWTConfig
Pmx *Pmx
RequireTokenMode string
SigHeader string
TokenMappings []TokenMapping
AllowCookieToken string
CookieTokenName string
Version string
}
type JWTConfig struct {
PublicKey *rsa.PublicKey `json:"public_key"`
}
type Pmx struct {
Requests prometheus.Counter
Latency prometheus.Summary
AuthFails prometheus.Counter
}
func (p *Proxy) Handle(w http.ResponseWriter, r *http.Request) {
var admit = true
p.Pmx.Requests.Inc()
requireTokenMode := "true"
if p.RequireTokenMode == "false" {
requireTokenMode = "false"
}
// prep headers: remove used headers to prevent existing value
// from leaking through
for _, tknMap := range p.TokenMappings {
r.Header.Del(tknMap.Header)
}
r.Header.Del(StatusHeader)
r.Header.Del(HeadersHeader)
r.Header.Del(RequireTokenModeHeader)
r.Header.Del(SignatureHeader)
r.Header.Add(HeadersHeader, HeadersHeader)
r.Header.Add(HeadersHeader, StatusHeader)
r.Header.Add(HeadersHeader, RequireTokenModeHeader)
r.Header.Add(HeadersHeader, SignatureHeader)
r.Header.Add(HeadersHeader, VersionHeader)
r.Header.Add(HeadersHeader, TokenLocationHeader)
r.Header.Add(HeadersHeader, CookieNameHeader)
r.Header.Add(VersionHeader, p.Version)
r.Header.Add(CookieNameHeader, p.CookieTokenName)
r.Header.Add(SignatureHeader, p.SigHeader)
r.Header.Add(RequireTokenModeHeader, requireTokenMode)
start := time.Now()
reqPath := r.URL.Path
reqMethod := r.Method
r.Host = p.Target.Host
// if there is a token we must process it, if there is
// not a token we check requireTokenMode for "false"
authHeader := r.Header.Get("Authorization")
tokenString := strings.TrimPrefix(authHeader, "Bearer ")
if tokenString != "" {
r.Header.Add(TokenLocationHeader, "Authorization")
}
// if Bearer token is empty and cookie token is true.
if tokenString == "" && p.AllowCookieToken == "true" {
tokenCookie, _ := r.Cookie(p.CookieTokenName)
if tokenCookie != nil {
r.Header.Add(TokenLocationHeader, "Cookie")
tokenString = tokenCookie.Value
p.Logger.Debug("Got token cookie", zap.String("cookie", tokenString))
}
}
if tokenString != "" {
err := p.ProxyTokenHandler(r, tokenString)
if err != nil {
p.Logger.Debug("ProxyTokenHandler Error",
zap.String("tokenString", tokenString),
zap.Error(err))
// fail
admit = false
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusUnauthorized)
_, _ = w.Write([]byte("{\"status\": \"" + strings.ToLower(err.Error()) + "\"}"))
}
}
// is requireTokenMode is true then a token is required
// return unauthorized
if admit && tokenString == "" && requireTokenMode == "true" {
// fail
admit = false
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusUnauthorized)
_, _ = w.Write([]byte("{\"status\": \"missing required token\"}"))
}
// serve backend
if admit == true {
p.Proxy.ServeHTTP(w, r)
}
end := time.Now()
latency := end.Sub(start)
p.Pmx.Latency.Observe(float64(latency))
p.Logger.Debug(reqPath,
zap.String("method", reqMethod),
zap.String("path", reqPath),
zap.String("time", end.Format(time.RFC3339)),
zap.Duration("latency", latency),
zap.Bool("admit", admit),
zap.Any("header", r.Header),
zap.String("token", tokenString),
)
}