Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -325,6 +325,20 @@ switch to the REST client by setting the ``SOPS_GCP_KMS_CLIENT_TYPE`` environmen
$ export SOPS_GCP_KMS_CLIENT_TYPE=rest # Use REST client
$ export SOPS_GCP_KMS_CLIENT_TYPE=grpc # Use gRPC client (default)

For sovereign cloud environments that expose a GCP-compatible KMS API at a
non-standard endpoint (e.g. S3NS/Thales TPC: ``cloudkms.s3nsapis.fr``),
you can override the endpoint or the universe domain:

.. code:: sh

# Override the KMS endpoint directly
$ export SOPS_GCP_KMS_ENDPOINT=cloudkms.example.com:443

# Or derive the endpoint from the universe domain (cloudkms.<domain>:443)
$ export SOPS_GCP_KMS_UNIVERSE_DOMAIN=example.com

.. note:: ``SOPS_GCP_KMS_ENDPOINT`` takes precedence over ``SOPS_GCP_KMS_UNIVERSE_DOMAIN`` if both are set.

Encrypting/decrypting with GCP KMS requires a KMS ResourceID. You can use the
cloud console the get the ResourceID or you can create one using the gcloud
sdk:
Expand Down
14 changes: 14 additions & 0 deletions gcpkms/keysource.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,14 @@ const (
// SopsGCPKMSClientTypeEnv is the environment variable used to specify the
// GCP KMS client type. Valid values are "grpc" (default) and "rest".
SopsGCPKMSClientTypeEnv = "SOPS_GCP_KMS_CLIENT_TYPE"
// SopsGCPKMSEndpointEnv overrides the GCP KMS endpoint URL. Useful for
// sovereign cloud environments that expose a GCP-compatible KMS API at a
// non-standard endpoint (e.g. S3NS/Thales TPC: cloudkms.s3nsapis.fr).
SopsGCPKMSEndpointEnv = "SOPS_GCP_KMS_ENDPOINT"
// SopsGCPKMSUniverseDomainEnv sets the universe domain for the GCP KMS
// client, which derives the endpoint as cloudkms.{UNIVERSE_DOMAIN}:443.
// Example: "s3nsapis.fr" for S3NS/Thales TPC.
SopsGCPKMSUniverseDomainEnv = "SOPS_GCP_KMS_UNIVERSE_DOMAIN"
// KeyTypeIdentifier is the string used to identify a GCP KMS MasterKey.
KeyTypeIdentifier = "gcp_kms"
)
Expand Down Expand Up @@ -320,6 +328,12 @@ func (key *MasterKey) newKMSClient(ctx context.Context) (*kms.KeyManagementClien
// Add extra options.
opts = append(opts, key.clientOpts...)

if endpoint := os.Getenv(SopsGCPKMSEndpointEnv); endpoint != "" {
opts = append(opts, option.WithEndpoint(endpoint))
} else if ud := os.Getenv(SopsGCPKMSUniverseDomainEnv); ud != "" {
opts = append(opts, option.WithUniverseDomain(ud))
}

// Select client type based on inputs.
clientType := strings.ToLower(os.Getenv(SopsGCPKMSClientTypeEnv))
var client *kms.KeyManagementClient
Expand Down
26 changes: 26 additions & 0 deletions gcpkms/keysource_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,32 @@ func TestMasterKey_createCloudKMSService_withoutCredentials(t *testing.T) {
assert.ErrorContains(t, err, "credentials: could not find default credentials")
}

func TestMasterKey_createCloudKMSService_withEndpointEnv(t *testing.T) {
t.Setenv(SopsGCPKMSEndpointEnv, "cloudkms.example.com:443")
t.Setenv(SopsGoogleCredentialsOAuthTokenEnv, "token")

masterKey := MasterKey{
ResourceID: testResourceID,
}

client, err := masterKey.newKMSClient(context.Background())
assert.NoError(t, err)
Comment thread
DnR-iData marked this conversation as resolved.
assert.Contains(t, client.Connection().Target(), "cloudkms.example.com")
}

func TestMasterKey_createCloudKMSService_withUniverseDomainEnv(t *testing.T) {
t.Setenv(SopsGCPKMSUniverseDomainEnv, "example.com")
t.Setenv(SopsGoogleCredentialsOAuthTokenEnv, "token")

masterKey := MasterKey{
ResourceID: testResourceID,
}

client, err := masterKey.newKMSClient(context.Background())
assert.NoError(t, err)
Comment thread
DnR-iData marked this conversation as resolved.
assert.Contains(t, client.Connection().Target(), "cloudkms.example.com")
}

func newGRPCServer(port string) *grpc.ClientConn {
serv := grpc.NewServer()
kmspb.RegisterKeyManagementServiceServer(serv, &mockKeyManagement)
Expand Down
Loading