git.iko.soy/typeable/oauth2-proxy
on master help sign in
182 lines 6.5 kB history raw
1package validation
3import (
4 "context"
5 "crypto/tls"
6 "fmt"
7 "net/http"
8 "net/url"
9 "strings"
11 "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/options"
12 "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/authentication/hmacauth"
13 "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/ip"
14 "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/logger"
15 internaloidc "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/providers/oidc"
16 "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/requests"
17 "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/util"
18 "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/util/ptr"
21// Validate checks that required options are set and validates those that they
22// are of the correct format
23func Validate(o *options.Options) error {
24 msgs := validateCookie(o.Cookie)
25 msgs = append(msgs, validateSessionCookieMinimal(o)...)
26 msgs = append(msgs, validateRedisSessionStore(o)...)
27 msgs = append(msgs, prefixValues("injectRequestHeaders: ", validateHeaders(o.InjectRequestHeaders)...)...)
28 msgs = append(msgs, prefixValues("injectResponseHeaders: ", validateHeaders(o.InjectResponseHeaders)...)...)
29 msgs = append(msgs, validateProviders(o)...)
30 msgs = append(msgs, validateAPIRoutes(o)...)
31 msgs = configureLogger(o.Logging, msgs)
32 msgs = parseSignatureKey(o, msgs)
34 if o.SSLInsecureSkipVerify {
35 transport := requests.DefaultTransport.(*http.Transport)
36 transport.TLSClientConfig = &tls.Config{InsecureSkipVerify: true} // #nosec G402 -- InsecureSkipVerify is a configurable option we allow
37 } else if len(o.Providers[0].CAFiles) > 0 {
38 pool, err := util.GetCertPool(o.Providers[0].CAFiles, ptr.Deref(o.Providers[0].UseSystemTrustStore, options.DefaultUseSystemTrustStore))
39 if err == nil {
40 transport := requests.DefaultTransport.(*http.Transport)
41 transport.TLSClientConfig = &tls.Config{
42 RootCAs: pool,
43 MinVersion: tls.VersionTLS12,
44 }
45 } else {
46 msgs = append(msgs, fmt.Sprintf("unable to load provider CA file(s): %v", err))
47 }
48 }
50 if o.AuthenticatedEmailsFile == "" && len(o.EmailDomains) == 0 && o.HtpasswdFile == "" {
51 msgs = append(msgs, "missing setting for email validation: email-domain or authenticated-emails-file required."+
52 "\n use email-domain=* to authorize all email addresses")
53 }
55 if o.SkipJwtBearerTokens {
56 // Configure extra issuers
57 if len(o.ExtraJwtIssuers) > 0 {
58 var jwtIssuers []jwtIssuer
59 jwtIssuers, msgs = parseJwtIssuers(o.ExtraJwtIssuers, msgs)
60 for _, jwtIssuer := range jwtIssuers {
61 verifier, err := newVerifierFromJwtIssuer(
62 o.Providers[0].OIDCConfig.AudienceClaims,
63 o.Providers[0].OIDCConfig.ExtraAudiences,
64 jwtIssuer,
65 )
66 if err != nil {
67 msgs = append(msgs, fmt.Sprintf("error building verifiers: %s", err))
68 }
69 o.SetJWTBearerVerifiers(append(o.GetJWTBearerVerifiers(), verifier))
70 }
71 }
72 }
74 var redirectURL *url.URL
75 redirectURL, msgs = parseURL(o.RawRedirectURL, "redirect", msgs)
76 o.SetRedirectURL(redirectURL)
77 if o.RawRedirectURL == "" && !o.Cookie.Secure && !o.ReverseProxy {
78 logger.Print("WARNING: no explicit redirect URL: redirects will default to insecure HTTP")
79 }
81 msgs = append(msgs, validateUpstreams(o.UpstreamServers)...)
83 if o.ReverseProxy {
84 parser, err := ip.GetRealClientIPParser(o.RealClientIPHeader)
85 if err != nil {
86 msgs = append(msgs, fmt.Sprintf("real_client_ip_header (%s) not accepted parameter value: %v", o.RealClientIPHeader, err))
87 }
88 o.SetRealClientIPParser(parser)
90 // Allow the logger to get client IPs
91 logger.SetGetClientFunc(func(r *http.Request) string {
92 return ip.GetClientString(o.GetRealClientIPParser(), r, false)
93 })
94 }
96 // Do this after ReverseProxy validation for TrustedIP coordinated checks
97 msgs = append(msgs, validateAllowlists(o)...)
99 if len(msgs) != 0 {
100 return fmt.Errorf("invalid configuration:\n %s",
101 strings.Join(msgs, "\n "))
102 }
103 return nil
106func parseSignatureKey(o *options.Options, msgs []string) []string {
107 if o.SignatureKey == "" {
108 return msgs
109 }
111 logger.Print("WARNING: `--signature-key` is deprecated. It will be removed in a future release")
113 components := strings.Split(o.SignatureKey, ":")
114 if len(components) != 2 {
115 return append(msgs, "invalid signature hash:key spec: "+
116 o.SignatureKey)
117 }
119 algorithm, secretKey := components[0], components[1]
120 hash, err := hmacauth.DigestNameToCryptoHash(algorithm)
121 if err != nil {
122 return append(msgs, "unsupported signature hash algorithm: "+o.SignatureKey)
123 }
124 o.SetSignatureData(&options.SignatureData{Hash: hash, Key: secretKey})
125 return msgs
128// parseJwtIssuers takes in an array of strings in the form of issuer=audience
129// and parses to an array of jwtIssuer structs.
130func parseJwtIssuers(issuers []string, msgs []string) ([]jwtIssuer, []string) {
131 parsedIssuers := make([]jwtIssuer, 0, len(issuers))
132 for _, jwtVerifier := range issuers {
133 components := strings.Split(jwtVerifier, "=")
134 if len(components) < 2 {
135 msgs = append(msgs, fmt.Sprintf("invalid jwt verifier uri=audience spec: %s", jwtVerifier))
136 continue
137 }
138 uri, audience := components[0], strings.Join(components[1:], "=")
139 parsedIssuers = append(parsedIssuers, jwtIssuer{issuerURI: uri, audience: audience})
140 }
141 return parsedIssuers, msgs
144// newVerifierFromJwtIssuer takes in issuer information in jwtIssuer info and returns
145// a verifier for that issuer.
146func newVerifierFromJwtIssuer(audienceClaims []string, extraAudiences []string, jwtIssuer jwtIssuer) (internaloidc.IDTokenVerifier, error) {
147 pvOpts := internaloidc.ProviderVerifierOptions{
148 AudienceClaims: audienceClaims,
149 ClientID: jwtIssuer.audience,
150 ExtraAudiences: extraAudiences,
151 IssuerURL: jwtIssuer.issuerURI,
152 }
154 pv, err := internaloidc.NewProviderVerifier(context.TODO(), pvOpts)
155 if err != nil {
156 // If the discovery didn't work, try again without discovery
157 pvOpts.JWKsURL = strings.TrimSuffix(jwtIssuer.issuerURI, "/") + "/.well-known/jwks.json"
158 pvOpts.SkipDiscovery = true
160 pv, err = internaloidc.NewProviderVerifier(context.TODO(), pvOpts)
161 if err != nil {
162 return nil, fmt.Errorf("could not construct provider verifier for JWT Issuer: %v", err)
163 }
164 }
166 return pv.Verifier(), nil
169// jwtIssuer hold parsed JWT issuer info that's used to construct a verifier.
170type jwtIssuer struct {
171 issuerURI string
172 audience string
175func parseURL(toParse string, urltype string, msgs []string) (*url.URL, []string) {
176 parsed, err := url.Parse(toParse)
177 if err != nil {
178 return nil, append(msgs, fmt.Sprintf(
179 "error parsing %s-url=%q %s", urltype, toParse, err))
180 }
181 return parsed, msgs