Skip to content

Commit

Permalink
feat: Add support for YubiKeys with KeePassXC
Browse files Browse the repository at this point in the history
  • Loading branch information
twpayne committed Jan 7, 2024
1 parent 7959bd3 commit 6a5d4a3
Show file tree
Hide file tree
Showing 13 changed files with 312 additions and 105 deletions.
4 changes: 4 additions & 0 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,10 @@ jobs:
curl -fsSL "https://github.com/FiloSottile/age/releases/download/v${AGE_VERSION}/age-v${AGE_VERSION}-darwin-amd64.tar.gz" | tar xzf -
sudo install -m 755 age/age /usr/local/bin
sudo install -m 755 age/age-keygen /usr/local/bin
- name: install-keepassxc
run: |
brew install keepassxc
keepassxc-cli --version
- name: test
env:
CHEZMOI_GITHUB_TOKEN: ${{ secrets.CHEZMOI_GITHUB_TOKEN }}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -286,6 +286,9 @@ sections:
description: KeePassXC CLI command
database:
description: KeePassXC database
mode:
default: '`cache-password`'
description: See section on KeePassXC
prompt:
type: bool
default: '`true`'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,11 @@ file. You will be prompted for the database password the first time
`keepassxc-cli` is run, and the password is cached, in plain text, in memory
until chezmoi terminates.

The command used can by changed by setting the `keepassxc.command`
configuration variable, and extra arguments can be added by setting
`keepassxc.args`. Also, you can disable the password prompt by setting
`keepassxc.prompt` to `false`.
The command used can by changed by setting the `keepassxc.command` configuration
variable, and extra arguments can be added by setting `keepassxc.args`. The
password prompt can be disabled by setting `keepassxc.prompt` to `false`.

By default, chezmoi will prompt for the KeePassXC password when required and
cache it for the duration of chezmoi's execution. Setting `keepassxc.mode` to
`open` will tell chezmoi to instead open KeePassXC's console with `keepassxc-cli
open`. chezmoi will use this console to request values from KeePassXC.
26 changes: 20 additions & 6 deletions assets/chezmoi.io/docs/user-guide/password-managers/keepassxc.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,25 @@ called `private-key`, its value is available as:
{{ keepassxcAttribute "SSH Key" "private-key" }}
```

!!! info
## Non-password-protected databases

The KeePassXC CLI does not currently support any persistent login, which
means that you will have to enter your password every time you run chezmoi.
If your database is not password protected, add `--no-password` to
`keepassxc.args` and `keepassxc.prompt = false`:

