Skip to content
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

Let keepassxc-cli run (exec) cli binaries with entries from the database exposed via env vars #11206

Open
7heo opened this issue Aug 28, 2024 · 7 comments

Comments

@7heo
Copy link

7heo commented Aug 28, 2024

Summary

Essentially what the title says: I would love if it would be possible to run (exec(3)) a command from the context of keepassxc-cli, with the latter filling the requested environment variables, or populating contextually-determined variables, with values from the database, after decryption.

The -e/--env flag should take an argument in the form of VAR_NAME=Internal/Path/to/entry:field_name, and the -p/--populate flag should take an argument in the form of Internal/Path/to/entry. The latter option should populate env vars from all available fields by default, with maybe a possibility to "blacklist" some with another flag.

It would probably be good to at least support the -i flag too, so that it behaves like the POSIX env, and eventually even support all flags from the GNU env.

Note: The -p/--populate is an idea I had for convenience, I'm absolutely not convinced it is a good idea... It could lead to people unknowingly leaking credentials to random binaries after blindly following online "tutorials", so implement at your own discretion ;)

Examples

With the -e/--env flag:

$ keepassxc-cli env /path/to/database -e USER_FOO=Internal/Path/to/foo:UserName \
-e PASSWD_FOO=Internal/Path/to/foo:Password \
-e USER_BAR=Internal/Path/to/bar:UserName \
-e PASSWD_BAR=Internal/Path/to/bar:Password -- /usr/bin/some_binary_needing_env_vars --verbose

Running some_binary_needing_env_vars. Please report bugs to <https://totallyawesomesoftware.com/binary>

We are running with the current values:

- Bar user: TheBarUserNameFromTheDatabase
- Bar password: ***********
- Foo user: TheFooUserNameFromTheDatabase
- Foo password: ***********

We did something completely useful! Have a nice day!

$

With the -p/--populate flag:

$ keepassxc-cli env /path/to/database -p Internal/Path/to/foo -p Internal/Path/to/bar -- /usr/bin/some_binary_needing_env_vars --verbose

Running some_binary_needing_env_vars. Please report bugs to <https://totallyawesomesoftware.com/binary>

We are running with the current values:

- Bar user: TheBarUserNameFromTheDatabase
- Bar password: ***********
- Foo user: TheFooUserNameFromTheDatabase
- Foo password: ***********

We did something completely useful! Have a nice day!

$

Context

I think this feature would be very convenient for running binaries that expect credentials in env vars, without defining said credentials (either in clear, or using some pretty convoluted solutions) in the .${SHELL}rc file, and consequently leaking them to ALL binaries invoked from said shell.

@droidmonkey
Copy link
Member

droidmonkey commented Aug 30, 2024

I don't really understand the need for KeePassXC to be the broker for your env vars and binary running. This should be handled in a script that calls on KeePassXC to harvest the appropriate credentials and uses them in a call to the binary from the script. I don't see us ever implementing this proposal, sorry.

FWIW, programs reading credentials from Env Vars should have bug reports lodged against them for ridiculously insecure behavior.

@7heo
Copy link
Author

7heo commented Aug 30, 2024

First off, thanks for taking the time to answer to my ticket. 🙏

This should be handled in a script that calls on KeePassXC to harvest the appropriate credentials and uses them in a call to the binary from the script.

As a matter of fact, this feature request was opened after I wrote a script to do exactly this, and found that it implemented a lot of code that should definitely not be in such script, as it would make a shell script far too complex to remain maintainable.

The best example I can give of this is: having to manage and store the credentials to open the database, as multiple separate calls to KeePassXC are needed in order to read an arbitrary number of entries. Even if we disregard the complexity of such implementation, it is also horribly insecure.

So, I would say that, at least in my opinion, a script to "glue" improperly written software (with debatable expectations) and KeePassXC would be a challenging endeavour at best, and will likely fail in dramatic ways.

FWIW, programs reading credentials from Env Vars should have bug reports lodged against them for ridiculously insecure behavior.

100% agree, but I don't see anyone suddenly and magically raising the quality of software implementation across the board. It is what it is. People solve this problem nowadays by running each process in a separate OS (or their idea of one, which often ends up being "a container"), and praying.


