diff --git a/README.md b/README.md index dadd8b9..8b7c7ff 100644 --- a/README.md +++ b/README.md @@ -5,53 +5,85 @@ [![Puppet Forge Downloads](https://img.shields.io/puppetforge/dt/tragiccode/azure_key_vault.svg)](https://forge.puppetlabs.com/tragiccode/azure_key_vault) [![Puppet Forge Endorsement](https://img.shields.io/puppetforge/e/tragiccode/azure_key_vault.svg)](https://forge.puppetlabs.com/tragiccode/azure_key_vault) -#### Table of Contents +## Table of Contents 1. [Description](#description) -1. [Setup](#setup) -1. [Managed Service Identity (MSI) vs Managed Identity for Azure Arc-enabled servers vs Service Principal Credentials](#managed-service-identity-msi-vs-managed-identity-for-azure-arc-enabled-servers-vs-service-principal-credentials) -1. [How it works](#how-it-works) +1. [Requirements](#requirements) +1. [Authentication Methods](#authentication-methods) + * [Managed Service Identity (MSI)](#managed-service-identity-msi) + * [Managed Identity for Azure Arc-enabled servers](#managed-identity-for-azure-arc-enabled-servers) + * [Service Principal Credentials](#service-principal-credentials) + * [Which Authentication Method Should I Use?](#which-authentication-method-should-i-use) +1. [Usage](#usage) * [Puppet Function](#puppet-function) * [Hiera Backend](#hiera-backend) -1. [How it's secure by default](#how-its-secure-by-default) -1. [Usage](#usage) - * [Embedding a secret in a file](#embedding-a-secret-in-a-file) - * [Retrieving a specific version of a secret](#retrieving-a-specific-version-of-a-secret) - * [Retrieving a certificate](#retrieving-a-certificate) -1. [Reference - An under-the-hood peek at what the module is doing and how](#reference) -1. [Development - Guide for contributing to the module](#development) + * [Manual Lookups](#manual-lookups) +1. [Configuration Options](#configuration-options) + * [API Versions](#api-versions) + * [confine_to_keys](#confine_to_keys) + * [key_replacement_token](#key_replacement_token) + * [strip_from_keys](#strip_from_keys) + * [prefixes](#prefixes) + * [Using Facts](#using-facts) +1. [Security](#security) + * [How It's Secure by Default](#how-its-secure-by-default) + * [Working with Sensitive Data](#working-with-sensitive-data) +1. [Examples](#examples) + * [Embedding a Secret in a File](#embedding-a-secret-in-a-file) + * [Retrieving a Specific Version](#retrieving-a-specific-version) + * [Retrieving a Certificate](#retrieving-a-certificate) +1. [Reference](#reference) +1. [Development and Contributing](#development-and-contributing) ## Description -Secure secrets management is essential and critical in order to protect data in the cloud. Key Vault is Microsoft Azure's solution to make this happen. -This module provides a Puppet function and a Hiera backend that allows you to easily fetch secrets securely on the puppet server and embed them into catalogs during compilation time. +Secure secrets management is essential for protecting data in the cloud. Azure Key Vault is Microsoft's solution for this purpose. This module provides a Puppet function and a Hiera backend that allow you to securely fetch secrets from Azure Key Vault on the Puppet server and embed them into catalogs during compilation time. + +## Requirements + +* **Puppet Agent**: Version 6.0.0 or later +* **Azure Subscription**: One or more Key Vaults created and populated with secrets +* **Authentication**: One of the authentication methods described below + +## Authentication Methods + +This module supports three authentication methods for accessing Azure Key Vault. Each method has different use cases and security implications. + +### Managed Service Identity (MSI) + +Managed Service Identity allows your Puppet Server to authenticate to Azure Key Vault without storing credentials. This requires your Puppet Server to run on an Azure VM with MSI enabled and granted appropriate permissions to access the Key Vault. + +**Setup**: Configure MSI on your Azure VM and grant it appropriate Key Vault permissions. [Learn more](https://docs.microsoft.com/en-us/azure/active-directory/managed-service-identity/tutorial-windows-vm-access-nonaad) + +### Managed Identity for Azure Arc-enabled servers + +For servers running outside of Azure (on-premises or other clouds), Azure Arc-enabled servers provide managed identity capabilities similar to MSI. -## Setup +**Setup**: Follow Microsoft's documentation to set up an Azure Arc-enabled server. [Learn more](https://learn.microsoft.com/en-us/azure/azure-arc/servers/learn/quick-enable-hybrid-vm) -The module requires the following: +### Service Principal Credentials -* Puppet Agent 6.0.0 or later. -* Azure Subscription with one or more vaults already created and loaded with secrets. -* One of the following authentication strategies - * Managed Service Identity ( MSI ) - * Puppet Server running on a machine with Managed Service Identity ( MSI ) and assigned the appropriate permissions - to pull secrets from the vault. To learn more or get help with this please visit https://docs.microsoft.com/en-us/azure/active-directory/managed-service-identity/tutorial-windows-vm-access-nonaad - * Managed Identity for Azure Arc-enabled servers - * Follow Microsofts documentation on setting up an Azure Arc-enabled server. To learn more or get help with this please visit https://learn.microsoft.com/en-us/azure/azure-arc/servers/learn/quick-enable-hybrid-vm - * Service Principal - * Following the required steps to setup a Service Principal. To learn more or get help with this please visit https://docs.microsoft.com/en-us/azure/active-directory/develop/quickstart-register-app +Service Principal authentication uses a client ID and secret to authenticate to Azure Key Vault. This requires creating a Service Principal in Azure Active Directory and securely storing its credentials. -# Managed Service Identity (MSI) vs Managed Identity for Azure Arc-enabled servers vs Service Principal Credentials +**Setup**: Create a Service Principal and grant it appropriate Key Vault permissions. [Learn more](https://docs.microsoft.com/en-us/azure/active-directory/develop/quickstart-register-app) -This module provides 3 ways for users to authenticate with azure key vault and pull secrets. These 3 options are Managed Service Identity ( MSI ), Managed Identity for Azure Arc-enabled servers, Service Principal Credentials. We highly recommend you utilize Managed Service Identity over service principal credentials whenever possible. This is because you do not have to manage and secure a file on our machines that contain credentials! In some cases, Managed Service Identity ( MSI ) might not be an option for you. One example of this is if your Puppet server and some of your puppet agents are not hosted in Azure. In this case, we highly recommend you look at and use Azure Arc-enabled servers. If for some reason this cannot be done, you should fallback to Service Principal Credentials. This would require you to create a Service Principal in Azure Active Directory, assign the appropriate permissions to this Service Principal, and both the function and Hiera Backend provided in this module can authenticate to Azure Keyvault using the credentials of this Service Principal. +### Which Authentication Method Should I Use? -## How it works +**Recommended Priority:** + +1. **Managed Service Identity (MSI)** - Use this when your Puppet Server runs in Azure. No credential management required! +2. **Managed Identity for Azure Arc-enabled servers** - Use this when your Puppet Server runs outside Azure but can be Arc-enabled. +3. **Service Principal Credentials** - Use this only when the above options are not available. Requires secure credential storage and management. + +## Usage + +This module can be used in two ways: as a Puppet function in your manifests, or as a Hiera backend for automatic parameter lookup. ### Puppet Function -This module contains a Puppet 4 function that allows you to securely retrieve secrets from Azure Key Vault. In order to get started simply call the function in your manifests passing in the required parameters. +The module provides the `azure_key_vault::secret` function to retrieve secrets directly in your Puppet manifests. -#### Using Managed Service Identity ( MSI ) +#### Function Example: Using MSI ```puppet $important_secret = azure_key_vault::secret('production-vault', 'important-secret', { @@ -60,16 +92,11 @@ $important_secret = azure_key_vault::secret('production-vault', 'important-secre }) ``` -This example fetches the latest secret with the name "important-secret" from the vault named "production-vault". Under the covers it calls the Azure instance metadata service api to get an access token in order to make authenticated requests to the vault api on-behalf-of the MSI. Once the secret is returned you can begin to use it throughout your puppet code. - -> NOTE: In order to improve performance and avoid the request limit for the metadata service api the api token retrieved once then stored in a cache that exists for the duration of the puppet run. +This fetches the latest version of "important-secret" from "production-vault" using MSI for authentication. -In the above example the api_versions hash is important. It is pinning both of the Azure specific api's ( instance metadata api & vault api ) used under the hood to specific versions so that you have full control as to when your puppet code starts calling newer/older versions of the apis. In order to understand what versions are available to your regions please visit the azure documentation +> **Performance Note:** To improve performance and avoid request limits, the API token is cached for the duration of the Puppet run. -* Instance Metadata Service Versions ( https://docs.microsoft.com/en-us/azure/virtual-machines/windows/instance-metadata-service ) -* Vault Versions ( TBD ) - -#### Using Managed Identity for Azure Arc-enabled servers +#### Function Example: Using Azure Arc ```puppet $important_secret = azure_key_vault::secret('production-vault', 'important-secret', { @@ -79,9 +106,9 @@ $important_secret = azure_key_vault::secret('production-vault', 'important-secre }) ``` -This example shows how to utilize Managed Identity for Azure Arc-enabled servers. Similar to the above, the metadata endpoint on the Azure Arc-enabled service will be accessed to generate a secret file on the local machine. The secret within the file will be read and used to authenticate the machine to the secret in the corresponding vault you requested. +Set `use_azure_arc_authentication => true` to use Azure Arc managed identity for authentication. -#### Using Service Principal Credentials +#### Function Example: Using Service Principal ```puppet $important_secret = azure_key_vault::secret('production-vault', 'important-secret', { @@ -94,69 +121,64 @@ $important_secret = azure_key_vault::secret('production-vault', 'important-secre }) ``` -This example show how to utilize service principal credentials if you for some reason are unable to use Managed Service Identity ( MSI ) at your organization. The client_secret must be of type "Sensitive". Please ensure you configure hiera to return the value wrapped in this type as this is what the secret function expects to ensure there is possibilty of leaking the client_secret. +The `client_secret` must be of type `Sensitive` to prevent accidental leakage in logs and reports. ### Hiera Backend -This module contains a Hiera 5 backend that allows you to securely retrieve secrets from Azure key vault and use them in hiera. +The module provides a Hiera 5 backend that integrates with Puppet's automatic parameter lookup (APL). -#### Using Managed Service Identity ( MSI ) +#### Hiera Example: Using MSI -Add a new entry to the `hierarchy` hash in `hiera.yaml` providing the following required lookup options: +Add this to your `hiera.yaml`: ```yaml - name: 'Azure Key Vault Secrets' - lookup_key: azure_key_vault::lookup - options: - vault_name: production-vault - vault_api_version: '2016-10-01' - metadata_api_version: '2018-04-02' - key_replacement_token: '-' - confine_to_keys: - - '^azure_.*' - - '^.*_password$' - - '^password.*' + lookup_key: azure_key_vault::lookup + options: + vault_name: production-vault + vault_api_version: '2016-10-01' + metadata_api_version: '2018-04-02' + key_replacement_token: '-' + confine_to_keys: + - '^azure_.*' + - '^.*_password$' + - '^password.*' ``` -#### Using Managed Identity for Azure Arc-enabled servers - -To utilize Managed Identity for Azure Arc-enabled servers in hiera simply add `use_azure_arc_authentication` with the value of `true`. +#### Hiera Example: Using Azure Arc ```yaml - name: 'Azure Key Vault Secrets' - lookup_key: azure_key_vault::lookup - options: - vault_name: production-vault - vault_api_version: '2016-10-01' - use_azure_arc_authentication: true - metadata_api_version: '2018-04-02' - key_replacement_token: '-' - confine_to_keys: - - '^azure_.*' - - '^.*_password$' - - '^password.*' + lookup_key: azure_key_vault::lookup + options: + vault_name: production-vault + vault_api_version: '2016-10-01' + metadata_api_version: '2018-04-02' + use_azure_arc_authentication: true + key_replacement_token: '-' + confine_to_keys: + - '^azure_.*' + - '^.*_password$' + - '^password.*' ``` -#### Using Service Principal Credentials - -To utilize service principal credentials in hiera simply replace `metadata_api_version` with `service_principal_credentials` and ensure it points to a valid yaml file that contains the service principal credentials you would like to use. +#### Hiera Example: Using Service Principal ```yaml - name: 'Azure Key Vault Secrets' - lookup_key: azure_key_vault::lookup - options: - vault_name: production-vault - vault_api_version: '2016-10-01' - service_principal_credentials: '/etc/puppetlabs/puppet/azure_key_vault_credentials.yaml' - key_replacement_token: '-' - confine_to_keys: - - '^azure_.*' - - '^.*_password$' - - '^password.*' - + lookup_key: azure_key_vault::lookup + options: + vault_name: production-vault + vault_api_version: '2016-10-01' + service_principal_credentials: '/etc/puppetlabs/puppet/azure_key_vault_credentials.yaml' + key_replacement_token: '-' + confine_to_keys: + - '^azure_.*' + - '^.*_password$' + - '^password.*' ``` -Below is the format of the file that is expected to contain your service principal credentials. +The credentials file should be in YAML format: ```yaml tenant_id: '00000000-0000-1234-1234-000000000000' @@ -164,30 +186,9 @@ client_id: '00000000-0000-1234-1234-000000000000' client_secret: some-secret ``` -#### Using facts for the vault name +### Manual Lookups -You can use a fact to specify different vaults for different groups of nodes. It is -recommended to use a trusted fact such as trusted.extensions.pp_environment as these facts -cannot be altered. -Alternatively a custom trusted fact can be included [in the certificate request](https://puppet.com/docs/puppet/latest/ssl_attributes_extensions.html) - -```yaml -- name: 'Azure Key Vault Secrets from trusted fact' - lookup_key: azure_key_vault::lookup - options: - vault_name: "%{trusted.extensions.pp_environment}" - vault_api_version: '2016-10-01' - metadata_api_version: '2018-04-02' - key_replacement_token: '-' - confine_to_keys: - - '^azure_.*' - - '^.*_password$' - - '^password.*' -``` - -#### Manual lookups - -To retrieve a secret in puppet code you can use the `lookup` function: +You can retrieve secrets in Puppet code using the `lookup` function: ```puppet notify { 'lookup': @@ -195,140 +196,144 @@ notify { 'lookup': } ``` -The alias function can also be used in hiera files, for example to set class parameters: +In Hiera files, use the `alias` function to preserve the `Sensitive` data type: ```yaml some_class::password: "%{alias('important-secret')}" ``` -**NOTE: The alias function must be used in the above example. Attempting to use the lookup function inside of your hiera files will not work. This is because, when using lookup, the result is interpolated as a string. Since this module is safe by default, it always returns secrets as Sensitive[String]. The reason we have to use alias is because it will preserve the datatype of the value. More information can be found [here](https://www.puppet.com/docs/puppet/7/hiera_merging.html#interpolation_functions)** +**Important:** Use `alias`, not `lookup`, in Hiera files. The `lookup` function interpolates as a string, which breaks the `Sensitive` type. [More information](https://www.puppet.com/docs/puppet/7/hiera_merging.html#interpolation_functions) + +**Best Practice:** Use Hiera's automatic parameter lookup (APL) instead of manual lookups when possible. -**NOTE: While the above examples show manual lookups happening, it's recommended and considered a best practice to utilize Hiera's automatic parameter lookup (APL) within your puppet code** +## Configuration Options -### What is confine_to_keys? +### API Versions -By design, hiera will traverse the configured heiarchy for a given key until one is found. This means that there can be a potentially large number of web requests against azure key vault. In order to improve performance and prevent hitting the Azure KeyVault rate limits ( ex: currently there is a maximum of 2,000 lookups every 10 seconds allowed against a key vault), the confine_to_keys allows you to provide an array of regexs that help avoid making a remote call. +The `vault_api_version` and `metadata_api_version` parameters pin the Azure APIs to specific versions, giving you control over when your Puppet code uses different API versions. -As an example, if you defined your confine_to_keys as shown below, hiera will only make a web request to get the secret in azure key vault when the key being lookedup matches atleast one of the provided regular expressions in the confine_to_keys array. +* **Instance Metadata Service Versions**: [Azure documentation](https://docs.microsoft.com/en-us/azure/virtual-machines/windows/instance-metadata-service) +* **Vault API Versions**: Check the Azure Key Vault documentation for available versions + +### confine_to_keys + +By design, Hiera traverses the configured hierarchy for each key until one is found. This can result in many web requests to Azure Key Vault. The `confine_to_keys` option improves performance and prevents rate limiting (Azure Key Vault allows 2,000 lookups every 10 seconds per vault) by specifying regular expressions that determine when to query Key Vault. + +Hiera will only query Key Vault when the lookup key matches at least one of the provided regular expressions. + +**Example:** ```yaml -- name: 'Azure Key Vault Secrets from trusted fact' - lookup_key: azure_key_vault::lookup - options: - vault_name: "%{trusted.extensions.pp_environment}" - vault_api_version: '2016-10-01' - metadata_api_version: '2018-04-02' - key_replacement_token: '-' - confine_to_keys: - - '^azure_.*' - - '^.*_password$' - - '^password.*' +confine_to_keys: + - '^azure_.*' + - '^.*_password$' + - '^password.*' ``` -**NOTE: The confine_to_keys is very important to make you sure get right. As a best practice, come up with some conventions to avoid having a large number of regexs you have to add/update/remove and also test. The above example provides a great starting point.** +**Best Practice:** Establish naming conventions for your secrets to minimize the number of regular expressions needed. -### What is key_replacement_token? +### key_replacement_token -KeyVault secret names can only contain the characters `0-9`, `a-z`, `A-Z`, and `-`. +Key Vault secret names can only contain characters `0-9`, `a-z`, `A-Z`, and `-`. Puppet variable names often contain `::` (module delimiter) or underscores, which are invalid in Key Vault. -When relying on automatic parameter lookup (APL), this is almost always going to contain the module delimiter (`::`) or underscores. +This module automatically converts variable names to valid Key Vault secret names by replacing each invalid character with the `key_replacement_token` (default: `-`). -This module will automatically convert the variable name to a valid value by replacing every invalid character with the `key_replacement_token` value, which defaults to `-`. +**Example:** `puppetdb::master::config::puppetdb_server` becomes `puppetdb--master--config--puppetdb-server` -For example, the hiera variable `puppetdb::master::config::puppetdb_server` will automatically be converted to `puppetdb--master--config--puppetdb-server` before being queried up in KeyVault. +**Troubleshooting:** Use `hiera --explain` to see the normalized key name: -When troubleshooting, you can run hiera from the commandline with the `--explain` option to see the key name being used : +```text +Using normalized KeyVault secret key for lookup: puppetdb--master--config--puppetdb-server +``` - Using normalized KeyVault secret key for lookup: puppetdb--master--config--puppetdb-server +### strip_from_keys -### What is strip_from_keys? +The `strip_from_keys` option removes specified patterns from secret names before lookup. This is useful when `confine_to_keys` adds prefixes that you don't want in your Key Vault secret names. -The `strip_from_keys` option allows you to specify one or more patterns to be stripped from the secret name just before looking up the secret in Azure Key Vault. To understand how this is useful, let's walk through an example. +**Example:** ```yaml - name: 'Azure Key Vault Secrets' - lookup_key: azure_key_vault::lookup - options: - vault_name: "prod-key-vault" - vault_api_version: '2016-10-01' - metadata_api_version: '2018-04-02' - key_replacement_token: '-' - confine_to_keys: - - '^azure_.*' + lookup_key: azure_key_vault::lookup + options: + vault_name: "prod-key-vault" + vault_api_version: '2016-10-01' + metadata_api_version: '2018-04-02' + key_replacement_token: '-' + strip_from_keys: + - 'azure_' + confine_to_keys: + - '^azure_.*' ``` -In the example above, `confine_to_keys` is used to scope certain secrets for lookup in Azure Key Vault, ensuring they are retrieved only when they truly exist there. However, as a side effect, `confine_to_keys` influences the secret name. In this case, the Azure Key Vault named "prod-key-vault" would need to have a secret named "profile--windows--sqlserver--azure-sql-user-password". +With this configuration, a lookup for `azure_sql_user_password` searches for `sql-user-password` in Key Vault. -To prevent this naming requirement, the `strip_from_keys` option was introduced. It allows you to remove specific patterns from the key just before lookup in Azure Key Vault. Below is an updated example demonstrating how `strip_from_keys` can be applied. +**Advanced Example - Stripping Module Paths:** ```yaml -- name: 'Azure Key Vault Secrets' - lookup_key: azure_key_vault::lookup - options: - vault_name: "prod-key-vault" - vault_api_version: '2016-10-01' - metadata_api_version: '2018-04-02' - key_replacement_token: '-' - strip_from_keys: - - 'azure_' - confine_to_keys: - - '^azure_.*' +strip_from_keys: + - '^profile::.*::' ``` -Now, with `strip_from_keys`, the "azure_" string pattern is removed from the secret name just before the lookup occurs. This ensures that the system searches for a secret named "profile--windows--sqlserver--sql-user-password" instead of "profile--windows--sqlserver--azure-sql-user-password" in the Azure Key Vault named "prod-key-vault". +A lookup for `profile::windows::sqlserver::azure_sql_user_password` searches for `azure_sql_user_password`. + +### prefixes + +The `prefixes` option creates a hierarchy within Key Vault, similar to the YAML backend. This enables node-specific secrets and custom lookup hierarchies. It's also useful for migrating from HashiCorp Hiera Vault, which has similar behavior. -How flexible can this get? Below shows an example of how you "could" remove profile::* from all your keys! +**Example:** ```yaml - name: 'Azure Key Vault Secrets' - lookup_key: azure_key_vault::lookup - options: - vault_name: "prod-key-vault" - vault_api_version: '2016-10-01' - metadata_api_version: '2018-04-02' - key_replacement_token: '-' - strip_from_keys: - - '^profile::.*::' - confine_to_keys: - - '^azure_.*' + lookup_key: azure_key_vault::lookup + options: + vault_name: secrets-vault + vault_api_version: '2016-10-01' + metadata_api_version: '2018-04-02' + key_replacement_token: '-' + prefixes: + - nodes--%{trusted.hostname}-- + - common-- + confine_to_keys: + - '^azure_.*' ``` -A lookup to 'profile::windows::sqlserver::azure_sql_user_password' or 'profile::linux::blah::blah_again::some_secret' would end up searching for secrets named azure_sql_user_password and some_secret in your Azure Key Vault named "prod-key-vault". +For a node with `trusted.hostname` of `WIN-SQL01.domain.com`, a lookup for `azure_sql_user_password` checks: -### What is prefixes? +1. `nodes--WIN-SQL-domain-com--azure-sql-user-password` (node-specific) +2. `common--azure-sql-user-password` (shared, if node-specific not found) -Prefixes are an optional feature that allow you to create a "hierarchy" similar to the YAML backend built into Puppet. This enables you to configure node-specific secrets and/or create a customized lookup hierarchy. It is also useful if you are migrating from HashiCorp Hiera Vault to Azure Key Vault, as this behavior is similar to that of the [HashiCorp hiera_lookup puppet module](https://forge.puppet.com/modules/petems/hiera_vault/readme). +**Note:** Prefixes are normalized using the `key_replacement_token` to ensure compatibility with Key Vault naming requirements. +### Using Facts -Let's walk through an example to see how this works. If you wanted the ability to configure node-specific secrets but fall back on a "common/shared" secret when none are configured, you can set up the prefixes as shown below: +You can use facts to specify different vaults for different node groups. Use trusted facts when possible, as they cannot be altered. + +**Example:** ```yaml - - name: 'Azure Key Vault Secrets' - lookup_key: azure_key_vault::lookup - options: - vault_name: secrets-vault - vault_api_version: '2016-10-01' - metadata_api_version: '2018-04-02' - key_replacement_token: '-' - prefixes: - - nodes--%{trusted.hostname}-- - - common-- - confine_to_keys: - - '^azure_.*' +- name: 'Azure Key Vault Secrets' + lookup_key: azure_key_vault::lookup + options: + vault_name: "%{trusted.extensions.pp_environment}" + vault_api_version: '2016-10-01' + metadata_api_version: '2018-04-02' + key_replacement_token: '-' + confine_to_keys: + - '^azure_.*' + - '^.*_password$' + - '^password.*' ``` -If a Puppet catalog is being compiled for a node with the `trusted.hostname` fact set to `WIN-SQL01.domain.com`, the following lookups would occur in the azure vault named `secrets-vault`: +You can also use [custom trusted facts in certificate requests](https://puppet.com/docs/puppet/latest/ssl_attributes_extensions.html). -1. Check for a secret named `nodes--WIN-SQL-domain-com--profile--windows--sqlserver--sensitive-azure-sql-user-password`. If it exists, cache and return the secret. If not, continue to the next level in the hierarchy. +## Security -2. Check for a secret named `common--profile--windows--sqlserver--sensitive-azure-sql-user-password`. If it exists, cache and return the secret. If it does not exist, return nothing and proceed to the next configured lookup if one exists in the hiera.yaml file. +### How It's Secure by Default -**NOTE: The prefixes are always normalized using the [key_replacement_token](#what-is-key_replacement_token). This is because certain facts (such as a machine's hostname) may contain characters that are not supported as part of a secret's name. This normalization prevents failures and avoids forcing users to make difficult decisions, such as changing a node's hostname, which is not ideal.** +To prevent accidental leakage of secrets in logs, reports, and other Puppet-generated data, the `azure_key_vault::secret` function and Hiera backend return secrets wrapped in Puppet's `Sensitive` data type. - -## How it's secure by default - -In order to prevent accidental leakage of your secrets throughout all of the locations puppet stores information the returned value of the `azure_key_vault::secret` function & Hiera backend return a string wrapped in a Sensitive data type. Lets look at an example of what this means and why it's important. Below is an example of pulling a secret and trying to output the value in a notice function. +**Example:** ```puppet $secret = azure_key_vault::secret('production-vault', 'important-secret', { @@ -338,22 +343,15 @@ $secret = azure_key_vault::secret('production-vault', 'important-secret', { notice($secret) ``` -This outputs `Notice: Scope(Class[main]): Sensitive [value redacted]` - -However, Sometimes you need to unwrap the secret to get to the original data. This is typically needed under the following but not limited to circumstances. +**Output:** `Notice: Scope(Class[main]): Sensitive [value redacted]` -1. You need to programatically change/alter/append to the secret that was retrieved. -1. The resource you are passing the secret to does not natively handle the Sensitive data type. +### Working with Sensitive Data -These 2 special cases are discussed in detail next. +Sometimes you need to unwrap secrets to manipulate them or use them with resources that don't support the `Sensitive` type. -#### Special Case 1 - Programatically Changing/Altering/Appending to a secret +#### Special Case 1: Modifying a Secret -In order to change the original secret you always follow the same 3 step process. - -1. unwrap -1. alter/change -1. rewrap +To modify a secret, follow this 3-step process: unwrap, modify, rewrap. ```puppet $secret = azure_key_vault::secret('production-vault', 'important-secret', { @@ -361,17 +359,17 @@ $secret = azure_key_vault::secret('production-vault', 'important-secret', { vault_api_version => '2016-10-01', }) -$rewraped_secret = Sensitive("password: ${secret.unwrap}") +$rewrapped_secret = Sensitive("password: ${secret.unwrap}") file { 'C:\\DataForApplication.secret': - content => $rewraped_secret, + content => $rewrapped_secret, ensure => file, } ``` -#### Special Case 2 - A Resource doesn't natively support Puppet's Sensitive Data type +#### Special Case 2: Resources Without Sensitive Support -Unfortunately, All resource's don't magically handle the sensitive data type. In order to know if a resource supports it or not simply read the documentation or browse through the code if it's available. If you are using a resource that doesn't support the sensitive data type you can unwrap the secret but but you are no longer guaranteed the secret will not get leaked in logs/reports depending on what the resource does with the secret you passed to it. Below is an example of an imaginary resource that doesn't support the sensitive data type and how you can unwrap to handle this situation. +Not all resources support the `Sensitive` type. Check the documentation or code to determine support. If a resource doesn't support `Sensitive`, you can unwrap the secret, but you're no longer guaranteed it won't leak in logs or reports. ```puppet $admin_password_secret = azure_key_vault::secret('production-vault', 'important-secret', { @@ -385,13 +383,13 @@ resource_not_supporting_sensitive { 'my_resource': } ``` -**NOTE: Whatever resource you run into that doesn't support the sensitive data type you should open a issue/ticket with the person/organization maintaining the resource.** +**Note:** If you encounter a resource that doesn't support `Sensitive`, please open an issue with the maintainer. -## Usage +## Examples -### Embedding a secret in a file +### Embedding a Secret in a File -Below shows an example of how to retrieve a secret and place it in a file on a node. This is typically done because some application/process is expecting the file to exist with the secret in order to get some work done ( such as connecting to a database ). +Retrieve a secret and write it to a file on a node: ```puppet file { 'C:\\DataForApplication.secret': @@ -403,9 +401,9 @@ file { 'C:\\DataForApplication.secret': } ``` -### Retrieving a specific version of a secret +### Retrieving a Specific Version -By Default, the latest secret is always retrieved from the vault. If you want to ensure only a specific version of a secret is retrieved simply pass a parameter to specify the exact version you want. +By default, the latest version of a secret is retrieved. To retrieve a specific version, pass the version ID as the fourth parameter: ```puppet $admin_password_secret = azure_key_vault::secret('production-vault', 'admin-password', { @@ -415,12 +413,11 @@ $admin_password_secret = azure_key_vault::secret('production-vault', 'admin-pass '067e89990f0a4a50a7bd854b40a56089') ``` -**NOTE: Retrieving a specific version of a secret is currently not available via the hiera backend** +**Note:** Retrieving specific versions is not available via the Hiera backend. -### Retrieving a certificate +### Retrieving a Certificate -Azure Key Vault stores certificates "under-the-covers" as secrets. This means you retrieving certificates can be done using the same `azure_key_vault::secret` -function. One thing to keep in mind is that the certificate will be based64 encoded and will need to be decoded before usage to have a valid certificate file. +Azure Key Vault stores certificates as secrets. Retrieve them using `azure_key_vault::secret`. The certificate will be base64 encoded and must be decoded before use. ```puppet $certificate_secret = azure_key_vault::secret('production-vault', "webapp-certificate", { @@ -441,17 +438,17 @@ sslcertificate { "Install-WebApp-Certificate" : } ``` -**NOTE: Retrieving a specific version of a secret is currently not available via the hiera backend** +**Note:** Retrieving specific certificate versions is not available via the Hiera backend. ## Reference See [REFERENCE.md](https://github.com/tragiccode/tragiccode-azure_key_vault/blob/master/REFERENCE.md) -## Development +## Development and Contributing -## Contributing +We welcome contributions! To contribute: -1. Fork it ( ) +1. Fork the repository () 1. Create your feature branch (`git checkout -b my-new-feature`) 1. Commit your changes (`git commit -am 'Add some feature'`) 1. Push to the branch (`git push origin my-new-feature`)