From 2ade94bbf7eda3dda49e22656c273a84bf9b7a86 Mon Sep 17 00:00:00 2001 From: Matheus Macabu Date: Wed, 19 Mar 2025 16:30:07 +0100 Subject: [PATCH] SecretsManager: Add roles and access control to APIs (#102456) --- pkg/registry/apis/secret/accesscontrol.go | 125 ++++++++++++++++++ pkg/registry/apis/secret/register.go | 7 +- pkg/services/accesscontrol/permreg/permreg.go | 38 +++--- pkg/services/authz/rbac/mapper.go | 4 + 4 files changed, 155 insertions(+), 19 deletions(-) create mode 100644 pkg/registry/apis/secret/accesscontrol.go diff --git a/pkg/registry/apis/secret/accesscontrol.go b/pkg/registry/apis/secret/accesscontrol.go new file mode 100644 index 00000000000..e93fe22d1cc --- /dev/null +++ b/pkg/registry/apis/secret/accesscontrol.go @@ -0,0 +1,125 @@ +package secret + +import ( + "github.com/grafana/grafana/pkg/services/accesscontrol" + "github.com/grafana/grafana/pkg/services/org" +) + +const ( + // SecureValues + ActionSecretSecureValuesCreate = "secret.securevalues:create" // CREATE. + ActionSecretSecureValuesWrite = "secret.securevalues:write" // UPDATE. + ActionSecretSecureValuesRead = "secret.securevalues:read" // GET + LIST. + ActionSecretSecureValuesDelete = "secret.securevalues:delete" // DELETE. + + // Keepers + ActionSecretKeepersCreate = "secret.keepers:create" // CREATE. + ActionSecretKeepersWrite = "secret.keepers:write" // UPDATE. + ActionSecretKeepersRead = "secret.keepers:read" // GET + LIST. + ActionSecretKeepersDelete = "secret.keepers:delete" // DELETE. +) + +var ( + ScopeProviderSecretSecureValues = accesscontrol.NewScopeProvider("secret.securevalues") + ScopeProviderSecretKeepers = accesscontrol.NewScopeProvider("secret.keepers") + + ScopeAllSecureValues = ScopeProviderSecretSecureValues.GetResourceAllScope() + ScopeAllKeepers = ScopeProviderSecretKeepers.GetResourceAllScope() +) + +func RegisterAccessControlRoles(service accesscontrol.Service) error { + // SecureValues + secureValuesReader := accesscontrol.RoleRegistration{ + Role: accesscontrol.RoleDTO{ + Name: "fixed:secret.securevalues:reader", + DisplayName: "Secrets Manager secure values reader", + Description: "Read and list secure values.", + Group: "Secrets Manager", + Permissions: []accesscontrol.Permission{ + { + Action: ActionSecretSecureValuesRead, + Scope: ScopeAllSecureValues, + }, + }, + }, + Grants: []string{string(org.RoleAdmin)}, + } + + secureValuesWriter := accesscontrol.RoleRegistration{ + Role: accesscontrol.RoleDTO{ + Name: "fixed:secret.securevalues:writer", + DisplayName: "Secrets Manager secure values writer", + Description: "Create, update and delete secure values.", + Group: "Secrets Manager", + Permissions: []accesscontrol.Permission{ + { + Action: ActionSecretSecureValuesCreate, + Scope: ScopeAllSecureValues, + }, + { + Action: ActionSecretSecureValuesRead, + Scope: ScopeAllSecureValues, + }, + { + Action: ActionSecretSecureValuesWrite, + Scope: ScopeAllSecureValues, + }, + { + Action: ActionSecretSecureValuesDelete, + Scope: ScopeAllSecureValues, + }, + }, + }, + Grants: []string{string(org.RoleAdmin)}, + } + + // Keepers + keepersReader := accesscontrol.RoleRegistration{ + Role: accesscontrol.RoleDTO{ + Name: "fixed:secret.keepers:reader", + DisplayName: "Secrets Manager keepers reader", + Description: "Read and list keepers.", + Group: "Secrets Manager", + Permissions: []accesscontrol.Permission{ + { + Action: ActionSecretKeepersRead, + Scope: ScopeAllKeepers, + }, + }, + }, + Grants: []string{string(org.RoleAdmin)}, + } + + keepersWriter := accesscontrol.RoleRegistration{ + Role: accesscontrol.RoleDTO{ + Name: "fixed:secret.keepers:writer", + DisplayName: "Secrets Manager keepers writer", + Description: "Create, update and delete keepers.", + Group: "Secrets Manager", + Permissions: []accesscontrol.Permission{ + { + Action: ActionSecretKeepersCreate, + Scope: ScopeAllKeepers, + }, + { + Action: ActionSecretKeepersRead, + Scope: ScopeAllKeepers, + }, + { + Action: ActionSecretKeepersWrite, + Scope: ScopeAllKeepers, + }, + { + Action: ActionSecretKeepersDelete, + Scope: ScopeAllKeepers, + }, + }, + }, + Grants: []string{string(org.RoleAdmin)}, + } + + return service.DeclareFixedRoles( + secureValuesReader, secureValuesWriter, + keepersReader, keepersWriter, + ) +} diff --git a/pkg/registry/apis/secret/register.go b/pkg/registry/apis/secret/register.go index 457df144c48..9b2ccfe85f6 100644 --- a/pkg/registry/apis/secret/register.go +++ b/pkg/registry/apis/secret/register.go @@ -21,6 +21,7 @@ import ( "github.com/grafana/grafana/pkg/registry/apis/secret/contracts" "github.com/grafana/grafana/pkg/registry/apis/secret/reststorage" "github.com/grafana/grafana/pkg/services/accesscontrol" + authsvc "github.com/grafana/grafana/pkg/services/apiserver/auth/authorizer" "github.com/grafana/grafana/pkg/services/apiserver/builder" "github.com/grafana/grafana/pkg/services/featuremgmt" "github.com/grafana/grafana/pkg/setting" @@ -68,6 +69,10 @@ func RegisterAPIService( return nil, nil } + if err := RegisterAccessControlRoles(accessControlService); err != nil { + return nil, fmt.Errorf("register secret access control roles: %w", err) + } + builder := NewSecretAPIBuilder( tracer, secureValueMetadataStorage, @@ -148,7 +153,7 @@ func (b *SecretAPIBuilder) GetOpenAPIDefinitions() common.GetOpenAPIDefinitions // For Secrets, this is not the case, but if we want to make it so, we need to update this ResourceAuthorizer to check the containing folder. // If we ever want to do that, get guidance from IAM first as well. func (b *SecretAPIBuilder) GetAuthorizer() authorizer.Authorizer { - return nil + return authsvc.NewResourceAuthorizer(b.accessClient) } // Register additional routes with the server. diff --git a/pkg/services/accesscontrol/permreg/permreg.go b/pkg/services/accesscontrol/permreg/permreg.go index 361b2600d08..1d46d749b38 100644 --- a/pkg/services/accesscontrol/permreg/permreg.go +++ b/pkg/services/accesscontrol/permreg/permreg.go @@ -76,24 +76,26 @@ func ProvidePermissionRegistry() PermissionRegistry { func newPermissionRegistry() *permissionRegistry { // defaultKindScopes maps the most specific accepted scope prefix for a given kind (folders, dashboards, etc) defaultKindScopes := map[string]string{ - "teams": "teams:id:", - "users": "users:id:", - "datasources": "datasources:uid:", - "dashboards": "dashboards:uid:", - "folders": "folders:uid:", - "annotations": "annotations:type:", - "apikeys": "apikeys:id:", - "orgs": "orgs:id:", - "plugins": "plugins:id:", - "provisioners": "provisioners:", - "reports": "reports:id:", - "permissions": "permissions:type:", - "serviceaccounts": "serviceaccounts:id:", - "settings": "settings:", - "global.users": "global.users:id:", - "roles": "roles:uid:", - "services": "services:", - "receivers": "receivers:uid:", + "teams": "teams:id:", + "users": "users:id:", + "datasources": "datasources:uid:", + "dashboards": "dashboards:uid:", + "folders": "folders:uid:", + "annotations": "annotations:type:", + "apikeys": "apikeys:id:", + "orgs": "orgs:id:", + "plugins": "plugins:id:", + "provisioners": "provisioners:", + "reports": "reports:id:", + "permissions": "permissions:type:", + "serviceaccounts": "serviceaccounts:id:", + "settings": "settings:", + "global.users": "global.users:id:", + "roles": "roles:uid:", + "services": "services:", + "receivers": "receivers:uid:", + "secret.securevalues": "secret.securevalues:uid:", + "secret.keepers": "secret.keepers:uid:", } return &permissionRegistry{ actionScopePrefixes: make(map[string]PrefixSet, 200), diff --git a/pkg/services/authz/rbac/mapper.go b/pkg/services/authz/rbac/mapper.go index 7635a193f7f..c930bb92fcb 100644 --- a/pkg/services/authz/rbac/mapper.go +++ b/pkg/services/authz/rbac/mapper.go @@ -63,6 +63,10 @@ func newMapper() mapper { "iam.grafana.app": { "teams": newResourceTranslation("teams", "id", false), }, + "secret.grafana.app": { + "securevalues": newResourceTranslation("secret.securevalues", "uid", false), + "keepers": newResourceTranslation("secret.keepers", "uid", false), + }, } }