Support for non-password authentication methods (e.g. YubiKey) is currently
poor. For more information, see [this GitHub
issue](https://github.com/twpayne/chezmoi/issues/2002).
```toml title="~/.config/chezmoi/chezmoi.toml"
[keepassxc]
args = ["--no-password"]
prompt = false
```

## YubiKey support

chezmoi includes an experimental mode to support using KeePassXC with YubiKeys.
Set `keepassxc.mode` to `open` and `keepassxc.args` to the arguments required to
set your YubiKey, for example:

```toml title="~/.config/chezmoi/chezmoi.toml"
[keepassxc]
args = ["--yubikey", "1:7370001"]
mode = "open"
```
5 changes: 5 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ require (
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.4.0
github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azsecrets v1.0.1
github.com/Masterminds/sprig/v3 v3.2.3
github.com/Netflix/go-expect v0.0.0-20220104043353-73e0943537d2
github.com/Shopify/ejson v1.4.1
github.com/alecthomas/assert/v2 v2.4.1
github.com/aws/aws-sdk-go-v2 v1.24.0
Expand Down Expand Up @@ -88,6 +89,7 @@ require (
github.com/charmbracelet/lipgloss v0.9.1 // indirect
github.com/cloudflare/circl v1.3.6 // indirect
github.com/containerd/console v1.0.4-0.20230313162750-1ae8d489ac81 // indirect
github.com/creack/pty/v2 v2.0.0-20231209135443-03db72c7b76c // indirect
github.com/cyphar/filepath-securejoin v0.2.4 // indirect
github.com/danieljoos/wincred v1.2.1 // indirect
github.com/dlclark/regexp2 v1.10.0 // indirect
Expand Down Expand Up @@ -148,3 +150,6 @@ exclude (
github.com/sergi/go-diff v1.3.0
github.com/sergi/go-diff v1.3.1 // https://github.com/twpayne/chezmoi/issues/2706
)

// github.com/Netflix/go-expect is unmaintained. Use a temporary fork.
replace github.com/Netflix/go-expect => github.com/twpayne/go-expect v0.0.1
8 changes: 8 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ github.com/Masterminds/sprig/v3 v3.2.3/go.mod h1:rXcFaZ2zZbLRJv/xSysmlgIM1u11eBa
github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY=
github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow=
github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM=
github.com/Netflix/go-expect v0.0.0-20220104043353-73e0943537d2 h1:+vx7roKuyA63nhn5WAunQHLTznkw5W8b1Xc0dNjp83s=
github.com/Netflix/go-expect v0.0.0-20220104043353-73e0943537d2/go.mod h1:HBCaDeC1lPdgDeDbhX8XFpy1jqjK0IBG8W5K+xYqA0w=
github.com/ProtonMail/go-crypto v0.0.0-20230923063757-afb1ddc0824c h1:kMFnB0vCcX7IL/m9Y5LO+KQYv+t1CQOiFe6+SV2J7bE=
github.com/ProtonMail/go-crypto v0.0.0-20230923063757-afb1ddc0824c/go.mod h1:EjAoLdwvbIOoOQr3ihjnSoLZRtE8azugULFRteWMNc0=
github.com/Shopify/ejson v1.4.1 h1:zGGojGJNTdIWza/kOT8gd2HKCg3ZkSi3CZ1ZX70NHsw=
Expand Down Expand Up @@ -113,6 +115,8 @@ github.com/cpuguy83/go-md2man/v2 v2.0.3 h1:qMCsGGgs+MAzDFyp9LpAe1Lqy/fY/qCovCm0q
github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY=
github.com/creack/pty v1.1.18/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4=
github.com/creack/pty/v2 v2.0.0-20231209135443-03db72c7b76c h1:5l8y/PgjeX1aUyZxXabtAf2ahCYQaqWzlFzQgU16o0U=
github.com/creack/pty/v2 v2.0.0-20231209135443-03db72c7b76c/go.mod h1:1gZ4PfMDNcYx8FxDdnF/6HYP327cTeB/ru6UdoWVQvw=
github.com/cyphar/filepath-securejoin v0.2.4 h1:Ugdm7cg7i6ZK6x3xDF1oEu1nfkyfH53EtKeQYTC3kyg=
github.com/cyphar/filepath-securejoin v0.2.4/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4=
github.com/danieljoos/wincred v1.2.1 h1:dl9cBrupW8+r5250DYkYxocLeZ1Y4vB1kxgtjxw8GQs=
Expand Down Expand Up @@ -321,13 +325,16 @@ github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpE
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/tailscale/hujson v0.0.0-20221223112325-20486734a56a h1:SJy1Pu0eH1C29XwJucQo73FrleVK6t4kYz4NVhp34Yw=
github.com/tailscale/hujson v0.0.0-20221223112325-20486734a56a/go.mod h1:DFSS3NAGHthKo1gTlmEcSBiZrRJXi28rLNd/1udP1c8=
github.com/twpayne/go-expect v0.0.1 h1:cRJ552FIdQzs4z98Q2OLQsGLSbkB7Xpm/IU6cyQ6mUM=
github.com/twpayne/go-expect v0.0.1/go.mod h1:+ffr+YtUt8ifebyvRQ3NhVTiLch/HnfxsAQqO5LeXss=
github.com/twpayne/go-pinentry v0.3.0 h1:Rr+fEOZXmeItOb4thjeVaBWJKB9Xa/eojolycyF/26c=
github.com/twpayne/go-pinentry v0.3.0/go.mod h1:iOIZD+9np/2V24OdCGos7Y1/xX90wc6VEAZsgb+r9D4=
github.com/twpayne/go-shell v0.4.0 h1:RAAMbjEj7mcwDdwC7SiFHGUKR+WDAURU6mnyd3r2p2E=
Expand Down Expand Up @@ -397,6 +404,7 @@ golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210616045830-e2b7044e8c71/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220204135822-1c1b9b1eba6a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
Expand Down
9 changes: 9 additions & 0 deletions internal/cmd/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -318,6 +318,8 @@ func newConfig(options ...configOption) (*Config, error) {
return nil, err
}

logger := zerolog.Nop()

c := &Config{
ConfigFile: newConfigFile(bds),

Expand Down Expand Up @@ -372,6 +374,7 @@ func newConfig(options ...configOption) (*Config, error) {
// Configuration.
fileSystem: vfs.OSFS,
bds: bds,
logger: &logger,

// Computed configuration.
homeDirAbsPath: homeDirAbsPath,
Expand Down Expand Up @@ -1723,6 +1726,11 @@ func (c *Config) persistentPostRunRootE(cmd *cobra.Command, args []string) error
return err
}

// Close any connection to keepassxc-cli.
if err := c.keepassxcClose(); err != nil {
return err
}

// Wait for any diff pager process to terminate.
if c.diffPagerCmd != nil {
if err := c.diffPagerCmdStdin.Close(); err != nil {
Expand Down Expand Up @@ -2689,6 +2697,7 @@ func newConfigFile(bds *xdg.BaseDirectorySpecification) ConfigFile {
Keepassxc: keepassxcConfig{
Command: "keepassxc-cli",
Prompt: true,
Mode: keepassxcModeCachePassword,
},
Keeper: keeperConfig{
Command: "keeper",
Expand Down
1 change: 1 addition & 0 deletions internal/cmd/doctorcmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -343,6 +343,7 @@ func (c *Config) runDoctorCmd(cmd *cobra.Command, args []string) error {
ifNotExist: checkResultInfo,
versionArgs: []string{"--version"},
versionRx: regexp.MustCompile(`^(\d+\.\d+\.\d+)`),
minVersion: &keepassxcMinVersion,
},
&fileCheck{
name: "keepassxc-db",
Expand Down
13 changes: 0 additions & 13 deletions internal/cmd/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,16 +55,3 @@ func (e *parseCmdOutputError) Error() string {
func (e *parseCmdOutputError) Unwrap() error {
return e.err
}

type parseVersionError struct {
output []byte
err error
}

func (e *parseVersionError) Error() string {
return fmt.Sprintf("%s: cannot parse version: %v", e.output, e.err)
}

func (e *parseVersionError) Unwrap() error {
return e.err
}
Loading

0 comments on commit 6a5d4a3

Please sign in to comment.