@@ -1,627 +0,0 @@
1−package main
2−
3−import (
4− "context"
5− "crypto"
6− "crypto/tls"
7− "encoding/base64"
8− "fmt"
9− "io/ioutil"
10− "net/http"
11− "net/url"
12− "os"
13− "regexp"
14− "strings"
15− "time"
16−
17− oidc "github.com/coreos/go-oidc"
18− "github.com/dgrijalva/jwt-go"
19− "github.com/mbland/hmacauth"
20− "github.com/pusher/oauth2_proxy/pkg/apis/options"
21− sessionsapi "github.com/pusher/oauth2_proxy/pkg/apis/sessions"
22− "github.com/pusher/oauth2_proxy/pkg/encryption"
23− "github.com/pusher/oauth2_proxy/pkg/logger"
24− "github.com/pusher/oauth2_proxy/pkg/sessions"
25− "github.com/pusher/oauth2_proxy/providers"
26− "gopkg.in/natefinch/lumberjack.v2"
27−)
28−
29−// Options holds Configuration Options that can be set by Command Line Flag,
30−// or Config File
31−type Options struct {
32− ProxyPrefix string `flag:"proxy-prefix" cfg:"proxy_prefix" env:"OAUTH2_PROXY_PROXY_PREFIX"`
33− PingPath string `flag:"ping-path" cfg:"ping_path" env:"OAUTH2_PROXY_PING_PATH"`
34− ProxyWebSockets bool `flag:"proxy-websockets" cfg:"proxy_websockets" env:"OAUTH2_PROXY_PROXY_WEBSOCKETS"`
35− HTTPAddress string `flag:"http-address" cfg:"http_address" env:"OAUTH2_PROXY_HTTP_ADDRESS"`
36− HTTPSAddress string `flag:"https-address" cfg:"https_address" env:"OAUTH2_PROXY_HTTPS_ADDRESS"`
37− RedirectURL string `flag:"redirect-url" cfg:"redirect_url" env:"OAUTH2_PROXY_REDIRECT_URL"`
38− ClientID string `flag:"client-id" cfg:"client_id" env:"OAUTH2_PROXY_CLIENT_ID"`
39− ClientSecret string `flag:"client-secret" cfg:"client_secret" env:"OAUTH2_PROXY_CLIENT_SECRET"`
40− TLSCertFile string `flag:"tls-cert-file" cfg:"tls_cert_file" env:"OAUTH2_PROXY_TLS_CERT_FILE"`
41− TLSKeyFile string `flag:"tls-key-file" cfg:"tls_key_file" env:"OAUTH2_PROXY_TLS_KEY_FILE"`
42− ProxyTimeOut time.Duration `flag:"proxy-time-out" cfg:"proxy_time_out" env:"OAUTH2_PROXY_PROXY_TIME_OUT"`
43−
44− AuthenticatedEmailsFile string `flag:"authenticated-emails-file" cfg:"authenticated_emails_file" env:"OAUTH2_PROXY_AUTHENTICATED_EMAILS_FILE"`
45− KeycloakGroup string `flag:"keycloak-group" cfg:"keycloak_group" env:"OAUTH2_PROXY_KEYCLOAK_GROUP"`
46− AzureTenant string `flag:"azure-tenant" cfg:"azure_tenant" env:"OAUTH2_PROXY_AZURE_TENANT"`
47− BitbucketTeam string `flag:"bitbucket-team" cfg:"bitbucket_team" env:"OAUTH2_PROXY_BITBUCKET_TEAM"`
48− BitbucketRepository string `flag:"bitbucket-repository" cfg:"bitbucket_repository" env:"OAUTH2_PROXY_BITBUCKET_REPOSITORY"`
49− EmailDomains []string `flag:"email-domain" cfg:"email_domains" env:"OAUTH2_PROXY_EMAIL_DOMAINS"`
50− WhitelistDomains []string `flag:"whitelist-domain" cfg:"whitelist_domains" env:"OAUTH2_PROXY_WHITELIST_DOMAINS"`
51− GitHubOrg string `flag:"github-org" cfg:"github_org" env:"OAUTH2_PROXY_GITHUB_ORG"`
52− GitHubTeam string `flag:"github-team" cfg:"github_team" env:"OAUTH2_PROXY_GITHUB_TEAM"`
53− GitLabGroup string `flag:"gitlab-group" cfg:"gitlab_group" env:"OAUTH2_PROXY_GITLAB_GROUP"`
54− GoogleGroups []string `flag:"google-group" cfg:"google_group" env:"OAUTH2_PROXY_GOOGLE_GROUPS"`
55− GoogleAdminEmail string `flag:"google-admin-email" cfg:"google_admin_email" env:"OAUTH2_PROXY_GOOGLE_ADMIN_EMAIL"`
56− GoogleServiceAccountJSON string `flag:"google-service-account-json" cfg:"google_service_account_json" env:"OAUTH2_PROXY_GOOGLE_SERVICE_ACCOUNT_JSON"`
57− HtpasswdFile string `flag:"htpasswd-file" cfg:"htpasswd_file" env:"OAUTH2_PROXY_HTPASSWD_FILE"`
58− DisplayHtpasswdForm bool `flag:"display-htpasswd-form" cfg:"display_htpasswd_form" env:"OAUTH2_PROXY_DISPLAY_HTPASSWD_FORM"`
59− CustomTemplatesDir string `flag:"custom-templates-dir" cfg:"custom_templates_dir" env:"OAUTH2_PROXY_CUSTOM_TEMPLATES_DIR"`
60− Banner string `flag:"banner" cfg:"banner" env:"OAUTH2_PROXY_BANNER"`
61− Footer string `flag:"footer" cfg:"footer" env:"OAUTH2_PROXY_FOOTER"`
62−
63− // Embed CookieOptions
64− options.CookieOptions
65−
66− // Embed SessionOptions
67− options.SessionOptions
68−
69− Upstreams []string `flag:"upstream" cfg:"upstreams" env:"OAUTH2_PROXY_UPSTREAMS"`
70− SkipAuthRegex []string `flag:"skip-auth-regex" cfg:"skip_auth_regex" env:"OAUTH2_PROXY_SKIP_AUTH_REGEX"`
71− SkipJwtBearerTokens bool `flag:"skip-jwt-bearer-tokens" cfg:"skip_jwt_bearer_tokens" env:"OAUTH2_PROXY_SKIP_JWT_BEARER_TOKENS"`
72− ExtraJwtIssuers []string `flag:"extra-jwt-issuers" cfg:"extra_jwt_issuers" env:"OAUTH2_PROXY_EXTRA_JWT_ISSUERS"`
73− PassBasicAuth bool `flag:"pass-basic-auth" cfg:"pass_basic_auth" env:"OAUTH2_PROXY_PASS_BASIC_AUTH"`
74− BasicAuthPassword string `flag:"basic-auth-password" cfg:"basic_auth_password" env:"OAUTH2_PROXY_BASIC_AUTH_PASSWORD"`
75− PassAccessToken bool `flag:"pass-access-token" cfg:"pass_access_token" env:"OAUTH2_PROXY_PASS_ACCESS_TOKEN"`
76− PassHostHeader bool `flag:"pass-host-header" cfg:"pass_host_header" env:"OAUTH2_PROXY_PASS_HOST_HEADER"`
77− SkipProviderButton bool `flag:"skip-provider-button" cfg:"skip_provider_button" env:"OAUTH2_PROXY_SKIP_PROVIDER_BUTTON"`
78− PassUserHeaders bool `flag:"pass-user-headers" cfg:"pass_user_headers" env:"OAUTH2_PROXY_PASS_USER_HEADERS"`
79− SSLInsecureSkipVerify bool `flag:"ssl-insecure-skip-verify" cfg:"ssl_insecure_skip_verify" env:"OAUTH2_PROXY_SSL_INSECURE_SKIP_VERIFY"`
80− SSLUpstreamInsecureSkipVerify bool `flag:"ssl-upstream-insecure-skip-verify" cfg:"ssl_upstream_insecure_skip_verify" env:"OAUTH2_PROXY_SSL_UPSTREAM_INSECURE_SKIP_VERIFY"`
81− SetXAuthRequest bool `flag:"set-xauthrequest" cfg:"set_xauthrequest" env:"OAUTH2_PROXY_SET_XAUTHREQUEST"`
82− SetAuthorization bool `flag:"set-authorization-header" cfg:"set_authorization_header" env:"OAUTH2_PROXY_SET_AUTHORIZATION_HEADER"`
83− PassAuthorization bool `flag:"pass-authorization-header" cfg:"pass_authorization_header" env:"OAUTH2_PROXY_PASS_AUTHORIZATION_HEADER"`
84− SkipAuthPreflight bool `flag:"skip-auth-preflight" cfg:"skip_auth_preflight" env:"OAUTH2_PROXY_SKIP_AUTH_PREFLIGHT"`
85− FlushInterval time.Duration `flag:"flush-interval" cfg:"flush_interval" env:"OAUTH2_PROXY_FLUSH_INTERVAL"`
86−
87− // These options allow for other providers besides Google, with
88− // potential overrides.
89− Provider string `flag:"provider" cfg:"provider" env:"OAUTH2_PROXY_PROVIDER"`
90− OIDCIssuerURL string `flag:"oidc-issuer-url" cfg:"oidc_issuer_url" env:"OAUTH2_PROXY_OIDC_ISSUER_URL"`
91− InsecureOIDCAllowUnverifiedEmail bool `flag:"insecure-oidc-allow-unverified-email" cfg:"insecure_oidc_allow_unverified_email" env:"OAUTH2_PROXY_INSECURE_OIDC_ALLOW_UNVERIFIED_EMAIL"`
92− SkipOIDCDiscovery bool `flag:"skip-oidc-discovery" cfg:"skip_oidc_discovery" env:"OAUTH2_PROXY_SKIP_OIDC_DISCOVERY"`
93− OIDCJwksURL string `flag:"oidc-jwks-url" cfg:"oidc_jwks_url" env:"OAUTH2_PROXY_OIDC_JWKS_URL"`
94− LoginURL string `flag:"login-url" cfg:"login_url" env:"OAUTH2_PROXY_LOGIN_URL"`
95− RedeemURL string `flag:"redeem-url" cfg:"redeem_url" env:"OAUTH2_PROXY_REDEEM_URL"`
96− ProfileURL string `flag:"profile-url" cfg:"profile_url" env:"OAUTH2_PROXY_PROFILE_URL"`
97− ProtectedResource string `flag:"resource" cfg:"resource" env:"OAUTH2_PROXY_RESOURCE"`
98− ValidateURL string `flag:"validate-url" cfg:"validate_url" env:"OAUTH2_PROXY_VALIDATE_URL"`
99− Scope string `flag:"scope" cfg:"scope" env:"OAUTH2_PROXY_SCOPE"`
100− ApprovalPrompt string `flag:"approval-prompt" cfg:"approval_prompt" env:"OAUTH2_PROXY_APPROVAL_PROMPT"`
101−
102− // Configuration values for logging
103− LoggingFilename string `flag:"logging-filename" cfg:"logging_filename" env:"OAUTH2_PROXY_LOGGING_FILENAME"`
104− LoggingMaxSize int `flag:"logging-max-size" cfg:"logging_max_size" env:"OAUTH2_PROXY_LOGGING_MAX_SIZE"`
105− LoggingMaxAge int `flag:"logging-max-age" cfg:"logging_max_age" env:"OAUTH2_PROXY_LOGGING_MAX_AGE"`
106− LoggingMaxBackups int `flag:"logging-max-backups" cfg:"logging_max_backups" env:"OAUTH2_PROXY_LOGGING_MAX_BACKUPS"`
107− LoggingLocalTime bool `flag:"logging-local-time" cfg:"logging_local_time" env:"OAUTH2_PROXY_LOGGING_LOCAL_TIME"`
108− LoggingCompress bool `flag:"logging-compress" cfg:"logging_compress" env:"OAUTH2_PROXY_LOGGING_COMPRESS"`
109− StandardLogging bool `flag:"standard-logging" cfg:"standard_logging" env:"OAUTH2_PROXY_STANDARD_LOGGING"`
110− StandardLoggingFormat string `flag:"standard-logging-format" cfg:"standard_logging_format" env:"OAUTH2_PROXY_STANDARD_LOGGING_FORMAT"`
111− RequestLogging bool `flag:"request-logging" cfg:"request_logging" env:"OAUTH2_PROXY_REQUEST_LOGGING"`
112− RequestLoggingFormat string `flag:"request-logging-format" cfg:"request_logging_format" env:"OAUTH2_PROXY_REQUEST_LOGGING_FORMAT"`
113− ExcludeLoggingPaths string `flag:"exclude-logging-paths" cfg:"exclude_logging_paths" env:"OAUTH2_PROXY_EXCLUDE_LOGGING_PATHS"`
114− SilencePingLogging bool `flag:"silence-ping-logging" cfg:"silence_ping_logging" env:"OAUTH2_PROXY_SILENCE_PING_LOGGING"`
115− AuthLogging bool `flag:"auth-logging" cfg:"auth_logging" env:"OAUTH2_PROXY_LOGGING_AUTH_LOGGING"`
116− AuthLoggingFormat string `flag:"auth-logging-format" cfg:"auth_logging_format" env:"OAUTH2_PROXY_AUTH_LOGGING_FORMAT"`
117− SignatureKey string `flag:"signature-key" cfg:"signature_key" env:"OAUTH2_PROXY_SIGNATURE_KEY"`
118− AcrValues string `flag:"acr-values" cfg:"acr_values" env:"OAUTH2_PROXY_ACR_VALUES"`
119− JWTKey string `flag:"jwt-key" cfg:"jwt_key" env:"OAUTH2_PROXY_JWT_KEY"`
120− JWTKeyFile string `flag:"jwt-key-file" cfg:"jwt_key_file" env:"OAUTH2_PROXY_JWT_KEY_FILE"`
121− PubJWKURL string `flag:"pubjwk-url" cfg:"pubjwk_url" env:"OAUTH2_PROXY_PUBJWK_URL"`
122− GCPHealthChecks bool `flag:"gcp-healthchecks" cfg:"gcp_healthchecks" env:"OAUTH2_PROXY_GCP_HEALTHCHECKS"`
123−
124− // internal values that are set after config validation
125− redirectURL *url.URL
126− proxyURLs []*url.URL
127− CompiledRegex []*regexp.Regexp
128− provider providers.Provider
129− sessionStore sessionsapi.SessionStore
130− signatureData *SignatureData
131− oidcVerifier *oidc.IDTokenVerifier
132− jwtBearerVerifiers []*oidc.IDTokenVerifier
133−}
134−
135−// SignatureData holds hmacauth signature hash and key
136−type SignatureData struct {
137− hash crypto.Hash
138− key string
139−}
140−
141−// NewOptions constructs a new Options with defaulted values
142−func NewOptions() *Options {
143− return &Options{
144− ProxyPrefix: "/oauth2",
145− PingPath: "/ping",
146− ProxyWebSockets: true,
147− HTTPAddress: "127.0.0.1:4180",
148− HTTPSAddress: ":443",
149− ProxyTimeOut: 30, //TIME_OUT in seconds
150− DisplayHtpasswdForm: true,
151− CookieOptions: options.CookieOptions{
152− CookieName: "_oauth2_proxy",
153− CookieSecure: true,
154− CookieHTTPOnly: true,
155− CookieExpire: time.Duration(168) * time.Hour,
156− CookieRefresh: time.Duration(0),
157− },
158− SessionOptions: options.SessionOptions{
159− Type: "cookie",
160− },
161− SetXAuthRequest: false,
162− SkipAuthPreflight: false,
163− PassBasicAuth: true,
164− PassUserHeaders: true,
165− PassAccessToken: false,
166− PassHostHeader: true,
167− SetAuthorization: false,
168− PassAuthorization: false,
169− ApprovalPrompt: "force",
170− InsecureOIDCAllowUnverifiedEmail: false,
171− SkipOIDCDiscovery: false,
172− LoggingFilename: "",
173− LoggingMaxSize: 100,
174− LoggingMaxAge: 7,
175− LoggingMaxBackups: 0,
176− LoggingLocalTime: true,
177− LoggingCompress: false,
178− ExcludeLoggingPaths: "",
179− SilencePingLogging: false,
180− StandardLogging: true,
181− StandardLoggingFormat: logger.DefaultStandardLoggingFormat,
182− RequestLogging: true,
183− RequestLoggingFormat: logger.DefaultRequestLoggingFormat,
184− AuthLogging: true,
185− AuthLoggingFormat: logger.DefaultAuthLoggingFormat,
186− }
187−}
188−
189−// jwtIssuer hold parsed JWT issuer info that's used to construct a verifier.
190−type jwtIssuer struct {
191− issuerURI string
192− audience string
193−}
194−
195−func parseURL(toParse string, urltype string, msgs []string) (*url.URL, []string) {
196− parsed, err := url.Parse(toParse)
197− if err != nil {
198− return nil, append(msgs, fmt.Sprintf(
199− "error parsing %s-url=%q %s", urltype, toParse, err))
200− }
201− return parsed, msgs
202−}
203−
204−// Validate checks that required options are set and validates those that they
205−// are of the correct format
206−func (o *Options) Validate() error {
207− if o.SSLInsecureSkipVerify {
208− // TODO: Accept a certificate bundle.
209− insecureTransport := &http.Transport{
210− TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
211− }
212− http.DefaultClient = &http.Client{Transport: insecureTransport}
213− }
214−
215− msgs := make([]string, 0)
216− if o.CookieSecret == "" {
217− msgs = append(msgs, "missing setting: cookie-secret")
218− }
219− if o.ClientID == "" {
220− msgs = append(msgs, "missing setting: client-id")
221− }
222− // login.gov uses a signed JWT to authenticate, not a client-secret
223− if o.ClientSecret == "" && o.Provider != "login.gov" {
224− msgs = append(msgs, "missing setting: client-secret")
225− }
226− if o.AuthenticatedEmailsFile == "" && len(o.EmailDomains) == 0 && o.HtpasswdFile == "" {
227− msgs = append(msgs, "missing setting for email validation: email-domain or authenticated-emails-file required."+
228− "\n use email-domain=* to authorize all email addresses")
229− }
230−
231− if o.OIDCIssuerURL != "" {
232−
233− ctx := context.Background()
234−
235− // Construct a manual IDTokenVerifier from issuer URL & JWKS URI
236− // instead of metadata discovery if we enable -skip-oidc-discovery.
237− // In this case we need to make sure the required endpoints for
238− // the provider are configured.
239− if o.SkipOIDCDiscovery {
240− if o.LoginURL == "" {
241− msgs = append(msgs, "missing setting: login-url")
242− }
243− if o.RedeemURL == "" {
244− msgs = append(msgs, "missing setting: redeem-url")
245− }
246− if o.OIDCJwksURL == "" {
247− msgs = append(msgs, "missing setting: oidc-jwks-url")
248− }
249− keySet := oidc.NewRemoteKeySet(ctx, o.OIDCJwksURL)
250− o.oidcVerifier = oidc.NewVerifier(o.OIDCIssuerURL, keySet, &oidc.Config{
251− ClientID: o.ClientID,
252− })
253− } else {
254− // Configure discoverable provider data.
255− provider, err := oidc.NewProvider(ctx, o.OIDCIssuerURL)
256− if err != nil {
257− return err
258− }
259− o.oidcVerifier = provider.Verifier(&oidc.Config{
260− ClientID: o.ClientID,
261− })
262−
263− o.LoginURL = provider.Endpoint().AuthURL
264− o.RedeemURL = provider.Endpoint().TokenURL
265− }
266− if o.Scope == "" {
267− o.Scope = "openid email profile"
268− }
269− }
270−
271− if o.SkipJwtBearerTokens {
272− // If we are using an oidc provider, go ahead and add that provider to the list
273− if o.oidcVerifier != nil {
274− o.jwtBearerVerifiers = append(o.jwtBearerVerifiers, o.oidcVerifier)
275− }
276− // Configure extra issuers
277− if len(o.ExtraJwtIssuers) > 0 {
278− var jwtIssuers []jwtIssuer
279− jwtIssuers, msgs = parseJwtIssuers(o.ExtraJwtIssuers, msgs)
280− for _, jwtIssuer := range jwtIssuers {
281− verifier, err := newVerifierFromJwtIssuer(jwtIssuer)
282− if err != nil {
283− msgs = append(msgs, fmt.Sprintf("error building verifiers: %s", err))
284− }
285− o.jwtBearerVerifiers = append(o.jwtBearerVerifiers, verifier)
286− }
287− }
288− }
289−
290− o.redirectURL, msgs = parseURL(o.RedirectURL, "redirect", msgs)
291−
292− for _, u := range o.Upstreams {
293− upstreamURL, err := url.Parse(u)
294− if err != nil {
295− msgs = append(msgs, fmt.Sprintf("error parsing upstream: %s", err))
296− } else {
297− if upstreamURL.Path == "" {
298− upstreamURL.Path = "/"
299− }
300− o.proxyURLs = append(o.proxyURLs, upstreamURL)
301− }
302− }
303−
304− for _, u := range o.SkipAuthRegex {
305− CompiledRegex, err := regexp.Compile(u)
306− if err != nil {
307− msgs = append(msgs, fmt.Sprintf("error compiling regex=%q %s", u, err))
308− continue
309− }
310− o.CompiledRegex = append(o.CompiledRegex, CompiledRegex)
311− }
312− msgs = parseProviderInfo(o, msgs)
313−
314− var cipher *encryption.Cipher
315− if o.PassAccessToken || o.SetAuthorization || o.PassAuthorization || (o.CookieRefresh != time.Duration(0)) {
316− validCookieSecretSize := false
317− for _, i := range []int{16, 24, 32} {
318− if len(secretBytes(o.CookieSecret)) == i {
319− validCookieSecretSize = true
320− }
321− }
322− var decoded bool
323− if string(secretBytes(o.CookieSecret)) != o.CookieSecret {
324− decoded = true
325− }
326− if validCookieSecretSize == false {
327− var suffix string
328− if decoded {
329− suffix = fmt.Sprintf(" note: cookie secret was base64 decoded from %q", o.CookieSecret)
330− }
331− msgs = append(msgs, fmt.Sprintf(
332− "cookie_secret must be 16, 24, or 32 bytes "+
333− "to create an AES cipher when "+
334− "pass_access_token == true or "+
335− "cookie_refresh != 0, but is %d bytes.%s",
336− len(secretBytes(o.CookieSecret)), suffix))
337− } else {
338− var err error
339− cipher, err = encryption.NewCipher(secretBytes(o.CookieSecret))
340− if err != nil {
341− msgs = append(msgs, fmt.Sprintf("cookie-secret error: %v", err))
342− }
343− }
344− }
345−
346− o.SessionOptions.Cipher = cipher
347− sessionStore, err := sessions.NewSessionStore(&o.SessionOptions, &o.CookieOptions)
348− if err != nil {
349− msgs = append(msgs, fmt.Sprintf("error initialising session storage: %v", err))
350− } else {
351− o.sessionStore = sessionStore
352− }
353−
354− if o.CookieRefresh >= o.CookieExpire {
355− msgs = append(msgs, fmt.Sprintf(
356− "cookie_refresh (%s) must be less than "+
357− "cookie_expire (%s)",
358− o.CookieRefresh.String(),
359− o.CookieExpire.String()))
360− }
361−
362− if len(o.GoogleGroups) > 0 || o.GoogleAdminEmail != "" || o.GoogleServiceAccountJSON != "" {
363− if len(o.GoogleGroups) < 1 {
364− msgs = append(msgs, "missing setting: google-group")
365− }
366− if o.GoogleAdminEmail == "" {
367− msgs = append(msgs, "missing setting: google-admin-email")
368− }
369− if o.GoogleServiceAccountJSON == "" {
370− msgs = append(msgs, "missing setting: google-service-account-json")
371− }
372− }
373−
374− msgs = parseSignatureKey(o, msgs)
375− msgs = validateCookieName(o, msgs)
376− msgs = setupLogger(o, msgs)
377−
378− if len(msgs) != 0 {
379− return fmt.Errorf("Invalid configuration:\n %s",
380− strings.Join(msgs, "\n "))
381− }
382− return nil
383−}
384−
385−func parseProviderInfo(o *Options, msgs []string) []string {
386− p := &providers.ProviderData{
387− Scope: o.Scope,
388− ClientID: o.ClientID,
389− ClientSecret: o.ClientSecret,
390− ApprovalPrompt: o.ApprovalPrompt,
391− }
392− p.LoginURL, msgs = parseURL(o.LoginURL, "login", msgs)
393− p.RedeemURL, msgs = parseURL(o.RedeemURL, "redeem", msgs)
394− p.ProfileURL, msgs = parseURL(o.ProfileURL, "profile", msgs)
395− p.ValidateURL, msgs = parseURL(o.ValidateURL, "validate", msgs)
396− p.ProtectedResource, msgs = parseURL(o.ProtectedResource, "resource", msgs)
397−
398− o.provider = providers.New(o.Provider, p)
399− switch p := o.provider.(type) {
400− case *providers.AzureProvider:
401− p.Configure(o.AzureTenant)
402− case *providers.GitHubProvider:
403− p.SetOrgTeam(o.GitHubOrg, o.GitHubTeam)
404− case *providers.KeycloakProvider:
405− p.SetGroup(o.KeycloakGroup)
406− case *providers.GoogleProvider:
407− if o.GoogleServiceAccountJSON != "" {
408− file, err := os.Open(o.GoogleServiceAccountJSON)
409− if err != nil {
410− msgs = append(msgs, "invalid Google credentials file: "+o.GoogleServiceAccountJSON)
411− } else {
412− p.SetGroupRestriction(o.GoogleGroups, o.GoogleAdminEmail, file)
413− }
414− }
415− case *providers.BitbucketProvider:
416− p.SetTeam(o.BitbucketTeam)
417− p.SetRepository(o.BitbucketRepository)
418− case *providers.OIDCProvider:
419− p.AllowUnverifiedEmail = o.InsecureOIDCAllowUnverifiedEmail
420− if o.oidcVerifier == nil {
421− msgs = append(msgs, "oidc provider requires an oidc issuer URL")
422− } else {
423− p.Verifier = o.oidcVerifier
424− }
425− case *providers.GitLabProvider:
426− p.AllowUnverifiedEmail = o.InsecureOIDCAllowUnverifiedEmail
427− p.Group = o.GitLabGroup
428− p.EmailDomains = o.EmailDomains
429−
430− if o.oidcVerifier != nil {
431− p.Verifier = o.oidcVerifier
432− } else {
433− // Initialize with default verifier for gitlab.com
434− ctx := context.Background()
435−
436− provider, err := oidc.NewProvider(ctx, "https://gitlab.com")
437− if err != nil {
438− msgs = append(msgs, "failed to initialize oidc provider for gitlab.com")
439− } else {
440− p.Verifier = provider.Verifier(&oidc.Config{
441− ClientID: o.ClientID,
442− })
443−
444− p.LoginURL, msgs = parseURL(provider.Endpoint().AuthURL, "login", msgs)
445− p.RedeemURL, msgs = parseURL(provider.Endpoint().TokenURL, "redeem", msgs)
446− }
447− }
448− case *providers.LoginGovProvider:
449− p.AcrValues = o.AcrValues
450− p.PubJWKURL, msgs = parseURL(o.PubJWKURL, "pubjwk", msgs)
451−
452− // JWT key can be supplied via env variable or file in the filesystem, but not both.
453− switch {
454− case o.JWTKey != "" && o.JWTKeyFile != "":
455− msgs = append(msgs, "cannot set both jwt-key and jwt-key-file options")
456− case o.JWTKey == "" && o.JWTKeyFile == "":
457− msgs = append(msgs, "login.gov provider requires a private key for signing JWTs")
458− case o.JWTKey != "":
459− // The JWT Key is in the commandline argument
460− signKey, err := jwt.ParseRSAPrivateKeyFromPEM([]byte(o.JWTKey))
461− if err != nil {
462− msgs = append(msgs, "could not parse RSA Private Key PEM")
463− } else {
464− p.JWTKey = signKey
465− }
466− case o.JWTKeyFile != "":
467− // The JWT key is in the filesystem
468− keyData, err := ioutil.ReadFile(o.JWTKeyFile)
469− if err != nil {
470− msgs = append(msgs, "could not read key file: "+o.JWTKeyFile)
471− }
472− signKey, err := jwt.ParseRSAPrivateKeyFromPEM(keyData)
473− if err != nil {
474− msgs = append(msgs, "could not parse private key from PEM file:"+o.JWTKeyFile)
475− } else {
476− p.JWTKey = signKey
477− }
478− }
479− }
480− return msgs
481−}
482−
483−func parseSignatureKey(o *Options, msgs []string) []string {
484− if o.SignatureKey == "" {
485− return msgs
486− }
487−
488− components := strings.Split(o.SignatureKey, ":")
489− if len(components) != 2 {
490− return append(msgs, "invalid signature hash:key spec: "+
491− o.SignatureKey)
492− }
493−
494− algorithm, secretKey := components[0], components[1]
495− var hash crypto.Hash
496− var err error
497− if hash, err = hmacauth.DigestNameToCryptoHash(algorithm); err != nil {
498− return append(msgs, "unsupported signature hash algorithm: "+
499− o.SignatureKey)
500− }
501− o.signatureData = &SignatureData{hash: hash, key: secretKey}
502− return msgs
503−}
504−
505−// parseJwtIssuers takes in an array of strings in the form of issuer=audience
506−// and parses to an array of jwtIssuer structs.
507−func parseJwtIssuers(issuers []string, msgs []string) ([]jwtIssuer, []string) {
508− var parsedIssuers []jwtIssuer
509− for _, jwtVerifier := range issuers {
510− components := strings.Split(jwtVerifier, "=")
511− if len(components) < 2 {
512− msgs = append(msgs, fmt.Sprintf("invalid jwt verifier uri=audience spec: %s", jwtVerifier))
513− continue
514− }
515− uri, audience := components[0], strings.Join(components[1:], "=")
516− parsedIssuers = append(parsedIssuers, jwtIssuer{issuerURI: uri, audience: audience})
517− }
518− return parsedIssuers, msgs
519−}
520−
521−// newVerifierFromJwtIssuer takes in issuer information in jwtIssuer info and returns
522−// a verifier for that issuer.
523−func newVerifierFromJwtIssuer(jwtIssuer jwtIssuer) (*oidc.IDTokenVerifier, error) {
524− config := &oidc.Config{
525− ClientID: jwtIssuer.audience,
526− }
527− // Try as an OpenID Connect Provider first
528− var verifier *oidc.IDTokenVerifier
529− provider, err := oidc.NewProvider(context.Background(), jwtIssuer.issuerURI)
530− if err != nil {
531− // Try as JWKS URI
532− jwksURI := strings.TrimSuffix(jwtIssuer.issuerURI, "/") + "/.well-known/jwks.json"
533− _, err := http.NewRequest("GET", jwksURI, nil)
534− if err != nil {
535− return nil, err
536− }
537− verifier = oidc.NewVerifier(jwtIssuer.issuerURI, oidc.NewRemoteKeySet(context.Background(), jwksURI), config)
538− } else {
539− verifier = provider.Verifier(config)
540− }
541− return verifier, nil
542−}
543−
544−func validateCookieName(o *Options, msgs []string) []string {
545− cookie := &http.Cookie{Name: o.CookieName}
546− if cookie.String() == "" {
547− return append(msgs, fmt.Sprintf("invalid cookie name: %q", o.CookieName))
548− }
549− return msgs
550−}
551−
552−func addPadding(secret string) string {
553− padding := len(secret) % 4
554− switch padding {
555− case 1:
556− return secret + "==="
557− case 2:
558− return secret + "=="
559− case 3:
560− return secret + "="
561− default:
562− return secret
563− }
564−}
565−
566−// secretBytes attempts to base64 decode the secret, if that fails it treats the secret as binary
567−func secretBytes(secret string) []byte {
568− b, err := base64.URLEncoding.DecodeString(addPadding(secret))
569− if err == nil {
570− return []byte(addPadding(string(b)))
571− }
572− return []byte(secret)
573−}
574−
575−func setupLogger(o *Options, msgs []string) []string {
576− // Setup the log file
577− if len(o.LoggingFilename) > 0 {
578− // Validate that the file/dir can be written
579− file, err := os.OpenFile(o.LoggingFilename, os.O_WRONLY|os.O_CREATE, 0666)
580− if err != nil {
581− if os.IsPermission(err) {
582− return append(msgs, "unable to write to log file: "+o.LoggingFilename)
583− }
584− }
585− file.Close()
586−
587− logger.Printf("Redirecting logging to file: %s", o.LoggingFilename)
588−
589− logWriter := &lumberjack.Logger{
590− Filename: o.LoggingFilename,
591− MaxSize: o.LoggingMaxSize, // megabytes
592− MaxAge: o.LoggingMaxAge, // days
593− MaxBackups: o.LoggingMaxBackups,
594− LocalTime: o.LoggingLocalTime,
595− Compress: o.LoggingCompress,
596− }
597−
598− logger.SetOutput(logWriter)
599− }
600−
601− // Supply a sanity warning to the logger if all logging is disabled
602− if !o.StandardLogging && !o.AuthLogging && !o.RequestLogging {
603− logger.Print("Warning: Logging disabled. No further logs will be shown.")
604− }
605−
606− // Pass configuration values to the standard logger
607− logger.SetStandardEnabled(o.StandardLogging)
608− logger.SetAuthEnabled(o.AuthLogging)
609− logger.SetReqEnabled(o.RequestLogging)
610− logger.SetStandardTemplate(o.StandardLoggingFormat)
611− logger.SetAuthTemplate(o.AuthLoggingFormat)
612− logger.SetReqTemplate(o.RequestLoggingFormat)
613−
614− excludePaths := make([]string, 0)
615− excludePaths = append(excludePaths, strings.Split(o.ExcludeLoggingPaths, ",")...)
616− if o.SilencePingLogging {
617− excludePaths = append(excludePaths, o.PingPath)
618− }
619−
620− logger.SetExcludePaths(excludePaths)
621−
622− if !o.LoggingLocalTime {
623− logger.SetFlags(logger.Flags() | logger.LUTC)
624− }
625−
626− return msgs
627−}