aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPatrick Spek <p.spek@tyil.nl>2024-11-17 12:02:24 +0100
committerPatrick Spek <p.spek@tyil.nl>2024-11-17 12:02:24 +0100
commit754507965173feffeba480879030bba1ef8e55b9 (patch)
tree3c83cb70fe3d3eea22bcd988ccce57548df69d20
parent1c545d73599df8bc0684266d1772f4532bcde02a (diff)
downloadbashtard-754507965173feffeba480879030bba1ef8e55b9.tar.gz
bashtard-754507965173feffeba480879030bba1ef8e55b9.tar.bz2
Redo entire secret encryption/decryption setup
-rw-r--r--CHANGELOG.md21
-rw-r--r--lib/subcommands/var.bash37
-rw-r--r--lib/util.bash4
-rw-r--r--lib/util/config.bash46
-rw-r--r--lib/util/secret.bash73
5 files changed, 122 insertions, 59 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 08f61f5..2f0a614 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -24,16 +24,17 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Changed
-- The mechanic for secret variables has been changed. The `secrets` is
- deprecated, and instead a new system based on OpenPGP is added. Variable files
- in the `$BASHTARD_ETCDIR` (`defaults`, `hosts.d/$host`, `os.d/$os`) can now
- have a `.pgp` extension. If they have, the file will be decrypted using `sq`,
- the CLI utility from SequoiaPGP. This will be available _alongside_ the
- regular plaintext files, so you can mix publicly available variables and
- encrypted variables. Using the `var` subcommand with the `-s` option will
- write the variable to the PGP encrypted host variable file
- (`hosts.d/$host.pgp`), again using `sq`. The key to use for encryption can be
- configured with `bashtard.secrets.key.id`.
+- The mechanic for secret variables has been changed. The `secrets` file is
+ deprecated, and instead a new system is put in to allow use of various
+ encryption tools. Currently OpenPGP (through
+ [Sequoia-PGP](https://sequoia-pgp.org/)) and
+ [age](https://github.com/FiloSottile/age) are supported, with age being the
+ default. The system used can be set through `bashtard.secret-method`. The
+ methods themselves have varying configuration flags. Variable files in the
+ `$BASHTARD_ETCDIR` can now have an extension to signify the encryption system
+ used. You can still use regular plaintext configuration files _alongside_
+ encrypted variants. The `var` subcommand can be used to write configuration
+ to an encrypted secret file using the `-s` option.
- The error thrown when a variable wasn't defined is now removed if the variable
started with either `pkg.` or `svc.`. This is intended to make it unnecessary
to define a default for these variables, but still allowing it to be
diff --git a/lib/subcommands/var.bash b/lib/subcommands/var.bash
index 5080925..9d2d676 100644
--- a/lib/subcommands/var.bash
+++ b/lib/subcommands/var.bash
@@ -1,6 +1,6 @@
#!/usr/bin/env bash
-# SPDX-FileCopyrightText: 2022 Patrick Spek <p.spek@tyil.nl>
+# SPDX-FileCopyrightText: 2024 Patrick Spek <p.spek@tyil.nl>
#
# SPDX-License-Identifier: AGPL-3.0-or-later
@@ -20,7 +20,7 @@ subcommand()
g) file="$BASHTARD_ETCDIR/defaults" ;;
p) export BASHTARD_PLAYBOOK="$OPTARG" ;;
s) secret=1 ;;
- *) emerg "Unused opt '$opt'?" ;;
+ *) emerg "$BASHTARD_NAME/var" "Unused opt '$opt'?" ;;
esac
done
@@ -31,7 +31,7 @@ subcommand()
if [[ -z "$key" ]]
then
- emerg "bashtard/var" "You must supply a key"
+ emerg "$BASHTARD_NAME/var" "You must supply a key"
return 3
fi
@@ -42,35 +42,28 @@ subcommand()
return
fi
- # Set the var for this specific host
+ # Get the full data of the config file to write to
if [[ $secret ]]
then
- if ! command -v sq > /dev/null
- then
- emerg "$BASHTARD_NAME/var" "You must have sq available in your \$PATH for secret variable functionality"
- return 5
- fi
-
- # Check if Key ID is configured, or bail out immediately
- if [[ "$(config "bashtard.secrets.key.id")" == "" ]]
- then
- emerg "$BASHTARD_NAME/var" "No Key ID specified in bashtard.secrets.key.id"
- return 4
- fi
-
- data="$(sq decrypt "$file.pgp" 2>/dev/null)"
+ case "$(config "bashtard.secret-method" "age")" in
+ age) data="$(secret_decrypt_age "$file.age")" ;;
+ pgp) data="$(secret_decrypt_pgp "$file.pgp")" ;;
+ esac
else
data="$(<"$file")"
fi
+ # Insert the key and re-sort the file
data="$(printf "%s\n%s=%s\n" "$(grep -v "^$key=" <<< "$data")" "$key" "$value" | sort)"
+ # Write the config file
if [[ $secret ]]
then
- sq encrypt --recipient-cert "$(config "bashtard.secrets.key.id")" - <<< "$data" >/dev/null \
- > "$file.pgp"
+ case "$(config "bashtard.secret-method" "age")" in
+ age) secret_encrypt_age <<< "$data" > "$file.age" ;;
+ pgp) secret_encrypt_pgp <<< "$data" > "$file.pgp" ;;
+ esac
else
- cat <<< "$data" \
- > "$file"
+ cat <<< "$data" > "$file"
fi
}
diff --git a/lib/util.bash b/lib/util.bash
index 1d6f49a..b032ea3 100644
--- a/lib/util.bash
+++ b/lib/util.bash
@@ -1,6 +1,6 @@
#!/usr/bin/env bash
-# SPDX-FileCopyrightText: 2023 Patrick Spek <p.spek@tyil.nl>
+# SPDX-FileCopyrightText: 2024 Patrick Spek <p.spek@tyil.nl>
#
# SPDX-License-Identifier: AGPL-3.0-or-later
@@ -10,6 +10,8 @@
. "$BASHTARD_LIBDIR/util/pkg.bash"
# shellcheck source=lib/util/svc.bash
. "$BASHTARD_LIBDIR/util/svc.bash"
+# shellcheck source=lib/util/secret.bash
+. "$BASHTARD_LIBDIR/util/secret.bash"
# Change the working directory. In usage, this is the same as using cd,
# however, it will make additional checks to ensure everything is going fine.
diff --git a/lib/util/config.bash b/lib/util/config.bash
index cac75e7..546a2b4 100644
--- a/lib/util/config.bash
+++ b/lib/util/config.bash
@@ -15,8 +15,8 @@ config_subkeys() {
}
config_for() {
- local host=$1 ; shift
- local key=$1 ; shift
+ local host="$1" ; shift
+ local key="$1" ; shift
# Use a variable definition test to define default, in order to ensure
# it is _not_ defined if no argument for it was passed, but _is_
@@ -56,20 +56,21 @@ config_for() {
[[ ! -f $file ]] && continue
- # Decrypt the file if needed, otherwise just get the file
- # contents as-is.
- case "$file" in
- *.pgp)
- if ! command -v sq > /dev/null
- then
- emerg "$BASHTARD_NAME/var" "You must have sq available in your \$PATH for secret variable functionality"
- fi
-
- data="$(sq decrypt - < "$file" 2>/dev/null)"
- ;;
- *)
- data="$(<"$file")"
- esac
+ if [[ -n $BASHTARD_VAR_SKIP_SECRETS ]]
+ then
+ # To avoid recursion when looking up decryption keys,
+ # the BASHTARD_VAR_SKIP_SECRETS variable can be set and
+ # all decryption logic will be skipped.
+ data="$(<"$file")"
+ else
+ # Decrypt the file if needed, otherwise just get the file
+ # contents as-is.
+ case "$file" in
+ *.age) data="$(secret_decrypt_age "$file")" ;;
+ *.pgp) data="$(secret_decrypt_pgp "$file")" ;;
+ *) data="$(<"$file")"
+ esac
+ fi
# Check if the lookup is a reference variable, defined by using
# &= instead of just a single = as seperator. If this exists,
@@ -157,16 +158,9 @@ config_subkeys_for() {
# Decrypt the file if needed, otherwise just get the file
# contents as-is.
case "$file" in
- *.pgp)
- if ! command -v sq > /dev/null
- then
- emerg "$BASHTARD_NAME/var" "You must have sq available in your \$PATH for secret variable functionality"
- fi
-
- data="$(sq decrypt - < "$file" 2>/dev/null)"
- ;;
- *)
- data="$(<"$file")"
+ *.age) data="$(secret_decrypt_age "$file")" ;;
+ *.pgp) data="$(secret_decrypt_pgp "$file")" ;;
+ *) data="$(<"$file")"
esac
while read -r result
diff --git a/lib/util/secret.bash b/lib/util/secret.bash
new file mode 100644
index 0000000..0813df5
--- /dev/null
+++ b/lib/util/secret.bash
@@ -0,0 +1,73 @@
+#!/usr/bin/env bash
+
+# SPDX-FileCopyrightText: 2024 Patrick Spek <p.spek@tyil.nl>
+#
+# SPDX-License-Identifier: AGPL-3.0-or-later
+
+secret_check_age() {
+ debug "$BASHTARD_NAME/secret/secret_check_age" "Checking usability of age"
+
+ local identity
+
+ if ! command -v age > /dev/null
+ then
+ emerg "$BASHTARD_NAME/util/secret" "You must have age available in your \$PATH for age-encrypted secret variable functionality"
+ return 1
+ fi
+
+ identity="$(BASHTARD_VAR_SKIP_SECRETS=1 config "bashtard.secrets.age.identity")"
+
+ if [[ -z "$identity" || ! -f "$identity" ]]
+ then
+ emerg "$BASHTARD_NAME/util/secret" "No valid identity configured for basthard.secrets.age.identity"
+ return 1
+ fi
+
+ return 0
+}
+
+secret_check_pgp() {
+ debug "$BASHTARD_NAME/secret/secret_check_pgp" "Checking usability of OpenPGP"
+
+ local key
+
+ if ! command -v sq > /dev/null
+ then
+ emerg "$BASHTARD_NAME/var" "You must have sq available in your \$PATH for OpenPGP-encrypted secret variable functionality"
+ return 1
+ fi
+
+ key="$(BASHTARD_VAR_SKIP_SECRETS=1 config "bashtard.secrets.pgp.key.id")"
+
+ if [[ "$key" == "" ]]
+ then
+ emerg "$BASHTARD_NAME/var" "No Key ID specified in bashtard.secrets.pgp.key.id"
+ return 1
+ fi
+
+ return 0
+}
+
+secret_decrypt_age() {
+ secret_check_age || return 1
+ debug "$BASHTARD_NAME/secret/secret_decrypt_age" "Decrypting secret $1 using age"
+ age -i "$(BASHTARD_VAR_SKIP_SECRETS=1 config "bashtard.secrets.age.identity")" -d - < "$1"
+}
+
+secret_decrypt_pgp() {
+ secret_check_pgp || return 1
+ debug "$BASHTARD_NAME/secret/secret_decrypt_pgp" "Decrypting secret $1 using sq"
+ sq decrypt - < "$1" 2>/dev/null
+}
+
+secret_encrypt_age() {
+ secret_check_age || return 1
+ debug "$BASHTARD_NAME/secret/secret_decrypt_age" "Encrypting data using age"
+ cat | age -i "$(BASHTARD_VAR_SKIP_SECRETS=1 config "bashtard.secrets.age.identity")" -e -
+}
+
+secret_encrypt_pgp() {
+ secret_check_pgp || return 1
+ debug "$BASHTARD_NAME/secret/secret_decrypt_pgp" "Encrypting data using sq"
+ cat | sq encrypt --recipient-cert "$(config "bashtard.secrets.pgp.key.id")" - >/dev/null
+}