To walk things back a little, my requirement is essentially proper IPC, in a secure context, for password management.

In a GUI context, this is handled by KeePassXC, simulating a keyboard input into specified GUI application (usually the one that had focus before).

What I am currently missing is a similar handler for CLI contexts. If we assume that the CLI application is prompting for passwords using standard streams (as opposed to getting them from the environment variables, or reading them from files, as is too often the case), it would be theoretically possible to input the requested credentials much in the same way the GUI application does.

I don't suppose you would be open to having an implementation of such a handler, working in a similar fashion to Don Libes's expect?

In case you aren't, is there a KeePassXC ABI? I could not find one.

@droidmonkey
Copy link
Member

Hmmm, well a script to achieve your example would look something like this (pseudocode):

  1. Accept parameters to define keepassxc database and command to exec
  2. Request database password
  3. Run a series of keepassxc-cli commands to retrieve data from the database, using stdin piping of the db password
  4. Construct the exec command and go

I'll reopen this, but I doubt this will ever get implemented.

@droidmonkey droidmonkey reopened this Aug 31, 2024
@7heo
Copy link
Author

7heo commented Sep 2, 2024

Hmmm, well a script to achieve your example would look something like this (pseudocode):

  1. Accept parameters to define keepassxc database and command to exec
  2. Request database password
  3. Run a series of keepassxc-cli commands to retrieve data from the database, using stdin piping of the db password
  4. Construct the exec command and go

Here is the script I wrote that prompted me to open this issue:

#!/bin/sh

_bin="/usr/bin/keepassxc-cli"
_newline="
"

usage() {
	cat >&2 <<EOF
Usage: $0 database [options] command [command options]
Exec binary with environment variables set from database values.

Options:
  -e, --env                      Specify the environment variable, the database
                                 entry, and the field to use, with the following
				 format: ENV_VAR=database/entry/path:field
  -h, --help                     Display this help.

Arguments:
  database                       Path of the database.
  command                        Path of the command to exec.
EOF
	exit "${1:-1}"
}

[ $# -lt 2 ] && usage 1
db_path="$1"
# TODO Do checks on $db_path here.
shift

# Read the db password so we can do multiple operations.
printf 'Enter password to unlock %s: ' "$db_path"
_tty_settings="$(stty -g)"
stty -echo
read -r _passwd
stty "$_tty_settings"
echo ""
# TODO Check password and fail early if incorrect

env_vars=""
sep=""
while [ $# -ge 1 ]; do
	case "$1" in
		('-e'|'--env')
			shift
			var_name="$(echo "$1" | cut -d= -f1)"
			entry_path="$(echo "$1" | cut -d= -f2 | cut -d: -f1)"
			field_name="$(echo "$1" | cut -d= -f2 | cut -d: -f2)"
			_val="$(echo "$_passwd" | "$_bin" show "$db_path" -a "$field_name" "$entry_path" 2>/dev/null)"
			env_vars="$env_vars${sep}$var_name=$_val"
			_val=""
			sep="$_newline"
			;;
		('--') shift; break ;;
		('-'*) usage 1;;
		(*) break ;;
	esac
	shift
done
_passwd=""
IFS="$_newline"
for var in $env_vars; do
	set -- "$var" "$@"
done
exec env "$@"

I'll reopen this, but I doubt this will ever get implemented.

Would a PR help?

@droidmonkey
Copy link
Member

droidmonkey commented Sep 2, 2024

Yes it would! I personally like the subcommand exec instead of env

@jo-so
Copy link

jo-so commented Sep 15, 2024

Me having a similar script to run restic, which would be much simpler with such an keepassxc-cli exec.

@wez
Copy link

wez commented Oct 6, 2024

FWIW, this is something I'd love to see because I currently use the 1Password cli run subcommand for this purpose, and it is a bit awkward to use when connecting via ssh because it pops up a gui on the remote system instead of prompting in the tty, and that is what led to find keepassxc's cli.

The approach taken by op run -- some command is that it scans the environment looking for values that start with its own op:// url scheme. For example: op://VaultName/EntryName/AttributeName. It will decrypt the identified attribute and substitute it into the environment before launching the program.

It can also process .env files in a similar way, which makes it very convenient to encode a set of secrets for eg: docker compose.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

4 participants