Protect Secrets Manager connection details with Google Cloud Key Management
Keeper Secrets Manager integrates with Google Cloud Key Management in order to provide encryption for Keeper Secrets Manager configuration files. With this integration, you can protect connection details on your machine while taking advantage of Keeper's zero-knowledge encryption of all your secret credentials.
Encrypt and Decrypt your Keeper Secrets Manager configuration files with Google Cloud Key Management.
Protect against unauthorized access to your Secrets Manager connections.
Requires only minor changes to code for immediate protection. Works with all Keeper Secrets Manager SDK functionality.
To configure, Google Cloud Key Management with Keeeper Security you need service account keys ended with .json
.
Key structure that is supported by this integration is `projects/<project_name>/locations/<location_name>/keyRings/<key_ring_name>/cryptoKeys/<key_name>/cryptoKeyVersions/<key_version>`
Support the Java/Kotlin Secrets Manager SDK.
Required GCP package google-cloud-kms
Google Cloud Key Management needs ENCRYPT
and DECRYPT
permissions.
Supports the JavaScript Secrets Manager SDK
Requires the @google-cloud/kms
package from GCP SDK.
GCP CKM Key needs ENCRYPT
and DECRYPT
permissions.
Supports the Python Secrets Manager SDK
Requires google-cloud-kms
package
GCP CKM Key needs ENCRYPT
and DECRYPT
permissions.
Supports the .Net Secrets Manager SDK
Requires Google.Apis.CloudKMS.v1
GCP CKM Key needs ENCRYPT
and DECRYPT
permissions.
Supports the GoLang Secrets Manager SDK
GCP CKM Key needs ENCRYPT
and DECRYPT
permissions.
Setting up project using Gradle or Maven
Gradle
repositories {
mavenCentral()
}
dependencies {
implementation("com.keepersecurity.secrets-manager:core:17.0.0")
implementation("com.keepersecurity.secrets-manager:gcp:1.0.0")
implementation ("com.google.cloud:google-cloud-kms:2.62.0")
implementation ("com.google.auth:google-auth-library-oauth2-http:1.33.1")
implementation("com.fasterxml.jackson.core:jackson-databind:2.18.2")
implementation("com.fasterxml.jackson.core:jackson-core:2.18.2")
implementation("com.google.code.gson:gson:2.12.1")
implementation("org.slf4j:slf4j-api:1.7.32"){
exclude("org.slf4j:slf4j-log4j12")
}
implementation("ch.qos.logback:logback-classic:1.2.6")
implementation("ch.qos.logback:logback-core:1.2.6")
implementation("org.bouncycastle:bc-fips:1.0.2.4")
}
Maven
<!-- KMS-core -->
<dependency>
<groupId>com.keepersecurity.secrets-manager</groupId>
<artifactId>core</artifactId>
<version>[17.0.0,)</version>
</dependency>
<dependency>
<groupId>com.keepersecurity.secrets-manager</groupId>
<artifactId>gcp</artifactId>
<version>1.0.0</version>
</dependency>
<!-- gcp-kms -->
<dependency>
<groupId>com.google.cloud</groupId>
<artifactId>google-cloud-kms</artifactId>
<version>2.62.0</version>
</dependency>
<!-- gcp auth -->
<dependency>
<groupId>com.google.auth</groupId>
<artifactId>google-auth-library-oauth2-http</artifactId>
<version>1.33.1</version>
</dependency>
<!--gson -->
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.12.1</version>
</dependency>
<!--jackson-core -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.18.2</version>
</dependency>
<!--jackson-databind -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.18.2</version>
</dependency>
<!-- slf4j-api -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.32</version>
<scope>runtime</scope>
</dependency>
<!-- logback-classic -->
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.6</version>
<scope>compile</scope>
</dependency>
<!-- logback-core -->
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-core</artifactId>
<version>1.2.6</version>
<scope>compile</scope>
</dependency>
<!-- bc-fips -->
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bc-fips</artifactId>
<version>1.0.2.4</version>
</dependency>
The Secrets Manager Google Cloud Key Management module can be installed using npm
npm install @keeper-security/secrets-manager-gcp
The Secrets Manager Google Cloud Key Management module can be installed using pip
pip3 install keeper-secrets-manager-storage-gcp-kms
The Secrets Manager Google Cloud Key Management module can be installed using dotnet nuget package manager.
dotnet add package Keeper.SecretsManager.GCPKeyManagement
The Secrets Manager Google Cloud Key Management module Integration can be installed using
go get github.com/keeper-security/secrets-manager-go/integrations/gcp
To enable secure authentication with Google Cloud Platform (GCP), generate a Service Account key in JSON format. This credential file will serve as the authentication mechanism for interacting with GCP services programmatically.
Once GCP connection has been configured, You can fetch the Key to encrypt / decrypt KSM configuration using integration and you need to tell the Secrets Manager SDK to utilize the KMS as storage.
Once setup, the Secrets Manager GCP Key Vault integration supports all Secrets Manager SDK functionality. Your code will need to be able to access the GCP CKM Keys in order to manage the encryption and decryption of the KSM configuration file. Using Specified Connection credentials
To do this, create GcpKeyValueStorage
instance and use this in SecretManagerOptions
constructor.
The GcpKeyValueStorage
will require the name of the Secrets Manager configuration file , gcp credential file and key details of Cloud Key Management.
import java.security.Security;
import org.bouncycastle.jcajce.provider.BouncyCastleFipsProvider;
import static com.keepersecurity.secretsManager.core.SecretsManager.initializeStorage;
import com.keepersecurity.secretsmanager.gcp.GcpKeyValueStorage;
import com.keepersecurity.secretsmanager.gcp.GcpSessionConfig;
import com.keepersecurity.secretsManager.core.SecretsManagerOptions;
public class Test {
public static void main(String args[]){
String oneTimeToken = "One_Time_Token";
String projectId = "projectId";
String location = "cloud_region";
String keyRing = "key_ring_name";
String keyId = "key_id";
String keyVersion = "key_version";
String configFileLocation = "client_config_test.json";
String credentialFileLocation = "<path_with_name_of_gcp_cred_file.json>";
Security.addProvider(new BouncyCastleFipsProvider());
try{
GcpSessionConfig sessionConfig = new GcpSessionConfig(projectId, location, keyRing, keyId, keyVersion, credentialFileLocation);
GcpKeyValueStorage storage = new GcpKeyValueStorage(configFileLocation, sessionConfig);
initializeStorage(storage, oneTimeToken);
SecretsManagerOptions options = new SecretsManagerOptions(storage);
}catch (Exception e) {
System.out.println(e.getMessage());
}
}
}
To do this, use GCPKeyValueStorage
as your Secrets Manager storage in the SecretsManager
constructor.
The storage will require an keyConfig
, gcpsessionConfig
(generated by GCPKSMClient
) , and the name of the Secrets Manager configuration file
which will be encrypted by GCP Cloud Key Management.
import {GCPKeyValueStorage,GCPKeyConfig,GCPKSMClient,LoggerLogLevelOptions} from "@keeper-security/secrets-manager-gcp";
const getKeeperRecordsGCP = async () => {
const gcpCredFile = "<path_with_name_of_gcp_cred_file.json>"
const keyConfig2 = new GCPKeyConfig("<key_version_resource_url_1>");
const keyConfig = new GCPKeyConfig("key_version_resource_url_2");
const gcpSessionConfig = new GCPKSMClient().createClientFromCredentialsFile(gcpCredFile)
let config_path = "<path to client-config.json>"
let logLevel = LoggerLogLevelOptions.debug;
const oneTimeToken = "<one_time_token>";
const storage = await new GCPKeyValueStorage(config_path, keyConfig, gcpSessionConfig, logLevel).init();
await initializeStorage(storage, oneTimeToken);
const {records} = await getSecrets({storage: storage});
console.log(records)
const firstRecord = records[0];
const firstRecordPassword = firstRecord.data.fields.find((x: { type: string; }) => x.type === 'bankAccount');
console.log(firstRecordPassword.value[0]);
}
console.log("start")
getKeeperRecordsGCP()
To do this, use GCPKeyValueStorage
as your Secrets Manager storage in the SecretsManager
constructor.
The storage will require gcp_key_config
(generated by GCPConfig
), gcp_session_config
object (generated by GCPKMSClientConfig
) and the name of the Secrets Manager configuration file
which will be encrypted by GCP Cloud Key Management.
from keeper_secrets_manager_storage_gcp_kms import GCPKeyConfig, GCPKeyValueStorage, GCPKMSClientConfig
from keeper_secrets_manager_core import SecretsManager
# Example key: projects/<project>/locations/<location>/keyRings/<keyring>/cryptoKeys/<key_name>/cryptoKeyVersions/<version>
gcp_key_config_1 = GCPKeyConfig("<key_resource_uri_1>")
gcp_key_config_2 = GCPKeyConfig("<key_resource_uri_2>")
gcp_cred_file_location_with_name = "<path_with_name_of_gcp_cred_file.json>"
gcp_session_config = GCPKMSClientConfig().create_client_from_credentials_file(gcp_cred_file_location_with_name)
config_path = "ksm_config.json"
one_time_token = "<one_time_token>"
storage = GCPKeyValueStorage(config_path, gcp_key_config_1, gcp_session_config)
secrets_manager = SecretsManager(token=one_time_token, config=storage)
all_records = secrets_manager.get_secrets()
first_record = all_records[0]
print(first_record)
To do this, use GCPKeyValueStorage
as your Secrets Manager storage in the SecretsManager
constructor.
The storage will require an keyConfig
(generated by GCPKeyConfig
), gcpSessionConfig
object (generated by GCPKMSClient
), and the name of the Secrets Manager configuration file
which will be encrypted by GCP Cloud Key Management.
using System;
using System.Linq;
using System.Threading.Tasks;
using SecretsManager;
using GCPKeyManagement;
using Microsoft.Extensions.Logging;
public class Program {
private static async Task getOneIndividualSecret() {
Console.WriteLine("execution started");
string key1ResourceName = "<KEY1ResourceURL>";
string key2ResourceName = "<Key2ResourceURL>";
string gcpConfigFilePath = "<GCP config file path with name>";
var keyConfig = new GCPKeyConfig(key1ResourceName);
var gcpSessionConfig = new GCPKMSClient().CreateClientFromCredentialsFile(gcpConfigFilePath);
var ksmConfigPath = "ksm_config.json";
var dotnet_access_token = "[One_Time_Token]";
var loggerFactory = LoggerFactory.Create(builder => {
builder.SetMinimumLevel(LogLevel.Debug);
builder.AddConsole();
});
var logger = loggerFactory.CreateLogger < GCPKeyValueStorage > ();
var gcp_storage = new GCPKeyValueStorage(keyConfig, gcpSessionConfig, ksmConfigPath, logger);
SecretsManagerClient.InitializeStorage(gcp_storage, dotnet_access_token);
}
static async Task Main() {
await getOneIndividualSecret();
}
}
To do this, use NewGCPKeyVaultStorage
as your Secrets Manager storage in the NewSecretsManager
The NewGCPKeyVaultStorage
requires the following parameters to encrypt the KSM configuration using GCP Cloud Key Management:
ksmConfigFileName
: The file name of KSM configuration.
keyResourceName
: ProvidekeyResourceName
of Google Cloud Key Management
credentialFileWithPath
: Provide file path with name of GCP credential file.
package main
import (
"encoding/json"
"fmt"
"github.com/keeper-security/secrets-manager-go/core"
gcpkv "github.com/keeper-security/secrets-manager-go/integrations/gcp"
)
func main() {
credentialFileWithPath := "<Location of credential file ending with .json>"
keyResourceName := "<Key_Resource_Name>"
ksmConfigFileName := "ksmConfig.json"
oneTimeToken := "<One_Time_Access_Token>"
cfg := gcpkv.NewGCPKeyVaultStorage(ksmConfigFileName, keyResourceName, credentialFileWithPath)
client_options := &core.ClientOptions{
Token: oneTimeToken,
Config: cfg,
}
fmt.Printf("Client ID Value: %s", cfg.Get(core.KEY_CLIENT_ID))
secrets_manager := core.NewSecretsManager(client_options)
secrets, err := secrets_manager.GetSecrets([]string{})
if err != nil {
// do something
fmt.Printf("Error while fetching secrets: %v\n", err)
}
for _, record := range secrets {
fmt.Printf("Records: %v\n", record)
}
}
We can change key that is used for encrypting the KSM configuration, examples below show the code needed to use it
//The method changeKey(keyID) will be used to encrypt the KSM config file with new Key and version.
GcpKeyValueStorage storage = new GcpKeyValueStorage(configFileLocation, sessionConfig);
String newKeyID = "<new Key ID>";
boolean isChanged = storage.changeKey(keyId);
System.out.println("Key Changed: "+isChanged); // Change the key for encryption/decryption
// To change the GCP CKM key used for encryption, you can call the `changeKey` method on the `OciKeyValueStorage` instance.
const storage = await new GCPKeyValueStorage(configPath,keyConfig,gcpSessionConfig).init();
await storage.changeKey(keyConfig2);
storage = GCPKeyValueStorage(config_path, gcp_key_config_1, gcp_session_config)
is_changed = storage.change_key(gcp_key_config_2)
print("Key is changed:", is_changed)
// To change the Google CKM key used for encryption, you can call the `ChangeKeyAsync` method on the `GCPKeyValueStorage` instance.
// using Microsoft.Extensions.Logging;
var gcp_storage = new GCPKeyValueStorage(keyConfig2, gcpSessionConfig, path,logger);
bool isChanged = gcp_storage.ChangeKeyAsync(keyConfig1).Wait();
Console.WriteLine(isChanged)
// If you want to change the key not gcpp config, then pass nil in place of oracle config.
cfg := gcpkv.NewGCPKeyVaultStorage(ksmConfigFileName, keyResourceName, credentialFileWithPath)
updatedResourceName := "<Updated Key Resource Name>"
isChanged, err := cfg.ChangeKey(updatedResourceName, "")
if err != nil {
// do something
}
fmt.Printf("Key changed: %v\n", isChanged)
We can decrypt the config if current implementation is to be migrated onto a different cloud or if you want your raw credentials back. The function accepts a boolean which when set to true will save the decrypted configuration to file and if it is false, will just return decrypted configuration.
GcpKeyValueStorage storage = new GcpKeyValueStorage(configFileLocation, sessionConfig);
storage.decryptConfig(false); // Set false as a parameter to extract only plaintext.
//OR
storage.decryptConfig(true); // Set true as a parameter to extract plaintext and save config as a plaintext.
const storage = await new GCPKeyValueStorage(configPath,keyConfig,gcpSessionConfig).init();
await storage.decryptConfig(true); // Set true as a parameter to extract plaintext and save config as a plaintext.
// OR
await storage.decryptConfig(false); // Set false as a parameter to extract only plaintext.
storage = GCPKeyValueStorage(config_path, gcp_key_config_1, gcp_session_config)
# Extract only plaintext
plaintext = storage.decrypt_config(False)
print(plaintext)
# OR extract plaintext and save config as plaintext
plaintext = storage.decrypt_config(True)
print(plaintext)
# To decrypt the config file and save it again in plaintext, you can call the `DecryptConfigAsync` method on the `OracleKeyValueStorage` instance.
var gcp_storage = new GCPKeyValueStorage(keyConfig2, gcpSessionConfig, path,logger);
var conf = await gcp_storage.DecryptConfigAsync(false); # Set false as a parameter to extract only plaintext.
Console.WriteLine(conf);
# OR
var conf = await gcp_storage.DecryptConfigAsync(true); # Set true as a parameter to extract plaintext and save config as a plaintext.
Console.WriteLine(conf);
cfg := gcpkv.NewGCPKeyVaultStorage(ksmConfigFileName, keyResourceName, credentialFileWithPath)
plainText, err := cfg.DecryptConfig(false)
if err != nil {
// do something
fmt.Printf("Error while decrypting config: %v", err)
}
plainText, err := cfg.DecryptConfig(true) // Set true as a parameter to extract plaintext and save config as a plaintext.
if err != nil {
// do something
fmt.Printf("Error while decrypting config: %v", err)
}
You're ready to use the KSM integration 👍