@@ -1,6 +1,7 @@
11package options
22
33import (
4+ "encoding/base64"
45 "fmt"
56 "net/url"
67 "strconv"
@@ -15,6 +16,9 @@ type LegacyOptions struct {
1516 // Legacy options related to upstream servers
1617 LegacyUpstreams LegacyUpstreams `cfg:",squash"`
1718
19+ // Legacy options for injecting request/response headers
20+ LegacyHeaders LegacyHeaders `cfg:",squash"`
21+
1822 Options Options `cfg:",squash"`
1923}
2024
@@ -26,6 +30,11 @@ func NewLegacyOptions() *LegacyOptions {
2630 FlushInterval: time.Duration(1) * time.Second,
2731 },
2832
33+ LegacyHeaders: LegacyHeaders{
34+ PassBasicAuth: true,
35+ PassUserHeaders: true,
36+ },
37+
2938 Options: *NewOptions(),
3039 }
3140}
@@ -37,6 +46,7 @@ func (l *LegacyOptions) ToOptions() (*Options, error) {
3746 }
3847 l.Options.UpstreamServers = upstreams
3948
49+ l.Options.InjectRequestHeaders, l.Options.InjectResponseHeaders = l.LegacyHeaders.convert()
4050 return &l.Options, nil
4151}
4252
@@ -119,3 +129,254 @@ func (l *LegacyUpstreams) convert() (Upstreams, error) {
119129
120130 return upstreams, nil
121131}
132+
133+type LegacyHeaders struct {
134+ PassBasicAuth bool `flag:"pass-basic-auth" cfg:"pass_basic_auth"`
135+ PassAccessToken bool `flag:"pass-access-token" cfg:"pass_access_token"`
136+ PassUserHeaders bool `flag:"pass-user-headers" cfg:"pass_user_headers"`
137+ PassAuthorization bool `flag:"pass-authorization-header" cfg:"pass_authorization_header"`
138+
139+ SetBasicAuth bool `flag:"set-basic-auth" cfg:"set_basic_auth"`
140+ SetXAuthRequest bool `flag:"set-xauthrequest" cfg:"set_xauthrequest"`
141+ SetAuthorization bool `flag:"set-authorization-header" cfg:"set_authorization_header"`
142+
143+ PreferEmailToUser bool `flag:"prefer-email-to-user" cfg:"prefer_email_to_user"`
144+ BasicAuthPassword string `flag:"basic-auth-password" cfg:"basic_auth_password"`
145+ SkipAuthStripHeaders bool `flag:"skip-auth-strip-headers" cfg:"skip_auth_strip_headers"`
146+}
147+
148+func legacyHeadersFlagSet() *pflag.FlagSet {
149+ flagSet := pflag.NewFlagSet("headers", pflag.ExitOnError)
150+
151+ flagSet.Bool("pass-basic-auth", true, "pass HTTP Basic Auth, X-Forwarded-User and X-Forwarded-Email information to upstream")
152+ flagSet.Bool("pass-access-token", false, "pass OAuth access_token to upstream via X-Forwarded-Access-Token header")
153+ flagSet.Bool("pass-user-headers", true, "pass X-Forwarded-User and X-Forwarded-Email information to upstream")
154+ flagSet.Bool("pass-authorization-header", false, "pass the Authorization Header to upstream")
155+
156+ flagSet.Bool("set-basic-auth", false, "set HTTP Basic Auth information in response (useful in Nginx auth_request mode)")
157+ flagSet.Bool("set-xauthrequest", false, "set X-Auth-Request-User and X-Auth-Request-Email response headers (useful in Nginx auth_request mode)")
158+ flagSet.Bool("set-authorization-header", false, "set Authorization response headers (useful in Nginx auth_request mode)")
159+
160+ flagSet.Bool("prefer-email-to-user", false, "Prefer to use the Email address as the Username when passing information to upstream. Will only use Username if Email is unavailable, eg. htaccess authentication. Used in conjunction with -pass-basic-auth and -pass-user-headers")
161+ flagSet.String("basic-auth-password", "", "the password to set when passing the HTTP Basic Auth header")
162+ flagSet.Bool("skip-auth-strip-headers", false, "strips X-Forwarded-* style authentication headers & Authorization header if they would be set by oauth2-proxy for request paths in --skip-auth-regex")
163+
164+ return flagSet
165+}
166+
167+// convert takes the legacy request/response headers and converts them to
168+// the new format for InjectRequestHeaders and InjectResponseHeaders
169+func (l *LegacyHeaders) convert() ([]Header, []Header) {
170+ return l.getRequestHeaders(), l.getResponseHeaders()
171+}
172+
173+func (l *LegacyHeaders) getRequestHeaders() []Header {
174+ requestHeaders := []Header{}
175+
176+ if l.PassBasicAuth && l.BasicAuthPassword != "" {
177+ requestHeaders = append(requestHeaders, getBasicAuthHeader(l.PreferEmailToUser, l.BasicAuthPassword))
178+ }
179+
180+ // In the old implementation, PassUserHeaders is a subset of PassBasicAuth
181+ if l.PassBasicAuth || l.PassUserHeaders {
182+ requestHeaders = append(requestHeaders, getPassUserHeaders(l.PreferEmailToUser)...)
183+ requestHeaders = append(requestHeaders, getPreferredUsernameHeader())
184+ }
185+
186+ if l.PassAccessToken {
187+ requestHeaders = append(requestHeaders, getPassAccessTokenHeader())
188+ }
189+
190+ if l.PassAuthorization {
191+ requestHeaders = append(requestHeaders, getAuthorizationHeader())
192+ }
193+
194+ for i := range requestHeaders {
195+ requestHeaders[i].PreserveRequestValue = !l.SkipAuthStripHeaders
196+ }
197+
198+ return requestHeaders
199+}
200+
201+func (l *LegacyHeaders) getResponseHeaders() []Header {
202+ responseHeaders := []Header{}
203+
204+ if l.SetXAuthRequest {
205+ responseHeaders = append(responseHeaders, getXAuthRequestHeaders(l.PassAccessToken)...)
206+ }
207+
208+ if l.SetBasicAuth {
209+ responseHeaders = append(responseHeaders, getBasicAuthHeader(l.PreferEmailToUser, l.BasicAuthPassword))
210+ }
211+
212+ if l.SetAuthorization {
213+ responseHeaders = append(responseHeaders, getAuthorizationHeader())
214+ }
215+
216+ return responseHeaders
217+}
218+
219+func getBasicAuthHeader(preferEmailToUser bool, basicAuthPassword string) Header {
220+ claim := "user"
221+ if preferEmailToUser {
222+ claim = "email"
223+ }
224+
225+ return Header{
226+ Name: "Authorization",
227+ Values: []HeaderValue{
228+ {
229+ ClaimSource: &ClaimSource{
230+ Claim: claim,
231+ BasicAuthPassword: &SecretSource{
232+ Value: []byte(base64.StdEncoding.EncodeToString([]byte(basicAuthPassword))),
233+ },
234+ },
235+ },
236+ },
237+ }
238+}
239+
240+func getPassUserHeaders(preferEmailToUser bool) []Header {
241+ headers := []Header{
242+ {
243+ Name: "X-Forwarded-Groups",
244+ Values: []HeaderValue{
245+ {
246+ ClaimSource: &ClaimSource{
247+ Claim: "groups",
248+ },
249+ },
250+ },
251+ },
252+ }
253+
254+ if preferEmailToUser {
255+ return append(headers,
256+ Header{
257+ Name: "X-Forwarded-User",
258+ Values: []HeaderValue{
259+ {
260+ ClaimSource: &ClaimSource{
261+ Claim: "email",
262+ },
263+ },
264+ },
265+ },
266+ )
267+ }
268+
269+ return append(headers,
270+ Header{
271+ Name: "X-Forwarded-User",
272+ Values: []HeaderValue{
273+ {
274+ ClaimSource: &ClaimSource{
275+ Claim: "user",
276+ },
277+ },
278+ },
279+ },
280+ Header{
281+ Name: "X-Forwarded-Email",
282+ Values: []HeaderValue{
283+ {
284+ ClaimSource: &ClaimSource{
285+ Claim: "email",
286+ },
287+ },
288+ },
289+ },
290+ )
291+}
292+
293+func getPassAccessTokenHeader() Header {
294+ return Header{
295+ Name: "X-Forwarded-Access-Token",
296+ Values: []HeaderValue{
297+ {
298+ ClaimSource: &ClaimSource{
299+ Claim: "access_token",
300+ },
301+ },
302+ },
303+ }
304+}
305+
306+func getAuthorizationHeader() Header {
307+ return Header{
308+ Name: "Authorization",
309+ Values: []HeaderValue{
310+ {
311+ ClaimSource: &ClaimSource{
312+ Claim: "id_token",
313+ Prefix: "Bearer ",
314+ },
315+ },
316+ },
317+ }
318+}
319+
320+func getPreferredUsernameHeader() Header {
321+ return Header{
322+ Name: "X-Forwarded-Preferred-Username",
323+ Values: []HeaderValue{
324+ {
325+ ClaimSource: &ClaimSource{
326+ Claim: "preferred_username",
327+ },
328+ },
329+ },
330+ }
331+}
332+
333+func getXAuthRequestHeaders(passAccessToken bool) []Header {
334+ headers := []Header{
335+ {
336+ Name: "X-Auth-Request-User",
337+ Values: []HeaderValue{
338+ {
339+ ClaimSource: &ClaimSource{
340+ Claim: "user",
341+ },
342+ },
343+ },
344+ },
345+ {
346+ Name: "X-Auth-Request-Email",
347+ Values: []HeaderValue{
348+ {
349+ ClaimSource: &ClaimSource{
350+ Claim: "email",
351+ },
352+ },
353+ },
354+ },
355+ getPreferredUsernameHeader(),
356+ {
357+ Name: "X-Auth-Request-Groups",
358+ Values: []HeaderValue{
359+ {
360+ ClaimSource: &ClaimSource{
361+ Claim: "groups",
362+ },
363+ },
364+ },
365+ },
366+ }
367+
368+ if passAccessToken {
369+ headers = append(headers, Header{
370+ Name: "X-Auth-Request-Access-Token",
371+ Values: []HeaderValue{
372+ {
373+ ClaimSource: &ClaimSource{
374+ Claim: "access_token",
375+ },
376+ },
377+ },
378+ })
379+ }
380+
381+ return headers
382+}