feat(jwt-asm): support AWS Secrets Manager for JWT verification (#3763)

Signed-off-by: Matheus Pimenta <matheuscscp@gmail.com>
This commit is contained in:
Matheus Pimenta
2026-02-03 17:25:38 +00:00
committed by GitHub
parent 7f629b5d67
commit 0e5a339f11
12 changed files with 1853 additions and 27 deletions
+47
View File
@@ -580,6 +580,10 @@ func validateConfiguration(config *config.Config, logger zlog.Logger) error {
return err
}
if err := validateBearerConfig(config, logger); err != nil {
return err
}
if err := validateSync(config, logger); err != nil {
return err
}
@@ -711,6 +715,49 @@ func validateOpenIDConfig(cfg *config.Config, logger zlog.Logger) error {
return nil
}
func validateBearerConfig(cfg *config.Config, logger zlog.Logger) error {
authConfig := cfg.CopyAuthConfig()
if authConfig == nil || authConfig.Bearer == nil {
return nil
}
bearer := authConfig.Bearer
if bearer.Cert != "" && bearer.AWSSecretsManager != nil {
msg := "cannot configure both cert and awsSecretsManager for bearer authentication"
logger.Error().Err(zerr.ErrBadConfig).Msg(msg)
return fmt.Errorf("%w: %s", zerr.ErrBadConfig, msg)
}
if bearer.AWSSecretsManager != nil {
asm := bearer.AWSSecretsManager
if asm.Region == "" {
msg := "awsSecretsManager region must be specified"
logger.Error().Err(zerr.ErrBadConfig).Msg(msg)
return fmt.Errorf("%w: %s", zerr.ErrBadConfig, msg)
}
if asm.SecretName == "" {
msg := "awsSecretsManager secretName must be specified"
logger.Error().Err(zerr.ErrBadConfig).Msg(msg)
return fmt.Errorf("%w: %s", zerr.ErrBadConfig, msg)
}
if asm.RefreshInterval < 0 {
msg := "awsSecretsManager refreshInterval must be non-negative"
logger.Error().Err(zerr.ErrBadConfig).Msg(msg)
return fmt.Errorf("%w: %s", zerr.ErrBadConfig, msg)
}
}
return nil
}
func validateAuthzPolicies(config *config.Config, logger zlog.Logger) error {
authConfig := config.CopyAuthConfig()
accessControlConfig := config.CopyAccessControlConfig()
+104
View File
@@ -3079,3 +3079,107 @@ func TestRetentionDelayDefaults(t *testing.T) {
})
})
}
func TestBearerASMConfigValidation(t *testing.T) {
Convey("Test bearer ASM config validation", t, func() {
Convey("Reject both cert and awsSecretsManager", func() {
content := `{
"storage": {"rootDirectory": "/tmp/zot"},
"http": {
"address": "127.0.0.1", "port": "8080",
"auth": {
"bearer": {
"realm": "test", "service": "test",
"cert": "/some/cert.pem",
"awsSecretsManager": {"region": "us-east-1", "secretName": "my-secret"}
}
}
}
}`
cfg := config.New()
tmpfile := MakeTempFileWithContent(t, "zot-test.json", content)
err := cli.LoadConfiguration(cfg, tmpfile)
So(err, ShouldNotBeNil)
So(err, ShouldWrap, zerr.ErrBadConfig)
})
Convey("Reject empty region", func() {
content := `{
"storage": {"rootDirectory": "/tmp/zot"},
"http": {
"address": "127.0.0.1", "port": "8080",
"auth": {
"bearer": {
"realm": "test", "service": "test",
"awsSecretsManager": {"region": "", "secretName": "my-secret"}
}
}
}
}`
cfg := config.New()
tmpfile := MakeTempFileWithContent(t, "zot-test.json", content)
err := cli.LoadConfiguration(cfg, tmpfile)
So(err, ShouldNotBeNil)
So(err, ShouldWrap, zerr.ErrBadConfig)
})
Convey("Reject empty secretName", func() {
content := `{
"storage": {"rootDirectory": "/tmp/zot"},
"http": {
"address": "127.0.0.1", "port": "8080",
"auth": {
"bearer": {
"realm": "test", "service": "test",
"awsSecretsManager": {"region": "us-east-1", "secretName": ""}
}
}
}
}`
cfg := config.New()
tmpfile := MakeTempFileWithContent(t, "zot-test.json", content)
err := cli.LoadConfiguration(cfg, tmpfile)
So(err, ShouldNotBeNil)
So(err, ShouldWrap, zerr.ErrBadConfig)
})
Convey("Reject negative refreshInterval", func() {
content := `{
"storage": {"rootDirectory": "/tmp/zot"},
"http": {
"address": "127.0.0.1", "port": "8080",
"auth": {
"bearer": {
"realm": "test", "service": "test",
"awsSecretsManager": {"region": "us-east-1", "secretName": "my-secret", "refreshInterval": "-1s"}
}
}
}
}`
cfg := config.New()
tmpfile := MakeTempFileWithContent(t, "zot-test.json", content)
err := cli.LoadConfiguration(cfg, tmpfile)
So(err, ShouldNotBeNil)
So(err, ShouldWrap, zerr.ErrBadConfig)
})
Convey("Valid ASM config is accepted", func() {
content := `{
"storage": {"rootDirectory": "/tmp/zot"},
"http": {
"address": "127.0.0.1", "port": "8080",
"auth": {
"bearer": {
"realm": "test", "service": "test",
"awsSecretsManager": {"region": "us-east-1", "secretName": "my-secret"}
}
}
}
}`
cfg := config.New()
tmpfile := MakeTempFileWithContent(t, "zot-test.json", content)
err := cli.LoadConfiguration(cfg, tmpfile)
So(err, ShouldBeNil)
})
})
}