@@ -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 {
@@ -74,6 +74,9 @@ type OAuthProxy struct {
74
74
75
75
mux map [string ]* route
76
76
regexRoutes []* route
77
+
78
+ requestSigner * RequestSigner
79
+ publicCertsJSON []byte
77
80
}
78
81
79
82
type route struct {
@@ -93,11 +96,12 @@ type StateParameter struct {
93
96
94
97
// UpstreamProxy stores information necessary for proxying the request back to the upstream.
95
98
type UpstreamProxy struct {
96
- name string
97
- cookieName string
98
- handler http.Handler
99
- auth hmacauth.HmacAuth
100
- statsdClient * statsd.Client
99
+ name string
100
+ cookieName string
101
+ handler http.Handler
102
+ auth hmacauth.HmacAuth
103
+ requestSigner * RequestSigner
104
+ statsdClient * statsd.Client
101
105
}
102
106
103
107
// deleteSSOCookieHeader deletes the session cookie from the request header string.
@@ -118,6 +122,9 @@ func (u *UpstreamProxy) ServeHTTP(w http.ResponseWriter, r *http.Request) {
118
122
if u .auth != nil {
119
123
u .auth .SignRequest (r )
120
124
}
125
+ if u .requestSigner != nil {
126
+ u .requestSigner .Sign (r )
127
+ }
121
128
122
129
start := time .Now ()
123
130
u .handler .ServeHTTP (w , r )
@@ -211,13 +218,17 @@ func NewRewriteReverseProxy(route *RewriteRoute, config *UpstreamConfig) *httput
211
218
}
212
219
213
220
// NewReverseProxyHandler creates a new http.Handler given a httputil.ReverseProxy
214
- func NewReverseProxyHandler (reverseProxy * httputil.ReverseProxy , opts * Options , config * UpstreamConfig ) (http.Handler , []string ) {
221
+ func NewReverseProxyHandler (reverseProxy * httputil.ReverseProxy , opts * Options , config * UpstreamConfig , signer * RequestSigner ) (http.Handler , []string ) {
215
222
upstreamProxy := & UpstreamProxy {
216
- name : config .Service ,
217
- handler : reverseProxy ,
218
- auth : config .HMACAuth ,
219
- cookieName : opts .CookieName ,
220
- statsdClient : opts .StatsdClient ,
223
+ name : config .Service ,
224
+ handler : reverseProxy ,
225
+ auth : config .HMACAuth ,
226
+ cookieName : opts .CookieName ,
227
+ statsdClient : opts .StatsdClient ,
228
+ requestSigner : signer ,
229
+ }
230
+ if config .SkipRequestSigning {
231
+ upstreamProxy .requestSigner = nil
221
232
}
222
233
if config .FlushInterval != 0 {
223
234
return NewStreamingHandler (upstreamProxy , opts , config ), []string {"handler:streaming" }
@@ -255,7 +266,7 @@ func generateHmacAuth(signatureKey string) (hmacauth.HmacAuth, error) {
255
266
if err != nil {
256
267
return nil , fmt .Errorf ("unsupported signature hash algorithm: %s" , algorithm )
257
268
}
258
- auth := hmacauth .NewHmacAuth (hash , []byte (secret ), SignatureHeader , SignatureHeaders )
269
+ auth := hmacauth .NewHmacAuth (hash , []byte (secret ), HMACSignatureHeader , SignatureHeaders )
259
270
return auth , nil
260
271
}
261
272
@@ -283,6 +294,26 @@ func NewOAuthProxy(opts *Options, optFuncs ...func(*OAuthProxy) error) (*OAuthPr
283
294
c .Run ()
284
295
}()
285
296
297
+ // Configure the RequestSigner (used to sign requests with `Sso-Signature` header).
298
+ // Also build the `certs` static JSON-string which will be served from a public endpoint.
299
+ // The key published at this endpoint allows upstreams to decrypt the `Sso-Signature`
300
+ // header, and validate the integrity and authenticity of a request.
301
+ certs := make (map [string ]string )
302
+ var requestSigner * RequestSigner
303
+ if len (opts .RequestSigningKey ) > 0 {
304
+ if requestSigner , err = NewRequestSigner (opts .RequestSigningKey ); err != nil {
305
+ return nil , fmt .Errorf ("could not build RequestSigner: %s" , err )
306
+ }
307
+ id , key := requestSigner .PublicKey ()
308
+ certs [id ] = key
309
+ } else {
310
+ logger .Warn ("Running OAuthProxy without signing key. Requests will not be signed." )
311
+ }
312
+ certsAsStr , err := json .MarshalIndent (certs , "" , " " )
313
+ if err != nil {
314
+ return nil , fmt .Errorf ("could not marshal public certs as JSON: %s" , err )
315
+ }
316
+
286
317
p := & OAuthProxy {
287
318
CookieCipher : cipher ,
288
319
CookieDomain : opts .CookieDomain ,
@@ -303,6 +334,9 @@ func NewOAuthProxy(opts *Options, optFuncs ...func(*OAuthProxy) error) (*OAuthPr
303
334
redirectURL : & url.URL {Path : "/oauth2/callback" },
304
335
skipAuthPreflight : opts .SkipAuthPreflight ,
305
336
templates : getTemplates (),
337
+
338
+ requestSigner : requestSigner ,
339
+ publicCertsJSON : certsAsStr ,
306
340
}
307
341
308
342
for _ , optFunc := range optFuncs {
@@ -316,11 +350,13 @@ func NewOAuthProxy(opts *Options, optFuncs ...func(*OAuthProxy) error) (*OAuthPr
316
350
switch route := upstreamConfig .Route .(type ) {
317
351
case * SimpleRoute :
318
352
reverseProxy := NewReverseProxy (route .ToURL , upstreamConfig )
319
- handler , tags := NewReverseProxyHandler (reverseProxy , opts , upstreamConfig )
353
+ handler , tags := NewReverseProxyHandler (
354
+ reverseProxy , opts , upstreamConfig , requestSigner )
320
355
p .Handle (route .FromURL .Host , handler , tags , upstreamConfig )
321
356
case * RewriteRoute :
322
357
reverseProxy := NewRewriteReverseProxy (route , upstreamConfig )
323
- handler , tags := NewReverseProxyHandler (reverseProxy , opts , upstreamConfig )
358
+ handler , tags := NewReverseProxyHandler (
359
+ reverseProxy , opts , upstreamConfig , requestSigner )
324
360
p .HandleRegex (route .FromRegex , handler , tags , upstreamConfig )
325
361
default :
326
362
return nil , fmt .Errorf ("unknown route type" )
@@ -335,6 +371,7 @@ func (p *OAuthProxy) Handler() http.Handler {
335
371
mux := http .NewServeMux ()
336
372
mux .HandleFunc ("/favicon.ico" , p .Favicon )
337
373
mux .HandleFunc ("/robots.txt" , p .RobotsTxt )
374
+ mux .HandleFunc ("/oauth2/v1/certs" , p .Certs )
338
375
mux .HandleFunc ("/oauth2/sign_out" , p .SignOut )
339
376
mux .HandleFunc ("/oauth2/callback" , p .OAuthCallback )
340
377
mux .HandleFunc ("/oauth2/auth" , p .AuthenticateOnly )
@@ -533,6 +570,12 @@ func (p *OAuthProxy) RobotsTxt(rw http.ResponseWriter, _ *http.Request) {
533
570
fmt .Fprintf (rw , "User-agent: *\n Disallow: /" )
534
571
}
535
572
573
+ // Certs publishes the public key necessary for upstream services to validate the digital signature
574
+ // used to sign each request.
575
+ func (p * OAuthProxy ) Certs (rw http.ResponseWriter , _ * http.Request ) {
576
+ rw .Write (p .publicCertsJSON )
577
+ }
578
+
536
579
// Favicon will proxy the request as usual if the user is already authenticated
537
580
// but responds with a 404 otherwise, to avoid spurious and confusing
538
581
// authentication attempts when a browser automatically requests the favicon on
0 commit comments