@@ -24,8 +24,8 @@ import (
24
24
"github.com/datadog/datadog-go/statsd"
25
25
)
26
26
27
- // SignatureHeader is the header name where the signed request header is stored.
28
- const SignatureHeader = "Gap-Signature"
27
+ // HMACSignatureHeader is the header name where the signed request header is stored.
28
+ const HMACSignatureHeader = "Gap-Signature"
29
29
30
30
// SignatureHeaders are the headers that are valid in the request.
31
31
var SignatureHeaders = []string {
@@ -76,6 +76,9 @@ type OAuthProxy struct {
76
76
77
77
mux map [string ]* route
78
78
regexRoutes []* route
79
+
80
+ requestSigner * RequestSigner
81
+ publicCertsJSON []byte
79
82
}
80
83
81
84
type route struct {
@@ -95,11 +98,12 @@ type StateParameter struct {
95
98
96
99
// UpstreamProxy stores information necessary for proxying the request back to the upstream.
97
100
type UpstreamProxy struct {
98
- name string
99
- cookieName string
100
- handler http.Handler
101
- auth hmacauth.HmacAuth
102
- statsdClient * statsd.Client
101
+ name string
102
+ cookieName string
103
+ handler http.Handler
104
+ auth hmacauth.HmacAuth
105
+ requestSigner * RequestSigner
106
+ statsdClient * statsd.Client
103
107
}
104
108
105
109
// deleteSSOCookieHeader deletes the session cookie from the request header string.
@@ -120,6 +124,9 @@ func (u *UpstreamProxy) ServeHTTP(w http.ResponseWriter, r *http.Request) {
120
124
if u .auth != nil {
121
125
u .auth .SignRequest (r )
122
126
}
127
+ if u .requestSigner != nil {
128
+ u .requestSigner .Sign (r )
129
+ }
123
130
124
131
start := time .Now ()
125
132
u .handler .ServeHTTP (w , r )
@@ -213,13 +220,17 @@ func NewRewriteReverseProxy(route *RewriteRoute, config *UpstreamConfig) *httput
213
220
}
214
221
215
222
// NewReverseProxyHandler creates a new http.Handler given a httputil.ReverseProxy
216
- func NewReverseProxyHandler (reverseProxy * httputil.ReverseProxy , opts * Options , config * UpstreamConfig ) (http.Handler , []string ) {
223
+ func NewReverseProxyHandler (reverseProxy * httputil.ReverseProxy , opts * Options , config * UpstreamConfig , signer * RequestSigner ) (http.Handler , []string ) {
217
224
upstreamProxy := & UpstreamProxy {
218
- name : config .Service ,
219
- handler : reverseProxy ,
220
- auth : config .HMACAuth ,
221
- cookieName : opts .CookieName ,
222
- statsdClient : opts .StatsdClient ,
225
+ name : config .Service ,
226
+ handler : reverseProxy ,
227
+ auth : config .HMACAuth ,
228
+ cookieName : opts .CookieName ,
229
+ statsdClient : opts .StatsdClient ,
230
+ requestSigner : signer ,
231
+ }
232
+ if config .SkipRequestSigning {
233
+ upstreamProxy .requestSigner = nil
223
234
}
224
235
if config .FlushInterval != 0 {
225
236
return NewStreamingHandler (upstreamProxy , opts , config ), []string {"handler:streaming" }
@@ -257,7 +268,7 @@ func generateHmacAuth(signatureKey string) (hmacauth.HmacAuth, error) {
257
268
if err != nil {
258
269
return nil , fmt .Errorf ("unsupported signature hash algorithm: %s" , algorithm )
259
270
}
260
- auth := hmacauth .NewHmacAuth (hash , []byte (secret ), SignatureHeader , SignatureHeaders )
271
+ auth := hmacauth .NewHmacAuth (hash , []byte (secret ), HMACSignatureHeader , SignatureHeaders )
261
272
return auth , nil
262
273
}
263
274
@@ -285,6 +296,26 @@ func NewOAuthProxy(opts *Options, optFuncs ...func(*OAuthProxy) error) (*OAuthPr
285
296
c .Run ()
286
297
}()
287
298
299
+ // Configure the RequestSigner (used to sign requests with `Sso-Signature` header).
300
+ // Also build the `certs` static JSON-string which will be served from a public endpoint.
301
+ // The key published at this endpoint allows upstreams to decrypt the `Sso-Signature`
302
+ // header, and validate the integrity and authenticity of a request.
303
+ certs := make (map [string ]string )
304
+ var requestSigner * RequestSigner
305
+ if len (opts .RequestSigningKey ) > 0 {
306
+ if requestSigner , err = NewRequestSigner (opts .RequestSigningKey ); err != nil {
307
+ return nil , fmt .Errorf ("could not build RequestSigner: %s" , err )
308
+ }
309
+ id , key := requestSigner .PublicKey ()
310
+ certs [id ] = key
311
+ } else {
312
+ logger .Warn ("Running OAuthProxy without signing key. Requests will not be signed." )
313
+ }
314
+ certsAsStr , err := json .MarshalIndent (certs , "" , " " )
315
+ if err != nil {
316
+ return nil , fmt .Errorf ("could not marshal public certs as JSON: %s" , err )
317
+ }
318
+
288
319
p := & OAuthProxy {
289
320
CookieCipher : cipher ,
290
321
CookieDomain : opts .CookieDomain ,
@@ -306,6 +337,9 @@ func NewOAuthProxy(opts *Options, optFuncs ...func(*OAuthProxy) error) (*OAuthPr
306
337
skipAuthPreflight : opts .SkipAuthPreflight ,
307
338
templates : getTemplates (),
308
339
PassAccessToken : opts .PassAccessToken ,
340
+
341
+ requestSigner : requestSigner ,
342
+ publicCertsJSON : certsAsStr ,
309
343
}
310
344
311
345
for _ , optFunc := range optFuncs {
@@ -319,11 +353,13 @@ func NewOAuthProxy(opts *Options, optFuncs ...func(*OAuthProxy) error) (*OAuthPr
319
353
switch route := upstreamConfig .Route .(type ) {
320
354
case * SimpleRoute :
321
355
reverseProxy := NewReverseProxy (route .ToURL , upstreamConfig )
322
- handler , tags := NewReverseProxyHandler (reverseProxy , opts , upstreamConfig )
356
+ handler , tags := NewReverseProxyHandler (
357
+ reverseProxy , opts , upstreamConfig , requestSigner )
323
358
p .Handle (route .FromURL .Host , handler , tags , upstreamConfig )
324
359
case * RewriteRoute :
325
360
reverseProxy := NewRewriteReverseProxy (route , upstreamConfig )
326
- handler , tags := NewReverseProxyHandler (reverseProxy , opts , upstreamConfig )
361
+ handler , tags := NewReverseProxyHandler (
362
+ reverseProxy , opts , upstreamConfig , requestSigner )
327
363
p .HandleRegex (route .FromRegex , handler , tags , upstreamConfig )
328
364
default :
329
365
return nil , fmt .Errorf ("unknown route type" )
@@ -338,6 +374,7 @@ func (p *OAuthProxy) Handler() http.Handler {
338
374
mux := http .NewServeMux ()
339
375
mux .HandleFunc ("/favicon.ico" , p .Favicon )
340
376
mux .HandleFunc ("/robots.txt" , p .RobotsTxt )
377
+ mux .HandleFunc ("/oauth2/v1/certs" , p .Certs )
341
378
mux .HandleFunc ("/oauth2/sign_out" , p .SignOut )
342
379
mux .HandleFunc ("/oauth2/callback" , p .OAuthCallback )
343
380
mux .HandleFunc ("/oauth2/auth" , p .AuthenticateOnly )
@@ -537,6 +574,12 @@ func (p *OAuthProxy) RobotsTxt(rw http.ResponseWriter, _ *http.Request) {
537
574
fmt .Fprintf (rw , "User-agent: *\n Disallow: /" )
538
575
}
539
576
577
+ // Certs publishes the public key necessary for upstream services to validate the digital signature
578
+ // used to sign each request.
579
+ func (p * OAuthProxy ) Certs (rw http.ResponseWriter , _ * http.Request ) {
580
+ rw .Write (p .publicCertsJSON )
581
+ }
582
+
540
583
// Favicon will proxy the request as usual if the user is already authenticated
541
584
// but responds with a 404 otherwise, to avoid spurious and confusing
542
585
// authentication attempts when a browser automatically requests the favicon on
0 commit comments