config: support files for shared_secret, client_secret, cookie_secret and signing_key (#3453)

This commit is contained in:
Caleb Doxsey 2022-06-29 10:44:08 -06:00 committed by GitHub
parent 1eca93cc75
commit 86625a4ddb
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 136 additions and 34 deletions

View file

@ -23,7 +23,11 @@ func ValidateOptions(o *config.Options) error {
if _, err := cryptutil.NewAEADCipher(sharedKey); err != nil {
return fmt.Errorf("authenticate: 'SHARED_SECRET' invalid: %w", err)
}
if _, err := cryptutil.NewAEADCipherFromBase64(o.CookieSecret); err != nil {
cookieSecret, err := o.GetCookieSecret()
if err != nil {
return fmt.Errorf("authenticate: 'COOKIE_SECRET' invalid: %w", err)
}
if _, err := cryptutil.NewAEADCipher(cookieSecret); err != nil {
return fmt.Errorf("authenticate: 'COOKIE_SECRET' invalid %w", err)
}
if o.AuthenticateCallbackPath == "" {

View file

@ -19,7 +19,10 @@ func defaultGetIdentityProvider(options *config.Options, idpID string) (identity
}
redirectURL.Path = options.AuthenticateCallbackPath
idp := options.GetIdentityProviderForID(idpID)
idp, err := options.GetIdentityProviderForID(idpID)
if err != nil {
return nil, err
}
return identity.NewAuthenticator(oauth.Options{
RedirectURL: redirectURL,
ProviderName: idp.GetType(),

View file

@ -101,7 +101,7 @@ func newAuthenticateStateFromConfig(cfg *config.Config) (*authenticateState, err
}
// private state encoder setup, used to encrypt oauth2 tokens
state.cookieSecret, err = base64.StdEncoding.DecodeString(cfg.Options.CookieSecret)
state.cookieSecret, err = cfg.Options.GetCookieSecret()
if err != nil {
return nil, err
}
@ -131,7 +131,11 @@ func newAuthenticateStateFromConfig(cfg *config.Config) (*authenticateState, err
state.sessionStore = cookieStore
state.sessionLoaders = []sessions.SessionLoader{headerStore, cookieStore}
state.jwk = new(jose.JSONWebKeySet)
if cfg.Options.SigningKey != "" {
signingKey, err := cfg.Options.GetSigningKey()
if err != nil {
return nil, err
}
if signingKey != "" {
decodedCert, err := base64.StdEncoding.DecodeString(cfg.Options.SigningKey)
if err != nil {
return nil, fmt.Errorf("authenticate: failed to decode signing key: %w", err)

View file

@ -103,10 +103,15 @@ func newPolicyEvaluator(opts *config.Options, store *store.Store) (*evaluator.Ev
return nil, fmt.Errorf("authorize: invalid authenticate url: %w", err)
}
signingKey, err := opts.GetSigningKey()
if err != nil {
return nil, fmt.Errorf("authorize: invalid signing key: %w", err)
}
return evaluator.New(ctx, store,
evaluator.WithPolicies(opts.GetAllPolicies()),
evaluator.WithClientCA(clientCA),
evaluator.WithSigningKey(opts.SigningKey),
evaluator.WithSigningKey(signingKey),
evaluator.WithAuthenticateURL(authenticateURL.String()),
evaluator.WithGoogleCloudServerlessAuthenticationServiceAccount(opts.GetGoogleCloudServerlessAuthenticationServiceAccount()),
evaluator.WithJWTClaimsHeaders(opts.JWTClaimsHeaders),

View file

@ -166,7 +166,11 @@ func (a *Authorize) requireLoginResponse(
checkRequestURL.Scheme = "https"
q.Set(urlutil.QueryRedirectURI, checkRequestURL.String())
q.Set(urlutil.QueryIdentityProviderID, opts.GetIdentityProviderForPolicy(request.Policy).GetId())
idp, err := opts.GetIdentityProviderForPolicy(request.Policy)
if err != nil {
return nil, err
}
q.Set(urlutil.QueryIdentityProviderID, idp.GetId())
signinURL.RawQuery = q.Encode()
redirectTo := urlutil.NewSignedURL(state.sharedKey, signinURL).String()
@ -210,7 +214,11 @@ func (a *Authorize) requireWebAuthnResponse(
q.Set(urlutil.QueryDeviceType, webauthnutil.DefaultDeviceType)
}
q.Set(urlutil.QueryRedirectURI, checkRequestURL.String())
q.Set(urlutil.QueryIdentityProviderID, opts.GetIdentityProviderForPolicy(request.Policy).GetId())
idp, err := opts.GetIdentityProviderForPolicy(request.Policy)
if err != nil {
return nil, err
}
q.Set(urlutil.QueryIdentityProviderID, idp.GetId())
signinURL.RawQuery = q.Encode()
redirectTo := urlutil.NewSignedURL(state.sharedKey, signinURL).String()

View file

@ -222,14 +222,18 @@ func (src *FileWatcherSource) check(ctx context.Context, cfg *Config) {
cfg.Options.CertFile,
cfg.Options.ClientCAFile,
cfg.Options.ClientCRLFile,
cfg.Options.ClientSecretFile,
cfg.Options.CookieSecretFile,
cfg.Options.DataBrokerStorageCAFile,
cfg.Options.DataBrokerStorageCertFile,
cfg.Options.DataBrokerStorageCertKeyFile,
cfg.Options.KeyFile,
cfg.Options.PolicyFile,
cfg.Options.MetricsClientCAFile,
cfg.Options.MetricsCertificateFile,
cfg.Options.MetricsCertificateKeyFile,
cfg.Options.MetricsClientCAFile,
cfg.Options.PolicyFile,
cfg.Options.SharedSecretFile,
cfg.Options.SigningKeyFile,
}
for _, pair := range cfg.Options.CertificateFiles {

View file

@ -6,11 +6,14 @@ import (
// GetIdentityProviderForID returns the identity provider associated with the given IDP id.
// If none is found the default provider is returned.
func (o *Options) GetIdentityProviderForID(idpID string) *identity.Provider {
func (o *Options) GetIdentityProviderForID(idpID string) (*identity.Provider, error) {
for _, policy := range o.GetAllPolicies() {
idp := o.GetIdentityProviderForPolicy(&policy) //nolint
idp, err := o.GetIdentityProviderForPolicy(&policy) //nolint
if err != nil {
return nil, err
}
if idp.GetId() == idpID {
return idp
return idp, nil
}
}
@ -19,10 +22,15 @@ func (o *Options) GetIdentityProviderForID(idpID string) *identity.Provider {
// GetIdentityProviderForPolicy gets the identity provider associated with the given policy.
// If policy is nil, or changes none of the default settings, the default provider is returned.
func (o *Options) GetIdentityProviderForPolicy(policy *Policy) *identity.Provider {
func (o *Options) GetIdentityProviderForPolicy(policy *Policy) (*identity.Provider, error) {
clientSecret, err := o.GetClientSecret()
if err != nil {
return nil, err
}
idp := &identity.Provider{
ClientId: o.ClientID,
ClientSecret: o.ClientSecret,
ClientSecret: clientSecret,
Type: o.Provider,
Scopes: o.Scopes,
ServiceAccount: o.ServiceAccount,
@ -38,5 +46,5 @@ func (o *Options) GetIdentityProviderForPolicy(policy *Policy) *identity.Provide
}
}
idp.Id = idp.Hash()
return idp
return idp, nil
}

View file

@ -71,7 +71,8 @@ type Options struct {
// SharedKey is the shared secret authorization key used to mutually authenticate
// requests between services.
SharedKey string `mapstructure:"shared_secret" yaml:"shared_secret,omitempty"`
SharedKey string `mapstructure:"shared_secret" yaml:"shared_secret,omitempty"`
SharedSecretFile string `mapstructure:"shared_secret_file" yaml:"shared_secret_file,omitempty"`
// Services is a list enabled service mode. If none are selected, "all" is used.
// Available options are : "all", "authenticate", "proxy".
@ -132,21 +133,23 @@ type Options struct {
// Session/Cookie management
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie
CookieName string `mapstructure:"cookie_name" yaml:"cookie_name,omitempty"`
CookieSecret string `mapstructure:"cookie_secret" yaml:"cookie_secret,omitempty"`
CookieDomain string `mapstructure:"cookie_domain" yaml:"cookie_domain,omitempty"`
CookieSecure bool `mapstructure:"cookie_secure" yaml:"cookie_secure,omitempty"`
CookieHTTPOnly bool `mapstructure:"cookie_http_only" yaml:"cookie_http_only,omitempty"`
CookieExpire time.Duration `mapstructure:"cookie_expire" yaml:"cookie_expire,omitempty"`
CookieName string `mapstructure:"cookie_name" yaml:"cookie_name,omitempty"`
CookieSecret string `mapstructure:"cookie_secret" yaml:"cookie_secret,omitempty"`
CookieSecretFile string `mapstructure:"cookie_secret_file" yaml:"cookie_secret_file,omitempty"`
CookieDomain string `mapstructure:"cookie_domain" yaml:"cookie_domain,omitempty"`
CookieSecure bool `mapstructure:"cookie_secure" yaml:"cookie_secure,omitempty"`
CookieHTTPOnly bool `mapstructure:"cookie_http_only" yaml:"cookie_http_only,omitempty"`
CookieExpire time.Duration `mapstructure:"cookie_expire" yaml:"cookie_expire,omitempty"`
// Identity provider configuration variables as specified by RFC6749
// https://openid.net/specs/openid-connect-basic-1_0.html#RFC6749
ClientID string `mapstructure:"idp_client_id" yaml:"idp_client_id,omitempty"`
ClientSecret string `mapstructure:"idp_client_secret" yaml:"idp_client_secret,omitempty"`
Provider string `mapstructure:"idp_provider" yaml:"idp_provider,omitempty"`
ProviderURL string `mapstructure:"idp_provider_url" yaml:"idp_provider_url,omitempty"`
Scopes []string `mapstructure:"idp_scopes" yaml:"idp_scopes,omitempty"`
ServiceAccount string `mapstructure:"idp_service_account" yaml:"idp_service_account,omitempty"`
ClientID string `mapstructure:"idp_client_id" yaml:"idp_client_id,omitempty"`
ClientSecret string `mapstructure:"idp_client_secret" yaml:"idp_client_secret,omitempty"`
ClientSecretFile string `mapstructure:"idp_client_secret_file" yaml:"idp_client_secret_file,omitempty"`
Provider string `mapstructure:"idp_provider" yaml:"idp_provider,omitempty"`
ProviderURL string `mapstructure:"idp_provider_url" yaml:"idp_provider_url,omitempty"`
Scopes []string `mapstructure:"idp_scopes" yaml:"idp_scopes,omitempty"`
ServiceAccount string `mapstructure:"idp_service_account" yaml:"idp_service_account,omitempty"`
// Identity provider refresh directory interval/timeout settings.
RefreshDirectoryTimeout time.Duration `mapstructure:"idp_refresh_directory_timeout" yaml:"idp_refresh_directory_timeout,omitempty"`
RefreshDirectoryInterval time.Duration `mapstructure:"idp_refresh_directory_interval" yaml:"idp_refresh_directory_interval,omitempty"`
@ -173,7 +176,8 @@ type Options struct {
// SigningKey is the private key used to add a JWT-signature to upstream requests.
// https://www.pomerium.com/docs/topics/getting-users-identity.html
SigningKey string `mapstructure:"signing_key" yaml:"signing_key,omitempty"`
SigningKey string `mapstructure:"signing_key" yaml:"signing_key,omitempty"`
SigningKeyFile string `mapstructure:"signing_key_file" yaml:"signing_key_file,omitempty"`
HeadersEnv string `yaml:",omitempty"`
// SetResponseHeaders to set on all proxied requests. Add a 'disable' key map to turn off.
@ -895,12 +899,16 @@ func (o *Options) GetOauthOptions() (oauth.Options, error) {
redirectURL = redirectURL.ResolveReference(&url.URL{
Path: o.AuthenticateCallbackPath,
})
clientSecret, err := o.GetClientSecret()
if err != nil {
return oauth.Options{}, err
}
return oauth.Options{
RedirectURL: redirectURL,
ProviderName: o.Provider,
ProviderURL: o.ProviderURL,
ClientID: o.ClientID,
ClientSecret: o.ClientSecret,
ClientSecret: clientSecret,
Scopes: o.Scopes,
ServiceAccount: o.ServiceAccount,
}, nil
@ -991,6 +999,13 @@ func (o *Options) GetCertificates() ([]tls.Certificate, error) {
// GetSharedKey gets the decoded shared key.
func (o *Options) GetSharedKey() ([]byte, error) {
sharedKey := o.SharedKey
if o.SharedSecretFile != "" {
bs, err := os.ReadFile(o.SharedSecretFile)
if err != nil {
return nil, err
}
sharedKey = string(bs)
}
// mutual auth between services on the same host can be generated at runtime
if IsAll(o.Services) && o.SharedKey == "" && o.DataBrokerStorageType == StorageInMemoryName {
sharedKey = randomSharedKey
@ -1174,6 +1189,49 @@ func (o *Options) GetAllRouteableHTTPDomainsForTLSServerName(tlsServerName strin
return domains.ToSlice(), nil
}
// GetClientSecret gets the client secret.
func (o *Options) GetClientSecret() (string, error) {
if o == nil {
return "", nil
}
if o.ClientSecretFile != "" {
bs, err := os.ReadFile(o.ClientSecretFile)
if err != nil {
return "", err
}
return string(bs), nil
}
return o.ClientSecret, nil
}
// GetCookieSecret gets the decoded cookie secret.
func (o *Options) GetCookieSecret() ([]byte, error) {
cookieSecret := o.CookieSecret
if o.CookieSecretFile != "" {
bs, err := os.ReadFile(o.CookieSecretFile)
if err != nil {
return nil, err
}
cookieSecret = string(bs)
}
return base64.StdEncoding.DecodeString(cookieSecret)
}
// GetSigningKey gets the signing key.
func (o *Options) GetSigningKey() (string, error) {
if o == nil {
return "", nil
}
if o.SigningKeyFile != "" {
bs, err := os.ReadFile(o.SigningKeyFile)
if err != nil {
return "", err
}
return string(bs), nil
}
return o.SigningKey, nil
}
// Checksum returns the checksum of the current options struct
func (o *Options) Checksum() uint64 {
return hashutil.MustHash(o)

View file

@ -158,13 +158,18 @@ func (c *DataBroker) update(ctx context.Context, cfg *config.Config) error {
return fmt.Errorf("databroker: invalid oauth options: %w", err)
}
clientSecret, err := cfg.Options.GetClientSecret()
if err != nil {
return fmt.Errorf("databroker: error retrieving IPD client secret: %w", err)
}
directoryProvider := directory.GetProvider(directory.Options{
ServiceAccount: cfg.Options.ServiceAccount,
Provider: cfg.Options.Provider,
ProviderURL: cfg.Options.ProviderURL,
QPS: cfg.Options.GetQPS(),
ClientID: cfg.Options.ClientID,
ClientSecret: cfg.Options.ClientSecret,
ClientSecret: clientSecret,
})
c.mu.Lock()
c.directoryProvider = directoryProvider

View file

@ -38,7 +38,11 @@ func ValidateOptions(o *config.Options) error {
return fmt.Errorf("proxy: invalid 'SHARED_SECRET': %w", err)
}
if _, err := cryptutil.NewAEADCipherFromBase64(o.CookieSecret); err != nil {
cookieSecret, err := o.GetCookieSecret()
if err != nil {
return fmt.Errorf("proxy: invalid 'COOKIE_SECRET': %w", err)
}
if _, err := cryptutil.NewAEADCipher(cookieSecret); err != nil {
return fmt.Errorf("proxy: invalid 'COOKIE_SECRET': %w", err)
}

View file

@ -2,7 +2,6 @@ package proxy
import (
"crypto/cipher"
"encoding/base64"
"net/url"
"sync/atomic"
@ -51,7 +50,7 @@ func newProxyStateFromConfig(cfg *config.Config) (*proxyState, error) {
return nil, err
}
state.cookieSecret, err = base64.StdEncoding.DecodeString(cfg.Options.CookieSecret)
state.cookieSecret, err = cfg.Options.GetCookieSecret()
if err != nil {
return nil, err
}