mirror of
https://github.com/project-zot/zot.git
synced 2024-12-16 21:56:37 -05:00
fix(authn): make hashing/encryption keys used to secure cookies (#2536)
fix(authn): configurable hashing/encryption keys used to secure cookies If they are not configured zot will generate a random hashing key at startup, invalidating all cookies if zot is restarted. closes: #2526 Signed-off-by: Petu Eusebiu <peusebiu@cisco.com>
This commit is contained in:
parent
17dbb56ea1
commit
b461619682
11 changed files with 219 additions and 71 deletions
|
@ -370,6 +370,36 @@ Using that cookie on subsequent calls will authenticate them, asumming the cooki
|
|||
In case of using filesystem storage sessions are saved in zot's root directory.
|
||||
In case of using cloud storage sessions are saved in memory.
|
||||
|
||||
|
||||
### Securing session based login
|
||||
|
||||
In order to secure session cookies used in session based authentication process you need to set the path to a file containg keys used to hash and encrypt the cookies:
|
||||
|
||||
`sessionKeysFile`
|
||||
|
||||
```
|
||||
"auth": {
|
||||
"htpasswd": {
|
||||
"path": "test/data/htpasswd"
|
||||
},
|
||||
"sessionKeysFile": "/home/user/keys",
|
||||
"apikey": true,
|
||||
}
|
||||
```
|
||||
|
||||
```
|
||||
user@host:~/zot$ cat ../keys | jq
|
||||
{
|
||||
"hashKey": "my-very-secret",
|
||||
"encryptKey": "another-secret"
|
||||
}
|
||||
```
|
||||
|
||||
- hashKey - used to authenticate the cookie value using HMAC. It is recommended to use a key with exactly 32 or 64 bytes.
|
||||
- encryptKey - this is optional, used to encrypt the cookie value. If set, the length must correspond to the block size of the encryption algorithm. For AES, used by default, valid lengths are 16, 24, or 32 bytes to select AES-128, AES-192, or AES-256.
|
||||
|
||||
If at least hashKey is not set zot will create a random one which on zot restarts it will invalidate all currently valid cookies and their sessions, requiring all users to login again.
|
||||
|
||||
#### API keys
|
||||
|
||||
zot allows authentication for REST API calls using your API key as an alternative to your password.
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
"htpasswd": {
|
||||
"path": "test/data/htpasswd"
|
||||
},
|
||||
"sessionKeysFile": "examples/sessionKeys.json",
|
||||
"apikey": true,
|
||||
"openid": {
|
||||
"providers": {
|
||||
|
|
4
examples/sessionKeys.json
Normal file
4
examples/sessionKeys.json
Normal file
|
@ -0,0 +1,4 @@
|
|||
{
|
||||
"hashKey": "3lrioGLGO2RfG9Y7HQGgWa3ayBjMLw2auMXqEWcSXjQKc9SoQ3fKTIbO+toPYa7e",
|
||||
"encryptKey": "KOzt01JrDz2uC//UBC5ZikxQw4owfmI8"
|
||||
}
|
|
@ -22,7 +22,6 @@ import (
|
|||
"github.com/google/go-github/v52/github"
|
||||
"github.com/google/uuid"
|
||||
"github.com/gorilla/mux"
|
||||
"github.com/gorilla/securecookie"
|
||||
"github.com/gorilla/sessions"
|
||||
godigest "github.com/opencontainers/go-digest"
|
||||
"github.com/zitadel/oidc/v3/pkg/client/rp"
|
||||
|
@ -334,10 +333,12 @@ func (amw *AuthnMiddleware) tryAuthnHandlers(ctlr *Controller) mux.MiddlewareFun
|
|||
|
||||
for provider := range ctlr.Config.HTTP.Auth.OpenID.Providers {
|
||||
if config.IsOpenIDSupported(provider) {
|
||||
rp := NewRelyingPartyOIDC(context.TODO(), ctlr.Config, provider, ctlr.Log)
|
||||
rp := NewRelyingPartyOIDC(context.TODO(), ctlr.Config, provider, ctlr.Config.HTTP.Auth.SessionHashKey,
|
||||
ctlr.Config.HTTP.Auth.SessionEncryptKey, ctlr.Log)
|
||||
ctlr.RelyingParties[provider] = rp
|
||||
} else if config.IsOauth2Supported(provider) {
|
||||
rp := NewRelyingPartyGithub(ctlr.Config, provider, ctlr.Log)
|
||||
rp := NewRelyingPartyGithub(ctlr.Config, provider, ctlr.Config.HTTP.Auth.SessionHashKey,
|
||||
ctlr.Config.HTTP.Auth.SessionEncryptKey, ctlr.Log)
|
||||
ctlr.RelyingParties[provider] = rp
|
||||
}
|
||||
}
|
||||
|
@ -610,20 +611,25 @@ func (rh *RouteHandler) AuthURLHandler() http.HandlerFunc {
|
|||
}
|
||||
}
|
||||
|
||||
func NewRelyingPartyOIDC(ctx context.Context, config *config.Config, provider string, log log.Logger) rp.RelyingParty {
|
||||
issuer, clientID, clientSecret, redirectURI, scopes, options := getRelyingPartyArgs(config, provider, log)
|
||||
func NewRelyingPartyOIDC(ctx context.Context, config *config.Config, provider string,
|
||||
hashKey, encryptKey []byte, log log.Logger,
|
||||
) rp.RelyingParty {
|
||||
issuer, clientID, clientSecret, redirectURI, scopes, options := getRelyingPartyArgs(config,
|
||||
provider, hashKey, encryptKey, log)
|
||||
|
||||
relyingParty, err := rp.NewRelyingPartyOIDC(ctx, issuer, clientID, clientSecret, redirectURI, scopes, options...)
|
||||
if err != nil {
|
||||
log.Panic().Err(err).Str("issuer", issuer).Str("redirectURI", redirectURI).Strs("scopes", scopes).
|
||||
Msg("failed to get new relying party oicd")
|
||||
Msg("failed to initialize new relying party oidc")
|
||||
}
|
||||
|
||||
return relyingParty
|
||||
}
|
||||
|
||||
func NewRelyingPartyGithub(config *config.Config, provider string, log log.Logger) rp.RelyingParty {
|
||||
_, clientID, clientSecret, redirectURI, scopes, options := getRelyingPartyArgs(config, provider, log)
|
||||
func NewRelyingPartyGithub(config *config.Config, provider string, hashKey, encryptKey []byte, log log.Logger,
|
||||
) rp.RelyingParty {
|
||||
_, clientID, clientSecret, redirectURI, scopes,
|
||||
options := getRelyingPartyArgs(config, provider, hashKey, encryptKey, log)
|
||||
|
||||
rpConfig := &oauth2.Config{
|
||||
ClientID: clientID,
|
||||
|
@ -636,13 +642,13 @@ func NewRelyingPartyGithub(config *config.Config, provider string, log log.Logge
|
|||
relyingParty, err := rp.NewRelyingPartyOAuth(rpConfig, options...)
|
||||
if err != nil {
|
||||
log.Panic().Err(err).Str("redirectURI", redirectURI).Strs("scopes", scopes).
|
||||
Msg("failed to get new relying party oauth")
|
||||
Msg("failed to initialize new relying party oauth")
|
||||
}
|
||||
|
||||
return relyingParty
|
||||
}
|
||||
|
||||
func getRelyingPartyArgs(cfg *config.Config, provider string, log log.Logger) (
|
||||
func getRelyingPartyArgs(cfg *config.Config, provider string, hashKey, encryptKey []byte, log log.Logger) (
|
||||
string, string, string, string, []string, []rp.Option,
|
||||
) {
|
||||
if _, ok := cfg.HTTP.Auth.OpenID.Providers[provider]; !ok {
|
||||
|
@ -683,9 +689,8 @@ func getRelyingPartyArgs(cfg *config.Config, provider string, log log.Logger) (
|
|||
rp.WithVerifierOpts(rp.WithIssuedAtOffset(issuedAtOffset)),
|
||||
}
|
||||
|
||||
key := securecookie.GenerateRandomKey(32) //nolint:mnd
|
||||
cookieHandler := httphelper.NewCookieHandler(hashKey, encryptKey, httphelper.WithMaxAge(relyingPartyCookieMaxAge))
|
||||
|
||||
cookieHandler := httphelper.NewCookieHandler(key, key, httphelper.WithMaxAge(relyingPartyCookieMaxAge))
|
||||
options = append(options, rp.WithCookieHandler(cookieHandler))
|
||||
|
||||
if clientSecret == "" {
|
||||
|
@ -839,14 +844,14 @@ func OAuth2Callback(ctlr *Controller, w http.ResponseWriter, r *http.Request, st
|
|||
stateOrigin, ok := stateCookie.Values["state"].(string)
|
||||
if !ok {
|
||||
ctlr.Log.Error().Err(zerr.ErrInvalidStateCookie).Str("component", "openID").
|
||||
Msg(": failed to get 'state' cookie from request")
|
||||
Msg("failed to get 'state' cookie from request")
|
||||
|
||||
return "", zerr.ErrInvalidStateCookie
|
||||
}
|
||||
|
||||
if stateOrigin != state {
|
||||
ctlr.Log.Error().Err(zerr.ErrInvalidStateCookie).Str("component", "openID").
|
||||
Msg(": 'state' cookie differs from the actual one")
|
||||
Msg("'state' cookie differs from the actual one")
|
||||
|
||||
return "", zerr.ErrInvalidStateCookie
|
||||
}
|
||||
|
|
|
@ -960,7 +960,7 @@ func TestCookiestoreCleanup(t *testing.T) {
|
|||
DefaultStore: imgStore,
|
||||
}
|
||||
|
||||
cookieStore, err := api.NewCookieStore(storeController)
|
||||
cookieStore, err := api.NewCookieStore(storeController, nil, nil)
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
cookieStore.RunSessionCleaner(taskScheduler)
|
||||
|
@ -995,7 +995,7 @@ func TestCookiestoreCleanup(t *testing.T) {
|
|||
DefaultStore: imgStore,
|
||||
}
|
||||
|
||||
cookieStore, err := api.NewCookieStore(storeController)
|
||||
cookieStore, err := api.NewCookieStore(storeController, []byte("secret"), nil)
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
err = os.Chmod(rootDir, 0o000)
|
||||
|
|
|
@ -67,12 +67,15 @@ type AuthHTPasswd struct {
|
|||
}
|
||||
|
||||
type AuthConfig struct {
|
||||
FailDelay int
|
||||
HTPasswd AuthHTPasswd
|
||||
LDAP *LDAPConfig
|
||||
Bearer *BearerConfig
|
||||
OpenID *OpenIDConfig
|
||||
APIKey bool
|
||||
FailDelay int
|
||||
HTPasswd AuthHTPasswd
|
||||
LDAP *LDAPConfig
|
||||
Bearer *BearerConfig
|
||||
OpenID *OpenIDConfig
|
||||
APIKey bool
|
||||
SessionKeysFile string
|
||||
SessionHashKey []byte `json:"-"`
|
||||
SessionEncryptKey []byte `json:"-"`
|
||||
}
|
||||
|
||||
type BearerConfig struct {
|
||||
|
@ -81,6 +84,11 @@ type BearerConfig struct {
|
|||
Cert string
|
||||
}
|
||||
|
||||
type SessionKeys struct {
|
||||
HashKey string
|
||||
EncryptKey string `mapstructure:",omitempty"`
|
||||
}
|
||||
|
||||
type OpenIDConfig struct {
|
||||
Providers map[string]OpenIDProviderConfig
|
||||
}
|
||||
|
|
|
@ -15,6 +15,7 @@ import (
|
|||
"time"
|
||||
|
||||
"github.com/gorilla/mux"
|
||||
"github.com/gorilla/securecookie"
|
||||
"github.com/zitadel/oidc/v3/pkg/client/rp"
|
||||
|
||||
"zotregistry.dev/zot/errors"
|
||||
|
@ -308,7 +309,14 @@ func (c *Controller) InitImageStore() error {
|
|||
func (c *Controller) initCookieStore() error {
|
||||
// setup sessions cookie store used to preserve logged in user in web sessions
|
||||
if c.Config.IsBasicAuthnEnabled() {
|
||||
cookieStore, err := NewCookieStore(c.StoreController)
|
||||
if c.Config.HTTP.Auth.SessionHashKey == nil {
|
||||
c.Log.Warn().Msg("hashKey is not set in config, generating a random one")
|
||||
|
||||
c.Config.HTTP.Auth.SessionHashKey = securecookie.GenerateRandomKey(64) //nolint: gomnd
|
||||
}
|
||||
|
||||
cookieStore, err := NewCookieStore(c.StoreController, c.Config.HTTP.Auth.SessionHashKey,
|
||||
c.Config.HTTP.Auth.SessionEncryptKey)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -2272,7 +2272,7 @@ func TestAuthnErrors(t *testing.T) {
|
|||
}
|
||||
|
||||
So(func() {
|
||||
api.NewRelyingPartyGithub(conf, "prov", log.NewLogger("debug", ""))
|
||||
api.NewRelyingPartyGithub(conf, "prov", nil, nil, log.NewLogger("debug", ""))
|
||||
}, ShouldPanic)
|
||||
|
||||
err = os.Chmod(tmpFile, 0o644)
|
||||
|
@ -4099,7 +4099,7 @@ func TestNewRelyingPartyOIDC(t *testing.T) {
|
|||
}
|
||||
|
||||
Convey("provider not found in config", func() {
|
||||
So(func() { _ = api.NewRelyingPartyOIDC(ctx, conf, "notDex", log.NewLogger("debug", "")) }, ShouldPanic)
|
||||
So(func() { _ = api.NewRelyingPartyOIDC(ctx, conf, "notDex", nil, nil, log.NewLogger("debug", "")) }, ShouldPanic)
|
||||
})
|
||||
|
||||
Convey("key path not found on disk", func() {
|
||||
|
@ -4107,7 +4107,7 @@ func TestNewRelyingPartyOIDC(t *testing.T) {
|
|||
oidcProviderCfg.KeyPath = "path/to/file"
|
||||
conf.HTTP.Auth.OpenID.Providers["oidc"] = oidcProviderCfg
|
||||
|
||||
So(func() { _ = api.NewRelyingPartyOIDC(ctx, conf, "oidc", log.NewLogger("debug", "")) }, ShouldPanic)
|
||||
So(func() { _ = api.NewRelyingPartyOIDC(ctx, conf, "oidc", nil, nil, log.NewLogger("debug", "")) }, ShouldPanic)
|
||||
})
|
||||
|
||||
Convey("https callback", func() {
|
||||
|
@ -4116,7 +4116,7 @@ func TestNewRelyingPartyOIDC(t *testing.T) {
|
|||
Key: ServerKey,
|
||||
}
|
||||
|
||||
rp := api.NewRelyingPartyOIDC(ctx, conf, "oidc", log.NewLogger("debug", ""))
|
||||
rp := api.NewRelyingPartyOIDC(ctx, conf, "oidc", nil, nil, log.NewLogger("debug", ""))
|
||||
So(rp, ShouldNotBeNil)
|
||||
})
|
||||
|
||||
|
@ -4125,7 +4125,7 @@ func TestNewRelyingPartyOIDC(t *testing.T) {
|
|||
oidcProvider.ClientSecret = ""
|
||||
conf.HTTP.Auth.OpenID.Providers["oidc"] = oidcProvider
|
||||
|
||||
rp := api.NewRelyingPartyOIDC(ctx, conf, "oidc", log.NewLogger("debug", ""))
|
||||
rp := api.NewRelyingPartyOIDC(ctx, conf, "oidc", nil, nil, log.NewLogger("debug", ""))
|
||||
So(rp, ShouldNotBeNil)
|
||||
})
|
||||
|
||||
|
@ -4134,7 +4134,7 @@ func TestNewRelyingPartyOIDC(t *testing.T) {
|
|||
oidcProvider.Issuer = ""
|
||||
conf.HTTP.Auth.OpenID.Providers["oidc"] = oidcProvider
|
||||
|
||||
So(func() { _ = api.NewRelyingPartyOIDC(ctx, conf, "oidc", log.NewLogger("debug", "")) }, ShouldPanic)
|
||||
So(func() { _ = api.NewRelyingPartyOIDC(ctx, conf, "oidc", nil, nil, log.NewLogger("debug", "")) }, ShouldPanic)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
@ -4148,9 +4148,10 @@ func TestOpenIDMiddleware(t *testing.T) {
|
|||
conf.HTTP.Port = port
|
||||
|
||||
testCases := []struct {
|
||||
testCaseName string
|
||||
address string
|
||||
externalURL string
|
||||
testCaseName string
|
||||
address string
|
||||
externalURL string
|
||||
useSessionKeys bool
|
||||
}{
|
||||
{
|
||||
address: "0.0.0.0",
|
||||
|
@ -4162,6 +4163,12 @@ func TestOpenIDMiddleware(t *testing.T) {
|
|||
externalURL: "",
|
||||
testCaseName: "without ExternalURL provided in config",
|
||||
},
|
||||
{
|
||||
address: "127.0.0.1",
|
||||
externalURL: "",
|
||||
testCaseName: "without ExternalURL provided in config and session keys for cookies",
|
||||
useSessionKeys: true,
|
||||
},
|
||||
}
|
||||
|
||||
// need a username different than ldap one, to test both logic
|
||||
|
@ -4246,6 +4253,11 @@ func TestOpenIDMiddleware(t *testing.T) {
|
|||
|
||||
for _, testcase := range testCases {
|
||||
t.Run(testcase.testCaseName, func(t *testing.T) {
|
||||
if testcase.useSessionKeys {
|
||||
ctlr.Config.HTTP.Auth.SessionHashKey = []byte("3lrioGLGO2RfG9Y7HQGgWa3ayBjMLw2auMXqEWcSXjQKc9SoQ3fKTIbO+toPYa7e")
|
||||
ctlr.Config.HTTP.Auth.SessionEncryptKey = []byte("KOzt01JrDz2uC//UBC5ZikxQw4owfmI8")
|
||||
}
|
||||
|
||||
Convey("make controller", t, func() {
|
||||
dir := t.TempDir()
|
||||
|
||||
|
|
|
@ -11,10 +11,8 @@ import (
|
|||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/gorilla/securecookie"
|
||||
"github.com/gorilla/sessions"
|
||||
|
||||
zerr "zotregistry.dev/zot/errors"
|
||||
"zotregistry.dev/zot/pkg/scheduler"
|
||||
"zotregistry.dev/zot/pkg/storage"
|
||||
storageConstants "zotregistry.dev/zot/pkg/storage/constants"
|
||||
|
@ -38,16 +36,11 @@ func (c *CookieStore) RunSessionCleaner(sch *scheduler.Scheduler) {
|
|||
}
|
||||
}
|
||||
|
||||
func NewCookieStore(storeController storage.StoreController) (*CookieStore, error) {
|
||||
func NewCookieStore(storeController storage.StoreController, hashKey, encryptKey []byte) (*CookieStore, error) {
|
||||
// To store custom types in our cookies
|
||||
// we must first register them using gob.Register
|
||||
gob.Register(map[string]interface{}{})
|
||||
|
||||
hashKey, err := getHashKey()
|
||||
if err != nil {
|
||||
return &CookieStore{}, err
|
||||
}
|
||||
|
||||
var store sessions.Store
|
||||
|
||||
var sessionsDir string
|
||||
|
@ -60,14 +53,14 @@ func NewCookieStore(storeController storage.StoreController) (*CookieStore, erro
|
|||
return &CookieStore{}, err
|
||||
}
|
||||
|
||||
localStore := sessions.NewFilesystemStore(sessionsDir, hashKey)
|
||||
localStore := sessions.NewFilesystemStore(sessionsDir, hashKey, encryptKey)
|
||||
|
||||
localStore.MaxAge(cookiesMaxAge)
|
||||
|
||||
store = localStore
|
||||
needsCleanup = true
|
||||
} else {
|
||||
memStore := sessions.NewCookieStore(hashKey)
|
||||
memStore := sessions.NewCookieStore(hashKey, encryptKey)
|
||||
|
||||
memStore.MaxAge(cookiesMaxAge)
|
||||
|
||||
|
@ -81,15 +74,6 @@ func NewCookieStore(storeController storage.StoreController) (*CookieStore, erro
|
|||
}, nil
|
||||
}
|
||||
|
||||
func getHashKey() ([]byte, error) {
|
||||
hashKey := securecookie.GenerateRandomKey(64)
|
||||
if hashKey == nil {
|
||||
return nil, zerr.ErrHashKeyNotCreated
|
||||
}
|
||||
|
||||
return hashKey, nil
|
||||
}
|
||||
|
||||
func IsExpiredSession(dirEntry fs.DirEntry) bool {
|
||||
fileInfo, err := dirEntry.Info()
|
||||
if err != nil { // may have been deleted in the meantime
|
||||
|
|
|
@ -839,6 +839,12 @@ func LoadConfiguration(config *config.Config, configPath string) error {
|
|||
return err
|
||||
}
|
||||
|
||||
if err := loadSessionKeys(config); err != nil {
|
||||
log.Error().Err(err).Msg("failed to read sessionKeysFile")
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// defaults
|
||||
applyDefaultValues(config, viperInstance, log)
|
||||
|
||||
|
@ -853,6 +859,26 @@ func LoadConfiguration(config *config.Config, configPath string) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func loadSessionKeys(conf *config.Config) error {
|
||||
if conf.HTTP.Auth != nil && conf.HTTP.Auth.SessionKeysFile != "" {
|
||||
var sessionKeys config.SessionKeys
|
||||
|
||||
if err := readSecretFile(conf.HTTP.Auth.SessionKeysFile, &sessionKeys, false); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if sessionKeys.HashKey != "" {
|
||||
conf.HTTP.Auth.SessionHashKey = []byte(sessionKeys.HashKey)
|
||||
}
|
||||
|
||||
if sessionKeys.EncryptKey != "" {
|
||||
conf.HTTP.Auth.SessionEncryptKey = []byte(sessionKeys.EncryptKey)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func updateLDAPConfig(conf *config.Config) error {
|
||||
if conf.HTTP.Auth == nil || conf.HTTP.Auth.LDAP == nil {
|
||||
return nil
|
||||
|
@ -864,8 +890,9 @@ func updateLDAPConfig(conf *config.Config) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
newLDAPCredentials, err := readLDAPCredentials(conf.HTTP.Auth.LDAP.CredentialsFile)
|
||||
if err != nil {
|
||||
var newLDAPCredentials config.LDAPCredentials
|
||||
|
||||
if err := readSecretFile(conf.HTTP.Auth.LDAP.CredentialsFile, &newLDAPCredentials, true); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
@ -875,48 +902,46 @@ func updateLDAPConfig(conf *config.Config) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func readLDAPCredentials(ldapConfigPath string) (config.LDAPCredentials, error) {
|
||||
func readSecretFile(path string, v any, checkUnsetFields bool) error { //nolint: varnamelen
|
||||
viperInstance := viper.NewWithOptions(viper.KeyDelimiter("::"))
|
||||
|
||||
viperInstance.SetConfigFile(ldapConfigPath)
|
||||
viperInstance.SetConfigFile(path)
|
||||
|
||||
if err := viperInstance.ReadInConfig(); err != nil {
|
||||
log.Error().Err(err).Msg("failed to read configuration")
|
||||
log.Error().Err(err).Str("path", path).Msg("failed to read secret file configuration")
|
||||
|
||||
return config.LDAPCredentials{}, errors.Join(zerr.ErrBadConfig, err)
|
||||
return errors.Join(zerr.ErrBadConfig, err)
|
||||
}
|
||||
|
||||
var ldapCredentials config.LDAPCredentials
|
||||
|
||||
metaData := &mapstructure.Metadata{}
|
||||
if err := viperInstance.Unmarshal(&ldapCredentials, metadataConfig(metaData)); err != nil {
|
||||
log.Error().Err(err).Msg("failed to unmarshal ldap credentials config")
|
||||
if err := viperInstance.Unmarshal(v, metadataConfig(metaData)); err != nil {
|
||||
log.Error().Err(err).Str("path", path).Msg("failed to unmarshal secret file config")
|
||||
|
||||
return config.LDAPCredentials{}, errors.Join(zerr.ErrBadConfig, err)
|
||||
return errors.Join(zerr.ErrBadConfig, err)
|
||||
}
|
||||
|
||||
if len(metaData.Keys) == 0 {
|
||||
log.Error().Err(zerr.ErrBadConfig).
|
||||
Msg("failed to load ldap credentials config due to the absence of any key:value pair")
|
||||
log.Error().Err(zerr.ErrBadConfig).Str("path", path).
|
||||
Msg("failed to load secret file due to the absence of any key:value pair")
|
||||
|
||||
return config.LDAPCredentials{}, zerr.ErrBadConfig
|
||||
return zerr.ErrBadConfig
|
||||
}
|
||||
|
||||
if len(metaData.Unused) > 0 {
|
||||
log.Error().Err(zerr.ErrBadConfig).Strs("keys", metaData.Unused).
|
||||
Msg("failed to load ldap credentials config due to unknown keys")
|
||||
log.Error().Err(zerr.ErrBadConfig).Str("path", path).Strs("keys", metaData.Unused).
|
||||
Msg("failed to load secret file due to unknown keys")
|
||||
|
||||
return config.LDAPCredentials{}, zerr.ErrBadConfig
|
||||
return zerr.ErrBadConfig
|
||||
}
|
||||
|
||||
if len(metaData.Unset) > 0 {
|
||||
if checkUnsetFields && len(metaData.Unset) > 0 {
|
||||
log.Error().Err(zerr.ErrBadConfig).Strs("keys", metaData.Unset).
|
||||
Msg("failed to load ldap credentials config due to unset keys")
|
||||
|
||||
return config.LDAPCredentials{}, zerr.ErrBadConfig
|
||||
return zerr.ErrBadConfig
|
||||
}
|
||||
|
||||
return ldapCredentials, nil
|
||||
return nil
|
||||
}
|
||||
|
||||
func authzContainsOnlyAnonymousPolicy(cfg *config.Config) bool {
|
||||
|
|
|
@ -1350,6 +1350,77 @@ storage:
|
|||
So(err, ShouldBeNil)
|
||||
})
|
||||
|
||||
Convey("Test verify good session keys config with both keys", t, func(c C) {
|
||||
tmpFile, err := os.CreateTemp("", "zot-test*.json")
|
||||
So(err, ShouldBeNil)
|
||||
defer os.Remove(tmpFile.Name())
|
||||
|
||||
tmpCredsFile, err := os.CreateTemp("", "zot-cred*.json")
|
||||
So(err, ShouldBeNil)
|
||||
defer os.Remove(tmpCredsFile.Name())
|
||||
|
||||
content := []byte(`{
|
||||
"hashKey":"very-secret",
|
||||
"encryptKey":"another-secret"
|
||||
}`)
|
||||
|
||||
_, err = tmpCredsFile.Write(content)
|
||||
So(err, ShouldBeNil)
|
||||
err = tmpCredsFile.Close()
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
content = []byte(fmt.Sprintf(`{ "distSpecVersion": "1.1.0-dev",
|
||||
"storage": { "rootDirectory": "/tmp/zot" }, "http": { "address": "127.0.0.1", "port": "8080",
|
||||
"auth":{"htpasswd":{"path":"test/data/htpasswd"}, "sessionKeysFile": "%s",
|
||||
"failDelay": 5 } }, "log": { "level": "debug" } }`,
|
||||
tmpCredsFile.Name()),
|
||||
)
|
||||
|
||||
_, err = tmpFile.Write(content)
|
||||
So(err, ShouldBeNil)
|
||||
err = tmpFile.Close()
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
os.Args = []string{"cli_test", "verify", tmpFile.Name()}
|
||||
err = cli.NewServerRootCmd().Execute()
|
||||
So(err, ShouldBeNil)
|
||||
})
|
||||
|
||||
Convey("Test verify good session keys config with one key", t, func(c C) {
|
||||
tmpFile, err := os.CreateTemp("", "zot-test*.json")
|
||||
So(err, ShouldBeNil)
|
||||
defer os.Remove(tmpFile.Name())
|
||||
|
||||
tmpCredsFile, err := os.CreateTemp("", "zot-cred*.json")
|
||||
So(err, ShouldBeNil)
|
||||
defer os.Remove(tmpCredsFile.Name())
|
||||
|
||||
content := []byte(`{
|
||||
"hashKey":"very-secret"
|
||||
}`)
|
||||
|
||||
_, err = tmpCredsFile.Write(content)
|
||||
So(err, ShouldBeNil)
|
||||
err = tmpCredsFile.Close()
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
content = []byte(fmt.Sprintf(`{ "distSpecVersion": "1.1.0-dev",
|
||||
"storage": { "rootDirectory": "/tmp/zot" }, "http": { "address": "127.0.0.1", "port": "8080",
|
||||
"auth":{"htpasswd":{"path":"test/data/htpasswd"}, "sessionKeysFile": "%s",
|
||||
"failDelay": 5 } }, "log": { "level": "debug" } }`,
|
||||
tmpCredsFile.Name()),
|
||||
)
|
||||
|
||||
_, err = tmpFile.Write(content)
|
||||
So(err, ShouldBeNil)
|
||||
err = tmpFile.Close()
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
os.Args = []string{"cli_test", "verify", tmpFile.Name()}
|
||||
err = cli.NewServerRootCmd().Execute()
|
||||
So(err, ShouldBeNil)
|
||||
})
|
||||
|
||||
Convey("Test verify good ldap config", t, func(c C) {
|
||||
tmpFile, err := os.CreateTemp("", "zot-test*.json")
|
||||
So(err, ShouldBeNil)
|
||||
|
|
Loading…
Reference in a new issue