The open and composable observability and data visualization platform. Visualize metrics, logs, and traces from multiple sources like Prometheus, Loki, Elasticsearch, InfluxDB, Postgres and many more.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 
grafana/pkg/registry/apis/secret/decrypt/authorizer.go

101 lines
2.9 KiB

package decrypt
import (
"context"
"strings"
"github.com/grafana/authlib/authn"
claims "github.com/grafana/authlib/types"
secretv0alpha1 "github.com/grafana/grafana/pkg/apis/secret/v0alpha1"
"github.com/grafana/grafana/pkg/registry/apis/secret/contracts"
)
// decryptAuthorizer is the authorizer implementation for decrypt operations.
type decryptAuthorizer struct {
allowList contracts.DecryptAllowList
}
func ProvideDecryptAuthorizer(allowList contracts.DecryptAllowList) contracts.DecryptAuthorizer {
return &decryptAuthorizer{
allowList: allowList,
}
}
// authorize checks whether the auth info token has the right permissions to decrypt the secure value.
func (a *decryptAuthorizer) Authorize(ctx context.Context, secureValueName string, secureValueDecrypters []string) (string, bool) {
authInfo, ok := claims.AuthInfoFrom(ctx)
if !ok {
return "", false
}
serviceIdentityList, ok := authInfo.GetExtra()[authn.ServiceIdentityKey]
if !ok {
return "", false
}
// If there's more than one service identity, something is suspicious and we reject it.
if len(serviceIdentityList) != 1 {
return "", false
}
serviceIdentity := serviceIdentityList[0]
// TEMPORARY: while we can't onboard every app into secrets, we can block them from decrypting
// securevalues preemptively here before even reaching out to the database.
// This check can be removed once we open the gates for any service to use secrets.
if _, exists := a.allowList[serviceIdentity]; !exists || serviceIdentity == "" {
return serviceIdentity, false
}
// Checks whether the token has the permission to decrypt secure values.
if !hasPermissionInToken(authInfo.GetTokenPermissions(), secureValueName) {
return serviceIdentity, false
}
// Finally check whether the service identity is allowed to decrypt this secure value.
allowed := false
for _, decrypter := range secureValueDecrypters {
if decrypter == serviceIdentity {
allowed = true
break
}
}
return serviceIdentity, allowed
}
// Adapted from https://github.com/grafana/authlib/blob/1492b99410603ca15730a1805a9220ce48232bc3/authz/client.go#L138
// Changes: 1) we don't support `*` for verbs; 2) we support specific names in the permission.
func hasPermissionInToken(tokenPermissions []string, name string) bool {
var (
group = secretv0alpha1.GROUP
resource = secretv0alpha1.SecureValuesResourceInfo.GetName()
verb = "decrypt"
)
for _, p := range tokenPermissions {
tokenGR, tokenVerb, found := strings.Cut(p, ":")
if !found || tokenVerb != verb {
continue
}
parts := strings.SplitN(tokenGR, "/", 3)
switch len(parts) {
// secret.grafana.app/securevalues:decrypt
case 2:
if parts[0] == group && parts[1] == resource {
return true
}
// secret.grafana.app/securevalues/<name>:decrypt
case 3:
if parts[0] == group && parts[1] == resource && parts[2] == name && name != "" {
return true
}
}
}
return false
}