Skip to content

Commit

Permalink
feat: Add bitwardenSecrets template function
Browse files Browse the repository at this point in the history
  • Loading branch information
twpayne committed Aug 27, 2023
1 parent 02ffbd7 commit f6947d3
Show file tree
Hide file tree
Showing 10 changed files with 175 additions and 2 deletions.
Expand Up @@ -111,6 +111,10 @@ sections:
command:
default: '`bw`'
description: Bitwarden CLI command
bitwardenSecrets:
command:
default: '`bws`'
description: Bitwarden Secrets CLI command
cd:
args:
type: '[]string'
Expand Down
@@ -0,0 +1,20 @@
# `bitwardenSecrets` *secret-id* [*access-token*]

`bitwardenSecrets` returns structured data from
[Bitwarden](https://bitwarden.com) using the [Bitwarden Secrets
CLI](https://bitwarden.com/help/secrets-manager-cli/) (`bws`). *secret-id* is
passed to `bws secret get` and the output from `bws secret get` is parsed as
JSON and returned.

If the additional *access-token* argument is given, it is passed to `bws secret
get` with the `--access-token` flag.

The output from `bws secret get` is cached so calling `bitwardenSecrets`
multiple times with the same *secret-id* and *access-token* will only invoke
`bws secret get ` once.

!!!

```
{{ (bitwardenSecrets "be8e0ad8-d545-4017-a55a-b02f014d4158").value }}
```
Expand Up @@ -2,5 +2,6 @@

The `bitwarden*` and `rbw*` functions return data from
[Bitwarden](https://bitwarden.com) using the [Bitwarden
CLI](https://bitwarden.com/help/article/cli/) (`bw`) and
CLI](https://bitwarden.com/help/article/cli/) (`bw`), [Bitwarden Secrets
CLI](https://bitwarden.com/help/secrets-manager-cli/) (`bws`), and
[`rbw`](https://github.com/doy/rbw) commands.
26 changes: 25 additions & 1 deletion assets/chezmoi.io/docs/user-guide/password-managers/bitwarden.md
@@ -1,9 +1,13 @@
# Bitwarden

chezmoi includes support for [Bitwarden](https://bitwarden.com/) using the
[Bitwarden CLI](https://bitwarden.com/help/cli) to expose data as a template
[Bitwarden CLI](https://bitwarden.com/help/cli) (`bw`), [Bitwarden
Secrets CLI](https://bitwarden.com/help/secrets-manager-cli/) (`bws`), and
[`rbw`](https://github.com/doy/rbw) commands to expose data as a template
function.

## Bitwarden CLI

Log in to Bitwarden using a normal method

```console
Expand Down Expand Up @@ -65,3 +69,23 @@ or
```
{{ bitwardenAttachmentByRef "id_rsa" "item" "example.com" }}
```

## Bitwarden Secrets CLI

Generate an [access token](https://bitwarden.com/help/access-tokens/) for a
specific [service account](https://bitwarden.com/help/service-accounts/).

Either set the `BWS_ACCESS_TOKEN` environment variable or store the access token
in a template variable, e.g.

```toml title="~/.config/chezmoi/chezmoi.toml"
[data]
accessToken = "0.48c78342-1635-48a6-accd-afbe01336365.C0tMmQqHnAp1h0gL8bngprlPOYutt0:B3h5D+YgLvFiQhWkIq6Bow=="
```

You can then retrive secrets using the `bitwardenSecrets` template function, for
example:

```
{{ (bitwardenSecrets "be8e0ad8-d545-4017-a55a-b02f014d4158" .accessToken).value }}
```
1 change: 1 addition & 0 deletions assets/chezmoi.io/mkdocs.yml
Expand Up @@ -251,6 +251,7 @@ nav:
- bitwardenAttachment: reference/templates/bitwarden-functions/bitwardenAttachment.md
- bitwardenAttachmentByRef: reference/templates/bitwarden-functions/bitwardenAttachmentByRef.md
- bitwardenFields: reference/templates/bitwarden-functions/bitwardenFields.md
- bitwardenSecrets: reference/templates/bitwarden-functions/bitwardenSecrets.md
- rbw: reference/templates/bitwarden-functions/rbw.md
- rbwFields: reference/templates/bitwarden-functions/rbwFields.md
- Dashlane functions:
Expand Down
59 changes: 59 additions & 0 deletions internal/cmd/bitwardensecretstemplatefuncs.go
@@ -0,0 +1,59 @@
package cmd

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

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

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

func (c *Config) bitwardenSecretsTemplateFunc(secretID string, additionalArgs ...string) any {
args := []string{"secret", "get", secretID}
switch len(additionalArgs) {
case 0:
// Do nothing.
case 1:
args = append(args, "--access-token", additionalArgs[0])
default:
panic(fmt.Errorf("expected 1 or 2 arguments, got %d", len(additionalArgs)+1))
}
output, err := c.bitwardenSecretsOutput(args)
if err != nil {
panic(err)
}
var data map[string]any
if err := json.Unmarshal(output, &data); err != nil {
panic(newParseCmdOutputError(c.BitwardenSecrets.Command, args, output, err))
}
return data
}

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

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

if c.BitwardenSecrets.outputCache == nil {
c.BitwardenSecrets.outputCache = make(map[string][]byte)
}
c.BitwardenSecrets.outputCache[key] = output
return output, nil
}
5 changes: 5 additions & 0 deletions internal/cmd/config.go
Expand Up @@ -127,6 +127,7 @@ type ConfigFile struct {
AWSSecretsManager awsSecretsManagerConfig `json:"awsSecretsManager" mapstructure:"awsSecretsManager" yaml:"awsSecretsManager"`
AzureKeyVault azureKeyVaultConfig `json:"azureKeyVault" mapstructure:"azureKeyVault" yaml:"azureKeyVault"`
Bitwarden bitwardenConfig `json:"bitwarden" mapstructure:"bitwarden" yaml:"bitwarden"`
BitwardenSecrets bitwardenSecretsConfig `json:"bitwardenSecrets" mapstructure:"bitwardenSecrets" yaml:"bitwardenSecrets"`
Dashlane dashlaneConfig `json:"dashlane" mapstructure:"dashlane" yaml:"dashlane"`
Doppler dopplerConfig `json:"doppler" mapstructure:"doppler" yaml:"doppler"`
Ejson ejsonConfig `json:"ejson" mapstructure:"ejson" yaml:"ejson"`
Expand Down Expand Up @@ -392,6 +393,7 @@ func newConfig(options ...configOption) (*Config, error) {
"bitwardenAttachment": c.bitwardenAttachmentTemplateFunc,
"bitwardenAttachmentByRef": c.bitwardenAttachmentByRefTemplateFunc,
"bitwardenFields": c.bitwardenFieldsTemplateFunc,
"bitwardenSecrets": c.bitwardenSecretsTemplateFunc,
"comment": c.commentTemplateFunc,
"dashlaneNote": c.dashlaneNoteTemplateFunc,
"dashlanePassword": c.dashlanePasswordTemplateFunc,
Expand Down Expand Up @@ -2594,6 +2596,9 @@ func newConfigFile(bds *xdg.BaseDirectorySpecification) ConfigFile {
Bitwarden: bitwardenConfig{
Command: "bw",
},
BitwardenSecrets: bitwardenSecretsConfig{
Command: "bws",
},
Dashlane: dashlaneConfig{
Command: "dcli",
},
Expand Down
8 changes: 8 additions & 0 deletions internal/cmd/doctorcmd.go
Expand Up @@ -305,6 +305,14 @@ func (c *Config) runDoctorCmd(cmd *cobra.Command, args []string) error {
versionArgs: []string{"--version"},
versionRx: regexp.MustCompile(`^(\d+\.\d+\.\d+)`),
},
&binaryCheck{
name: "bitwarden-secrets-command",
binaryname: c.BitwardenSecrets.Command,
ifNotSet: checkResultWarning,
ifNotExist: checkResultInfo,
versionArgs: []string{"--version"},
versionRx: regexp.MustCompile(`Bitwarden\s+Secrets\s+CLI\s+(\d+\.\d+\.\d+)`),
},
&binaryCheck{
name: "dashlane-command",
binaryname: c.Dashlane.Command,
Expand Down
45 changes: 45 additions & 0 deletions internal/cmd/testdata/scripts/bitwarden.txtar
@@ -1,5 +1,7 @@
[!windows] chmod 755 bin/bw
[!windows] chmod 755 bin/bws
[windows] unix2dos bin/bw.cmd
[windows] unix2dos bin/bws.cmd
[windows] unix2dos golden/bitwarden-attachment

# test bitwarden template function
Expand All @@ -18,6 +20,10 @@ cmp stdout golden/bitwarden-attachment
exec chezmoi execute-template '{{ (bitwardenAttachmentByRef "filename" "item" "example.com") }}'
cmp stdout golden/bitwarden-attachment

# test bitwardenSecrets template function
exec chezmoi execute-template '{{ (bitwardenSecrets "be8e0ad8-d545-4017-a55a-b02f014d4158" "0.48c78342-1635-48a6-accd-afbe01336365.C0tMmQqHnAp1h0gL8bngprlPOYutt0:B3h5D+YgLvFiQhWkIq6Bow==").value }}'
stdout '^0\.982492bc-7f37-4475-9e60$'

-- bin/bw --
#!/bin/sh

Expand Down Expand Up @@ -113,5 +119,44 @@ IF "%*" == "get item example.com" (
echo "See --help for a list of available commands."
exit /b 1
)
-- bin/bws --
#!/bin/sh

case "$*" in
"secret get be8e0ad8-d545-4017-a55a-b02f014d4158 --access-token 0.48c78342-1635-48a6-accd-afbe01336365.C0tMmQqHnAp1h0gL8bngprlPOYutt0:B3h5D+YgLvFiQhWkIq6Bow==")
cat <<EOF
{
"object": "secret",
"id": "be8e0ad8-d545-4017-a55a-b02f014d4158",
"organizationId": "10e8cbfa-7bd2-4361-bd6f-b02e013f9c41",
"projectId": "e325ea69-a3ab-4dff-836f-b02e013fe530",
"key": "SES_KEY",
"value": "0.982492bc-7f37-4475-9e60",
"note": "",
"creationDate": "2023-06-28T20:13:20.643567Z",
"revisionDate": "2023-06-28T20:13:20.643567Z"
}
EOF
;;
*)
exit 1
esac
-- bin/bws.cmd --
@echo off
IF "%*" == "secret get be8e0ad8-d545-4017-a55a-b02f014d4158 --access-token 0.48c78342-1635-48a6-accd-afbe01336365.C0tMmQqHnAp1h0gL8bngprlPOYutt0:B3h5D+YgLvFiQhWkIq6Bow==" (
echo.{
echo. "object": "secret",
echo. "id": "be8e0ad8-d545-4017-a55a-b02f014d4158",
echo. "organizationId": "10e8cbfa-7bd2-4361-bd6f-b02e013f9c41",
echo. "projectId": "e325ea69-a3ab-4dff-836f-b02e013fe530",
echo. "key": "SES_KEY",
echo. "value": "0.982492bc-7f37-4475-9e60",
echo. "note": "",
echo. "creationDate": "2023-06-28T20:13:20.643567Z",
echo. "revisionDate": "2023-06-28T20:13:20.643567Z"
echo.}
) ELSE (
exit /b 1
)
-- golden/bitwarden-attachment --
hidden-file-value
6 changes: 6 additions & 0 deletions internal/cmd/testdata/scripts/doctor_unix.txtar
Expand Up @@ -2,6 +2,7 @@

chmod 755 bin/age
chmod 755 bin/bw
chmod 755 bin/bws
chmod 755 bin/dcli
chmod 755 bin/doppler
chmod 755 bin/git
Expand Down Expand Up @@ -48,6 +49,7 @@ stdout '^ok\s+gpg-command\s+'
stdout '^ok\s+pinentry-command\s+'
stdout '^ok\s+1password-command\s+'
stdout '^ok\s+bitwarden-command\s+'
stdout '^ok\s+bitwarden-secrets-command\s+'
stdout '^ok\s+dashlane-command\s+'
stdout '^ok\s+doppler-command\s+'
stdout '^ok\s+gopass-command\s+'
Expand Down Expand Up @@ -91,6 +93,10 @@ echo "(devel)"
#!/bin/sh

echo "1.12.1"
-- bin/bws --
#!/bin/sh

echo "Bitwarden Secrets CLI 0.3.0"
-- bin/dcli --
#!/bin/sh

Expand Down

0 comments on commit f6947d3

Please sign in to comment.