mirror of
https://github.com/pomerium/pomerium.git
synced 2025-05-12 08:37:38 +02:00
config: support files for shared_secret, client_secret, cookie_secret and signing_key (#3453)
This commit is contained in:
parent
1eca93cc75
commit
86625a4ddb
11 changed files with 136 additions and 34 deletions
|
@ -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 == "" {
|
||||
|
|
|
@ -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(),
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -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()
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue