diff options
| author | Patrick Spek <p.spek@tyil.nl> | 2024-11-17 12:02:24 +0100 |
|---|---|---|
| committer | Patrick Spek <p.spek@tyil.nl> | 2024-11-17 12:02:24 +0100 |
| commit | 754507965173feffeba480879030bba1ef8e55b9 (patch) | |
| tree | 3c83cb70fe3d3eea22bcd988ccce57548df69d20 | |
| parent | 1c545d73599df8bc0684266d1772f4532bcde02a (diff) | |
| download | bashtard-754507965173feffeba480879030bba1ef8e55b9.tar.gz bashtard-754507965173feffeba480879030bba1ef8e55b9.tar.bz2 | |
Redo entire secret encryption/decryption setup
| -rw-r--r-- | CHANGELOG.md | 21 | ||||
| -rw-r--r-- | lib/subcommands/var.bash | 37 | ||||
| -rw-r--r-- | lib/util.bash | 4 | ||||
| -rw-r--r-- | lib/util/config.bash | 46 | ||||
| -rw-r--r-- | lib/util/secret.bash | 73 |
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 +} |
