Created
August 7, 2022 18:46
-
-
Save neuthral/245c89f78c2bda07a8cfd853646fad63 to your computer and use it in GitHub Desktop.
for using in plain text passwords
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/bin/sh | |
################################################################################ | |
# Copyright (c) 2020 Plyint, LLC <[email protected]>. All Rights Reserved. | |
# This file is licensed under the MIT License (MIT). | |
# Please see LICENSE.txt for more information. | |
# | |
# DESCRIPTION: | |
# This script allows a user to encrypt a password (or any other secret) at | |
# runtime and then use it, decrypted, within a script. This prevents shoulder | |
# surfing passwords and avoids storing the password in plain text, which could | |
# inadvertently be sent to or discovered by an individual at a later date. | |
# | |
# This script generates an AES 256 bit symmetric key for each script (or user- | |
# defined bucket) that stores secrets. This key will then be used to encrypt | |
# all secrets for that script or bucket. encpass.sh sets up a directory | |
# (.encpass) under the user's home directory where keys and secrets will be | |
# stored. | |
# | |
# For further details, see README.md or run "./encpass ?" from the command line. | |
# | |
################################################################################ | |
ENCPASS_VERSION="v4.1.3" | |
encpass_checks() { | |
[ -n "$ENCPASS_CHECKS" ] && return | |
if [ -z "$ENCPASS_HOME_DIR" ]; then | |
ENCPASS_HOME_DIR="$HOME/.encpass" | |
fi | |
[ ! -d "$ENCPASS_HOME_DIR" ] && mkdir -m 700 "$ENCPASS_HOME_DIR" | |
if [ -f "$ENCPASS_HOME_DIR/.extension" ]; then | |
# Extension enabled, load it... | |
ENCPASS_EXTENSION="$(cat "$ENCPASS_HOME_DIR/.extension")" | |
ENCPASS_EXT_FILE="encpass-$ENCPASS_EXTENSION.sh" | |
if [ -f "./extensions/$ENCPASS_EXTENSION/$ENCPASS_EXT_FILE" ]; then | |
# shellcheck source=/dev/null | |
. "./extensions/$ENCPASS_EXTENSION/$ENCPASS_EXT_FILE" | |
elif [ ! -z "$(command -v encpass-"$ENCPASS_EXTENSION".sh)" ]; then | |
# shellcheck source=/dev/null | |
. "$(command -v encpass-$ENCPASS_EXTENSION.sh)" | |
else | |
encpass_die "Error: Extension $ENCPASS_EXTENSION could not be found." | |
fi | |
# Extension specific checks, mandatory function for extensions | |
encpass_"${ENCPASS_EXTENSION}"_checks | |
else | |
# Use default OpenSSL implementation | |
if [ ! -x "$(command -v openssl)" ]; then | |
echo "Error: OpenSSL is not installed or not accessible in the current path." \ | |
"Please install it and try again." >&2 | |
exit 1 | |
fi | |
[ ! -d "$ENCPASS_HOME_DIR/keys" ] && mkdir -m 700 "$ENCPASS_HOME_DIR/keys" | |
[ ! -d "$ENCPASS_HOME_DIR/secrets" ] && mkdir -m 700 "$ENCPASS_HOME_DIR/secrets" | |
[ ! -d "$ENCPASS_HOME_DIR/exports" ] && mkdir -m 700 "$ENCPASS_HOME_DIR/exports" | |
fi | |
ENCPASS_CHECKS=1 | |
} | |
# Checks if the enabled extension has implented the passed function and if so calls it | |
encpass_ext_func() { | |
[ ! -z "$ENCPASS_EXTENSION" ] && ENCPASS_EXT_FUNC="$(command -v "encpass_${ENCPASS_EXTENSION}_$1")" || return | |
[ ! -z "$ENCPASS_EXT_FUNC" ] && shift && $ENCPASS_EXT_FUNC "$@" | |
} | |
# Initializations performed when the script is included by another script | |
encpass_include_init() { | |
encpass_ext_func "include_init" "$@" | |
[ ! -z "$ENCPASS_EXT_FUNC" ] && return | |
if [ -n "$1" ] && [ -n "$2" ]; then | |
ENCPASS_BUCKET=$1 | |
ENCPASS_SECRET_NAME=$2 | |
elif [ -n "$1" ]; then | |
if [ -z "$ENCPASS_BUCKET" ]; then | |
ENCPASS_BUCKET=$(basename "$0") | |
fi | |
ENCPASS_SECRET_NAME=$1 | |
else | |
ENCPASS_BUCKET=$(basename "$0") | |
ENCPASS_SECRET_NAME="password" | |
fi | |
} | |
encpass_generate_private_key() { | |
ENCPASS_KEY_DIR="$ENCPASS_HOME_DIR/keys/$ENCPASS_BUCKET" | |
[ ! -d "$ENCPASS_KEY_DIR" ] && mkdir -m 700 "$ENCPASS_KEY_DIR" | |
if [ ! -f "$ENCPASS_KEY_DIR/private.key" ]; then | |
(umask 0377 && printf "%s" "$(openssl rand -hex 32)" >"$ENCPASS_KEY_DIR/private.key") | |
fi | |
} | |
encpass_set_private_key_abs_name() { | |
ENCPASS_PRIVATE_KEY_ABS_NAME="$ENCPASS_HOME_DIR/keys/$ENCPASS_BUCKET/private.key" | |
[ ! -n "$1" ] && [ ! -f "$ENCPASS_PRIVATE_KEY_ABS_NAME" ] && encpass_generate_private_key | |
} | |
encpass_set_secret_abs_name() { | |
ENCPASS_SECRET_ABS_NAME="$ENCPASS_HOME_DIR/secrets/$ENCPASS_BUCKET/$ENCPASS_SECRET_NAME.enc" | |
[ ! -n "$1" ] && [ ! -f "$ENCPASS_SECRET_ABS_NAME" ] && set_secret | |
} | |
encpass_rmfifo() { | |
trap - EXIT | |
kill "$1" 2>/dev/null | |
rm -f "$2" | |
} | |
encpass_mkfifo() { | |
fifo="$ENCPASS_HOME_DIR/$1.$$" | |
mkfifo -m 600 "$fifo" || encpass_die "Error: unable to create named pipe" | |
printf '%s\n' "$fifo" | |
} | |
get_secret() { | |
encpass_checks | |
encpass_ext_func "get_secret" "$@"; [ ! -z "$ENCPASS_EXT_FUNC" ] && return | |
[ "$(basename "$0")" != "encpass.sh" ] && encpass_include_init "$1" "$2" | |
encpass_set_private_key_abs_name | |
encpass_set_secret_abs_name | |
encpass_decrypt_secret "$@" | |
} | |
set_secret() { | |
encpass_checks | |
encpass_ext_func "set_secret" "$@"; [ ! -z "$ENCPASS_EXT_FUNC" ] && return | |
if [ "$1" != "reuse" ] || { [ -z "$ENCPASS_SECRET_INPUT" ] && [ -z "$ENCPASS_CSECRET_INPUT" ]; }; then | |
echo "Enter $ENCPASS_SECRET_NAME:" >&2 | |
stty -echo | |
read -r ENCPASS_SECRET_INPUT | |
stty echo | |
echo "Confirm $ENCPASS_SECRET_NAME:" >&2 | |
stty -echo | |
read -r ENCPASS_CSECRET_INPUT | |
stty echo | |
# Use named pipe to securely pass secret to openssl | |
fifo="$(encpass_mkfifo set_secret_fifo)" | |
fi | |
if [ "$ENCPASS_SECRET_INPUT" = "$ENCPASS_CSECRET_INPUT" ]; then | |
encpass_set_private_key_abs_name | |
ENCPASS_SECRET_DIR="$ENCPASS_HOME_DIR/secrets/$ENCPASS_BUCKET" | |
[ ! -d "$ENCPASS_SECRET_DIR" ] && mkdir -m 700 "$ENCPASS_SECRET_DIR" | |
# Generate IV and create secret file | |
printf "%s" "$(openssl rand -hex 16)" > "$ENCPASS_SECRET_DIR/$ENCPASS_SECRET_NAME.enc" | |
ENCPASS_OPENSSL_IV="$(cat "$ENCPASS_SECRET_DIR/$ENCPASS_SECRET_NAME.enc")" | |
echo "$ENCPASS_SECRET_INPUT" > "$fifo" & | |
# Allow expansion now so PID is set | |
# shellcheck disable=SC2064 | |
trap "encpass_rmfifo $! $fifo" EXIT HUP TERM INT TSTP | |
# Append encrypted secret to IV in the secret file | |
openssl enc -aes-256-cbc -e -a -iv "$ENCPASS_OPENSSL_IV" \ | |
-K "$(cat "$ENCPASS_HOME_DIR/keys/$ENCPASS_BUCKET/private.key")" \ | |
-in "$fifo" 1>> "$ENCPASS_SECRET_DIR/$ENCPASS_SECRET_NAME.enc" | |
else | |
encpass_die "Error: secrets do not match. Please try again." | |
fi | |
} | |
encpass_decrypt_secret() { | |
encpass_ext_func "decrypt_secret" "$@"; [ ! -z "$ENCPASS_EXT_FUNC" ] && return | |
if [ -f "$ENCPASS_PRIVATE_KEY_ABS_NAME" ]; then | |
ENCPASS_DECRYPT_RESULT="$(dd if="$ENCPASS_SECRET_ABS_NAME" ibs=1 skip=32 2> /dev/null | openssl enc -aes-256-cbc \ | |
-d -a -iv "$(head -c 32 "$ENCPASS_SECRET_ABS_NAME")" -K "$(cat "$ENCPASS_PRIVATE_KEY_ABS_NAME")" 2> /dev/null)" | |
if [ ! -z "$ENCPASS_DECRYPT_RESULT" ]; then | |
echo "$ENCPASS_DECRYPT_RESULT" | |
else | |
# If a failed unlock command occurred and the user tries to show the secret | |
# Present either a locked or failed decrypt error. | |
if [ -f "$ENCPASS_HOME_DIR/keys/$ENCPASS_BUCKET/private.lock" ]; then | |
echo "**Locked**" | |
else | |
# The locked file wasn't present as expected. Let's display a failure | |
echo "Error: Failed to decrypt" | |
fi | |
fi | |
elif [ -f "$ENCPASS_HOME_DIR/keys/$ENCPASS_BUCKET/private.lock" ]; then | |
echo "**Locked**" | |
else | |
echo "Error: Unable to decrypt. The key file \"$ENCPASS_PRIVATE_KEY_ABS_NAME\" is not present." | |
fi | |
} | |
encpass_die() { | |
echo "$@" >&2 | |
exit 1 | |
} | |
#LITE | |
########################################################## | |
# COMMAND LINE MANAGEMENT SUPPORT | |
# ------------------------------- | |
# If you don't need to manage the secrets for the scripts | |
# with encpass.sh you can delete all code below this point | |
# in order to significantly reduce the size of encpass.sh. | |
# This is useful if you want to bundle encpass.sh with | |
# your existing scripts and just need the retrieval | |
# functions. | |
########################################################## | |
encpass_show_secret() { | |
encpass_ext_func "show_secret" "$@"; [ ! -z "$ENCPASS_EXT_FUNC" ] && return | |
ENCPASS_BUCKET=$1 | |
encpass_set_private_key_abs_name 0 | |
if [ ! -z "$2" ]; then | |
ENCPASS_SECRET_NAME=$2 | |
encpass_set_secret_abs_name 0 | |
[ -z "$ENCPASS_SECRET_ABS_NAME" ] && encpass_die "No secret named $2 found for bucket $1." | |
encpass_decrypt_secret | |
else | |
ENCPASS_FILE_LIST=$(ls -1 "$ENCPASS_HOME_DIR"/secrets/"$1") | |
for ENCPASS_F in $ENCPASS_FILE_LIST; do | |
ENCPASS_SECRET_NAME=$(basename "$ENCPASS_F" .enc) | |
encpass_set_secret_abs_name 0 | |
[ -z "$ENCPASS_SECRET_ABS_NAME" ] && encpass_die "No secret named $ENCPASS_SECRET_NAME found for bucket $1." | |
echo "$ENCPASS_SECRET_NAME = $(encpass_decrypt_secret)" | |
done | |
fi | |
} | |
encpass_getche() { | |
old=$(stty -g) | |
stty raw min 1 time 0 | |
printf '%s' "$(dd bs=1 count=1 2>/dev/null)" | |
stty "$old" | |
} | |
encpass_remove() { | |
encpass_ext_func "remove" "$@"; [ ! -z "$ENCPASS_EXT_FUNC" ] && return | |
if [ ! -n "$ENCPASS_FORCE_REMOVE" ]; then | |
if [ ! -z "$ENCPASS_SECRET" ]; then | |
printf "Are you sure you want to remove the secret \"%s\" from bucket \"%s\"? [y/N]" "$ENCPASS_SECRET" "$ENCPASS_BUCKET" | |
else | |
printf "Are you sure you want to remove the bucket \"%s?\" [y/N]" "$ENCPASS_BUCKET" | |
fi | |
ENCPASS_CONFIRM="$(encpass_getche)" | |
printf "\n" | |
if [ "$ENCPASS_CONFIRM" != "Y" ] && [ "$ENCPASS_CONFIRM" != "y" ]; then | |
exit 0 | |
fi | |
fi | |
if [ ! -z "$ENCPASS_SECRET" ]; then | |
rm -f "$1" | |
printf "Secret \"%s\" removed from bucket \"%s\".\n" "$ENCPASS_SECRET" "$ENCPASS_BUCKET" | |
else | |
rm -Rf "$ENCPASS_HOME_DIR/keys/$ENCPASS_BUCKET" | |
rm -Rf "$ENCPASS_HOME_DIR/secrets/$ENCPASS_BUCKET" | |
printf "Bucket \"%s\" removed.\n" "$ENCPASS_BUCKET" | |
fi | |
} | |
encpass_save_err() { | |
if read -r x; then | |
{ printf "%s\n" "$x"; cat; } > "$1" | |
elif [ "$x" != "" ]; then | |
printf "%s" "$x" > "$1" | |
fi | |
} | |
encpass_remove_man_format() { | |
sed -r 's/\.TH//g; s/\.\\//g; s/\.RS//g; s/\.RE//g; s/\\fB//g; s/\\fR//g; s/\\fI//g; s/\\-/-/g; s/^ //g; s/.{80}/\0\n/g' | grep -v '^"' | grep -v '^man' | sed 's/^/ /g; s/ \.SH//g' | |
} | |
encpass_help_prog() { | |
if [ ! -z "$(command -v man)" ]; then | |
if [ "$(man -l 2>&1 | grep 'invalid' | awk '{print $2}')" = "invalid" ]; then | |
# man exists, but no -l option is available (e.g macOS) | |
# let's attempt to emulate what man does | |
{ /usr/bin/tbl | /usr/bin/groff -Wall -mtty-char -Tascii -mandoc -c | /usr/bin/less -is; } | |
else | |
man -l - | |
fi | |
else | |
# No man, strip formatting and fallback to less | |
encpass_remove_man_format | less | |
fi | |
} | |
encpass_help() { | |
# Descriptions for commands that will be displayed in the help | |
# Can be overridden by an extension. (Useful when behavior is changed | |
# or not supporeted) | |
ENCPASS_HELP_ADD_CMD_DESC="Add a secret to the specified bucket. The bucket will be created if it does not already exist. If a secret with the same name already exists for the specified bucket, then the user will be prompted to confirm overwriting the value. If the -f option is passed, then the add operation will perform a forceful overwrite of the value. (i.e. no prompt)" | |
ENCPASS_HELP_UPDATE_CMD_DESC="Updates a secret in the specified bucket. This command is similar to using an \"add -f\" command, but it has a safety check to only proceed if the specified secret exists. If the secret, does not already exist, then an error will be reported. There is no forceable update implemented. Use \"add -f\" for any required forceable update scenarios." | |
ENCPASS_HELP_REMOVE_CMD_DESC="Remove a secret from the specified bucket. If only a bucket is specified then the entire bucket (i.e. all secrets and keys) will be removed. By default the user is asked to confirm the removal of the secret or the bucket. If the -f option is passed then a forceful removal will be performed. (i.e. no prompt)" | |
ENCPASS_HELP_LIST_CMD_DESC="Display the names of the secrets held in the bucket. If no bucket is specified, then the names of all existing buckets will be displayed." | |
ENCPASS_HELP_SHOW_CMD_DESC="Show the unencrypted value of the secret from the specified bucket. If no secret is specified then all secrets for the bucket are displayed. If no bucket is specified then all secrets for all buckets are displayed." | |
ENCPASS_HELP_LOCK_CMD_DESC="Locks all keys used by encpass.sh using a password. The user will be prompted to enter a password and confirm it. A user should take care to securely store the password. If the password is lost then keys can not be unlocked. When keys are locked, secrets can not be retrieved. (e.g. the output of the values in the \"show\" command will be displayed as \"**Locked**\")" | |
ENCPASS_HELP_UNLOCK_CMD_DESC="Unlocks all the keys for encpass.sh. The user will be prompted to enter the password and confirm it." | |
ENCPASS_HELP_REKEY_CMD_DESC="Replaces the key of the specified \fIbucket\fR and then re-encrypts all secrets for the bucket using the new key." | |
ENCPASS_HELP_EXPORT_CMD_DESC="Export the encrypted secret(s) for the specified \fIbucket\fR to a gzip compressed archive file (.tgz). The exported file will be placed in the \fIENCPASS_HOME_DIR\fR/exports folder. If a \fIsecret\fR is specified, only the specific \fIsecret\fR for the \fIbucket\fR will be exported. If no, \fIbucket\fR is specified all secrets will be exported. If \fI-p\fR is specified, the exported file will be encrypted with a password and exported with a \".tgz.enc\" extension. The encrypted password can be passed as an argument to the \fI-p\fR option or if no argument is given, then the user will be prompted to enter a password. The encryption cipher used by default is aes-256-cbc, salted, with the pseudorandom function pbkdf2 at 10,000 iterations. | |
By default, the export command will only export the encrypted secrets in the \fIbucket\fR specified. If you wish to export the keys as well you must pass the \fI-k\fR option. When the \fI-k\fR option is specified a password will be required to be entered regardless of whether the \fI-p\fR option was specified or not, in order to protect the keys being exported." | |
ENCPASS_HELP_IMPORT_CMD_DESC="Import the encrypted secret(s) from a gzip compressed tar archive file (.tgz). Importation from an encrypted archive file (.tgz.enc) is also supported. If encrypted, the format is assumed to be the same as what the export command uses. (i.e. aes-256-cbc, salted, with pbkdf2 at 10,000 iterations) To import and encrypted archive file you will need to pass the \fI-p\fR option, which can accept an optional argument for the the password. If no password is provided, when the \fI-p\fR option is specified, then the user will be prompted to enter one. | |
By default, the import command will display the \fIENCPASS_HOME_DIR\fR location the secrets/keys will be imported to and prompt the user to confirm whether to proceed. To prevent the prompt from appearing the \fI-f\fR option can be specified. When secrets/keys are imported, if a secret/key exists with the same name it will not be overridden and the remaining secrets/keys will be imported. This behavior can be changed to overwrite secrets/keys on import if they exist by passing the \fI-o\fR option." | |
ENCPASS_HELP_EXTENSION_CMD_DESC="Enables/disables an extension for encpass.sh. Only one extension can be enabled for one ENCPASS_HOME_DIR to ensure there are no unexpected side effects with multiple extensions enabled at once. An extension must be named \"encpass-\fIextension\fR\.sh\" and placed in the directory \"./extensions/\fIextension\fR/\" relative to the \"encpass.sh\" script or be available in \$PATH. | |
\fIaction\fR must be set to either \"enable\" (enables an extension), \"disable\" (disables the current extension), or \"list\" (displays the available extensions). If \fIaction\fR is set to \"enable\" then the name of the extension must be passed as an additional parameter. If no \fIaction\fR is specified then the currently enabled extension is displayed." | |
ENCPASS_HELP_DIR_CMD_DESC="Prints out the current directory that ENCPASS_HOME_DIR is set to. If the optional subcommand \"ls\" is passed, the ENCPASS_DIR_LIST environment variable will be parsed as a colon delimited list of directories and displayed on stdout." | |
ENCPASS_HELP_LITE_CMD_DESC="Generates a lightweight version of encpass.sh by removing the command line management code. It does this by searching for the comment #LITE and truncates the file to that line number. The truncated file will be output to stdout. You can redirect the output to a new file of your choosing. (e.g. encpass.sh lite > encpass-lite.sh)" | |
ENCPASS_HELP_VERSION_CMD_DESC="Prints out the tag version for encpass.sh and the SHA256 checksums (if sha256sum is available) for encpass.sh and any enabled extension. The tag version corresponds to the git commit that is tagged with that same version number. It is possible that the script on your local could contain additional changes beyond that particular tag version (e.g. you pulled it directly from the master branch), but those changes would only at most go up to just before the next tag version number. | |
You can determine if your version of encpass.sh is identical to a specific commit or tag in the official repo by computing the SHA256 checksum of a particular commit of the encpass.sh script. To find the SHA256 checksum of an encpass.sh commit or tag from the git repo, just curl the raw script to your local and pipe it into sha256sum: | |
curl --silent https://raw.githubusercontent.com/plyint/encpass.sh/93d42340c24e62132049430dd26c26736697e440/encpass.sh | sha256sum" | |
# Load extension description and additional commands if they exist | |
if [ ! -z "$ENCPASS_EXTENSION" ]; then | |
encpass_"${ENCPASS_EXTENSION}"_help_extension | |
encpass_"${ENCPASS_EXTENSION}"_help_commands | |
fi | |
encpass_help_prog << EOF | |
.\" Manpage for encpass.sh. | |
.\" Email [email protected] to correct errors or typos. | |
.TH man 8 "06 March 2020" "1.0" "encpass.sh man page" | |
.SH NAME | |
encpass.sh \- Use encrypted passwords in shell scripts | |
${ENCPASS_EXT_HELP_EXTENSION} | |
.SH SYNOPSIS | |
Include in shell scripts and call the \fBget_secret\fR function: | |
#!/bin/sh | |
\fB. encpass.sh | |
password=\$(get_secret)\fR | |
Or invoke/manage from the command line: | |
\fBencpass.sh\fR [ COMMAND ] [ OPTIONS ]... [ ARGS ]... | |
.SH DESCRIPTION | |
A lightweight solution for using encrypted passwords in shell scripts. It allows a user to encrypt a password (or any other secret) at runtime and then use it, decrypted, within a script. This prevents shoulder surfing passwords and avoids storing the password in plain text, within a script, which could inadvertently be sent to or discovered by an individual at a later date. | |
This script generates an AES 256 bit symmetric key for each script (or user-defined bucket) that stores secrets. This key will then be used to encrypt all secrets for that script or bucket. | |
Subsequent calls to retrieve a secret will not prompt for the value of that secret to be entered as the file with the encrypted value already exists. | |
Note: By default, encpass.sh uses OpenSSL to handle the encryption/decryption and sets up a directory (.encpass) under the user's home directory where keys and secrets will be stored. This directory can be overridden by setting the environment variable ENCPASS_HOME_DIR to a directory of your choice. | |
~/.encpass (or the directory specified by ENCPASS_HOME_DIR) will contain the following subdirectories: | |
- keys (Holds the private key for each script/bucket) | |
- secrets (Holds the secrets stored for each script/bucket) | |
.SH SHELL SCRIPT USAGE | |
To use the encpass.sh script within a shell script, source the script and then call the get_secret function. | |
#!/bin/sh | |
\fB. encpass.sh | |
password=\$(get_secret)\fR | |
Note: When no arguments are passed to the get_secret function, then the bucket name is set to the name of the script and the secret name is set to "password". | |
- bucket name = <script name> | |
- secret name = "password" | |
There are 2 additional ways to call the get_secret function: | |
Specify a secret name: | |
\fBpassword=\$(get_secret user)\fR | |
- bucket name = <script name> | |
- secret name = "user" | |
Specify both a secret name and a bucket name: | |
\fBpassword=\$(get_secret personal user)\fR | |
- bucket name = "personal" | |
- secret name = "user" | |
.SH COMMANDS | |
\fBadd\fR [-f] \fIbucket\fR \fIsecret\fR | |
.RS | |
$ENCPASS_HELP_ADD_CMD_DESC | |
.RE | |
\fBupdate\fR \fIbucket\fR \fIsecret\fR | |
.RS | |
$ENCPASS_HELP_UPDATE_CMD_DESC | |
.RE | |
\fBremove\fR|\fBrm\fR [-f] \fIbucket\fR [\fIsecret\fR] | |
.RS | |
$ENCPASS_HELP_REMOVE_CMD_DESC | |
.RE | |
\fBlist\fR|\fBls\fR [\fIbucket\fR] | |
.RS | |
$ENCPASS_HELP_LIST_CMD_DESC | |
.RE | |
\fBshow\fR [\fIbucket\fR] [\fIsecret\fR] | |
.RS | |
$ENCPASS_HELP_SHOW_CMD_DESC | |
.RE | |
\fBlock\fR | |
.RS | |
$ENCPASS_HELP_LOCK_CMD_DESC | |
.RE | |
\fBunlock\fR | |
.RS | |
$ENCPASS_HELP_UNLOCK_CMD_DESC | |
.RE | |
\fBrekey\fR \fIbucket\fR | |
.RS | |
$ENCPASS_HELP_REKEY_CMD_DESC | |
.RE | |
\fBdir\fR [ls] | |
.RS | |
$ENCPASS_HELP_DIR_CMD_DESC | |
.RE | |
\fBexport\fR [-k] [-p [\fIpassword\fR]] [\fIbucket\fR] [\fIsecret\fR] | |
.RS | |
$ENCPASS_HELP_EXPORT_CMD_DESC | |
.RE | |
\fBimport\fR [-f] [-o] [-p [\fIpassword\fR]] \fIfile\fR | |
.RS | |
$ENCPASS_HELP_IMPORT_CMD_DESC | |
.RE | |
\fBextension\fR [\fIaction\fR] [\fIextension\fR] | |
.RS | |
$ENCPASS_HELP_EXTENSION_CMD_DESC | |
.RE | |
\fBlite\fR | |
.RS | |
$ENCPASS_HELP_LITE_CMD_DESC | |
.RE | |
\fBversion\fR|\fB--version\fR|\fB-version\fR|\fB-v\fR | |
.RS | |
$ENCPASS_HELP_VERSION_CMD_DESC | |
.RE | |
\fBhelp\fR|\fB--help\fR|\fBusage\fR|\fB--usage\fR|\fB?\fR | |
.RS | |
Display this help manual. | |
.RE | |
Note: Wildcard handling is implemented for all commands that take secret | |
and bucket names as arguments. This enables performing operations like | |
adding/removing a secret to/from multiple buckets at once. | |
${ENCPASS_EXT_HELP_COMMANDS} | |
.SH AUTHOR | |
Plyint LLC ([email protected]) | |
EOF | |
} | |
encpass_cmd_add() { | |
encpass_ext_func "cmd_add" "$@"; [ ! -z "$ENCPASS_EXT_FUNC" ] && return | |
while getopts ":f" ENCPASS_OPTS; do | |
case "$ENCPASS_OPTS" in | |
f ) ENCPASS_FORCE_ADD=1;; | |
esac | |
done | |
if [ -n "$ENCPASS_FORCE_ADD" ]; then | |
shift $((OPTIND-1)) | |
fi | |
if [ ! -z "$1" ] && [ ! -z "$2" ]; then | |
# Allow globbing | |
# shellcheck disable=SC2027,SC2086 | |
ENCPASS_ADD_LIST="$(ls -1d "$ENCPASS_HOME_DIR/secrets/"$1"" 2>/dev/null)" | |
if [ -z "$ENCPASS_ADD_LIST" ]; then | |
ENCPASS_ADD_LIST="$1" | |
fi | |
for ENCPASS_ADD_F in $ENCPASS_ADD_LIST; do | |
ENCPASS_ADD_DIR="$(basename "$ENCPASS_ADD_F")" | |
ENCPASS_BUCKET="$ENCPASS_ADD_DIR" | |
if [ ! -n "$ENCPASS_FORCE_ADD" ] && [ -f "$ENCPASS_ADD_F/$2.enc" ]; then | |
echo "Warning: A secret with the name \"$2\" already exists for bucket $ENCPASS_BUCKET." | |
echo "Would you like to overwrite the value? [y/N]" | |
ENCPASS_CONFIRM="$(encpass_getche)" | |
if [ "$ENCPASS_CONFIRM" != "Y" ] && [ "$ENCPASS_CONFIRM" != "y" ]; then | |
continue | |
fi | |
fi | |
ENCPASS_SECRET_NAME="$2" | |
echo "Adding secret \"$ENCPASS_SECRET_NAME\" to bucket \"$ENCPASS_BUCKET\"..." | |
set_secret "reuse" | |
done | |
else | |
encpass_die "Error: A bucket name and secret name must be provided when adding a secret." | |
fi | |
} | |
encpass_cmd_update() { | |
encpass_ext_func "cmd_update" "$@"; [ ! -z "$ENCPASS_EXT_FUNC" ] && return | |
if [ ! -z "$1" ] && [ ! -z "$2" ]; then | |
ENCPASS_SECRET_NAME="$2" | |
# Allow globbing | |
# shellcheck disable=SC2027,SC2086 | |
ENCPASS_UPDATE_LIST="$(ls -1d "$ENCPASS_HOME_DIR/secrets/"$1"" 2>/dev/null)" | |
for ENCPASS_UPDATE_F in $ENCPASS_UPDATE_LIST; do | |
# Allow globbing | |
# shellcheck disable=SC2027,SC2086 | |
if [ -f "$ENCPASS_UPDATE_F/"$2".enc" ]; then | |
ENCPASS_UPDATE_DIR="$(basename "$ENCPASS_UPDATE_F")" | |
ENCPASS_BUCKET="$ENCPASS_UPDATE_DIR" | |
echo "Updating secret \"$ENCPASS_SECRET_NAME\" to bucket \"$ENCPASS_BUCKET\"..." | |
set_secret "reuse" | |
else | |
encpass_die "Error: A secret with the name \"$2\" does not exist for bucket $1." | |
fi | |
done | |
else | |
encpass_die "Error: A bucket name and secret name must be provided when updating a secret." | |
fi | |
} | |
encpass_cmd_remove() { | |
encpass_ext_func "cmd_remove" "$@"; [ ! -z "$ENCPASS_EXT_FUNC" ] && return | |
while getopts ":f" ENCPASS_OPTS; do | |
case "$ENCPASS_OPTS" in | |
f ) ENCPASS_FORCE_REMOVE=1;; | |
esac | |
done | |
if [ -n "$ENCPASS_FORCE_REMOVE" ]; then | |
shift $((OPTIND-1)) | |
fi | |
if [ -z "$1" ]; then | |
echo "Error: A bucket must be specified for removal." | |
fi | |
# Allow globbing | |
# shellcheck disable=SC2027,SC2086 | |
ENCPASS_REMOVE_BKT_LIST="$(ls -1d "$ENCPASS_HOME_DIR/secrets/"$1"" 2>/dev/null)" | |
if [ ! -z "$ENCPASS_REMOVE_BKT_LIST" ]; then | |
for ENCPASS_REMOVE_B in $ENCPASS_REMOVE_BKT_LIST; do | |
ENCPASS_BUCKET="$(basename "$ENCPASS_REMOVE_B")" | |
if [ ! -z "$2" ]; then | |
# Removing secrets for a specified bucket | |
# Allow globbing | |
# shellcheck disable=SC2027,SC2086 | |
ENCPASS_REMOVE_LIST="$(ls -1p "$ENCPASS_REMOVE_B/"$2".enc" 2>/dev/null)" | |
if [ -z "$ENCPASS_REMOVE_LIST" ]; then | |
encpass_die "Error: No secrets found for $2 in bucket $ENCPASS_BUCKET." | |
fi | |
for ENCPASS_REMOVE_F in $ENCPASS_REMOVE_LIST; do | |
ENCPASS_SECRET="$2" | |
encpass_remove "$ENCPASS_REMOVE_F" | |
done | |
else | |
# Removing a specified bucket | |
encpass_remove | |
fi | |
done | |
else | |
encpass_die "Error: The bucket named $1 does not exist." | |
fi | |
} | |
encpass_cmd_show() { | |
encpass_ext_func "cmd_show" "$@"; [ ! -z "$ENCPASS_EXT_FUNC" ] && return | |
[ -z "$1" ] && ENCPASS_SHOW_DIR="*" || ENCPASS_SHOW_DIR=$1 | |
# Allow globbing | |
# shellcheck disable=SC2027,SC2086 | |
ENCPASS_SHOW_BKT_LIST="$(ls -1d "$ENCPASS_HOME_DIR/secrets/"$ENCPASS_SHOW_DIR"" 2>/dev/null)" | |
if [ ! -z "$ENCPASS_SHOW_BKT_LIST" ]; then | |
for ENCPASS_SHOW_B in $ENCPASS_SHOW_BKT_LIST; do | |
ENCPASS_BUCKET="$(basename "$ENCPASS_SHOW_B")" | |
if [ ! -z "$2" ]; then | |
# Showing secrets for a specified bucket | |
# Allow globbing | |
# shellcheck disable=SC2027,SC2086 | |
ENCPASS_SHOW_LIST="$(ls -1p "$ENCPASS_SHOW_B/"$2".enc" 2>/dev/null)" | |
if [ -z "$ENCPASS_SHOW_LIST" ]; then | |
encpass_die "Error: No secrets found for $2 in bucket $ENCPASS_BUCKET." | |
fi | |
for ENCPASS_SHOW_F in $ENCPASS_SHOW_LIST; do | |
ENCPASS_SECRET="$(basename "$ENCPASS_SHOW_F" .enc)" | |
encpass_show_secret "$ENCPASS_BUCKET" "$ENCPASS_SECRET" | |
done | |
else | |
ENCPASS_SHOW_DIR="$(basename "$ENCPASS_SHOW_B")" | |
echo "$ENCPASS_SHOW_DIR:" | |
encpass_show_secret "$ENCPASS_SHOW_DIR" | |
echo " " | |
fi | |
done | |
else | |
if [ "$ENCPASS_SHOW_DIR" = "*" ]; then | |
encpass_die "Error: No buckets exist." | |
else | |
encpass_die "Error: Bucket $1 does not exist." | |
fi | |
fi | |
} | |
encpass_cmd_list() { | |
encpass_ext_func "cmd_list" "$@"; [ ! -z "$ENCPASS_EXT_FUNC" ] && return | |
if [ ! -z "$1" ]; then | |
# Allow globbing | |
# shellcheck disable=SC2027,SC2086 | |
ENCPASS_FILE_LIST="$(ls -1p "$ENCPASS_HOME_DIR/secrets/"$1"" 2>/dev/null)" | |
if [ -z "$ENCPASS_FILE_LIST" ]; then | |
# Allow globbing | |
# shellcheck disable=SC2027,SC2086 | |
ENCPASS_DIR_EXISTS="$(ls -d "$ENCPASS_HOME_DIR/secrets/"$1"" 2>/dev/null)" | |
if [ ! -z "$ENCPASS_DIR_EXISTS" ]; then | |
encpass_die "Bucket $1 is empty." | |
else | |
encpass_die "Error: Bucket $1 does not exist." | |
fi | |
fi | |
ENCPASS_NL="" | |
for ENCPASS_F in $ENCPASS_FILE_LIST; do | |
if [ -d "${ENCPASS_F%:}" ]; then | |
printf "$ENCPASS_NL%s\n" "$(basename "$ENCPASS_F")" | |
ENCPASS_NL="\n" | |
else | |
printf "%s\n" "$(basename "$ENCPASS_F" .enc)" | |
fi | |
done | |
else | |
# Allow globbing | |
# shellcheck disable=SC2027,SC2086 | |
ENCPASS_BUCKET_LIST="$(ls -1p "$ENCPASS_HOME_DIR/secrets/"$1"" 2>/dev/null)" | |
for ENCPASS_C in $ENCPASS_BUCKET_LIST; do | |
if [ -d "${ENCPASS_C%:}" ]; then | |
printf "\n%s" "\n$(basename "$ENCPASS_C")" | |
else | |
basename "$ENCPASS_C" .enc | |
fi | |
done | |
fi | |
} | |
encpass_cmd_lock() { | |
encpass_ext_func "cmd_lock" "$@"; [ ! -z "$ENCPASS_EXT_FUNC" ] && return | |
echo "************************!!!WARNING!!!*************************" >&2 | |
echo "* You are about to lock your keys with a password. *" >&2 | |
echo "* You will not be able to use your secrets again until you *" >&2 | |
echo "* unlock the keys with the same password. It is important *" >&2 | |
echo "* that you securely store the password, so you can recall it *" >&2 | |
echo "* in the future. If you forget your password you will no *" >&2 | |
echo "* longer be able to access your secrets. *" >&2 | |
echo "************************!!!WARNING!!!*************************" >&2 | |
printf "\n%s\n" "About to lock keys held in directory $ENCPASS_HOME_DIR/keys/" | |
printf "\nEnter Password to lock keys:" >&2 | |
stty -echo | |
read -r ENCPASS_KEY_PASS | |
printf "\nConfirm Password:" >&2 | |
read -r ENCPASS_CKEY_PASS | |
printf "\n" | |
stty echo | |
[ -z "$ENCPASS_KEY_PASS" ] && encpass_die "Error: You must supply a password value." | |
if [ "$ENCPASS_KEY_PASS" = "$ENCPASS_CKEY_PASS" ]; then | |
ENCPASS_NUM_KEYS_LOCKED=0 | |
ENCPASS_KEYS_LIST="$(ls -1d "$ENCPASS_HOME_DIR/keys/"*"/" 2>/dev/null)" | |
# Create named pipe to pass secret to openssl outside for loop, | |
# so it can be reused for multiple calls | |
fifo="$(encpass_mkfifo cmd_lock_fifo)" | |
for ENCPASS_KEY_F in $ENCPASS_KEYS_LIST; do | |
if [ -d "${ENCPASS_KEY_F%:}" ]; then | |
ENCPASS_KEY_NAME="$(basename "$ENCPASS_KEY_F")" | |
ENCPASS_KEY_VALUE="" | |
if [ -f "$ENCPASS_KEY_F/private.key" ]; then | |
ENCPASS_KEY_VALUE="$(cat "$ENCPASS_KEY_F/private.key")" | |
if [ ! -f "$ENCPASS_KEY_F/private.lock" ]; then | |
echo "Locking key $ENCPASS_KEY_NAME..." | |
else | |
echo "Error: The key $ENCPASS_KEY_NAME appears to have been previously locked." | |
echo " The current key file may hold a bad value. Exiting to avoid encrypting" | |
echo " a bad value and overwriting the lock file." | |
exit 1 | |
fi | |
else | |
encpass_die "Error: Private key file ${ENCPASS_KEY_F}private.key missing for bucket $ENCPASS_KEY_NAME." | |
fi | |
if [ ! -z "$ENCPASS_KEY_VALUE" ]; then | |
# Use named pipe to securely pass secret to openssl | |
echo "$ENCPASS_KEY_PASS" > "$fifo" & | |
# Allow expansion now so PID is set | |
# shellcheck disable=SC2064 | |
trap "encpass_rmfifo $! $fifo" EXIT HUP TERM INT TSTP | |
umask 0377 | |
openssl enc -aes-256-cbc -pbkdf2 -iter 10000 -salt -in "$ENCPASS_KEY_F/private.key" -out "$ENCPASS_KEY_F/private.lock" -pass file:"$fifo" | |
if [ -f "$ENCPASS_KEY_F/private.key" ] && [ -f "$ENCPASS_KEY_F/private.lock" ]; then | |
# Both the key and lock file exist. We can remove the key file now | |
rm -f "$ENCPASS_KEY_F/private.key" | |
echo "Locked key $ENCPASS_KEY_NAME." | |
ENCPASS_NUM_KEYS_LOCKED=$(( ENCPASS_NUM_KEYS_LOCKED + 1 )) | |
else | |
echo "Error: The key fle and/or lock file were not found as expected for key $ENCPASS_KEY_NAME." | |
fi | |
else | |
encpass_die "Error: No key value found for the $ENCPASS_KEY_NAME key." | |
fi | |
fi | |
done | |
echo "Locked $ENCPASS_NUM_KEYS_LOCKED keys." | |
else | |
echo "Error: Passwords do not match." | |
fi | |
} | |
encpass_cmd_unlock() { | |
encpass_ext_func "cmd_unlock" "$@"; [ ! -z "$ENCPASS_EXT_FUNC" ] && return | |
printf "%s\n" "About to unlock keys held in the $ENCPASS_HOME_DIR/keys/ directory." | |
printf "\nEnter Password to unlock keys: " >&2 | |
stty -echo | |
read -r ENCPASS_KEY_PASS | |
printf "\n" | |
stty echo | |
if [ ! -z "$ENCPASS_KEY_PASS" ]; then | |
ENCPASS_NUM_KEYS_UNLOCKED=0 | |
ENCPASS_KEYS_LIST="$(ls -1d "$ENCPASS_HOME_DIR/keys/"*"/" 2>/dev/null)" | |
# Create named pipe to pass secret to openssl outside for loop, | |
# so it can be reused for multiple calls | |
fifo="$(encpass_mkfifo cmd_unlock_fifo)" | |
for ENCPASS_KEY_F in $ENCPASS_KEYS_LIST; do | |
if [ -d "${ENCPASS_KEY_F%:}" ]; then | |
ENCPASS_KEY_NAME="$(basename "$ENCPASS_KEY_F")" | |
echo "Unlocking key $ENCPASS_KEY_NAME..." | |
if [ -f "$ENCPASS_KEY_F/private.key" ] && [ ! -f "$ENCPASS_KEY_F/private.lock" ]; then | |
encpass_die "Error: Key $ENCPASS_KEY_NAME appears to be unlocked already." | |
fi | |
if [ -f "$ENCPASS_KEY_F/private.lock" ]; then | |
# Remove the failed file in case previous decryption attempts were unsuccessful | |
rm -f "$ENCPASS_KEY_F/failed" 2>/dev/null | |
# Use named pipe to securely pass secret to openssl | |
echo "$ENCPASS_KEY_PASS" > "$fifo" & | |
# Allow expansion now so PID is set | |
# shellcheck disable=SC2064 | |
trap "encpass_rmfifo $! $fifo" EXIT HUP TERM INT TSTP | |
# Decrypt key. Log any failure to the "failed" file. | |
umask 0377 | |
openssl enc -aes-256-cbc -d -pbkdf2 -iter 10000 -salt \ | |
-in "$ENCPASS_KEY_F/private.lock" -out "$ENCPASS_KEY_F/private.key" \ | |
-pass file:"$fifo" 2>&1 | encpass_save_err "$ENCPASS_KEY_F/failed" | |
if [ ! -f "$ENCPASS_KEY_F/failed" ]; then | |
# No failure has occurred. | |
if [ -f "$ENCPASS_KEY_F/private.key" ] && [ -f "$ENCPASS_KEY_F/private.lock" ]; then | |
# Both the key and lock file exist. We can remove the lock file now. | |
rm -f "$ENCPASS_KEY_F/private.lock" | |
echo "Unlocked key $ENCPASS_KEY_NAME." | |
ENCPASS_NUM_KEYS_UNLOCKED=$(( ENCPASS_NUM_KEYS_UNLOCKED + 1 )) | |
else | |
echo "Error: The key file and/or lock file were not found as expected for key $ENCPASS_KEY_NAME." | |
fi | |
else | |
printf "Error: Failed to unlock key %s.\n" "$ENCPASS_KEY_NAME" | |
printf " Please view %sfailed for details.\n" "$ENCPASS_KEY_F" | |
fi | |
else | |
echo "Error: No lock file found for the $ENCPASS_KEY_NAME key." | |
fi | |
fi | |
done | |
echo "Unlocked $ENCPASS_NUM_KEYS_UNLOCKED keys." | |
else | |
echo "No password entered." | |
fi | |
} | |
encpass_cmd_dir() { | |
encpass_ext_func "cmd_dir" "$@"; [ ! -z "$ENCPASS_EXT_FUNC" ] && return | |
if [ ! -z "$1" ]; then | |
if [ "$1" = "ls" ]; then | |
echo "$ENCPASS_DIR_LIST" | awk '{split($1,DIRS,/:/); for ( D in DIRS ) {printf "%s\n", DIRS[D];} }' | |
else | |
echo "Error: $1 is not a valid command." | |
fi | |
else | |
echo "ENCPASS_HOME_DIR=$ENCPASS_HOME_DIR" | |
fi | |
} | |
encpass_cmd_rekey() { | |
encpass_ext_func "cmd_rekey" "$@"; [ ! -z "$ENCPASS_EXT_FUNC" ] && return | |
if [ -z "$1" ]; then | |
encpass_die "Error: You must specify a bucket to rekey." | |
else | |
if [ ! -d "$ENCPASS_HOME_DIR/keys/$1" ]; then | |
encpass_die "Error: Bucket $1 does not exist" | |
fi | |
# Generate a new key | |
ENCPASS_BUCKET="$1_NEW" | |
encpass_generate_private_key | |
# Use named pipe to securely pass secret to openssl | |
fifo="$(encpass_mkfifo set_secret_fifo)" | |
# Allow globbing | |
# shellcheck disable=SC2027,SC2086 | |
ENCPASS_BUCKET_LIST="$(ls -1p "$ENCPASS_HOME_DIR/secrets/"$1"" 2>/dev/null)" | |
for ENCPASS_C in $ENCPASS_BUCKET_LIST; do | |
# Set each of the existing secrets for the new key | |
if [ ! -d "${ENCPASS_C%:}" ]; then | |
ENCPASS_SECRET_NAME=$(basename "$ENCPASS_C" .enc) | |
ENCPASS_BUCKET="$1" | |
ENCPASS_SECRET_INPUT=$(get_secret "$1" "$ENCPASS_SECRET_NAME") | |
ENCPASS_CSECRET_INPUT="$ENCPASS_SECRET_INPUT" | |
ENCPASS_BUCKET="$1_NEW" | |
set_secret "reuse" | |
fi | |
done | |
# Replace existing key and secrets with new versions | |
mv -f "$ENCPASS_HOME_DIR/keys/$1_NEW/"* "$ENCPASS_HOME_DIR/keys/$1" | |
mv -f "$ENCPASS_HOME_DIR/secrets/$1_NEW/"* "$ENCPASS_HOME_DIR/secrets/$1" | |
rmdir "$ENCPASS_HOME_DIR/keys/$1_NEW" | |
rmdir "$ENCPASS_HOME_DIR/secrets/$1_NEW" | |
fi | |
} | |
encpass_cmd_export() { | |
encpass_ext_func "cmd_export" "$@"; [ ! -z "$ENCPASS_EXT_FUNC" ] && return | |
while getopts ":kp" ENCPASS_OPTS; do | |
case "$ENCPASS_OPTS" in | |
k ) ENCPASS_EXPORT_OPT_KEYS=1 | |
shift $((OPTIND-1));; | |
p ) # Lookahead at next option to support | |
# optional argument to password option. | |
eval nextopt="\${$OPTIND}" | |
# nextopt is assigned by eval function | |
# Allow globbing | |
# shellcheck disable=SC2154,SC2086,SC2027 | |
ENCPASS_BUCKET_DIR="$(ls -1d "$ENCPASS_HOME_DIR/secrets/"$nextopt"" 2>/dev/null)" | |
if [ ! -z "$ENCPASS_BUCKET_DIR" ]; then | |
ENCPASS_EXPORT_OPT_PASS=1 | |
elif [ "$nextopt" = "-k" ]; then | |
ENCPASS_EXPORT_OPT_PASS=1 | |
else | |
ENCPASS_EXPORT_OPT_PASS=1 | |
ENCPASS_EXPORT_PASSWORD="$nextopt" | |
shift $((OPTIND-1)) | |
fi | |
shift $((OPTIND-1)) | |
;; | |
esac | |
done | |
if [ -n "$ENCPASS_EXPORT_OPT_KEYS" ] && [ ! -n "$ENCPASS_EXPORT_OPT_PASS" ]; then | |
echo "Exporting keys requires a password to be set for the export file." | |
ENCPASS_EXPORT_OPT_PASS=1 | |
fi | |
if [ -n "$ENCPASS_EXPORT_OPT_PASS" ] && [ -z "$ENCPASS_EXPORT_PASSWORD" ]; then | |
printf "\nEnter Password for export file:" >&2 | |
stty -echo | |
read -r ENCPASS_KEY_PASS | |
printf "\nConfirm Password:" >&2 | |
read -r ENCPASS_CKEY_PASS | |
printf "\n\n" | |
stty echo | |
[ -z "$ENCPASS_KEY_PASS" ] && encpass_die "Error: You must supply a password value." | |
[ "$ENCPASS_KEY_PASS" != "$ENCPASS_CKEY_PASS" ] && encpass_die "Error: password values do not match" | |
ENCPASS_EXPORT_PASSWORD="$ENCPASS_KEY_PASS" | |
fi | |
[ -z "$1" ] && ENCPASS_EXPORT_DIR="*" || ENCPASS_EXPORT_DIR=$1 | |
[ -z "$ENCPASS_EXTENSION" ] && ENCPASS_EXPORT_TYPE="openssl" || ENCPASS_EXPORT_TYPE="$ENCPASS_EXTENSION" | |
ENCPASS_EXPORT_FILENAME="encpass-$ENCPASS_EXPORT_TYPE-$(date '+%Y-%m-%d-%s').tgz" | |
if [ ! -z "$ENCPASS_EXPORT_PASSWORD" ]; then | |
ENCPASS_EXPORT_FILENAME="$ENCPASS_EXPORT_FILENAME.enc" | |
# Use named pipe to securely pass secret to openssl | |
fifo="$(encpass_mkfifo cmd_export_fifo)" | |
echo "$ENCPASS_EXPORT_PASSWORD" > "$fifo" & | |
# Allow expansion now so PID is set | |
# shellcheck disable=SC2064 | |
trap "encpass_rmfifo $! $fifo" EXIT HUP TERM INT TSTP | |
fi | |
cd "$ENCPASS_HOME_DIR" || encpass_die "Could not change to $ENCPASS_HOME_DIR directory" | |
umask 0377 | |
if [ ! -z "$2" ]; then | |
# Allow globbing | |
# shellcheck disable=SC2027,SC2086 | |
ENCPASS_EXPORT_SECRET_LIST="$(ls -p "secrets/"$ENCPASS_EXPORT_DIR"/"$2".enc" 2>/dev/null)" | |
if [ -z "$ENCPASS_EXPORT_SECRET_LIST" ]; then | |
encpass_die "Error: No secrets found for $2 in bucket $1." | |
fi | |
if [ ! -z "$ENCPASS_EXPORT_OPT_KEYS" ]; then | |
echo "Exporting the following keys and secret(s) for bucket $1:" | |
# Allow globbing | |
# shellcheck disable=SC2027,SC2086 | |
printf "%s\n" "keys/"$1"" | |
printf "%s\n" "$ENCPASS_EXPORT_SECRET_LIST" | |
echo "" | |
ENCPASS_EXPORT_PATHS="$ENCPASS_EXPORT_SECRET_LIST keys/$1" | |
else | |
echo "Exporting the following secret(s) for bucket $1:" | |
echo "$ENCPASS_EXPORT_SECRET_LIST" | |
echo "" | |
ENCPASS_EXPORT_PATHS="$ENCPASS_EXPORT_SECRET_LIST" | |
fi | |
if [ ! -z "$ENCPASS_EXPORT_PASSWORD" ]; then | |
# Allow globbing | |
# shellcheck disable=SC2027,SC2086 | |
tar -C "$ENCPASS_HOME_DIR" -czO $ENCPASS_EXPORT_PATHS | openssl enc -aes-256-cbc -pbkdf2 -iter 10000 -salt -out "$ENCPASS_HOME_DIR/exports/$ENCPASS_EXPORT_FILENAME" -pass file:"$fifo" | |
else | |
# Allow globbing | |
# shellcheck disable=SC2027,SC2086 | |
tar -C "$ENCPASS_HOME_DIR" -czf "$ENCPASS_HOME_DIR/exports/$ENCPASS_EXPORT_FILENAME" $ENCPASS_EXPORT_PATHS | |
fi | |
if [ -f "$ENCPASS_HOME_DIR/exports/$ENCPASS_EXPORT_FILENAME" ]; then | |
echo "Successfully created export file $ENCPASS_EXPORT_FILENAME in $ENCPASS_HOME_DIR/exports" | |
fi | |
else | |
# Allow globbing | |
# shellcheck disable=SC2027,SC2086 | |
ENCPASS_EXPORT_LIST="$(ls -1d "$ENCPASS_HOME_DIR/secrets/"$ENCPASS_EXPORT_DIR"" 2>/dev/null)" | |
if [ -z "$ENCPASS_EXPORT_LIST" ]; then | |
if [ "$ENCPASS_EXPORT_DIR" = "*" ]; then | |
encpass_die "Error: No buckets exist." | |
else | |
encpass_die "Error: Bucket $ENCPASS_EXPORT_DIR does not exist." | |
fi | |
fi | |
if [ ! -z "$ENCPASS_EXPORT_OPT_KEYS" ]; then | |
if [ "$ENCPASS_EXPORT_DIR" = "*" ]; then | |
echo "Exporting all keys and secrets for ALL buckets" | |
else | |
echo "Exporting all keys and secrets for bucket $ENCPASS_EXPORT_DIR" | |
# Allow globbing | |
# shellcheck disable=SC2027,SC2086 | |
printf "%s\n" "keys/"$ENCPASS_EXPORT_DIR"" | |
# Allow globbing | |
# shellcheck disable=SC2027,SC2086 | |
printf "%s\n" "secrets/"$ENCPASS_EXPORT_DIR"/"*"" | |
fi | |
echo "" | |
ENCPASS_EXPORT_PATHS="secrets/$1 keys/$1" | |
else | |
if [ "$ENCPASS_EXPORT_DIR" = "*" ]; then | |
echo "Exporting all secrets for ALL buckets" | |
else | |
echo "Exporting all secrets for bucket $ENCPASS_EXPORT_DIR" | |
# Allow globbing | |
# shellcheck disable=SC2027,SC2086 | |
printf "%s\n" "keys/"$ENCPASS_EXPORT_DIR"" | |
# Allow globbing | |
# shellcheck disable=SC2027,SC2086 | |
printf "%s\n" "secrets/"$ENCPASS_EXPORT_DIR"/"*"" | |
fi | |
echo "" | |
ENCPASS_EXPORT_PATHS="secrets/$1" | |
fi | |
if [ ! -z "$ENCPASS_EXPORT_PASSWORD" ]; then | |
# Allow globbing | |
# shellcheck disable=SC2027,SC2086 | |
tar -C "$ENCPASS_HOME_DIR" -czO --exclude="[.]*" $ENCPASS_EXPORT_PATHS | openssl enc -aes-256-cbc -pbkdf2 -iter 10000 -salt -out "$ENCPASS_HOME_DIR/exports/$ENCPASS_EXPORT_FILENAME" -pass file:"$fifo" | |
else | |
# Allow globbing | |
# shellcheck disable=SC2027,SC2086 | |
tar -C "$ENCPASS_HOME_DIR" -czf "$ENCPASS_HOME_DIR/exports/$ENCPASS_EXPORT_FILENAME" --exclude="[.]*" $ENCPASS_EXPORT_PATHS | |
fi | |
if [ -f "$ENCPASS_HOME_DIR/exports/$ENCPASS_EXPORT_FILENAME" ]; then | |
echo "Successfully created export file $ENCPASS_EXPORT_FILENAME in $ENCPASS_HOME_DIR/exports" | |
fi | |
fi | |
} | |
encpass_cmd_import() { | |
encpass_ext_func "cmd_import" "$@"; [ ! -z "$ENCPASS_EXT_FUNC" ] && return | |
ENCPASS_IMPORT_TAR_OPTIONS="-k" | |
while getopts ":fop" ENCPASS_OPTS; do | |
case "$ENCPASS_OPTS" in | |
f ) ENCPASS_IMPORT_OPT_FORCE=1 | |
shift $((OPTIND-1));; | |
o ) ENCPASS_IMPORT_OPT_OVERWRITE=1 | |
ENCPASS_IMPORT_TAR_OPTIONS="" | |
shift $((OPTIND-1));; | |
p ) # Lookahead at next option to support | |
# optional argument to password option. | |
eval nextopt="\${$OPTIND}" | |
# nextopt is assigned by eval function | |
# shellcheck disable=SC2154 | |
if [ -f "$nextopt" ]; then | |
ENCPASS_IMPORT_OPT_PASS=1 | |
elif [ "$nextopt" = "-o" ] || [ "$nextopt" = "-f" ]; then | |
ENCPASS_IMPORT_OPT_PASS=1 | |
else | |
ENCPASS_IMPORT_OPT_PASS=1 | |
ENCPASS_IMPORT_PASSWORD="$nextopt" | |
shift $((OPTIND-1)) | |
fi | |
shift $((OPTIND-1)) | |
;; | |
esac | |
done | |
[ -z "$1" ] && encpass_die "You must specify a filename to import." | |
echo "Import file: $1" | |
echo "To directory: $ENCPASS_HOME_DIR" | |
if [ -n "$ENCPASS_IMPORT_OPT_PASS" ] && [ -z "$ENCPASS_IMPORT_PASSWORD" ]; then | |
printf "\nEnter Password for import file:" >&2 | |
stty -echo | |
read -r ENCPASS_KEY_PASS | |
printf "\nConfirm Password:" >&2 | |
read -r ENCPASS_CKEY_PASS | |
printf "\n" | |
stty echo | |
[ -z "$ENCPASS_KEY_PASS" ] && encpass_die "Error: You must supply a password value." | |
[ "$ENCPASS_KEY_PASS" != "$ENCPASS_CKEY_PASS" ] && encpass_die "Error: password values do not match" | |
ENCPASS_IMPORT_PASSWORD="$ENCPASS_KEY_PASS" | |
fi | |
if [ -f "$1" ]; then | |
if [ ! -n "$ENCPASS_IMPORT_OPT_FORCE" ]; then | |
[ -n "$ENCPASS_IMPORT_OPT_OVERWRITE" ] && printf "\n%s" "WARNING: Overwrite flag is set. Importation will overwrite any existing secrets/keys that have the same name." | |
printf "\n%s" "Are you sure you want to proceed with the import? [y/N]" | |
ENCPASS_CONFIRM="$(encpass_getche)" | |
printf "\n" | |
if [ "$ENCPASS_CONFIRM" != "Y" ] && [ "$ENCPASS_CONFIRM" != "y" ]; then | |
exit 0 | |
fi | |
fi | |
if [ ! -z "$ENCPASS_IMPORT_PASSWORD" ]; then | |
# Use named pipe to securely pass secret to openssl | |
fifo="$(encpass_mkfifo cmd_import_fifo)" | |
echo "$ENCPASS_IMPORT_PASSWORD" > "$fifo" & | |
# Allow expansion now so PID is set | |
# shellcheck disable=SC2064 | |
trap "encpass_rmfifo $! $fifo" EXIT HUP TERM INT TSTP | |
# Ignore globbing, just the overwrite variable | |
# shellcheck disable=SC2027,SC2086 | |
openssl enc -aes-256-cbc -d -pbkdf2 -iter 10000 -salt \ | |
-in "$1" -pass file:"$fifo" \ | |
| tar $ENCPASS_IMPORT_TAR_OPTIONS -C "$ENCPASS_HOME_DIR" -xzf - \ | |
|| encpass_die "Error: Some values could not be imported" | |
else | |
# Ignore globbing, just the overwrite variable | |
# shellcheck disable=SC2027,SC2086 | |
tar $ENCPASS_IMPORT_TAR_OPTIONS -C "$ENCPASS_HOME_DIR" -xzf "$1" \ | |
|| encpass_die "Error: Some values could not be imported" | |
fi | |
echo "Import successful." | |
else | |
encpass_die "Error: Import file $1 does not exist." | |
fi | |
} | |
encpass_cmd_extension() { | |
encpass_ext_func "cmd_extension" "$@"; [ ! -z "$ENCPASS_EXT_FUNC" ] && return | |
if [ -z "$1" ]; then | |
if [ -f "$ENCPASS_HOME_DIR/.extension" ]; then | |
ENCPASS_EXTENSION="$(cat "$ENCPASS_HOME_DIR/.extension")" | |
echo "The extension $ENCPASS_EXTENSION is currently enabled." | |
else | |
echo "No extension set. Using default OpenSSL implementation" | |
fi | |
elif [ "$1" = "enable" ]; then | |
if [ -f "$ENCPASS_HOME_DIR/.extension" ]; then | |
ENCPASS_EXTENSION="$(cat "$ENCPASS_HOME_DIR/.extension")" | |
echo "The extension $ENCPASS_EXTENSION is enabled. You must disable it first to enable a new extension." | |
elif [ ! -z "$2" ]; then | |
if [ -d "./extensions" ]; then | |
if [ -f "./extensions/$2/encpass-$2.sh" ]; then | |
echo "$2" > "$ENCPASS_HOME_DIR/.extension" | |
echo "Extension $2 enabled." | |
else | |
echo "Error: Extension $2 not found." | |
fi | |
else | |
ENCPASS_PATH_DIR="$(dirname "$(command -v encpass.sh)")" | |
ENCPASS_EXTENSION_FILE_LIST="$(ls -1p "$ENCPASS_PATH_DIR/encpass-"*)" | |
for ENCPASS_EXTENSION_FILE in $ENCPASS_EXTENSION_FILE_LIST; do | |
ENCPASS_EXTENSION="$(basename "$ENCPASS_EXTENSION_FILE" | awk -F '[-.]' '{print $2}')" | |
if [ "$ENCPASS_EXTENSION" = "$2" ]; then | |
echo "$2" > "$ENCPASS_HOME_DIR/.extension" | |
echo "Extension $2 enabled." | |
exit 0 | |
fi | |
done | |
echo "Error: Extension $2 not found" | |
fi | |
else | |
echo "Please specify an extension to enable." | |
fi | |
elif [ "$1" = "disable" ]; then | |
if [ -f "$ENCPASS_HOME_DIR/.extension" ]; then | |
ENCPASS_EXTENSION="$(cat "$ENCPASS_HOME_DIR/.extension")" | |
printf "Disabling the \"%s\" extension may cause any existing secrets to become inaccessible. Are you sure you want to proceed? [y/N]" "$ENCPASS_EXTENSION" | |
ENCPASS_CONFIRM="$(encpass_getche)" | |
printf "\n" | |
if [ "$ENCPASS_CONFIRM" = "Y" ] || [ "$ENCPASS_CONFIRM" = "y" ]; then | |
rm "$ENCPASS_HOME_DIR/.extension" | |
fi | |
fi | |
elif [ "$1" = "list" ]; then | |
echo "The following extensions are available:" | |
if [ -d "./extensions" ]; then | |
ENCPASS_EXTENSION_LIST="$(basename "$(ls -1d ./extensions/*)")" | |
for ENCPASS_EXTENSION in $ENCPASS_EXTENSION_LIST; do | |
echo "$ENCPASS_EXTENSION" | |
done | |
else | |
ENCPASS_PATH_DIR="$(dirname "$(command -v encpass.sh)")" | |
ENCPASS_EXTENSION_FILE_LIST="$(ls -1p "$ENCPASS_PATH_DIR/encpass-"*)" | |
for ENCPASS_EXTENSION_FILE in $ENCPASS_EXTENSION_FILE_LIST; do | |
ENCPASS_EXTENSION="$(basename "$ENCPASS_EXTENSION_FILE" | awk -F '[-.]' '{print $2}')" | |
echo "$ENCPASS_EXTENSION" | |
done | |
fi | |
else | |
echo "Error: unrecognized argument $1" | |
fi | |
} | |
encpass_cmd_lite() { | |
encpass_ext_func "cmd_lite" "$@"; [ ! -z "$ENCPASS_EXT_FUNC" ] && return | |
head -n"$(awk '/\#LITE/{print NR;exit}' "$0")" "$0" | |
} | |
encpass_cmd_version() { | |
echo "tag version: $ENCPASS_VERSION" | |
[ -x "$(command -v sha256sum)" ] && printf "SHA256 Checksum: %s\n" "$(sha256sum "$0")" | |
encpass_ext_func "cmd_version" "$@" | |
} | |
if [ "$(basename "$0")" = "encpass.sh" ]; then | |
# Subcommands for cli support | |
case "$1" in | |
add ) shift; encpass_checks; encpass_cmd_add "$@" ;; | |
update ) shift; encpass_checks; encpass_cmd_update "$@" ;; | |
rm|remove ) shift; encpass_checks; encpass_cmd_remove "$@" ;; | |
show ) shift; encpass_checks; encpass_cmd_show "$@" ;; | |
ls|list ) shift; encpass_checks; encpass_cmd_list "$@" ;; | |
lock ) shift; encpass_checks; encpass_cmd_lock "$@" ;; | |
unlock ) shift; encpass_checks; encpass_cmd_unlock "$@" ;; | |
dir ) shift; encpass_checks; encpass_cmd_dir "$@" ;; | |
rekey ) shift; encpass_checks; encpass_cmd_rekey "$@" ;; | |
export ) shift; encpass_checks; encpass_cmd_export "$@" ;; | |
import ) shift; encpass_checks; encpass_cmd_import "$@" ;; | |
extension ) shift; encpass_checks; encpass_cmd_extension "$@" ;; | |
lite ) shift; encpass_checks; encpass_cmd_lite "$@" ;; | |
version|--version|-version|-v ) encpass_checks; encpass_cmd_version "$@" ;; | |
help|--help|usage|--usage|\? ) encpass_checks; encpass_help ;; | |
* ) | |
if [ ! -z "$1" ]; then | |
encpass_checks | |
encpass_ext_func "commands" "$@" [ ! -z "$ENCPASS_EXT_FUNC" ] && exit 0 | |
encpass_die "Command not recognized. See \"encpass.sh help\" for a list commands." | |
fi | |
;; | |
esac | |
fi |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment