Skip to content

Commit

Permalink
feat: support crc32c checksum validation
Browse files Browse the repository at this point in the history
SecretVersions created after July 2021 all have a checksum either
generated by the end user when calling AddSecretVersion or server
side by the Secret Manager API when initially written.

https://cloud.google.com/secret-manager/docs/data-integrity

This change adds an additional crc32c validation to catch possible
data corruption. Note this will not catch corruption that may occur
downstream of the provider (i.e. by the driver process, linux tmpfs
mount, or the process consuming the secret).
  • Loading branch information
tam7t committed May 26, 2022
1 parent 3299132 commit ddbdee4
Show file tree
Hide file tree
Showing 2 changed files with 97 additions and 3 deletions.
10 changes: 7 additions & 3 deletions server/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ package server
import (
"context"
"fmt"
"hash/crc32"
"os"
"strconv"
"strings"
Expand Down Expand Up @@ -114,9 +115,12 @@ func handleMountEvent(ctx context.Context, client *secretmanager.Client, creds c
req := &secretmanagerpb.AccessSecretVersionRequest{
Name: secret.ResourceName,
}
resp, err := client.AccessSecretVersion(ctx, req, callAuth)
results[i] = resp
errs[i] = err
results[i], errs[i] = client.AccessSecretVersion(ctx, req, callAuth)
if resp := results[i]; resp != nil && resp.GetPayload().DataCrc32C != nil {
if int64(crc32.Checksum(resp.GetPayload().Data, crc32c)) != *resp.GetPayload().DataCrc32C {
errs[i] = status.Error(codes.DataLoss, "detected crc32c missmatch")
}
}
}()
}
wg.Wait()
Expand Down
90 changes: 90 additions & 0 deletions server/server_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,96 @@ func TestHandleMountEvent(t *testing.T) {
}
}

func TestHandleMountEvent_GoodChecksum(t *testing.T) {
cfg := &config.MountConfig{
Secrets: []*config.Secret{
{
ResourceName: "projects/project/secrets/test/versions/latest",
FileName: "good1.txt",
},
},
Permissions: 777,
PodInfo: &config.PodInfo{
Namespace: "default",
Name: "test-pod",
},
}

client := mock(t, &mockSecretServer{
accessFn: func(ctx context.Context, _ *secretmanagerpb.AccessSecretVersionRequest) (*secretmanagerpb.AccessSecretVersionResponse, error) {
var checksum int64 = 1064160727
return &secretmanagerpb.AccessSecretVersionResponse{
Name: "projects/project/secrets/test/versions/2",
Payload: &secretmanagerpb.SecretPayload{
Data: []byte("My Secret"),
DataCrc32C: &checksum,
},
}, nil
},
})

want := &v1alpha1.MountResponse{
ObjectVersion: []*v1alpha1.ObjectVersion{
{
Id: "projects/project/secrets/test/versions/latest",
Version: "projects/project/secrets/test/versions/2",
},
},
Files: []*v1alpha1.File{
{
Path: "good1.txt",
Mode: 777,
Contents: []byte("My Secret"),
},
},
}

got, err := handleMountEvent(context.Background(), client, NewFakeCreds(), cfg)
if err != nil {
t.Errorf("handleMountEvent() got err = %v, want err = nil", err)
}
if diff := cmp.Diff(want, got, protocmp.Transform()); diff != "" {
t.Errorf("handleMountEvent() returned unexpected response (-want +got):\n%s", diff)
}
}

func TestHandleMountEvent_BadChecksum(t *testing.T) {
cfg := &config.MountConfig{
Secrets: []*config.Secret{
{
ResourceName: "projects/project/secrets/test/versions/latest",
FileName: "good1.txt",
},
},
Permissions: 777,
PodInfo: &config.PodInfo{
Namespace: "default",
Name: "test-pod",
},
}

client := mock(t, &mockSecretServer{
accessFn: func(ctx context.Context, _ *secretmanagerpb.AccessSecretVersionRequest) (*secretmanagerpb.AccessSecretVersionResponse, error) {
var checksum int64 = 123 // 1064160727
return &secretmanagerpb.AccessSecretVersionResponse{
Name: "projects/project/secrets/test/versions/2",
Payload: &secretmanagerpb.SecretPayload{
Data: []byte("My Secret"),
DataCrc32C: &checksum,
},
}, nil
},
})

_, got := handleMountEvent(context.Background(), client, NewFakeCreds(), cfg)
if got == nil {
t.Fatalf("handleMountEvent() got success, want err")
}
if !strings.Contains(got.Error(), "crc32c missmatch") {
t.Errorf("handleMountEvent() got err = %v, want err = nil", got)
}
}

func TestHandleMountEventSMError(t *testing.T) {
cfg := &config.MountConfig{
Secrets: []*config.Secret{
Expand Down

0 comments on commit ddbdee4

Please sign in to comment.