Secrets Store Replacement
To prepare for a production environment, replace Hashicorp Vault* with a secrets management provider.
To replace secrets management, update these services:
- MPS
- RPS
What You'll Do¶
This guide focuses on updating the secrets management with Azure Key Vault*.
Here are the main tasks:
- Review Vault Schema
- Add Secret Provider Dependency (if necessary)
- Update Configuration
- Implement the Code
Secrets Management Recipe
The example implementation below provides a step-by-step outline of secrets management deployment. However, it is intended as a general guideline. You will need to write specific source code to support your custom solution.
Note
This guide will assume Azure Key Vault is already configured and ready for use as it focuses on the code that needs to be implemented in the microservices.
Review Vault Schema¶
Below are the paths/keys in the vault that are used by the Open AMT Cloud Toolkit.
# RPS
CIRAConfigs/[cira_config_name]/MPS_PASSWORD
certs/[domain_profile_name]/CERT
certs/[domain_profile_name]/CERT_PASSWORD
profiles/[profile_name]/AMT_PASSWORD
profiles/[profile_name]/MEBX_PASSWORD
wireless/[wireless_profile_name]/PSK_PASSPHRASE
# MPS
devices/[device_guid]/AMT_PASSWORD
devices/[device_guid]/MEBX_PASSWORD
devices/[device_guid]/MPS_PASSWORD
Add Dependency¶
To install the required dependencies:
Open a Terminal or Command Prompt and navigate to a directory of your choice for development:
npm install @azure/keyvault-secrets
npm install @azure/identity
Note
To read more about this dependency, check out Azure Key Value Secret Client library for JavaScript.
Update Configuration¶
To modify the configuration:
-
Modify the properties for Hashicorp Vault:
Before:
{ "secrets_path": "secret/data/", "vault_address": "http://localhost:8200", "vault_token": "myroot", }
After:
For Azure Key Vault, you only need the address:
{ "secrets_path": "", "vault_address": "https://<YOUR KEYVAULT NAME>.vault.azure.net", "vault_token": "", }
-
Set these three ENV variables:
AZURE_TENANT_ID=<YOUR-TENANT-ID> AZURE_CLIENT_ID=<YOUR-CLIENT-ID> AZURE_CLIENT_SECRET=<YOUR-CLIENT-SECRET>
Implement the Code¶
To support secrets management:
-
Consider the exported interface
ISecretManagerService
.export interface ISecretManagerService { getSecretFromKey: (path: string, key: string) => Promise<string> getSecretAtPath: (path: string) => Promise<any> listSecretsAtPath: (path: string) => Promise<any> readJsonFromKey: (path: string, key: string) => Promise<string> writeSecretWithKey: (path: string, key: string, keyvalue: any) => Promise<void> writeSecretWithObject: (path: string, data: any) => Promise<void> deleteSecretWithPath: (path: string) => Promise<void> }
-
This example focuses on
getSecretFromKey
, set up and implemented below:const { DefaultAzureCredential } = require("@azure/identity") const { SecretClient } = require("@azure/keyvault-secrets") export class AzureSecretManagerService implements ISecretManagerService { vaultClient: SecretClient logger: ILogger constructor (logger: ILogger) { // DefaultAzureCredential expects the following three environment variables: // * AZURE_TENANT_ID: The tenant ID in Azure Active Directory // * AZURE_CLIENT_ID: The application (client) ID registered in the AAD tenant // * AZURE_CLIENT_SECRET: The client secret for the registered application const credential = new DefaultAzureCredential() // Lastly, create our secrets client and connect to the service const client = new SecretClient(EnvReader.GlobalEnvConfig.VaultConfig.address, credential); } async getSecretFromKey (path: string, key: string): Promise<string> { try { this.logger.verbose(`getting secret from vault: ${path}, ${key}`) const latestSecret = await client.getSecret(key); this.logger.debug(`got data back from vault: ${path}, ${key}`) return latestSecret } catch (error) { this.logger.error('getSecretFromKey error \r\n') this.logger.error(error) return null } } }
The example above is for one interface. You'll need to implement each interface defined in
ISecretManagerService
. -
After all the functions have been implemented, finish up by instantiating the
AzureSecretManagerService
in thesrc/Configurator.ts
file.constructor() { //existing //this.secretsManager = new SecretManagerService(new Logger('SecretManagerService')) //new implementation this.secretsManager = new AzureSecretManagerService(new Logger('AzureSecretManagerService')) }
Best Practice
That's it! Deployment complete.
After replacing the secrets management, ensure all the APIs are working as expected by running the API Tests with the Postman* application. You'll find the tests in the ./src/test/collections
folder.