diff --git a/.golangci.yml b/.golangci.yml index a46ca46..68c778c 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -682,7 +682,6 @@ linters-settings: "PKI", "PSU", "PTR", - "REST", "RSA", "RTP", "RX", diff --git a/CHANGELOG.md b/CHANGELOG.md index 1baeeb4..60e6a7c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,10 @@ See updating [Changelog example here](https://keepachangelog.com/en/1.0.0/) ## [Unreleased] +### Added + +- controller: support for data at rest encryption (using encrypted snapshots as volume source is not supported yet) + ## [1.0.1] ### Fixed diff --git a/example/test-pvc-encryption-at-rest.yaml b/example/test-pvc-encryption-at-rest.yaml new file mode 100644 index 0000000..4563edc --- /dev/null +++ b/example/test-pvc-encryption-at-rest.yaml @@ -0,0 +1,21 @@ +kind: StorageClass +apiVersion: storage.k8s.io/v1 +metadata: + name: upcloud-encrypted-block-storage + namespace: kube-system +parameters: + tier: maxiops + encryption: "data-at-rest" +provisioner: storage.csi.upcloud.com +--- +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: csi-pvc-encrypted +spec: + accessModes: + - ReadWriteOnce + resources: + requests: + storage: 10Gi + storageClassName: upcloud-encrypted-block-storage diff --git a/go.mod b/go.mod index 797dfbd..deb0dc5 100644 --- a/go.mod +++ b/go.mod @@ -22,7 +22,7 @@ require ( require github.com/kubernetes-csi/csi-test/v5 v5.0.0 -require github.com/UpCloudLtd/upcloud-go-api/v6 v6.6.0 +require github.com/UpCloudLtd/upcloud-go-api/v6 v6.12.0 require ( github.com/emicklei/go-restful/v3 v3.9.0 // indirect diff --git a/go.sum b/go.sum index a88b013..a13a95f 100644 --- a/go.sum +++ b/go.sum @@ -2,8 +2,8 @@ cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMT cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= -github.com/UpCloudLtd/upcloud-go-api/v6 v6.6.0 h1:Fc9a083OBzl8i4pDV2KXCAfxo4gCjJFHgRuPvRnroBY= -github.com/UpCloudLtd/upcloud-go-api/v6 v6.6.0/go.mod h1:I8rWmBBl+OhiY3AGzKbrobiE5TsLCLNYkCQxE4eJcTg= +github.com/UpCloudLtd/upcloud-go-api/v6 v6.12.0 h1:Qol8WuStmqWTXO8Hfel6FjCgLOZ98MGVCvg3ExcEs68= +github.com/UpCloudLtd/upcloud-go-api/v6 v6.12.0/go.mod h1:I8rWmBBl+OhiY3AGzKbrobiE5TsLCLNYkCQxE4eJcTg= github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= github.com/buger/jsonparser v1.1.1/go.mod h1:6RYKKt7H4d4+iWqouImQ9R2FZql3VbhNgx27UK13J/0= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= diff --git a/internal/controller/controller.go b/internal/controller/controller.go index 90d6fc8..9260655 100644 --- a/internal/controller/controller.go +++ b/internal/controller/controller.go @@ -86,11 +86,12 @@ func (c *Controller) CreateVolume(ctx context.Context, req *csi.CreateVolumeRequ } } else { volumeReq := &request.CreateStorageRequest{ - Zone: c.zone, - Title: req.GetName(), - Size: storageSizeGB, - Tier: tier, - Labels: c.storageLabels, + Zone: c.zone, + Title: req.GetName(), + Size: storageSizeGB, + Tier: tier, + Labels: c.storageLabels, + Encrypted: upcloud.FromBool(createVolumeRequestEncryptionAtRest(req)), } logger.WithServiceRequest(log, volumeReq).Info("creating volume") if vol, err = c.svc.CreateStorage(ctx, volumeReq); err != nil { @@ -167,15 +168,20 @@ func (c *Controller) createVolumeFromSource(ctx context.Context, req *csi.Create } return nil, status.Errorf(codes.InvalidArgument, err.Error()) } + if src.Encrypted.Bool() != createVolumeRequestEncryptionAtRest(req) { + // To prevent unexpected dst device properties, only allow cloning from device with same encryption policy. + return nil, status.Errorf(codes.InvalidArgument, "source and destination volumes needs to have same encryption policy") + } log.Info("checking that source storage is online") if err := c.svc.RequireStorageOnline(ctx, &src.Storage); err != nil { return nil, status.Error(codes.Internal, err.Error()) } volumeReq := &request.CloneStorageRequest{ - UUID: src.Storage.UUID, - Zone: c.zone, - Tier: tier, - Title: req.GetName(), + UUID: src.Storage.UUID, + Zone: c.zone, + Tier: tier, + Title: req.GetName(), + Encrypted: src.Encrypted, } logger.WithServiceRequest(log, volumeReq).Info("cloning volume") vol, err := c.svc.CloneStorage(ctx, volumeReq, c.storageLabels...) @@ -750,6 +756,14 @@ func createVolumeRequestTier(r *csi.CreateVolumeRequest) (string, error) { return "", status.Error(codes.InvalidArgument, fmt.Sprintf("storage tier '%s' not supported", tier)) } +func createVolumeRequestEncryptionAtRest(r *csi.CreateVolumeRequest) bool { + e, ok := r.Parameters["encryption"] + if ok && e == "data-at-rest" { + return true + } + return false +} + func validateCreateVolumeRequest(r *csi.CreateVolumeRequest, zone string) error { if r.GetName() == "" { return status.Error(codes.InvalidArgument, "CreateVolume Name cannot be empty") diff --git a/internal/controller/controller_internal_test.go b/internal/controller/controller_internal_test.go index 3558e6f..23b0276 100644 --- a/internal/controller/controller_internal_test.go +++ b/internal/controller/controller_internal_test.go @@ -4,7 +4,9 @@ import ( "testing" "github.com/UpCloudLtd/upcloud-go-api/v6/upcloud" + "github.com/container-storage-interface/spec/lib/go/csi" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) func TestPaginateStorage(t *testing.T) { @@ -88,3 +90,21 @@ func TestIsValidStorageUUID(t *testing.T) { assert.True(t, isValidUUID("1160ffc3-58ec-4670-bdc9-27fe385d281d")) assert.True(t, isValidUUID("0160ffc3-58ec-4670-bdc9-27fe385d281d")) } + +func TestCreateVolumeRequestEncryptionAtRest(t *testing.T) { + t.Parallel() + + require.False(t, createVolumeRequestEncryptionAtRest(&csi.CreateVolumeRequest{})) + + p := map[string]string{} + require.False(t, createVolumeRequestEncryptionAtRest(&csi.CreateVolumeRequest{Parameters: p})) + + p["encryption"] = "data-at-restx" + require.False(t, createVolumeRequestEncryptionAtRest(&csi.CreateVolumeRequest{Parameters: p})) + + p["encryption"] = "" + require.False(t, createVolumeRequestEncryptionAtRest(&csi.CreateVolumeRequest{Parameters: p})) + + p["encryption"] = "data-at-rest" + require.True(t, createVolumeRequestEncryptionAtRest(&csi.CreateVolumeRequest{Parameters: p})) +} diff --git a/internal/service/mock/service.go b/internal/service/mock/service.go index 6978e9d..3528b92 100644 --- a/internal/service/mock/service.go +++ b/internal/service/mock/service.go @@ -23,9 +23,10 @@ func newMockStorage(size int, label ...upcloud.Label) *upcloud.Storage { id, _ := uuid.NewUUID() return &upcloud.Storage{ - Size: size, - UUID: id.String(), - Labels: label, + Size: size, + UUID: id.String(), + Labels: label, + Encrypted: 0, } } @@ -62,8 +63,10 @@ func (m *UpCloudServiceMock) GetStorageByName(ctx context.Context, storageName s func (m *UpCloudServiceMock) CreateStorage(ctx context.Context, csr *request.CreateStorageRequest) (*upcloud.StorageDetails, error) { id, _ := uuid.NewUUID() + storage := newMockStorage(m.StorageSize) + storage.Encrypted = csr.Encrypted s := &upcloud.StorageDetails{ - Storage: *newMockStorage(m.StorageSize), + Storage: *storage, ServerUUIDs: upcloud.ServerUUIDSlice{id.String()}, // TODO change UUID prefix } @@ -72,8 +75,10 @@ func (m *UpCloudServiceMock) CreateStorage(ctx context.Context, csr *request.Cre func (m *UpCloudServiceMock) CloneStorage(ctx context.Context, csr *request.CloneStorageRequest, label ...upcloud.Label) (*upcloud.StorageDetails, error) { id, _ := uuid.NewUUID() + storage := newMockStorage(m.CloneStorageSize, label...) + storage.Encrypted = csr.Encrypted s := &upcloud.StorageDetails{ - Storage: *newMockStorage(m.CloneStorageSize, label...), + Storage: *storage, ServerUUIDs: upcloud.ServerUUIDSlice{id.String()}, // TODO change UUID prefix }