Secrets: Propagate ctx cancel and label it on decryptions (#116058)
This commit is contained in:
@@ -11,9 +11,6 @@ import (
|
||||
_ "github.com/Azure/azure-sdk-for-go/services/keyvault/v7.1/keyvault"
|
||||
_ "github.com/Azure/go-autorest/autorest"
|
||||
_ "github.com/Azure/go-autorest/autorest/adal"
|
||||
_ "github.com/aws/aws-sdk-go-v2/credentials"
|
||||
_ "github.com/aws/aws-sdk-go-v2/service/secretsmanager"
|
||||
_ "github.com/aws/aws-sdk-go-v2/service/sts"
|
||||
_ "github.com/beevik/etree"
|
||||
_ "github.com/blugelabs/bluge"
|
||||
_ "github.com/blugelabs/bluge_segment_api"
|
||||
|
||||
@@ -127,6 +127,10 @@ func (s *decryptStorage) Decrypt(ctx context.Context, namespace xkube.Namespace,
|
||||
// function call happens after this.
|
||||
sv, err := s.secureValueMetadataStorage.Read(ctx, namespace, name, contracts.ReadOpts{})
|
||||
if err != nil {
|
||||
if errors.Is(err, context.Canceled) {
|
||||
return "", fmt.Errorf("operation canceled while reading secure value metadata storage: %v (%w)", err, context.Canceled)
|
||||
}
|
||||
|
||||
return "", fmt.Errorf("failed to read secure value metadata storage: %v (%w)", err, contracts.ErrDecryptNotFound)
|
||||
}
|
||||
|
||||
@@ -137,6 +141,10 @@ func (s *decryptStorage) Decrypt(ctx context.Context, namespace xkube.Namespace,
|
||||
|
||||
keeperConfig, err := s.keeperMetadataStorage.GetKeeperConfig(ctx, namespace.String(), sv.Status.Keeper, contracts.ReadOpts{})
|
||||
if err != nil {
|
||||
if errors.Is(err, context.Canceled) {
|
||||
return "", fmt.Errorf("operation canceled while reading keeper config metadata storage: %v (%w)", err, context.Canceled)
|
||||
}
|
||||
|
||||
return "", fmt.Errorf("failed to read keeper config metadata storage: %v (%w)", err, contracts.ErrDecryptFailed)
|
||||
}
|
||||
|
||||
@@ -148,13 +156,22 @@ func (s *decryptStorage) Decrypt(ctx context.Context, namespace xkube.Namespace,
|
||||
if sv.Spec.Ref != nil {
|
||||
exposedValue, err := keeper.RetrieveReference(ctx, keeperConfig, *sv.Spec.Ref)
|
||||
if err != nil {
|
||||
if errors.Is(err, context.Canceled) {
|
||||
return "", fmt.Errorf("operation canceled while exposing secret using reference: %v (%w)", err, context.Canceled)
|
||||
}
|
||||
|
||||
return "", fmt.Errorf("failed to expose secret using reference: %v (%w)", err, contracts.ErrDecryptFailed)
|
||||
}
|
||||
|
||||
return exposedValue, nil
|
||||
}
|
||||
|
||||
exposedValue, err := keeper.Expose(ctx, keeperConfig, namespace, name, sv.Status.Version)
|
||||
if err != nil {
|
||||
if errors.Is(err, context.Canceled) {
|
||||
return "", fmt.Errorf("operation canceled while exposing secret: %v (%w)", err, context.Canceled)
|
||||
}
|
||||
|
||||
return "", fmt.Errorf("failed to expose secret: %v (%w)", err, contracts.ErrDecryptFailed)
|
||||
}
|
||||
|
||||
|
||||
@@ -68,6 +68,40 @@ func TestIntegrationDecrypt(t *testing.T) {
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("when the context is cancelled, it returns an error", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
|
||||
svcIdentity := "svc"
|
||||
|
||||
// Create auth context with proper permissions that match the decrypters
|
||||
authCtx := createAuthContext(ctx, "default", []string{"secret.grafana.app/securevalues:decrypt"}, svcIdentity, types.TypeUser)
|
||||
|
||||
// Setup service
|
||||
sut := testutils.Setup(t)
|
||||
|
||||
// Create a secure value
|
||||
spec := secretv1beta1.SecureValueSpec{
|
||||
Description: "description",
|
||||
Decrypters: []string{svcIdentity},
|
||||
Value: ptr.To(secretv1beta1.NewExposedSecureValue("value")),
|
||||
}
|
||||
sv := &secretv1beta1.SecureValue{Spec: spec}
|
||||
sv.Name = "sv-test"
|
||||
sv.Namespace = "default"
|
||||
|
||||
_, err := sut.CreateSv(authCtx, testutils.CreateSvWithSv(sv))
|
||||
require.NoError(t, err)
|
||||
|
||||
// Cancel immediately!
|
||||
cancel()
|
||||
|
||||
exposed, err := sut.DecryptStorage.Decrypt(authCtx, "default", "sv-test")
|
||||
require.ErrorIs(t, err, context.Canceled)
|
||||
require.Empty(t, exposed)
|
||||
})
|
||||
|
||||
t.Run("when happy path with valid auth and permissions, it returns decrypted value", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package metrics
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"sync"
|
||||
|
||||
@@ -185,6 +186,8 @@ func DecryptResultLabel(err error) string {
|
||||
return "error_not_found"
|
||||
} else if errors.Is(err, contracts.ErrDecryptNotAuthorized) {
|
||||
return "error_unauthorized"
|
||||
} else if errors.Is(err, context.Canceled) {
|
||||
return "error_context_canceled"
|
||||
}
|
||||
|
||||
return "error_generic_failure"
|
||||
|
||||
Reference in New Issue
Block a user