Skip to content

Commit

Permalink
feat: Add support for HCP Vault Secrets
Browse files Browse the repository at this point in the history
  • Loading branch information
twpayne committed Jun 28, 2023
1 parent 26a02d4 commit 2fd6696
Show file tree
Hide file tree
Showing 12 changed files with 226 additions and 13 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,10 @@ sections:
symmetric:
type: bool
description: Use symmetric GPG encryption
hcpVaultSecrets:
command:
default: '`vlt`'
description: HCP Vault Secrets CLI command
hooks:
'*command*`.post.args`':
type: '[]string'
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# `hcpVaultSecrets` *key* [*app-name* [*project* [*organization*]]]

`hcpVaultSecrets` returns the plaintext secret from [HCP Vault
Secrets](https://developer.hashicorp.com/hcp/docs/vault-secrets) using the `vlt`
CLI.

!!! example

```
{{ hcpVaultSecrets "username" }}
```
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# `hcpVaultSecrets` *key* [*app-name* [*project* [*organization*]]]

`hcpVaultSecrets` returns the secret from [HCP Vault
Secrets](https://developer.hashicorp.com/hcp/docs/vault-secrets) using the `vlt`
CLI.

!!! example

```
{{ (hcpVaultSecretsJson "username") }}
```
10 changes: 10 additions & 0 deletions assets/chezmoi.io/docs/reference/templates/vlt-functions/index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# HCP Vault Secrets

chezmoi includes support for [HCP Vault
Secrets](https://developer.hashicorp.com/hcp/docs/vault-secrets) using the `vlt`
CLI to expose data through the `hcpVaultSecrets` and `hcpVaultSecretsJson`
template functions.

!!! warning

HCP Vault Secrets is in beta and chezmoi's support for it may change at any time.
21 changes: 11 additions & 10 deletions assets/chezmoi.io/docs/user-guide/password-managers/custom.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,14 @@ configuration file. You can then invoke this command with the `secret` and
output respectively. All of the above secret managers can be supported in this
way:

| Secret Manager | `secret.command` | Template skeleton |
| --------------- | ---------------- | -------------------------------------------------- |
| 1Password | `op` | `{{ secretJSON "get" "item" "$ID" }}` |
| Bitwarden | `bw` | `{{ secretJSON "get" "$ID" }}` |
| HashiCorp Vault | `vault` | `{{ secretJSON "kv" "get" "-format=json" "$ID" }}` |
| LastPass | `lpass` | `{{ secretJSON "show" "--json" "$ID" }}` |
| KeePassXC | `keepassxc-cli` | Not possible (interactive command only) |
| Keeper | `keeper` | `{{ secretJSON "get" "--format=json" "$ID" }}` |
| pass | `pass` | `{{ secret "show" "$ID" }}` |
| passhole | `ph` | `{{ secret "$ID" "password" }}` |
| Secret Manager | `secret.command` | Template skeleton |
| ----------------- | ---------------- | -------------------------------------------------- |
| 1Password | `op` | `{{ secretJSON "get" "item" "$ID" }}` |
| Bitwarden | `bw` | `{{ secretJSON "get" "$ID" }}` |
| HashiCorp Vault | `vault` | `{{ secretJSON "kv" "get" "-format=json" "$ID" }}` |
| HCP Vault Secrets | `vlt` | `{{ secret "secrets" "get" "--plaintext" "$ID }}` |
| LastPass | `lpass` | `{{ secretJSON "show" "--json" "$ID" }}` |
| KeePassXC | `keepassxc-cli` | Not possible (interactive command only) |
| Keeper | `keeper` | `{{ secretJSON "get" "--format=json" "$ID" }}` |
| pass | `pass` | `{{ secret "show" "$ID" }}` |
| passhole | `ph` | `{{ secret "$ID" "password" }}` |
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# HCP Vault Secrets

chezmoi includes support for [HCP Vault
Secrets](https://developer.hashicorp.com/hcp/docs/vault-secrets) using the `vlt`
CLI to expose data through the `hcpVaultSecrets` and `hcpVaultSecretsJson`
template functions.

!!! warning

HCP Vault Secrets is in beta and chezmoi's support for it may change at any time.
7 changes: 4 additions & 3 deletions assets/chezmoi.io/docs/what-does-chezmoi-do.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,9 +45,10 @@ format of your choice. chezmoi can retrieve secrets from
[1Password](https://1password.com/), [AWS Secrets
Manager](https://aws.amazon.com/secrets-manager/),
[Bitwarden](https://bitwarden.com/), [Dashlane](https://www.dashlane.com/),
[gopass](https://www.gopass.pw/), [KeePassXC](https://keepassxc.org/),
[Keeper](https://www.keepersecurity.com/), [LastPass](https://lastpass.com/),
[pass](https://www.passwordstore.org/),
[gopass](https://www.gopass.pw/), [HCP Vault
Secrets](https://developer.hashicorp.com/hcp/docs/vault-secrets),
[KeePassXC](https://keepassxc.org/), [Keeper](https://www.keepersecurity.com/),
[LastPass](https://lastpass.com/), [pass](https://www.passwordstore.org/),
[passhole](https://github.com/Evidlo/passhole),
[Vault](https://www.vaultproject.io/), Keychain,
[Keyring](https://wiki.gnome.org/Projects/GnomeKeyring), or any command-line
Expand Down
5 changes: 5 additions & 0 deletions assets/chezmoi.io/mkdocs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ nav:
- pass: user-guide/password-managers/pass.md
- passhole: user-guide/password-managers/passhole.md
- Vault: user-guide/password-managers/vault.md
- Hashicorp Vault Secrets: user-guide/password-managers/hcp-vault-secrets.md
- Custom: user-guide/password-managers/custom.md
- Encryption:
- user-guide/encryption/index.md
Expand Down Expand Up @@ -254,6 +255,10 @@ nav:
- reference/templates/gopass-functions/index.md
- gopass: reference/templates/gopass-functions/gopass.md
- gopassRaw: reference/templates/gopass-functions/gopassRaw.md
- HCP Vault Secrets functions:
- reference/templates/hcp-vault-secrets-functions/index.md
- hcpVaultSecret: reference/templates/hcp-vault-secrets-functions/hcpVaultSecret.md
- hcpVaultSecretJson: reference/templates/hcp-vault-secrets-functions/hcpVaultSecretJson.md
- KeePassXC functions:
- reference/templates/keepassxc-functions/index.md
- keepassxc: reference/templates/keepassxc-functions/keepassxc.md
Expand Down
6 changes: 6 additions & 0 deletions pkg/cmd/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,7 @@ type ConfigFile struct {
Dashlane dashlaneConfig `json:"dashlane" mapstructure:"dashlane" yaml:"dashlane"`
Ejson ejsonConfig `json:"ejson" mapstructure:"ejson" yaml:"ejson"`
Gopass gopassConfig `json:"gopass" mapstructure:"gopass" yaml:"gopass"`
HCPVaultSecrets hcpVaultSecretsConfig `json:"hcpVaultSecrets" mapstructure:"hcpVaultSecrets" yaml:"hcpVaultSecrets"`
Keepassxc keepassxcConfig `json:"keepassxc" mapstructure:"keepassxc" yaml:"keepassxc"`
Keeper keeperConfig `json:"keeper" mapstructure:"keeper" yaml:"keeper"`
Lastpass lastpassConfig `json:"lastpass" mapstructure:"lastpass" yaml:"lastpass"`
Expand Down Expand Up @@ -401,6 +402,8 @@ func newConfig(options ...configOption) (*Config, error) {
"glob": c.globTemplateFunc,
"gopass": c.gopassTemplateFunc,
"gopassRaw": c.gopassRawTemplateFunc,
"hcpVaultSecrets": c.hcpVaultSecretsTemplateFunc,
"hcpVaultSecretsJson": c.hcpVaultSecretsJSONTemplateFunc,
"hexDecode": c.hexDecodeTemplateFunc,
"hexEncode": c.hexEncodeTemplateFunc,
"include": c.includeTemplateFunc,
Expand Down Expand Up @@ -2581,6 +2584,9 @@ func newConfigFile(bds *xdg.BaseDirectorySpecification) ConfigFile {
Gopass: gopassConfig{
Command: "gopass",
},
HCPVaultSecrets: hcpVaultSecretsConfig{
Command: "vlt",
},
Keepassxc: keepassxcConfig{
Command: "keepassxc-cli",
Prompt: true,
Expand Down
90 changes: 90 additions & 0 deletions pkg/cmd/hcpvaultsecretsttemplatefuncs.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
package cmd

import (
"encoding/json"
"fmt"
"os"
"os/exec"
"strings"

"github.com/twpayne/chezmoi/v2/pkg/chezmoilog"
)

type hcpVaultSecretsConfig struct {
Command string `json:"command" mapstructure:"command" yaml:"command"`
outputCache map[string][]byte
}

func (c *Config) hcpVaultSecretsTemplateFunc(key string, additionalArgs ...string) string {
args, err := appendHCPVaultSecretsAdditionalArgs(
[]string{"secrets", "get", "--plaintext"},
additionalArgs,
)
if err != nil {
panic(err)
}
output, err := c.vltOutput(append(args, key))
if err != nil {
panic(err)
}
return string(output)
}

func (c *Config) hcpVaultSecretsJSONTemplateFunc(key string, additionalArgs ...string) any {
args, err := appendHCPVaultSecretsAdditionalArgs(
[]string{"secrets", "get", "--format", "json"},
additionalArgs,
)
if err != nil {
panic(err)
}
data, err := c.vltOutput(append(args, key))
if err != nil {
panic(err)
}
var value any
if err := json.Unmarshal(data, &value); err != nil {
panic(err)
}
return value
}

func appendHCPVaultSecretsAdditionalArgs(args, additionalArgs []string) ([]string, error) {
if len(additionalArgs) > 0 && additionalArgs[0] != "" {
args = append(args, "--app-name", additionalArgs[0])
}
if len(additionalArgs) > 1 && additionalArgs[1] != "" {
args = append(args, "--project", additionalArgs[1])
}
if len(additionalArgs) > 2 && additionalArgs[2] != "" {
args = append(args, "--organization", additionalArgs[2])
}
if len(additionalArgs) > 3 {
// Add one to the number of received arguments as the hcpVaultSecrets
// and hcpVaultSecretsJson template functions report this error and take
// the key as the first argument.
return nil, fmt.Errorf("expected 1 to 4 arguments, got %d", len(additionalArgs)+1)
}
return args, nil
}

func (c *Config) vltOutput(args []string) ([]byte, error) {
key := strings.Join(args, "\x00")
if data, ok := c.HCPVaultSecrets.outputCache[key]; ok {
return data, nil
}

cmd := exec.Command(c.HCPVaultSecrets.Command, args...) //nolint:gosec
cmd.Stdin = os.Stdin
cmd.Stderr = os.Stderr
output, err := chezmoilog.LogCmdOutput(cmd)
if err != nil {
return nil, newCmdOutputError(cmd, output, err)
}

if c.HCPVaultSecrets.outputCache == nil {
c.HCPVaultSecrets.outputCache = make(map[string][]byte)
}
c.HCPVaultSecrets.outputCache[key] = output
return output, nil
}
48 changes: 48 additions & 0 deletions pkg/cmd/testdata/scripts/hcpvaultsecrets.txtar
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
[windows] skip 'UNIX only'
[!windows] chmod 755 bin/vlt

# test hcpVaultSecrets template function
exec chezmoi execute-template '{{ hcpVaultSecrets "username" }}'
stdout ^db-user$

# test hcpVaultSecrets template function with app name, project, and organization arguments
exec chezmoi execute-template '{{ hcpVaultSecrets "password" "app-name" "project" "organization" }}'
stdout ^password$

# test hcpVaultSecrets template function with empty app name, project, and organization arguments
exec chezmoi execute-template '{{ hcpVaultSecrets "username" "" "" "" }}'
stdout ^db-user$

# test hcpVaultSecretsJson template function
exec chezmoi execute-template '{{ (hcpVaultSecretsJson "username").created_by.email }}'
stdout ^username@example\.com$

-- bin/vlt --
#!/bin/sh

case "$*" in
"secrets get --format json username")
cat <<EOF
{
"created_at": "2023-06-09T13:14:28.140Z",
"created_by": {
"email": "[email protected]",
"name": "example",
"type": "TYPE_USER"
},
"latest_version": "2",
"name": "username"
}
EOF
;;
"secrets get --plaintext username")
echo db-user
;;
"secrets get --plaintext --app-name app-name --project project --organization organization password")
echo password
;;
*)
echo "$*: unknown command"
exit 1
;;
esac
16 changes: 16 additions & 0 deletions pkg/cmd/testdata/scripts/issue3065.txtar
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# test that chezmoi apply does not say that a file in an external has changed when it has not
exec chezmoi apply
exec chezmoi apply -v
! stdout .

-- home/user/.local/share/chezmoi/.chezmoiexternal.toml --
["Library/Rime"]
type = "archive"
url = "https://github.com/iDvel/rime-ice/archive/refs/heads/main.zip"
exact = true
stripComponents = 1
refreshPeriod = "720h"
-- home/user/.local/share/chezmoi/Library/exact_Rime/default.custom.yaml --
patch:
menu:
page_size: 7

0 comments on commit 2fd6696

Please sign in to comment.