-
Notifications
You must be signed in to change notification settings - Fork 129
Secrets Management RFC #208
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
0ff55ad
f5de83a
d1d2aaa
5cf9db6
31337ac
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,207 @@ | ||
--- | ||
RFC: RFCxxxx | ||
Author: Steve Lee | ||
Status: Draft | ||
SupercededBy: N/A | ||
Version: 0.1 | ||
Area: Security | ||
Comments Due: July 31st, 2019 | ||
Plan to implement: Yes, PS7 | ||
--- | ||
|
||
# Simplify and secure use of secrets in scripts | ||
|
||
Advanced scripts that touch many systems require multiple secrets and | ||
types of secrets particularly when orchestrating across different clouds. | ||
Best practice is to not hard code any secrets in scripts, but currently | ||
this requires custom code on different platforms to handle this securely. | ||
|
||
This RFC proposes new cmdlets to make this simpler and secure. | ||
|
||
## Motivation | ||
|
||
> As a PowerShell script author, | ||
> I can securely use multiple secrets, | ||
> so that I can automate complex orchestration across multiple remote resources. | ||
|
||
## High Level Design | ||
|
||
This is a new independent module called `Microsoft.PowerShell.SecretsManagement`. | ||
Secrets are stored securely in a local vault. | ||
SteveL-MSFT marked this conversation as resolved.
Show resolved
Hide resolved
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can you please list the cmdlets exposed from the module? I find it hard to infer from rest of the RFC about exposed cmdlets. |
||
The local vault is expected to only allow access to the user who owns that | ||
vault. | ||
Secrets required to access remote vaults are stored in the local vault and used by the secrets management | ||
cmdlets to retrieve remote secrets. | ||
|
||
`User Context` --> `Local Vault` --> `SecretsVault` --> `Remote Vault` | ||
|
||
SteveL-MSFT marked this conversation as resolved.
Show resolved
Hide resolved
|
||
## User Experience | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I suggest to move this second to where the extension model is discussed. For someone who is not familiar with |
||
|
||
Registering and using remote secrets: | ||
|
||
```powershell | ||
# Install the Az.KeyVault module to be used in this example. | ||
# This module doesn't conform to the extensions model so would need to be | ||
# updated to actually work. | ||
Install-Module Az.KeyVault | ||
|
||
# In this example, we explicitly register this extension | ||
Register-SecretsVault -Name Azure -Cmdlet Get-AzKeyVaultSecret ` | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should be Register-SecretVault (pluralization in cmdlet names) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not true. Seems like you missed @SteveL-MSFT's reply about that. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. While there might only be one "SecretsVault", and it might actually contain multiple "Secrets", the point of the "No Plurals" rule is about how plurals translate. At this point in the ecosystem, it's also about a reasonable expectation. I do not believe that any PowerShell user will assume that Register-SecretVault will register a vault and then make it secret (it kinda flies in the face of the cmdlet name). I do believe that, especially as we have "Add-Secret", "Register-SecretsVault" would end up being confusing in practice. Your brain would remember one or the other, and you'd be more likely to flub it. The other rationale, past pure pluralization, is the desire for common noun roots and the easy memorization that comes with it. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. One other call out here, now that I see it, is that it shold support a -Parameter @{} ,which will be passed down to the undelying command. The scenario for this is that my cmdlet fits fine, but I want to supply default parameters to map to the underlying store.
As I type out that bunch of code, I believe it would be nice to accept a command via pipeline, or to look up the command / assume defaults given the name. e.g.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. One more thing is that the -Cmdlet parameter is somewhat nonstandard. In a quick check of PS 5.1, the only cmds with -Cmdlet are:
All of which actually need cmdlet. Conversely, it's -Commad on:
The last pair is especially instructive here, as I recall a discussion when naming that parameter on this topic. We wanted to avoid naming command breakpoints -Cmdlet because they could use commands that were not Cmdelts. Unless you're saying this secret vault has to be a Cmdlet, the parameter should be -Command There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
-Module Az.KeyVault | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe move this line to the previous line instead of using a line continuation? |
||
|
||
# When using remote vaults, expectation is that secrets are | ||
# stored using their tooling, so this module is only for retrieving secrets | ||
# from remote vaults | ||
$azDevOpsToken = Get-Secret -Name AzDevOps | ||
Invoke-RestMethod https://dev.azure.com/… -Credential $azDevOpsToken -Authentication Basic | ||
``` | ||
|
||
Registering and using local secret: | ||
|
||
```powershell | ||
# For local vault, we can register custom secrets | ||
# In this example, we store a PSCredential object | ||
Add-Secret -Secret $cred -Name MyCreds | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is it possible to add tags? It seems quite useful to add additional descriptive data (and it looks to be supported by the back end store). Also, I would like to have a description of all the parameters. |
||
New-PSSession -ComputerName myServer -Credential (Get-Secret -Name MyCreds) | ||
``` | ||
|
||
## Specification | ||
|
||
### Secrets Vault | ||
|
||
There are two types of vaults: local and remote. | ||
A local vault, by default, is already created and named `Default`. | ||
`Default` on Windows is [CredMan](https://docs.microsoft.com/en-us/windows/desktop/SecAuthN/credentials-management). | ||
`Default` on macOS is [KeyChain](https://developer.apple.com/documentation/security/keychain_services). | ||
|
||
>[!NOTE] | ||
>KeyChain support is unlikely to be available in the first release of this feature. | ||
|
||
`Default` on Linux is [Gnome Keyring](https://wiki.gnome.org/Projects/GnomeKeyring/). | ||
|
||
>[!NOTE] | ||
>Linux has many options for local credential management. The choice of Gnome Keyring | ||
>is that it's a simple local vault we can easily test against and validate that our | ||
>design works with a well-known Linux local vault. Since the extension model is | ||
>open, we expect additional vault support to come from owners of those vaults or | ||
>the community. | ||
|
||
### Credential Vault Extensions | ||
|
||
Vault extensions whether they are local or remote are provided as modules. | ||
|
||
Extensions are modules that either: | ||
|
||
- Call `Register-SecretsVault` upon loading the module, or | ||
- Expose a command to the user which calls `Register-SecretsVault` using | ||
input from the user | ||
|
||
Modules that provide a vault extension should have the tag `SecretsVaultExtension`. | ||
|
||
Extensions can register themselves by calling `Register-SecretsVault` | ||
passing the name of the relevant cmdlet to `-Cmdlet` and the module name to `-Module`. | ||
A `-Name` parameter is used for the user to define a friendly name. | ||
There is an optional `-Parameters` parameter that takes a hashtable that will | ||
be splatted to the extension cmdlet to support additional metadata needed | ||
by the extension (such as authentication). | ||
|
||
When using this model, the extension cmdlet would have to match the parameters and | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this is pretty sketchy - would you please provide a demo.txt, or similar? |
||
output of `Get-Secret` to be compatible. | ||
|
||
Alternatively, a `-ScriptBlock` parameter can be used instead of `-Cmdlet` and `-Module` | ||
to allow using existing cmdlets without the need to write a wrapper module: | ||
|
||
```powershell | ||
Register-SecretsVault -Name AzKeyVault { | ||
param($Name) | ||
Get-AzKeyVaultSecret -VaultName (Get-Secret AzureKeyVaultName) -Name $Name | | ||
Select -Expand SecretValue | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The downside to this kind of implementation (as opposed to a C# implementation with standardized implementations) is that there's no consistency as to how things like error handling work. Can we include a list of standard error messages that providers should use for a few common scenarios such as:
If providers all implement the exact same error handling for these scenarios, then it will make switching between providers much easier, as error handling code won't have to be rewritten. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. As ideal as standardized error codes are, I do not believe this is something where one could guarantee underlying consistency. A web-based SecretVault will have a different return code under the covers compared to a C-api based SecretVault. We could encourage people to use standard -ErrorIDs ( or at least a standard -ErrorCategory) to attempt to address the problem. Luckily, -ErrorCategory already contains the two we need: ObjectNotFound and PermissionDenied (or AuthenticationError). As far as the need/want for a C# api, one can always use PowerShell's C# api to treat these cmdlets as a C# API. e.g.
Thus I believe not only isn't it in scope for this RFC, it shouldn't be for any future RFCs. I believe to do so opens a nasty pandora's box which would encourage other aspects of PowerShell to provide a C# api as well. This in turn de-emphasizes the need or want to ever write scripts. Thus I believe we should specify standard error categories, but should not be in the habit of making special C# wrappers for PowerShell. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Cool, the important part of my comment above was the standardization of errors. C# makes it easier to enforce standards, but having a section in the RFC stating that providers should implement the existing error categories/ID's to ensure consistency between different providers is the next best thing. For example, if one had multiple remote providers and wanted to find a secret but wasn't certain which provider contained it (e.g. during a migration from one provider to another) then they could write a script that iterates through each installed remote provider and puts a "continue" in the catch block if it encounters an exception in the "ObjectNotFound" category. It would suck to have to handle each provider's error in a different way. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
} | ||
``` | ||
|
||
`Register-SecretsVault` has a `-Default` switch to register the | ||
default local vault (if applicable, see above) if it has been unregistered. | ||
|
||
If neither are supplied, then the current user context is used and relies on | ||
the extension cmdlet to handle authentication. | ||
SteveL-MSFT marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
If registering a vault with a Name that already exists, an error will be returned. | ||
|
||
`Unregister-SecretsVault` should be used to remove any existing | ||
registration or when updating a registration to a new version. | ||
SteveL-MSFT marked this conversation as resolved.
Show resolved
Hide resolved
|
||
A `-Name` parameter is mandatory to remove a specific extension. | ||
|
||
`Get-SecretsVault` will enumerate registered extensions returning | ||
the module name and cmdlet used (if appropriate) and the friendly name. | ||
|
||
A `SecretsVaultInfo` object will contain properties for all supported | ||
parameters by registration. | ||
|
||
```output | ||
Name Module Cmdlet ScriptBlock | ||
---- ------ ------ ----------- | ||
AzKeyVault AzKeyVault Get-AzKeyVaultSecret | ||
myVault param($Name) | ||
``` | ||
|
||
### Storing Secrets | ||
|
||
The `Add-Secret` cmdlet is used to store a secret. | ||
The `-Name` must be unique within a vault. | ||
The `-Vault` parameter defaults to the local vault. | ||
A `-NoClobber` parameter will cause this cmdlet to fail if the secret already exists. | ||
A `-Secret` parameter accepts one of the supported types outlined below. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this is where I would love to have some sort of |
||
|
||
SteveL-MSFT marked this conversation as resolved.
Show resolved
Hide resolved
|
||
### Retrieving Secrets | ||
|
||
The `Get-Secret` cmdlet is used to retrieve secrets as the same type as they | ||
were originally added. | ||
The `-Name` parameter retrieves the secret associated with that name. | ||
The `-Vault` parameter defaults to the local vault. | ||
The returned object will be the original stored secret type. | ||
|
||
`Get-Secret` without `-Name` will enumerate all stored secrets returning the | ||
name and the vault. | ||
`-Vault` can be used to filter to a specific vault for the enumeration. | ||
|
||
### Removing Secrets | ||
|
||
The `Remove-Secret` cmdlet is used to remove a stored secret. | ||
|
||
SteveL-MSFT marked this conversation as resolved.
Show resolved
Hide resolved
|
||
### Authorization | ||
|
||
Access to the credential vault is always using the current process security context. | ||
In the case of remote vaults, the remote credential is stored within the local | ||
default vault and retrieved as needed when accessing secrets from that remote | ||
vault. | ||
|
||
SteveL-MSFT marked this conversation as resolved.
Show resolved
Hide resolved
|
||
### Secrets Supported | ||
|
||
Secrets supported will be: | ||
|
||
- PSCredential | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. network credential? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is that a secret of any kind? It has a plaintext password property. |
||
- SecureString | ||
SteveL-MSFT marked this conversation as resolved.
Show resolved
Hide resolved
|
||
- String | ||
- HashTable | ||
- Byte[] | ||
|
||
SteveL-MSFT marked this conversation as resolved.
Show resolved
Hide resolved
|
||
## Alternate Proposals and Considerations | ||
|
||
In this release, the following are non-goals that can be addressed in the future: | ||
|
||
- Provision to rotate certs/access tokens | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this seems, in any event, as additional code based on the ones described herein. |
||
- Sharing local vaults across different computer systems | ||
- Sharing local vault across different user contexts | ||
- A PSProvider for a Secrets: PSDrive | ||
- C# interface for extensions | ||
- C# API to retrieve secrets for C# based modules | ||
- Delegation support | ||
- Local vault requiring to be unlocked automatically | ||
- Ubiquitous `-Secret` parameter taking a hashtable to automatically populate | ||
a cmdlet's parameter taking a secret from the vault: | ||
|
||
```powershell | ||
Invoke-WebRequest -Secret @{Credential="GitHubCred"} | ||
# this retrieves the secret called GitHubCred and passes it to the `-Credential` | ||
# parameter of Invoke-WebRequest | ||
``` |
Uh oh!
There was an error while loading. Please reload this page.