git.iko.soy/typeable/oauth2-proxy
on master help sign in
176 lines 5.3 kB history raw
1package validation
3import (
4 "fmt"
5 "os"
7 jose "github.com/go-jose/go-jose/v4"
8 "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/options"
9 "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/util/ptr"
12var supportedOIDCSigningAlgorithms = map[jose.SignatureAlgorithm]struct{}{
13 jose.EdDSA: {},
14 jose.HS256: {},
15 jose.HS384: {},
16 jose.HS512: {},
17 jose.RS256: {},
18 jose.RS384: {},
19 jose.RS512: {},
20 jose.ES256: {},
21 jose.ES384: {},
22 jose.ES512: {},
23 jose.PS256: {},
24 jose.PS384: {},
25 jose.PS512: {},
28// validateProviders is the initial validation migration for multiple providrers
29// It currently includes only logic that can verify the providers one by one and does not break the valdation pipe
30func validateProviders(o *options.Options) []string {
31 msgs := []string{}
33 // validate general multiple provider configuration
34 if len(o.Providers) == 0 {
35 msgs = append(msgs, "at least one provider has to be defined")
36 }
37 if o.SkipProviderButton && len(o.Providers) > 1 {
38 msgs = append(msgs, "SkipProviderButton and multiple providers are mutually exclusive")
39 }
41 providerIDs := make(map[string]struct{})
43 for _, provider := range o.Providers {
44 msgs = append(msgs, validateProvider(provider, providerIDs)...)
45 }
47 return msgs
50func validateProvider(provider options.Provider, providerIDs map[string]struct{}) []string {
51 msgs := []string{}
53 if provider.ID == "" {
54 msgs = append(msgs, "provider has empty id: ids are required for all providers")
55 }
57 // Ensure provider IDs are unique
58 if _, ok := providerIDs[provider.ID]; ok {
59 msgs = append(msgs, fmt.Sprintf("multiple providers found with id %s: provider ids must be unique", provider.ID))
60 }
61 providerIDs[provider.ID] = struct{}{}
63 if provider.ClientID == "" {
64 msgs = append(msgs, "provider missing setting: client-id")
65 }
67 if providerRequiresClientSecret(provider) {
68 msgs = append(msgs, validateClientSecret(provider)...)
69 }
71 if provider.Type == "google" {
72 msgs = append(msgs, validateGoogleConfig(provider)...)
73 }
75 if provider.Type == "entra-id" {
76 msgs = append(msgs, validateEntraConfig(provider)...)
77 }
79 msgs = append(msgs, validateOIDCSigningAlgorithms(provider)...)
81 return msgs
84func validateOIDCSigningAlgorithms(provider options.Provider) []string {
85 msgs := []string{}
87 for _, algorithm := range provider.OIDCConfig.EnabledSigningAlgs {
88 if _, ok := supportedOIDCSigningAlgorithms[jose.SignatureAlgorithm(algorithm)]; ok {
89 continue
90 }
92 msgs = append(msgs, fmt.Sprintf("provider %s has invalid EnabledSigningAlgs entry %q", provider.ID, algorithm))
93 }
95 return msgs
98// providerRequiresClientSecret checks if provider requires client secret to be set
99// or it can be omitted in favor of JWT token to authenticate oAuth client
100func providerRequiresClientSecret(provider options.Provider) bool {
101 if provider.Type == "entra-id" && ptr.Deref(provider.MicrosoftEntraIDConfig.FederatedTokenAuth, options.DefaultMicrosoftEntraIDUseFederatedToken) {
102 return false
103 }
105 if provider.Type == "login.gov" {
106 return false
107 }
109 return true
112func validateClientSecret(provider options.Provider) []string {
113 msgs := []string{}
115 if provider.ClientSecret == "" && provider.ClientSecretFile == "" {
116 msgs = append(msgs, "missing setting: client-secret or client-secret-file")
117 }
118 if provider.ClientSecret == "" && provider.ClientSecretFile != "" {
119 _, err := os.ReadFile(provider.ClientSecretFile)
120 if err != nil {
121 msgs = append(msgs, "could not read client secret file: "+provider.ClientSecretFile)
122 }
123 }
125 return msgs
128func validateGoogleConfig(provider options.Provider) []string {
129 msgs := []string{}
131 hasAdminEmail := provider.GoogleConfig.AdminEmail != ""
132 hasSAJSON := provider.GoogleConfig.ServiceAccountJSON != ""
133 useADC := ptr.Deref(provider.GoogleConfig.UseApplicationDefaultCredentials, options.DefaultUseApplicationDefaultCredentials)
135 if !hasAdminEmail && !hasSAJSON && !useADC {
136 return msgs
137 }
139 if !hasAdminEmail {
140 msgs = append(msgs, "missing setting: google-admin-email")
141 }
143 _, err := os.Stat(provider.GoogleConfig.ServiceAccountJSON)
144 if !useADC {
145 if !hasSAJSON {
146 msgs = append(msgs, "missing setting: google-service-account-json or google-use-application-default-credentials")
147 } else if err != nil {
148 msgs = append(msgs, fmt.Sprintf("Google credentials file not found: %s", provider.GoogleConfig.ServiceAccountJSON))
149 }
150 } else if hasSAJSON {
151 msgs = append(msgs, "invalid setting: can't use both google-service-account-json and google-use-application-default-credentials")
152 }
154 return msgs
157func validateEntraConfig(provider options.Provider) []string {
158 msgs := []string{}
160 if ptr.Deref(provider.MicrosoftEntraIDConfig.FederatedTokenAuth, options.DefaultMicrosoftEntraIDUseFederatedToken) {
161 federatedTokenPath := os.Getenv("AZURE_FEDERATED_TOKEN_FILE")
163 if federatedTokenPath == "" {
164 msgs = append(msgs, "entra federated token authentication is enabled, but AZURE_FEDERATED_TOKEN_FILE variable is not set, check your workload identity configuration.")
165 return msgs
166 }
168 // #nosec G703 -- AZURE_FEDERATED_TOKEN_FILE is set by the operator, not user input
169 _, err := os.Stat(federatedTokenPath)
170 if err != nil {
171 msgs = append(msgs, "could not read entra federated token file")
172 }
173 }
175 return msgs