Protect Secrets Manager connection details with Azure Key Vault Keys
Keeper Secrets Manager integrates with Azure Key Vault 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.
Features
Encrypt and Decrypt your Keeper Secrets Manager configuration files with Azure Key Vault.
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.
The Secrets Manager azure KSM module Integration can be installed using
go get github.com/keeper-security/secrets-manager-go/integrations/azure
2. Configure Azure Key Vault Connection
Ensure that you have an Azure Key Vault instance available, The following param needed to connect azure key vault
AZURE_TENANT_ID: The Microsoft Entra tenant (directory) ID.
AZURE_CLIENT_ID: The client (application) ID of an App Registration in the tenant.
AZURE_CLIENT_SECRET: A client secret that was generated for the App Registration.
You will need an Azure App directory App to use the Azure Key Vault integration.
For more information on Azure App Directory App registration and Permissions see the Azure documentation:
Once azure connection has been configured, You can fetch the Key to encrypt / decrypt KSM configurations using azure key and you need to tell the Secrets Manager SDK to utilize the key vault as storage.
Using Azure Key Vault Integration
Once setup, the Secrets Manager Azure Key Vault integration supports all Secrets Manager SDK functionality. Your code will need to be able to access the Azure Keys in order to manage the encryption and decryption of the KSM configuration file.
Using Specified Connection credentials
To do this, create AzureKeyValueStorage instance and use this in SecretManagerOptions constructor.
The AzureKeyValueStorage will require the name of the Secrets Manager configuration file with azure_key_id , azure_keyvault_URL and configuration.
To do this, use AzureKeyValueStorage as your Secrets Manager storage in the SecretsManager constructor.
The storage will require an Azure Key ID, as well as the name of the Secrets Manager configuration file which will be encrypted by Azure Key Vault.
import { getSecrets, initializeStorage, localConfigStorage } from '@keeper-security/secrets-manager-core';
import {AzureKeyValueStorage, AzureSessionConfig} from "@keeper/secrets-manager-azure";
const getKeeperRecords = async () => {
const tenant_id="<tenant_id>"
const client_id="<client_id>"
const client_secret="<client-secret>"
const azureSessionConfig = new AzureSessionConfig(tenant_id, client_id, client_secret)
let config_path = "<path to ksm-client-config.json>"
const logLevel = LoggerLogLevelOptions.info;
// oneTimeToken is used only once to initialize the storage
// after the first run, subsequent calls will use ksm-config.txt
const oneTimeToken = "[One Time Token]";
const keyId = 'https://<vault_name>.vault.azure.net/keys/<key_name>/<version>'
const storage = await new AzureKeyValueStorage(keyId,config_path,azureSessionConfig,logLevel).init();
await initializeStorage(storage, oneTimeToken);
const {records} = await getSecrets({storage: storage});
console.log(records)
}
getKeeperRecords()
To do this, use AzureKeyValueStorage as your Secrets Manager storage in the SecretsManager constructor as config along with a token.
The storage will require a Azure Key ID, as well as the location of the Secrets Manager configuration file which will be encrypted by Azure-KSM Integration and Azure session configuration as shown below.
from keeper_secrets_manager_storage.storage_azure_keyvault import AzureSessionConfig,AzureKeyValueStorage
from keeper_secrets_manager_core import SecretsManager
tenant_id = "<Tenant_ID>"
client_id = "<CLIENT_ID>"
client_secret = "<CLIENT_SECRET>"
azure_session_config = AzureSessionConfig(tenant_id, client_id, client_secret)
config_path = "<path_to_client_config_python.json>"
token="<One Time Token>"
key_id = "<key_id>"
azure_key_value_storage = AzureKeyValueStorage(key_id=keyId,config_file_location=config_path ,az_session_config=azure_session_config)
secrets_manager = SecretsManager(token = token,config=azure_key_value_storage)
records = secrets_manager.get_secrets()
for record in records:
print(record)
To do this, use AzureKeyValueStorage as your Secrets Manager storage in the SecretsManager constructor.
The storage will require an Azure Key ID, as well as the name of the Secrets Manager configuration file which will be encrypted by Azure Key Vault. Optionally AzureSessionConfig can be provided. If credentials are not provided the default credentials are used.
using System;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using OracleKeyManagement;
using SecretsManager;
public class Program
{
private static async Task getOneIndividualSecret()
{
var tenant_id = "<TENANT_ID>";
var client_secret = "<CLIENT_SECRET>";
var client_id = "<CLIENT_ID>";
var keyId = "<KEY_ID>";
var path = "ksmConfigDotnet.json";
var dotnet_access_token = "<ACCESS_TOKEN>";
var loggerFactory = LoggerFactory.Create(builder =>
{
builder.SetMinimumLevel(LogLevel.Debug);
builder.AddConsole();
});
var logger = loggerFactory.CreateLogger<AzureKeyValueStorage>();
var azure_session_config = new AzureSessionConfig(tenant_id, client_id, client_secret);
AzureKeyValueStorage azure_storage = new AzureKeyValueStorage(keyId, path, azure_session_config, logger);
SecretsManagerClient.InitializeStorage(azure_storage, dotnet_access_token);
var options = new SecretsManagerOptions(azure_storage);
var records_list = await SecretsManagerClient.GetSecrets(options);
records_list.Records.ToList().ForEach(record => Console.WriteLine(record.RecordUid + " - " + record.Data.title));
}
static async Task Main()
{
await getOneIndividualSecret();
}
}
To do this, use NewAzureKeyValueStorage as your Secrets Manager storage in the SecretsManager constructor.
The NewAzureKeyVaultStorage requires the following parameters to encrypt the KSM configuration using Azure Key Vault:
ksmConfigFileName : The file name of KSM configuration.
keyURL: Azure Key URL
package main
import (
"encoding/json"
"fmt"
"github.com/keeper-security/secrets-manager-go/core"
azurekv "github.com/keeper-security/secrets-manager-go/integrations/azure"
)
func main() {
ksmConfigFile := ""
oneTimeToken := "oneTimeToken"
keyURL := "<Key URL>" }
//Initialize the Azure Key Vault Storage
cfg := azurekv.NewAzureKeyValueStorage(ksmConfigFileName, keyURL, &azurekv.AzureConfig{
TenantID: "<Azure Tenant ID>",
ClientID: "<Azure Client ID>",
ClientSecret: "<Azure Client Secret>",
})
// create a new secrets manager client
secrets_manager := core.NewSecretsManager(
&core.ClientOptions{
Config: cfg,
Token: oneTimeToken,
},
)
// Fetch all the secrets from the vault
secrets, err := secrets_manager.GetSecrets([]string{})
if err != nil {
// do something
fmt.Printf("Error: %s\n", err)
} else {
for _, secret := range secrets {
fmt.Printf("Recieved secret: %s\n", secret.Title())
}
}
}
Additional Options
Change Key
We can change key that is used for encrypting the KSM configuration, examples below show the code needed to use it
//The method changeKey(newKeyID) will be used to encrypt the KSM config file with new azure key.
....
String newKeyID = "https://<vault-name>.vault.azure.net/keys/<keyname>/<keyversion>";
AzureKeyValueStorage azkvstorage = AzureKeyValueStorage.getInternalStorage(keyId, configFileLocation, azConfig);
boolean isChanged = azkvstorage.changeKey(newKeyID); // Change the key for encryption/decryption
....
// To change the Azure Key Vault key used for encryption, you can call the `changeKey` method on the `OciKeyValueStorage` instance.
.....
const keyId = 'https://<vault_name>.vault.azure.net/keys/<key_name>/<version>'
const keyId2 = "https://<vault_name>.vault.azure.net/keys/<key_name>/<version>"
const storage = await new AzureKeyValueStorage(keyId,config_path,azureSessionConfig).init();
await storage.changeKey(keyId2);
.....
// To change the Azure key used for encryption, you can call the `ChangeKeyAsync` method on the `AzureKeyValueStorage` instance.
using Microsoft.Extensions.Logging;
....
var keyId2 = "<KEY_ID_2>";
var loggerFactory = LoggerFactory.Create(builder =>
{
builder.SetMinimumLevel(LogLevel.Debug);
builder.AddConsole();
});
var logger = loggerFactory.CreateLogger<AzureKeyValueStorage>();
AzureKeyValueStorage azure_storage = new AzureKeyValueStorage(keyId, path, azure_session_config, logger);
azure_storage.ChangeKeyAsync(keyId2).Wait();
....
....
updatedConfig := &azurekv.AzureConfig{
TenantID: "<Updated Tenant ID>",
ClientID: "<Updated Client ID>",
ClientSecret: "<Updated Client Secret>",
}
updatedKeyURL := "<Updated Key URL>"
// Changes the key
// If you don't want to change Config, pass nil as a paramter
isChanged, err := cfg.ChangeKey(updatedKeyURL, updatedConfig)
if err != nil {
fmt.Printf("Error while changing key: %v", err)
} else {
fmt.Printf("Key changed: %v\n", isChanged)
}
.....
Decrypt Config
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. This function accepts a boolean, when set to true will save the decrypted configuration to file and when set to false will return decrypted configuration.
....
AzureKeyValueStorage azkvstorage = AzureKeyValueStorage.getInternalStorage(keyId, configFileLocation, azConfig);
azkvstorage.decryptConfig(false); // Set false as a parameter to extract only plaintext.
//OR
azkvstorage.decryptConfig(true); // Set true as a parameter to extract plaintext and save config as a plaintext.
....
//To decrypt the config file and save it again in plaintext, you can call the `DecryptConfigAsync` method on the `AzureKeyValueStorage` instance.
....
const storage = await new AzureKeyValueStorage(keyId,config_path,azureSessionConfig).init();
const decryptedConfig = storage.DecryptConfigAsync(true).wait(); // return decrypted and Saves to file
//OR
const decryptedConfig = await storage.decryptConfig(false); // returns the decrypted config
....
storage = AzureKeyValueStorage(key_id=keyId,config_file_location=config_path ,az_session_config=azure_session_config)
storage.decrypt_config() #saved plain config to file
# or
storage.decrypt_config(False)# returns config as return value, file will stay encrypted
//To decrypt the config file and save it again in plaintext, you can call the `DecryptConfigAsync` method on the `AzureKeyValueStorage` instance.
....
var keyId2 = "<KEY_ID_2>";
AzureKeyValueStorage azure_storage = new AzureKeyValueStorage(keyId, path, azure_session_config, logger);
await azure_storage.DecryptConfigAsync(true); // return decrypted and Saves to file
//OR
await azure_storage.DecryptConfigAsync(false); // returns the decrypted config
....
.....
configs := make(map[core.ConfigKey]interface{})
plainText, err := cfg.DecryptConfig(true)
if err != nil {
// do something
fmt.Printf("Error while decrypting config: %v", err)
} else {
if err := json.Unmarshal([]byte(plainText), &configs); err != nil {
fmt.Printf("Error while unmarshalling: %v", err)
}
fmt.Printf("Decrypted data: %v\n", configs["clientId"])
}
.....