Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(bigtable): Customer Managed Encryption (CMEK) #3899

Merged
merged 33 commits into from Apr 29, 2021
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
98ccaff
feat(bigtable): add EncryptionInfo method to admin surface
crwilcox Mar 19, 2021
2c97163
feat(bigtable: add EncryptionInfo to backups
crwilcox Mar 22, 2021
84206e6
test(bigtable): add bit of testing for new encryption info
crwilcox Mar 24, 2021
42a9437
chore(): add some notes for self
crwilcox Mar 31, 2021
b7008bf
test: add additional tests for methods
crwilcox Apr 1, 2021
9d89cdc
test: bring back some test bits
crwilcox Apr 6, 2021
366a695
test: wait for cmek to become 'ready', before evaluating other fields
crwilcox Apr 7, 2021
e9cebaa
chore: cleanup
crwilcox Apr 7, 2021
2aa4d06
chore: some pr feedback cleanup
crwilcox Apr 7, 2021
095d606
cleanup
crwilcox Apr 9, 2021
c9f9abf
tritone feedback, move backup info to have pointer to encryption info…
crwilcox Apr 9, 2021
1b45af1
create custom type instead of map
crwilcox Apr 14, 2021
71dcf18
iota
crwilcox Apr 14, 2021
4818177
cleanup
crwilcox Apr 14, 2021
bf94324
Comment improvements
crwilcox Apr 15, 2021
843d988
use same key specified in contributing
crwilcox Apr 16, 2021
8117c7e
Merge branch 'master' into bigtable-cmek
crwilcox Apr 16, 2021
b5a7efd
Code cleanup
crwilcox Apr 16, 2021
ee8f42a
Merge branch 'master' into bigtable-cmek
crwilcox Apr 16, 2021
09a4ef0
Merge branch 'master' into bigtable-cmek
crwilcox Apr 19, 2021
f413fc8
gofmt
crwilcox Apr 19, 2021
4f400f2
Address nits from codyoss
crwilcox Apr 20, 2021
749ae3a
Merge branch 'master' into bigtable-cmek
crwilcox Apr 20, 2021
4a0010b
Merge branch 'master' into bigtable-cmek
crwilcox Apr 21, 2021
20c839d
docs
crwilcox Apr 21, 2021
533fe8c
Merge branch 'master' into bigtable-cmek
crwilcox Apr 22, 2021
bdb52a7
fix: address kolea2 comments
crwilcox Apr 26, 2021
60e210e
Merge branch 'bigtable-cmek' of github.com:crwilcox/google-cloud-go i…
crwilcox Apr 26, 2021
22957b5
fix: narrow view for getting tables
crwilcox Apr 28, 2021
19a5bb1
Merge branch 'master' into bigtable-cmek
crwilcox Apr 28, 2021
6c492a6
refactor: remove repeated Encryption keyword
crwilcox Apr 28, 2021
b699e1a
Merge branch 'master' into bigtable-cmek
crwilcox Apr 29, 2021
891e52f
fix: infer service account email for contributing guide
crwilcox Apr 29, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
134 changes: 122 additions & 12 deletions bigtable/admin.go
Expand Up @@ -39,6 +39,7 @@ import (
"google.golang.org/api/option"
gtransport "google.golang.org/api/transport/grpc"
btapb "google.golang.org/genproto/googleapis/bigtable/admin/v2"
"google.golang.org/genproto/googleapis/rpc/status"
"google.golang.org/genproto/protobuf/field_mask"
"google.golang.org/grpc/metadata"
)
Expand Down Expand Up @@ -119,6 +120,76 @@ func (ac *AdminClient) backupPath(cluster, backup string) string {
return fmt.Sprintf("projects/%s/instances/%s/clusters/%s/backups/%s", ac.project, ac.instance, cluster, backup)
}

// EncryptionInfo represents the encryption info of a table.
type EncryptionInfo struct {
// TODO: compare to https://github.com/googleapis/java-bigtable/pull/656/files#diff-10655a12f2a8430533dca76435f6829a1a0bc978ada1295529b59a46afbcd95fR28
EncryptionStatus *status.Status // TODO: should this be wrapped as EncryptionType has been?
crwilcox marked this conversation as resolved.
Show resolved Hide resolved
EncryptionType EncryptionType
KMSKeyVersion string
}

func newEncryptionInfo(pbInfo *btapb.EncryptionInfo) EncryptionInfo {
info := EncryptionInfo{}
crwilcox marked this conversation as resolved.
Show resolved Hide resolved
info.EncryptionStatus = pbInfo.EncryptionStatus
// TODO: could also just return this as a string, but wrapped
crwilcox marked this conversation as resolved.
Show resolved Hide resolved
info.EncryptionType = EncryptionType(pbInfo.EncryptionType.Number())
info.KMSKeyVersion = pbInfo.KmsKeyVersion

return info
}

type EncryptionType int32

const (
// Encryption type was not specified, though data at rest remains encrypted.
crwilcox marked this conversation as resolved.
Show resolved Hide resolved
ENCRYPTION_TYPE_UNSPECIFIED EncryptionType = 0
crwilcox marked this conversation as resolved.
Show resolved Hide resolved
// The data backing this resource is encrypted at rest with a key that is
// fully managed by Google. No key version or status will be populated.
// This is the default state.
GOOGLE_DEFAULT_ENCRYPTION EncryptionType = 1
// The data backing this resource is encrypted at rest with a key that is
// managed by the customer.
// The in-use version of the key and its status are populated for
// CMEK-protected tables.
// CMEK-protected backups are pinned to the key version that was in use at
// the time the backup was taken. This key version is populated but its
// status is not tracked and is reported as `UNKNOWN`.
CUSTOMER_MANAGED_ENCRYPTION EncryptionType = 2
)

// Gets the current encryption info for the table across all of the clusters.
// The returned map will be keyed by cluster id and contain a status for all of the keys in use.
func (ac *AdminClient) EncryptionInfo(ctx context.Context, table string) (map[string][]EncryptionInfo, error) {
crwilcox marked this conversation as resolved.
Show resolved Hide resolved
crwilcox marked this conversation as resolved.
Show resolved Hide resolved
ctx = mergeOutgoingMetadata(ctx, ac.md)

res, err := ac.getTable(ctx, table)
crwilcox marked this conversation as resolved.
Show resolved Hide resolved
if err != nil {
return nil, err
}
// TODO: backups also needs this.
// TODO: why not just return the clusterstates, as it has a map of encryption info.
encryptionInfo := map[string][]EncryptionInfo{}
for key, cs := range res.ClusterStates {
// TODO: if we didn't wrap EncryptionInfo, this could reduce to
// returning map[string][]*btapb.EncryptionInfo directly
// encryptionInfo[key] = cs.EncryptionInfo
encInfo := cs.GetEncryptionInfo()
if encInfo == nil {
return nil, nil
crwilcox marked this conversation as resolved.
Show resolved Hide resolved
}
// TODO: can make a custom class?
for _, pbInfo := range cs.EncryptionInfo {
info := EncryptionInfo{}
info.EncryptionStatus = pbInfo.EncryptionStatus
info.EncryptionType = EncryptionType(pbInfo.EncryptionType.Number())
info.KMSKeyVersion = pbInfo.KmsKeyVersion
encryptionInfo[key] = append(encryptionInfo[key], info)
}
}

return encryptionInfo, nil
}

// Tables returns a list of the tables in the instance.
func (ac *AdminClient) Tables(ctx context.Context) ([]string, error) {
ctx = mergeOutgoingMetadata(ctx, ac.md)
Expand Down Expand Up @@ -247,12 +318,13 @@ type FamilyInfo struct {
GCPolicy string
}

// TableInfo retrieves information about a table.
func (ac *AdminClient) TableInfo(ctx context.Context, table string) (*TableInfo, error) {
func (ac *AdminClient) getTable(ctx context.Context, table string) (*btapb.Table, error) {
ctx = mergeOutgoingMetadata(ctx, ac.md)
prefix := ac.instancePrefix()
req := &btapb.GetTableRequest{
Name: prefix + "/tables/" + table,
View: btapb.Table_FULL,
crwilcox marked this conversation as resolved.
Show resolved Hide resolved
// View: btapb.Table_ENCRYPTION_VIEW,
crwilcox marked this conversation as resolved.
Show resolved Hide resolved
}

var res *btapb.Table
Expand All @@ -265,6 +337,17 @@ func (ac *AdminClient) TableInfo(ctx context.Context, table string) (*TableInfo,
if err != nil {
return nil, err
}
return res, nil
}

// TableInfo retrieves information about a table.
func (ac *AdminClient) TableInfo(ctx context.Context, table string) (*TableInfo, error) {
ctx = mergeOutgoingMetadata(ctx, ac.md)

res, err := ac.getTable(ctx, table)
if err != nil {
return nil, err
}

ti := &TableInfo{}
for name, fam := range res.ColumnFamilies {
Expand Down Expand Up @@ -960,13 +1043,26 @@ type ClusterConfig struct {
InstanceID, ClusterID, Zone string
NumNodes int32
StorageType StorageType

// Describes the Cloud KMS encryption key that will be used to protect the
crwilcox marked this conversation as resolved.
Show resolved Hide resolved
// destination Bigtable cluster. The requirements for this key are:
// 1) The Cloud Bigtable service account associated with the project that
// contains this cluster must be granted the
// `cloudkms.cryptoKeyEncrypterDecrypter` role on the CMEK key.
crwilcox marked this conversation as resolved.
Show resolved Hide resolved
// 2) Only regional keys can be used and the region of the CMEK key must
// match the region of the cluster.
// 3) All clusters within an instance must use the same CMEK key.
KMSKeyName string
}

func (cc *ClusterConfig) proto(project string) *btapb.Cluster {
ec := btapb.Cluster_EncryptionConfig{}
ec.KmsKeyName = cc.KMSKeyName
return &btapb.Cluster{
ServeNodes: cc.NumNodes,
DefaultStorageType: cc.StorageType.proto(),
Location: "projects/" + project + "/locations/" + cc.Zone,
EncryptionConfig: &ec,
}
}

Expand All @@ -977,6 +1073,7 @@ type ClusterInfo struct {
ServeNodes int // number of allocated serve nodes
State string // state of the cluster
StorageType StorageType // the storage type of the cluster
KMSKeyName string // the customer managed encryption key for the cluster
}

// CreateCluster creates a new cluster in an instance.
Expand Down Expand Up @@ -1045,6 +1142,7 @@ func (iac *InstanceAdminClient) Clusters(ctx context.Context, instanceID string)
ServeNodes: int(c.ServeNodes),
State: c.State.String(),
StorageType: storageTypeFromProto(c.DefaultStorageType),
KMSKeyName: c.EncryptionConfig.KmsKeyName,
})
}
if len(res.FailedLocations) > 0 {
Expand All @@ -1058,7 +1156,10 @@ func (iac *InstanceAdminClient) Clusters(ctx context.Context, instanceID string)
// GetCluster fetches a cluster in an instance
func (iac *InstanceAdminClient) GetCluster(ctx context.Context, instanceID, clusterID string) (*ClusterInfo, error) {
ctx = mergeOutgoingMetadata(ctx, iac.md)
req := &btapb.GetClusterRequest{Name: "projects/" + iac.project + "/instances/" + instanceID + "/clusters/" + clusterID}
req := &btapb.GetClusterRequest{
Name: "projects/" + iac.project + "/instances/" + instanceID + "/clusters/" + clusterID,
crwilcox marked this conversation as resolved.
Show resolved Hide resolved
// View: btapb.Table_ENCRYPTION_VIEW
}
var c *btapb.Cluster
err := gax.Invoke(ctx, func(ctx context.Context, _ gax.CallSettings) error {
var err error
Expand All @@ -1077,6 +1178,7 @@ func (iac *InstanceAdminClient) GetCluster(ctx context.Context, instanceID, clus
ServeNodes: int(c.ServeNodes),
State: c.State.String(),
StorageType: storageTypeFromProto(c.DefaultStorageType),
KMSKeyName: c.EncryptionConfig.KmsKeyName,
}
return cis, nil
}
Expand Down Expand Up @@ -1570,15 +1672,21 @@ func newBackupInfo(backup *btapb.Backup) (*BackupInfo, error) {
return nil, fmt.Errorf("invalid expireTime: %v", err)
}

return &BackupInfo{
bi := BackupInfo{
Name: name,
SourceTable: tableID,
SizeBytes: backup.SizeBytes,
StartTime: startTime,
EndTime: endTime,
ExpireTime: expireTime,
State: backup.State.String(),
}, nil
// EncryptionInfo: // populate this after verifying 'nil-nes'
}
// Do not attempt to populate encryption info if not available.
if backup.EncryptionInfo != nil {
bi.EncryptionInfo = newEncryptionInfo(backup.EncryptionInfo)
}
return &bi, nil
crwilcox marked this conversation as resolved.
Show resolved Hide resolved
}

// BackupIterator is an EntryIterator that iterates over log entries.
Expand Down Expand Up @@ -1607,13 +1715,14 @@ func (it *BackupIterator) Next() (*BackupInfo, error) {

// BackupInfo contains backup metadata. This struct is read-only.
type BackupInfo struct {
Name string
SourceTable string
SizeBytes int64
StartTime time.Time
EndTime time.Time
ExpireTime time.Time
State string
Name string
SourceTable string
SizeBytes int64
StartTime time.Time
EndTime time.Time
ExpireTime time.Time
State string
EncryptionInfo EncryptionInfo
crwilcox marked this conversation as resolved.
Show resolved Hide resolved
}

// BackupInfo gets backup metadata.
Expand All @@ -1623,6 +1732,7 @@ func (ac *AdminClient) BackupInfo(ctx context.Context, cluster, backup string) (

req := &btapb.GetBackupRequest{
Name: backupPath,
//View: btapb.Table_ENCRYPTION_VIEW,
}

var resp *btapb.Backup
Expand Down