ADP-216 sops automation

ADP-216-uat-sops-step1
Lee Watson 10 months ago
parent 7a09a4f179
commit 37a2ad9ea9

@ -5,33 +5,94 @@ Purpose: Manage gpg keys for:
# Key Management # Key Management
## howto create and add a gpg key Roles:
- please follow instruction on following link: https://docs.github.com/en/authentication/managing-commit-signature-verification/generating-a-new-gpg-key
- add ONLY the _PUBLIC_ part of your gpg key!!!
- checkin via MergeRequest/PullRequest
### import gpg keys * New User: new key to be added; can be a new employee being added for first time, existing employee getting access to a new repo, key rotation, etc
```shell * Existing User: user who already has access to the appropriate project
gpg --import /path/to/keys/*.gpg.pub * E.g. look up in the [groups](/groups/) dir
* E.g. look up in in [verify/.sops.yaml](verify/.sops.yaml)
## 1a. Onboarding: [New User]: create and add a gpg key
- create a branch titled `add_pubkey_<firstname>-<lastname>`
- e.g. `git branch add_pubkey_test-user`
- Note: no strict naming convention for the branch, it's strictly a Human-in-the-Loop process
- please follow steps 1-13 at the following link: https://docs.github.com/en/authentication/managing-commit-signature-verification/generating-a-new-gpg-key
- CAVEAT: step 14 is not necessary, as it is specific to a GitHub account
- add ONLY the _PUBLIC_ part of your gpg key!!! to this repo
- file format: `<email>@netgo.de.gpg.pub`
- git: commit the new file, push
- open a MergeRequest/PullRequest
- hand-off to an Existing User of the repo.
## 1b. Onboarding: [Existing User|New User]: Add new user to groups
Access for each repo is tracked using the `./groups/` directory; each sub-directory represents a "group" (Note: some "groups" are also "roles", e.g. `admin`)
Most of the groups correspond directly to git repository names, aka "project name"
```bash
cd groups/<project_name>
ln -s ../../<path_to_key.gpg.pub>
``` ```
### list imported gpg keys Note: this step can be performed by anyone (either new user or existing user), but it makes the most sense for an existing user to configure the groups since this is domain-specific knowledge (i.e. new users won't typically know the grups)
## 3. Onboarding: [Existing User]: Configure sops config
Context: This repo stores the keys used to encrypt secrets in other repos; these "consumer" repos each contain a sops config `.sops.yaml` which manages access to the encrypted files (e.g. `secrets.yaml`)
For verification purposes, this repo also contains a _sample_ `.sops.yaml` to which every key in the repo is added. This allows both Existing Users to instantly verify the new key, and New Users to verify that their sops installation works correctly.
### Update Verification SOPS Config
Follow the interactive prompts:
```shell ```shell
gpg --list-keys --keyid-format=long ./verify/usr_confirm_keycfg.sh
``` ```
# EOL: Archive Expired Keys ### Update Project SOPS Config
To mark a key as expired, move it to the `archive/` dir as follows: The following commands explain how to update the `.sops.yaml` for a repository:
```bash #### Prerequisite
mv ${keyname} "archive/${keyname}_$(date '+%Y-%m-%d').archive" ```shell
# E.g. update sops config for DevNSO
% git clone git@git.dev-at.de:cloud-solutions/nso/devnso-adp-argocd.git
% cd devnso-adp-argocd/
```
#### Commands
```shell
# List available groups
% ${PATH_TO_THIS_REPO}/bin/update_sops.sh --list_groups
# INFO: listing groups
admin
automation
devnso-adp-argocd
# For a given group, update sops config
% ${PATH_TO_THIS_REPO}/bin/update_sops.sh -g devnso-adp-argocd
# RUN: generate SOPS config
# WARN: no secrets file passed in, make sure to call 'sops updatekeys' on secrets files
# [OPTIONAL] For a given group, update sops config AND specified secrets file
% ${PATH_TO_THIS_REPO}/bin/update_sops.sh -g devnso-adp-argocd -s ./adp-api-devs/adp-api-devs/secrets.yaml
# commit the changes to any .sops.yaml or secrets files, e.g. with
## OPINIONATED GIT - use preferred method
% git add -p
% git commit -m "adds <firstname>.<lastname> to sops config"
% git push
``` ```
# Configure SOPS At this point, the New User has been configured and can grant themselves access to any of the secrets files in this project.
## 4. Onboarding: [New User] Configure SOPS
SOPS is used for encrypting secrets, e.g. credentials for various systems SOPS is used for encrypting secrets, e.g. credentials for various systems
## Install ## Install
https://github.com/getsops/sops https://github.com/getsops/sops
@ -51,7 +112,56 @@ Note: The `GPG_TTY` is necessary to have the password prompt appear. src: https:
Note: `secrets.yaml` is just an example; the file can have any name Note: `secrets.yaml` is just an example; the file can have any name
## Example ## 5. Offboarding: [Existing User]: Archive Expired Keys (EOL)
To mark a key as expired:
1. move it to the `archive/` dir
2. for each group, update the project repo
3. remove the key from the group
### 1. This repo: archive
```shell
# archive key - DO NOT delete - need this for auditing
git mv ${keyname} "archive/${keyname}_$(date '+%Y-%m-%d').archive"
# remove from verification sops
./verify/usr_confirm_keycfg.sh
```
### 2. For each group / repo:
**Prerequisite**: Local copy of each repo corresponding to a group
```shell
# list all groups to which the key is registered
find groups/ -name ${keyname}
# For each group, update sops config in that repo
# Example:
% cd devnso-adp-argocd
% ${PATH_TO_THIS_REPO}/bin/update_sops.sh -g devnso-adp-argocd
# now git commit, push, etc
```
### 3. This repo: update groups
```shell
# remove from groups
find groups -name ${keyname} | xargs git rm
```
# Advanced
# Reference: Commands for gpg keys
## import gpg keys
```shell
gpg --import /path/to/keys/*.gpg.pub
```
## list imported gpg keys
```shell
gpg --list-keys --keyid-format=long
```
## SOPS Example - Manual
The steps in the following example can be run locally in order to: The steps in the following example can be run locally in order to:
* create a sample secrets file * create a sample secrets file
@ -73,3 +183,9 @@ sops -e -i secrets.yaml
# decript, print to console # decript, print to console
sops -d secrets.yaml sops -d secrets.yaml
``` ```
# Contributing
Tests: `./verify/test.sh`
Caveat: requires working SOPS config,pgp key, etc

@ -0,0 +1,235 @@
#/usr/bin/env bash
# Purpose: manage .sops.yaml based on gpg keys in the same dir _and_ verify correct configuration
set -euo pipefail
function fn_gpg_extract_fpr(){
# PURPOSE: get fingerprint from gpg keyfile
gpgkeyfile=$1;shift;
# fingerprint
# caveat: restrict to netgo.de email, use-case:
# uid ... <...@mehrwerk.net>
# uid ... <...@netgo.de>
# fancy gpg src: https://unix.stackexchange.com/a/731872
fpr="$(gpg --show-keys --list-options show-only-fpr-mbox "${gpgkeyfile}" | grep '@netgo.de' | awk "{print \$1}")"
echo "${fpr}"
}
function fn_gpg_extract_uid(){
# PURPOSE: get user-id from gpg keyfile
gpgkeyfile=$1;shift;
# user id
# caveat: restrict to netgo.de email, use-case:
# uid ... <...@mehrwerk.net>
# uid ... <...@netgo.de>
# fancy gpg src: https://unix.stackexchange.com/a/731872
uid="$(gpg --show-keys --with-colons "${gpgkeyfile}" | awk -F':' '$1=="uid" {print $10}' | grep '@netgo.de')"
echo "${uid}"
}
function fn_sops_locate_config_in_git_repo(){
# PURPOSE: locate sops config
# Returns path sops config to be updated; defaults to returning "$(git rev-parse --show-toplevel)/.sops.yaml"
# sops locates config by recursively walking _up_ the tree from the execeution dir context,
# + _but_ does not have a mechanism to update the sops config
# This function does the same in order to locate the correct sops config to update
# starting dir, default: PWD. Note: 'realpath' to normalise the dir
start_dir="$(realpath "${1:-"${PWD}"}")";
stop_dir="$(git rev-parse --show-toplevel)"
>&2 echo "# ---"
>&2 echo "# start_dir: "${start_dir}""
>&2 echo "# stop_dir: "${stop_dir}""
# BEGIN
search_dir="${start_dir}"
contender="${search_dir}/.sops.yaml"
# base case - located the file OR stopping condition - at top of repo
if [[ -e "${contender}" ]]; then
>&2 echo "# BASE CASE: found ${contender}"
echo "${contender}"
elif [[ "${search_dir}" == "${stop_dir}" ]]; then
>&2 echo "# STOPPING CONDITION: no sops config found, suggesting: ${contender}"
echo "${contender}"
else
>&2 echo "# walk up one dir..."
fn_sops_locate_config_in_git_repo "$(dirname "${search_dir}")"
fi
}
function fn_sops_generate_config(){
# PURPOSE: generate sops config based on keyfiles
# sops.yaml doc: https://github.com/getsops/sops?tab=readme-ov-file#using-sops-yaml-conf-to-select-kms-pgp-and-age-for-new-files
# CAVEAT: dirty hacks, as DRY as feasible within bash
>&2 echo "# RUN: generate SOPS config"
# hack: 2D list workaround, i.e. difficult to have list-of-lists
fpr_list=()
uid_list=()
type_list=()
for gpgkeyfile in *automation*gpg.pub; do
type_list+=( "autom" )
fpr_list+=( "$(fn_gpg_extract_fpr "${gpgkeyfile}")" )
uid_list+=( "$(fn_gpg_extract_uid "${gpgkeyfile}")" )
done
for gpgkeyfile in $(ls *gpg.pub | grep -v automation); do
type_list+=( "human" )
fpr_list+=( "$(fn_gpg_extract_fpr "${gpgkeyfile}")" )
uid_list+=( "$(fn_gpg_extract_uid "${gpgkeyfile}")" )
done
# header
echo "# Fingerprint | User Type | User ID"
# entries/rows
for ind in "${!fpr_list[@]}"; do
printf "# %s | %s | %s\n" \
"${fpr_list[${ind}]}" \
"${type_list[${ind}]}" \
"${uid_list[${ind}]}"
done
echo "# keys in https://git.dev-at.de/smardigo-hetzner/communication-keys"
cat <<EOM
creation_rules:
# list of keys for encryption in stage
- pgp: >-
EOM
# all but last line get comma
ind_2nd_last=$((${#fpr_list[@]} - 1))
for fpr in ${fpr_list[@]:0:${ind_2nd_last}}; do
echo " ${fpr},"
done
# last line no comma
# echo " ${fpr_list[-1]}," # requires bash v4.1
echo " ${fpr_list[${ind_2nd_last}]}"
}
fn_sops_updatekeys_and_verify(){
# PURPOSE: call 'sops updatekeys' and dump contents of file so end user can visually verify functionality
sops_enc_file="${1}";shift;
# update keys in secrets file
test -e "${sops_enc_file}" || exit 1
# "update the keys of SOPS files using the config file"
>&2 echo "# RUN: sops updatekeys ${sops_enc_file}"
sops updatekeys "${sops_enc_file}"
# verify: dump secrets, GPG_TTY src: https://www.varokas.com/secrets-in-code-with-mozilla-sops/
GPG_TTY=$(tty) sops -d "${sops_enc_file}"
}
function main(){
if [[ ! -n "${@}" ]]; then
# if empty args, remove
shift
fi
# "anchor" for actions relevant to this script
repo_root="$(realpath $(dirname "${BASH_SOURCE[0]}")/..)"
# OPTIONS: ARGPARSING and VALIDATION
# assume location of script as running directly from repo with keys (instead of as a standalone packaged tool)
keyfiles_dir="${repo_root}"
# assume location of secrets config file in pwd
sops_config_dir=""
# path to group definitions
groups_def_dir="${repo_root}/groups"
# optional:
opt_list_groups=0
# optional: specify "groups"
groups_list=()
# optional: secrets files to be updated
secrets_file_list=()
while (( $# >= 1 ));do
cur="${1}";
case $cur in
# ARGS: print this help
-h|--help) echo "# ARGUMENTS:"; grep -A 1 '# ARGS:' "${BASH_SOURCE[0]}"; exit 0 ;;
# ARGS: [optional] dir containing gpg keyfiles. defaults to git repo root, var: ${repo_root}
-k|--key|--keyfiles) keyfiles_dir="${2}"; shift ;;
# ARGS: [optional] defines dir for sops config file (.sops.yaml), create if needed. defaults to git repo root, var: ${repo_root}
-c|--config_dir) sops_config_dir="${2}"; shift ;;
# ARGS: [optional] show list of groups and exit
-lg|--list_groups) opt_list_groups=1 ;;
# ARGS: [optional] [list] specify "groups" which correspond to e.g. job groups, projects, etc
-g|--group) groups_list+=( "${2}" ); shift ;;
# ARGS: [optional] [list] specify files containing sops-encrypted secrets
-s|--secrets_file|-f|--file) secrets_file_list+=( "${2}" ); shift ;;
# ARGS: [optional] [list] specify files containing sops-encrypted secrets
*) secrets_file_list+=( "${cur}" )
esac
shift;
done
# Resolve Parameters
# ... i.e. combine,override,etc options which interact
if [[ "${#groups_list[@]}" -eq 1 ]]; then
# simply change keyfiles_dir to the "groups" dir
keyfiles_dir="${groups_def_dir}/${groups_list[0]}"
elif [[ "${#groups_list[@]}" -gt 1 ]]; then
>&2 echo "# ERROR: only specify one group"
exit 1
fi
# VALIDATE INPUTS
keyfiles_dir="$(realpath "${keyfiles_dir}")"
test -d "${keyfiles_dir}" || (echo "E: specify dir containing keyfiles; invalid dir: '${keyfiles_dir}'" && exit 1)
# define sops config location
sops_config=""
if [[ -n "${sops_config_dir:-}" ]]; then
# user-specified
sops_config_dir="$(realpath "${sops_config_dir}")"
# vvv possibly redundant, since the 'realpath' will fail if dir not valid
test -d "${sops_config_dir}" || (echo "E: specify dir containing .sops.yaml, invalid dir: '${sops_config_dir}'" && exit 1)
sops_config="${sops_config_dir}/.sops.yaml"
else
# locate appropriate sops config if default assumption not found
# dev note: '2> /dev/null' to disable debug output
sops_config="$(fn_sops_locate_config_in_git_repo 2> /dev/null)"
fi
if [[ "${#secrets_file_list[@]}" != "0" ]]; then
for secrets_file in "${secrets_file_list[@]}"; do
test -e "${secrets_file}" || (echo "E: could not locate file with secrets, tried: ${secrets_file}" && exit 1)
done
fi
# /VALIDATE INPUTS
# /OPTIONS: ARGPARSING and VALIDATION
# BEGIN
if [[ "${opt_list_groups}" -eq 1 ]]; then
# list available groups and exit
pushd "${groups_def_dir}" > /dev/null 2>&1
>&2 echo "# INFO: listing groups"
ls -1d *
exit 0
popd > /dev/null 2>&1
fi
# UPDATE SOPS CONFIG
# update sops config
# TODO: remove the 'pushd;popd' workaround and make the functions aware of the dir being read
pushd "${keyfiles_dir}" > /dev/null 2>&1
(fn_sops_generate_config) > "${sops_config}"
popd > /dev/null 2>&1
# VERIFY
if [[ "${#secrets_file_list[@]}" != "0" ]]; then
# import keys
pushd "${keyfiles_dir}" > /dev/null 2>&1
>&2 echo "# RUN: gpg --import *.gpg.pub"
gpg_out="$(gpg --import *.gpg.pub 2>&1)"
popd > /dev/null 2>&1
# update
for secrets_file in "${secrets_file_list[@]}"; do
fn_sops_updatekeys_and_verify "${secrets_file}"
done
echo "# SUCCESS: all users with keys in this dir should have functional keys"
else
echo "# WARN: no secrets file passed in, make sure to call 'sops updatekeys' on secrets files"
fi
}
# pass-through, set default value
main "${@-}"
exit

@ -0,0 +1 @@
../../claus.paetow@netgo.de.gpg.pub

@ -0,0 +1 @@
../../hoan.to@netgo.de.gpg.pub

@ -0,0 +1 @@
../../lee.watson@netgo.de.gpg.pub

@ -0,0 +1 @@
../../michael.haehnel@netgo.de.gpg.pub

@ -0,0 +1 @@
../../smardigo_automation_buildinfra.gpg.pub

@ -0,0 +1 @@
../../smardigo_automation_demompmx.gpg.pub

@ -0,0 +1 @@
../../smardigo_automation_dev.gpg.pub

@ -0,0 +1 @@
../../smardigo_automation_devnso-adp.gpg.pub

@ -0,0 +1 @@
../../smardigo_automation_devnso.gpg.pub

@ -0,0 +1 @@
../../smardigo_automation_poc_workload01.gpg.pub

@ -0,0 +1 @@
../../smardigo_automation_prodnso.gpg.pub

@ -0,0 +1 @@
../../smardigo_automation_qa.gpg.pub

@ -0,0 +1 @@
../../smardigo_automation_qanso.gpg.pub

@ -0,0 +1 @@
../../smardigo_automation_rancher_poc.gpg.pub

@ -0,0 +1 @@
../../smardigo_automation_sot.gpg.pub

@ -0,0 +1 @@
../../smardigo_automation_sot_test.gpg.pub

@ -0,0 +1 @@
../../smardigo_automation_sspdev.gpg.pub

@ -0,0 +1 @@
../../frederik.marticke@netgo.de.gpg.pub

@ -0,0 +1 @@
../../hoan.to@netgo.de.gpg.pub

@ -0,0 +1 @@
../../kevin.bauske@netgo.de.gpg.pub

@ -0,0 +1 @@
../../kleanthis.damianidis@netgo.de.gpg.pub

@ -0,0 +1 @@
../../lee.watson@netgo.de.gpg.pub

@ -0,0 +1 @@
../../michael.haehnel@netgo.de.gpg.pub

@ -0,0 +1 @@
../../smardigo_automation_devnso-adp.gpg.pub

@ -0,0 +1,67 @@
# PURPOSE: BLUEPRINT for .sops.yaml config
# CAVEAT: DO NOT USE THIS FILE AS-IS in another project; copy it and remove the unauthorised users
# Fingerprint | User Type | User ID
# 533A89DD49FBCDA2BF014A936C962DD77704154A | autom | build-infra <NSO-Team-DevOps@netgo.de>
# EFBBBB131CF1D863005C18868C8C09CA950B1DFF | autom | smardigo automation DEMOMPMX (smardigo automation DEMOMPMX) <NSO-Team-DevOps@netgo.de>
# A7A1D860AA45B6B5B29BC192C55BD9B4CD8DE439 | autom | smardigo automation DEV (smardigo automation DEV) <NSO-Team-DevOps@netgo.de>
# C674EFA56D3EDFDA404B1684090D46D8F1D0C0F8 | autom | devnso adp (devnso-adp gpg key) <NSO-Team-DevOps@netgo.de>
# 0E8955A79FF4687A3ACF78E50B5E444C75867E58 | autom | smardigo automation DEVNSO (smardigo automation DEVNSO) <NSO-Team-DevOps@netgo.de>
# 98235A419EA3586BFF4E3FC692D4A5202A0D9519 | autom | poc-workload01 (poc-workload01 gpg key for sops) <NSO-Team-DevOps@netgo.de>
# E5B4FE1E0209DFFE320D2A2E47087747D89B72EC | autom | smardigo automation PRODNSO (smardigo automation PRODNSO) <NSO-Team-DevOps@netgo.de>
# 890B2EB48F343D4C6DB9DA0916826F30002D3C1D | autom | smardigo automation QA (smardigo automation QA) <NSO-Team-DevOps@netgo.de>
# 4069413B74A3AB13E10DF5FD3EA8F0D0FB1CAF36 | autom | smardigo automation QANSO (smardigo automation QANSO) <NSO-Team-DevOps@netgo.de>
# 42AA7F6BF795490C1A522C730ED842BE1216E0C1 | autom | rancher-poc <NSO-Team-DevOps@netgo.de>
# B4BAA59056DC362809388F3F2119881095EA7DED | autom | sot production (sot production gpg key) <NSO-Team-DevOps@netgo.de>
# DF977A1F65999F4CDD721A27516F64D5932B8AD9 | autom | sot integration (sot integration) <NSO-Team-DevOps@netgo.de>
# AC9B0DB590F4AE2017C2AD836113AEB66C510C3F | autom | ssp-prod <NSO-Team-DevOps@netgo.de>
# 5623CCAD4242CC3E0225A62986AF09C173781578 | autom | smardigo automation SSPDEV <NSO-Team-DevOps@netgo.de>
# 1EBAE111F6EAE0CF136358E8625C5A3B8DA21485 | human | Annika Biermann <annika.biermann@netgo.de>
# 17B8FDF68AC123EB666934B17D0DF6EC048A5D77 | human | Claus Paetow (Firmenadresse) <claus.paetow@netgo.de>
# 03ADBCD31737EB474A4BD235CE615FAF927A2054 | human | Esther Fuhrmann (GPG Key for SOPS) <esther.fuhrmann@netgo.de>
# 04ECA5DBF73E98EBCC1FF0B018F2D15CC56BE487 | human | Hans-Peter Wissenbach <ext.hans-peter.wissenbach@netgo.de>
# 9E561083EACDE14694C73A323A2F6C1D153D753F | human | Frederik Marticke <frederik.marticke@netgo.de>
# 9F08DA9D42379AFE6610E9E615CCEC6801DBA02E | human | Hoan To (Hoan To GPG Key) <hoan.to@netgo.de>
# BA6328948D50175F196AAB5111F324603D12DD56 | human | Jan Jantzen <jan.jantzen@netgo.de>
# B643A5D780A01F24E95AA100DE6F8E2C149C3748 | human | johannes.wicovsky <johannes.wicovsky@netgo.de>
# C19A7D807525CE24443CA9A49372E896B41FE700 | human | Kevin Bauske <kevin.bauske@netgo.de>
# 0DB51A7E90AC6418B7DB83724D38970874850C33 | human | Kleanthis Damianidis <kleanthis.damianidis@netgo.de>
# BF3D5CEA36DB58AAE1063D0BB341078652D87924 | human | Lee Watson (dies ist mein schluessel, es gibt viele ähnliche, aber diese ist meins) <lee.watson@netgo.de>
# 73C2C9954D1BC94DC6682525D2FA233B52AEC75C | human | Michael Haehnel (NSO DevOps) <michael.haehnel@netgo.de>
# 13E169CF5C35EF164628764AAA2EECBDE0B38CF7 | human | Philipp Eichhorn <philipp.eichhorn@netgo.de>
# 13D7771BDE5241293F3EDA442AB3335ECC93DDDC | human | Sebastian Schröder <sebastian.schroeder@netgo.de>
# 0C136F7514100470AD3EC8D37BF1FAEDB2ACCA9A | human | Thomas Steube <thomas.steube@netgo.de>
# 57F93F2A6585CF2DF9A3B31F13B9F45E122698D5 | human | Tobias Stroehl <tobias.stroehl@netgo.de>
# keys in https://git.dev-at.de/smardigo-hetzner/communication-keys
creation_rules:
# list of keys for encryption in stage
- pgp: >-
533A89DD49FBCDA2BF014A936C962DD77704154A,
EFBBBB131CF1D863005C18868C8C09CA950B1DFF,
A7A1D860AA45B6B5B29BC192C55BD9B4CD8DE439,
C674EFA56D3EDFDA404B1684090D46D8F1D0C0F8,
0E8955A79FF4687A3ACF78E50B5E444C75867E58,
98235A419EA3586BFF4E3FC692D4A5202A0D9519,
E5B4FE1E0209DFFE320D2A2E47087747D89B72EC,
890B2EB48F343D4C6DB9DA0916826F30002D3C1D,
4069413B74A3AB13E10DF5FD3EA8F0D0FB1CAF36,
42AA7F6BF795490C1A522C730ED842BE1216E0C1,
B4BAA59056DC362809388F3F2119881095EA7DED,
DF977A1F65999F4CDD721A27516F64D5932B8AD9,
AC9B0DB590F4AE2017C2AD836113AEB66C510C3F,
5623CCAD4242CC3E0225A62986AF09C173781578,
1EBAE111F6EAE0CF136358E8625C5A3B8DA21485,
17B8FDF68AC123EB666934B17D0DF6EC048A5D77,
03ADBCD31737EB474A4BD235CE615FAF927A2054,
04ECA5DBF73E98EBCC1FF0B018F2D15CC56BE487,
9E561083EACDE14694C73A323A2F6C1D153D753F,
9F08DA9D42379AFE6610E9E615CCEC6801DBA02E,
BA6328948D50175F196AAB5111F324603D12DD56,
B643A5D780A01F24E95AA100DE6F8E2C149C3748,
C19A7D807525CE24443CA9A49372E896B41FE700,
0DB51A7E90AC6418B7DB83724D38970874850C33,
BF3D5CEA36DB58AAE1063D0BB341078652D87924,
73C2C9954D1BC94DC6682525D2FA233B52AEC75C,
13E169CF5C35EF164628764AAA2EECBDE0B38CF7,
13D7771BDE5241293F3EDA442AB3335ECC93DDDC,
0C136F7514100470AD3EC8D37BF1FAEDB2ACCA9A,
57F93F2A6585CF2DF9A3B31F13B9F45E122698D5

@ -0,0 +1,512 @@
demo:
credentials:
secret: ENC[AES256_GCM,data:m3uKjs2CMg==,iv:REr+W6QAcMXjC6kt4+U9W680N7NmOaPCbf5ZsL5v+GU=,tag:JTcpjBULW1tb6wYyQO1cWw==,type:str]
sops:
kms: []
gcp_kms: []
azure_kv: []
hc_vault: []
age: []
lastmodified: "2025-01-30T16:32:18Z"
mac: ENC[AES256_GCM,data:DNsgx0KhSbgbCqqME9MUJxHQSt4vVc/C2xfkckwjhK5nb4dW1Bz3Y5wAzPlOkPPKp9YB70/q2spw8MKSiJgeujfqnogPtZ409lbi3/RyF/7WxnJM0KFxdrsQwxfOJdYjfXnXicW9CD6xYPn1+LUVVqnRDBXt4MIzc5AMqaXy8pQ=,iv:dhVa4Dig7E3fld2Y3upjw3/P87bV6/C2oGDBgqQYRVY=,tag:RA9qsnRNMnznkNBDkjssIw==,type:str]
pgp:
- created_at: "2025-02-26T16:39:44Z"
enc: |-
-----BEGIN PGP MESSAGE-----
wcDMA49EwzBHBfyRAQv8CT3tlOGMwEy01F0kZ8tcrYofenVzeKRrUA46FBgOEm/f
3L+1/C6+BLvDGacMF7e0V0BTJ7f/fzYYAcmSF0WQrICnDwRrrjNgN7qEjtYyhr8c
NqpCSEgeR5juiG/DBNEERDrAxG/nljx0Q5S5ILN3kOk1Bm6biK2f+QX4xf2UX6A+
vg52KGbpBpZaZLW7S0DxHlkEZAzRNbgKN7oxlPHDQwiQXA8DKw6pPSt26vuEFawN
WDalia6zU5bjQy5xqgtgf3QZJlSCK1HFVc4XCQT7kjdjlpCjEfLJl056dgUG/DZP
IE4oITG/9xQDreyUtOfOFkRugOySpFa0LPHOGFYnW52DTlTv8f1VaPBzHZcRf21b
R3nATTNgkU/XTdZWTb2gfj2AfbLv1xJzDRs38TAGHYpolsFN/+ehgFoFRO4+K0WW
GgMfkZ03Y5mbGXaGFhGcJEQMAzNbtS2RbuOhwkW/C0qm7qDW98KZXSLkeL1pjayo
07KGaAJFZd2S9Qsb3+3g0lEBmzLIOzqY/n6HCOHIS48/6etFY7jQ42JfN9/yhdRS
coNPK1gQBytmiZLhpVdQ/Nj+1LXzeRZehBPxT0eelH7ILcFTvZhIHPAxplUG33TD
rAg=
=XH0R
-----END PGP MESSAGE-----
fp: 533A89DD49FBCDA2BF014A936C962DD77704154A
- created_at: "2025-02-26T16:39:44Z"
enc: |-
-----BEGIN PGP MESSAGE-----
wV4DK0+AyNumsXsSAQdAmnV3rkI32tVB1ne2EE0ZCZoQzd6CDUrPNijrPaXtJk4w
KDShbO0ota2Jj6F5mx+bscpZkY7LhgcyUkg1GcoOconEizgxmcH9cLaX5m7Wfp5B
0lEBM1wVjbgWVER1GBBay0moPjZLkyucbUmz26QMYBZxTNK9u5c7cxdlvOR3soev
tHhATcB6piyFVIcAHc0Zl+nKfEih2TrH+4E3GPv62shGhrU=
=SC1t
-----END PGP MESSAGE-----
fp: EFBBBB131CF1D863005C18868C8C09CA950B1DFF
- created_at: "2025-02-26T16:39:44Z"
enc: |-
-----BEGIN PGP MESSAGE-----
wcFMA/3nDyRfgQqhAQ//VluVKHQNlwVoIN5NT3nQa77qYpSEg+Qe/kj9oUJxKhlL
PS9PLfr6jqMmxCpABOEAJU8c4BNNoqqJThrgu6TWQ8iL2nyS9/BspIFrkVSsuCFU
/fpWwkGhzl1bwEebqD0ZBJsYBFVfW/pxVgsreudbkVNMhtCgp1Bphj35+gbimdle
Tw0bOPqkYsPymea4oYAI3TNeqpwxWejuI0trseLQmPUSbseRwAxvX9mB8Id56VmR
GYWP7D5Ujg4RWrl4RRlR+a959ONpnWlkNSPanFgvmLvgEqGxU6ZfgSGjLZtUNC2c
ZPrnXnQc0+H2jg0MSkbSwbUADJ/WtukQEn4scDDWCucXZg8MLbGr4JM1MaSumgJg
QlW6hO0gJ8SgMvpBTUqIOW2+ajaXe/zP979SWotCcZgRLvigfLBRr0L2rITWcP5w
5a+1DPeZi/mTyDpkt5LmQ+Tb/6aDMD/aHyEqUskB/6dXh24VHQuJLUUrTPdURLe8
bWFGoGl9GztXJm88d4gOPcvos7mVpRGLRogo1UPe3nwnWnlvWfmV8ONXuAsL+k5d
mxWw99i+fIY/cARftdUmYs+BlUJ3pt8Skzw+Z5T6E7wYWiQnO+LyKp7glk7qnGo7
1XKrFfEG4R1luJzwBa5Yt/xBaJ7UYzzLhXvu6rsFHjtHEWhANlAoZifDh5UivsrS
UQEXD61DJ3GdTJf8CcBWBLt5doxTephRyBSszLEHE6cxnBOLqfwVhjzZNd4TBYNu
zr6v8vaR2/G9AJDoAGS38u2AVHPApgTwgQTdnSCsplIp7Q==
=jS8g
-----END PGP MESSAGE-----
fp: A7A1D860AA45B6B5B29BC192C55BD9B4CD8DE439
- created_at: "2025-02-26T16:39:44Z"
enc: |-
-----BEGIN PGP MESSAGE-----
wV4DuAaR73LFvScSAQdA3wlRX7/i3qiwCBfTpBaEImSab/kR44A06iwhJfepUjYw
5y+icqAGk8trnFXpdiQ+QokQAidnHjm5KfU2cGP/Wvx8IQ3WIBjRyhv9wft8VA3V
0lEBE1yVtElPn6jMr8pRlq/zpMfIhoJbQvBq1xnD9A3MlbK6GRTGXWOwsqcYc1DX
CReclVkSItk2+mn7DqJi4DwTkE6Lpr8X/aNS9OxyrhS4ZD4=
=pQIq
-----END PGP MESSAGE-----
fp: C674EFA56D3EDFDA404B1684090D46D8F1D0C0F8
- created_at: "2025-02-26T16:39:44Z"
enc: |-
-----BEGIN PGP MESSAGE-----
wcFMA3LBDEB9lMmaAQ/8Du5NDJm2S600M7ODYuB6wDeKFkuaEz42B9mpqyK/lxPG
Lp2jSTZk4qi93+6uBgAeGryJe5qOuUwRdgnnIe//IdiCppuI9iv8W2UAHx32PVEc
vNpipdz1pIhXEmBJuOfyv7C8TT/+nrqdM1NXy3hZeH/JhD/TgKP0VH3XoGFef545
+tEz9Cfjg6pvy3BumY2N89Ijv/GAuRkgS7BAdcdoFrc4PylXleYN1SS/Fwb0WyGl
uRF20gZgohdllBHFqApdgVTR/jMl5v1CvA9kMMYRcExHNcpsOZ0rFHyXjZoPA52L
VHLeeIovohDJ9nnETX6KHMIe/Z4MpdoYK/PdSXpBT0fw/WM1wC3b8px7stvsIekR
8ZEh14fTzgo6J4mqLYllfWxEOx3XrJ9D441IcSbV51pswjlg+vaaAjB8GKco/h6F
95TAiJnwqM6c51XVPfJ4CQWJMnpqdfhXZOeCWIju6EizXu0OCko6h0Dq3gfP2SHP
7y1KD2G0XbRHtd7u1gd/iMDRiDaO5UahZDgfRNWVqjMtMwkbXkVTz+auIy687VI0
usW1o6BjMw7v84UmCgcDwo8W/W0w0PmZ3t6WJRXB/EJfirfGR+qerM91KwBT7Mtf
qEG2P/43AYbzFKHndXuJYP8OlCHuWIuCDzb8w+beDqEf8Vh2BQX3tNEmDx+i+a7S
UQHgTUb0hqhOoL572k8SPlf4pW2gw930+Tm24w4VFPhSNp7vL1gDwuFuWfzQ5LcB
A1p6Xx4vBrdG8x0GvFRaiXV3KRhm2TU3XvgxunLtxebwTQ==
=I6Ll
-----END PGP MESSAGE-----
fp: 0E8955A79FF4687A3ACF78E50B5E444C75867E58
- created_at: "2025-02-26T16:39:44Z"
enc: |-
-----BEGIN PGP MESSAGE-----
wV4DpyXAcYv7T/8SAQdA5OoUcMnNDngzRgWJZ7Tx4VEGxhxq0XBxaKeSCwQjCGQw
fQpjfG7teEeJsoE1w9PduB7MBdx5s/a1U+6vORYng6oH6hyQsuMVS8Zi9j0eekYR
0lEB71yT9bwiOPPddxxL8gU0dzG/ntvRVTbDrt4bkNTb+RhBaM2IA1fNt6vAnMYG
cPLk3Z2wFIslMsjAfeXnQqu2IIhtZW7ytKReN8ShYq5vAhw=
=ElKW
-----END PGP MESSAGE-----
fp: 98235A419EA3586BFF4E3FC692D4A5202A0D9519
- created_at: "2025-02-26T16:39:44Z"
enc: |-
-----BEGIN PGP MESSAGE-----
wcFMA911WKxzIy2nAQ/+OGYey7y+fe2C0NpXJS3mKOpJP0ki8bXD/B5TjGPmEaqk
E3GGNZlBaTkhjEl8GWsN9z6otYIMKIWNYShjBWWADzCFjLFRoTADxxkdrB1npDBf
dgZNPEirxDo8zjuXWo8YX578Pj4MmTyfe6O7Ty56FbDe33sQ6Gl9uH1tp+nAhRag
MLYWSnrH5kTVNv1sqCe9d11YniQUgHaXfyLMCsPyvYlUJCGtBp2Z4ihv2ZbV0/xk
WkJIR7hOLPWKM5CQjQOAH6Ei2zkj7WMW1b0e2EM6d+O2nCjeQq33Y7KzsKzr3L3C
kwJYmZH0fPgibXzV0oyADvgoCdnNiaK3fdWA+5falvSG/Y+SSca3il7/jYEK7RPt
eRzJIeaFSzfJT2wbJX09jFKkgRxN0XTsBXFhasnplxhakEzNQYfJfezDur/BMK7m
Ned90hoRVc0zUKZc4/YOK0bOOGvKnen7VxxL+y4TfReFqkMoeUMMRPSykpKJKMml
6CuM9nFxEg65/TE1YB1TgI/DJyRYaVxXI+Fadb41yI8ccmtUu8vTOSqpsJiSBX3k
ITY3NQJjXx373Rt50MSbw5ELOybWrXrzqAyBOXLucc9dPz+55XsgBuSoRlzW8iIb
DJZtz3GZDNh7iLTxv6skCTtIuMWv9YY5KwKzvPt/CKus4EP/RtS28b1xb4yaOJPS
UQGh/k8RppgvTLsrgl+okzWlf6JQTXS4e6+WOYiqU0D5oi9VcIYi4hYRJKtGELoY
Xm34iNKYRyuSsUEbmr8ECo5HUsWQVWNSGgfOX2aIy/3pdA==
=JH8i
-----END PGP MESSAGE-----
fp: E5B4FE1E0209DFFE320D2A2E47087747D89B72EC
- created_at: "2025-02-26T16:39:44Z"
enc: |-
-----BEGIN PGP MESSAGE-----
wcFMA2Cr/drkb+8JAQ/+LxSEQ3e0lxIPVedBh+ANzGhsjA0BoHTl7sjVJGchkKlB
P5E2QQTm9bhRK+iP87h/5yy3JlSj+KS/GehNaqmczORUsrzOWbM13VeB7ESssHYo
Cky0QYNkzYOHxJc963haa4chmtqQbgn8yRmr1Ur1YObPCEdBvi2IaEFe4oojpntB
ym2I3uoOBgvM/xFifEXlCVK31c/mHVXuSVUVu+CYIWWIOcl6mdPLpagNBDxeiGe/
yY33+VKbF/k+9AtNsow5G623UE42M5ztAkvpLj3GKKwxkV11kdnfv8OCEyFLCu4u
SXRcKocNBLTEqfwKPeVQHsPCYdPG1SYrj1cqSWzEs8fL0/mH/2t1p+d2Jt30qSG4
GIIS0+wjwg4WTIwAbFk6p7jsSzI/InjB3Zg041Dz5ESQyussyfZZrEtvxHJA0aq6
ABAdZhA4WUbdzz3jElPmDGpVK2CZ/NavRap+Jwzm8DiaYTRE1C/59345KDkY1qBr
4WQFUXxbwlhlWCX7rzPastbF1BFfj6v+ay09udaCHJYvg+7URBQldy98RsCpSNUv
p87a8vruFdGoeQ4TXnhHf5m8qgtaGsoBWpTKnkmJ5y4aOJgiZ3HtMzSX+vL5J5MJ
UTK4WH2jQp3d5RtNrb6DgBO2AivADTmlMA2S2fLCnxQzHzUirvlcQTtQvXJrgeTS
UQFdm01Xnc/vdFhbW49YDnw6cfg8B23bgK8KQGuNYV09zrxIgnTUYRXP3hWgTtiv
cTvCMlbV1UY0KXC5cCqTTo66RmS8itbMTs97K3GT/yWNWg==
=eYl+
-----END PGP MESSAGE-----
fp: 890B2EB48F343D4C6DB9DA0916826F30002D3C1D
- created_at: "2025-02-26T16:39:44Z"
enc: |-
-----BEGIN PGP MESSAGE-----
wcFMA3Zl11RyMNviAQ/8C5itsVwJLo9SOkYS7/87z1zaESeMlQuf6+OB73GVNAn1
Li7EbBaxvp+vM73cCHO7XL7umkFCeZG6e9qRGX1XVTNnTZDzeHdHtVc5qkqYxfIi
MMMLK3TUSS5KSxhVFh19UmR+oh8j0uyuu869Bb/pTtiiJB205a0vCY8CAK/2vC7r
/2d+bM/01/5xjgGRLlp7hupe6ahJqg87UAiJe1ieWahsUQO0HWg68UbpxmKvADoF
345XL+azA13wgrKvSTaYfTjgU6jbqCwReE8YOtS3/mvo+S4U+HiVbcfhWlIzjktu
otjXokBdU+hew+ICaK1imuZhR0R79mEsHI7mLJChyV23lVdl+3K1ct3B0dITj3Un
T6lwMeOMNUz7BrhY8PK7Yf3xgR9gIzdstj99L2ViymIi9YibxBOhdQTGp94fVyOT
d83CQz1YgvfP+sMbfr0kI/ytmZHTl5ydh8nAYTULeGTEAX0T6dylwKhZsBB8652I
Vivwq4OVfVvjLRsDIN+Ph4kA1+mpyF9GTFVLax7Fjry3hgDHSFthiYADoe3ifNdo
DeN8BjmC4ofPjB1n9RTrwh4YRwwbTjcUgHG0KpYuDy2KWAnzg1GYOxIK7sIdOPbI
93k5BRiIF76pHF9q7220EfpfllLFzcGC9XCGcAucrVTO9FN5W9IRn3iESce8MrDS
UQETS9yF3qmHF7Fo4BD8b8pYIH5N1WjYxEYYUdd80rxfPn9xlCRz3EZ12o22UOF3
3N7sfLFCXT99mF/NWkfNDgxHt2+UuyIWMttffnaVSSpWFw==
=R5Jt
-----END PGP MESSAGE-----
fp: 4069413B74A3AB13E10DF5FD3EA8F0D0FB1CAF36
- created_at: "2025-02-26T16:39:44Z"
enc: |-
-----BEGIN PGP MESSAGE-----
wV4D0Z3mmZqL/KkSAQdAWib8+GRxR4uu43HT9lWpkizPQIB4RfmZS4k1opXHqnIw
F8fuwxqA8jSQ4jU0XvI/UCKXD1nm4hMdE7bb3f2tCQbFW+YDFL8OrnWw/lnEYWhH
0lEBATRPWV4BrFs4LqlsVOx3XUljHqs9XQ0lczS4eQsEmOBKvR5p1xOp5qwm3VpC
JQzkbVRHDtf1o/y6T+AWFgiSXbJjzbYOmiXnMcNJqNFT9N4=
=5cs2
-----END PGP MESSAGE-----
fp: 42AA7F6BF795490C1A522C730ED842BE1216E0C1
- created_at: "2025-02-26T16:39:44Z"
enc: |-
-----BEGIN PGP MESSAGE-----
wcDMA5pTFdxsndstAQv/UBdK0phdO3wdODohmgA6iiEH77FRCQR7ejamKiux4sip
6h2evC6yIrupB7KbdjiIjFqBE3luoF0+4gFbzv7oCl6t5U6K/mAV/i/3ChXHrUIL
BYGLs7Nbaakw+Xpj8QWa9waSPnzf5etF3l8Dw+uQZT5dgFmOL0YvqBcBr4gRzNz6
M0ln6e0/ghCQ9NylPSdsfnLU9CHjChQLU0VYAOzF3GIxkDV7M0PlH6/z+OVHEYGA
A89xm7zSieXU0GAQdrqfks6iC2QOdh5wkXysfgvlSaRGJg4bb2M3+LwvAX8WTxur
0H5Ob+xizTEbDo1RsuihVgW0Qjj/sAe/qB3aDNXmMUma31czckK2ZqjI0ma//+BV
wel83HSILZKkSzl36+fgFWqhmeSXIKq9PQ68TRi6//YtZ+/1/qGEdNx75bxrplsK
LZtJ3JAGVCysarbmqbPWPepxZKXQUuRYrnjQuND8AT2dTWaHyGwhZ+PZkX2yIYhC
8xPOGIDnGUNuM3n8GTou0lEBRrorCd/HjOYyy3U6v4Q1RUqsQA7bisycArqEpXmd
hfE2tKDFs/qM3VXJ5Wi5BzOZsQqC5m+S/az+XqmS25J5Ho+0VFDcZ9dJD98ImUfp
rgc=
=Wa4b
-----END PGP MESSAGE-----
fp: B4BAA59056DC362809388F3F2119881095EA7DED
- created_at: "2025-02-26T16:39:44Z"
enc: |-
-----BEGIN PGP MESSAGE-----
wV4DwvSwby3S138SAQdAOKuVWPXdHIVVb4Zp5TcobpWmXIv8OL34RlLDJwKQkgow
f4JLS6CS7MwIjQE6H9CGv144tyHed0v2CH8LPG2flUq6em+Z97hR9yg5QoD+UMKM
0lEBPhHLRZaz+qEmSIkUHrzSTNHMcaB9SxYJ7Y1fRaiznFuNf3+tCCK8LxHsSp85
UyGaxDu9Vt0yI6A/zCtTCCvAg5tTz2mDvuoW92YU8yh7oHA=
=hvTl
-----END PGP MESSAGE-----
fp: DF977A1F65999F4CDD721A27516F64D5932B8AD9
- created_at: "2025-02-26T16:39:44Z"
enc: |-
-----BEGIN PGP MESSAGE-----
hF4D3RW6IFeToCoSAQdAmI9V3uW4skY/V5u/CDXuj4+7ua+W0DnDouvwWfPUOC0w
+a5o0/2As2OiIY4m9q9yBiMWzad8bd1c7kWOSP3hCgZ6v19Zbj/Ddw89ApObrFBX
0l4BQDaZcmRQPP1wiCMUZkSFN5eDNr7YVHqyRCO2y77fotG8pPIs6OwF4zyIk2TP
xRSyqyg4HrNbrNlw8Qk3bO5Jt3kZHCHz1PIcbjrI+YShHLhbgSsdJ+EWcNuy3Zhc
=PAI5
-----END PGP MESSAGE-----
fp: AC9B0DB590F4AE2017C2AD836113AEB66C510C3F
- created_at: "2025-02-26T16:39:44Z"
enc: |-
-----BEGIN PGP MESSAGE-----
wcDMA1j++ZN0cDPgAQv/bT4ensVZeqgxctDgRtF3QbsWz7Yl0iFT362uHbv4auAQ
zEK1uP1KY8E9qKaE0ALOIudT+kcOh7apQrfa5C33Kp4cRnzHvv5xQfAzxNbFMHuh
TfjrFP9HLdcYvPJ8NyH8ZSxxpblk2y5x8TVB6Efl+hPk4lW3jHYA2dkCae/9eQG9
1A5pLzrz+q4J2jfHCW7XBbRDqevIIRz3AgEU/S3zrWyaGFLUkArTNTVBVHB99Ntb
DwqNJwQFOMJRHxQMHhB+Jsu79p7PhCFdYi7Oq886AJug0V9BW6qKF4Dsab60fcMP
RkELN7OIQhAFYIODHX9aN4cW93dTEdjaW5bnwbagO+gGlqjGfarCctSNOWDPNCWp
y5fZr/7JA1g0VMY/XEmrYjTVKZ+ZN2z+04j38LOvJ43oE69gbBfQV9Xl3jt1iIuf
lKF6zmZSo9bPrFFcAaWbR94pxpQxR/Tnj4G+g4H1l6gNStyrlXmGO8omFgf5gsFp
EumSxyD/MVdpDMdYEUsP0lEB3CHznlT+gsJ1UP/MFcHgH2N0QJJnZDR9YmR4Mp2d
1SGFI5nD/YaA8C5TnJOA980Db4IpuvzakmLOMlIXUFWfoC46DswxXNUVsWbdXK7F
An0=
=TgU7
-----END PGP MESSAGE-----
fp: 5623CCAD4242CC3E0225A62986AF09C173781578
- created_at: "2025-02-26T16:39:44Z"
enc: |-
-----BEGIN PGP MESSAGE-----
wcDMAx5drVN5V7yDAQv/SdfbVExUoA38vmJaYrHqPgtmqHhZAXZ71OhhZJxF30v8
wsyyaHHCgv/hPncBvZnCXiCUSO1T+Ci7uGHVK4M9+1ma5v02KjIe6ObcW848pfEc
HyN/nGmusPSCKZi0ty/tm7V96qzjJim/6ahfvQdxAGcsB/4MrG0fDB4OxRZ3nQKD
0ixMBaxGPjzWE9FOXb/TzbcuKFRZCMw3heLgm2NiFiCU/W1CyLeyFyVbsUyWFoTT
nLTZF3SuDkewXlKtZrObXRRXBIRkNIQ4iMGewRHhrp8LJgQ4D4x3Bw4OMUXV/x9S
oPleprSEkx9heWYHSb6vC1D/TvsMfU7+sE2cojYJI6imIhTLEW9rAZedNw/ZQw/j
1tWpHSCt4NBRMFB4wKLrrSZy4iQZd6+bYDrxcWYZBN65TvVVBO6OSnAEH5cC/7/s
lEMHYqWzkwsAD3d+BaewQIrGv9Dp5IKRGRrQYkel8/+d5qqE5vzwW1rktObdVhxy
Y9y+xXGKstpLSUUTieok0lEB+y2zGDLSUBsWlQIhN1Srf6rFmqRjZfe3WGj6DzoY
gmuEIzQemTcn1i0Z9sUYi6aM9vz0SAXvxdPsjzZmQgAz2TdANjjqjIBtbG9qcNO0
bDA=
=LuZC
-----END PGP MESSAGE-----
fp: 1EBAE111F6EAE0CF136358E8625C5A3B8DA21485
- created_at: "2025-02-26T16:39:44Z"
enc: |-
-----BEGIN PGP MESSAGE-----
wcFMA1kDHheI9SLWAQ//ZXmA/CzEJ9cfr5cLf3ofKaQaS7uLze02R/qOyWFYSVb+
vhF2x2OGoA0A7u3BnmihcmD/pjd2iVcf7Rr9DUGl2Kyzj7SIX3JbTjwIZt1SmNfE
e8nhuFQzFKXrsgLSXgiH23S6sh7tE5Tk2DQZ/OexLL3Z3OrHxmhRiFVx2Nwx1LU8
mkZKup1zkZL8XaOcArRwx/DAOoFSBKyqQ4PlIz17ipcuocZj6VxT94rnjEE9i0oL
JPjAU299RUS8nC19+o6XSu9DYOxQlEJ4ccvCksO7fRZBAnVmPC5gjOCu8h5oHooN
/k/VmLbtojaqVouzh5lxuXm+orexY0tE7APIEBbpS1jCTbo8ZNYnhmRVH0b2OxuF
bAA5P15e8MJr3BJRb8YwkDze7K0AKtvHW56L7uGqSetS7IAu7eyhG3w9Id8RylPr
5SAuhD3yZpPIp4aI/NhxbDruT5Acanvkfx9XbLgtnr+JaCL37N290yCwvlAnEKhw
jerrOEZuQ0CVoc15WTvxsj30NpH/50HeMNuLco5Mdqm9mvRAEgGYugTedn9rzcAS
qDUarFOaCnI6OOA+AsutIdH43OT1t4oOKlInfzx1zHbSF++hS7xKZi7fgM6mgE7c
BPXIAPFy/s0TaF5PQmrZIPRkSkH9bCL21Rb34Lb6M0jF4xuXzR4MRCJtjYfz0rbS
UQEDXQ4COYhunZVWcKG/g6TcezUnpK7jfdeO0+yQOXEDBIDBNA0fZyRhcAUbT1Bh
weRFN7s1qA0/0y3CD8Cn9Dmww3y665UFV4Hh/A0evhIMrA==
=dgwl
-----END PGP MESSAGE-----
fp: 17B8FDF68AC123EB666934B17D0DF6EC048A5D77
- created_at: "2025-02-26T16:39:44Z"
enc: |-
-----BEGIN PGP MESSAGE-----
wcDMA195cwtvYnl1AQv9Hr0Fpe5haXmpcy2kaoy5beaJP1AXqcEY5huomy3RpGex
7/J0LB5nvjx1Y5SFu94l8bD7AAwgTnY7jEQKVDPtqzKuHRFItuHcYEYyYukZk/OT
tRs5QSiOB4zfboGB8uFwI0YO5QWfUaBcK5O+v4KgaywecxDlEed7FaV66CuEfNIo
GFC342+dh+TX9cwKxj76tUFtaoQLfB/ThCejxcOu8Z20tkagGd4hV54p1Hvo1RXr
5sNop4ppbq0yDmuyjYADTOnv/eQKBBXTXDhR/ZxzRRc5+lHvx5oOxQplqDAsd09k
YjjichphXK090iJTrHVB5EAQb8ePA05JBysA2nE8rMTFcZ2bkCek27nWN/SoIVEi
0faFpiMM4Mfy2cSAlyod+xrWVCOX7XvvoO7a9mEKl21wEO8q3FAVjl07DdDPM0sN
wMplp8fLaz5P7KDMf92m6MclbqsyFXqFfsHI9gNSajZi8ylzaB9HLTTC2d+4YVjO
QUIuH8MXBa+fs9RIcV5Q0lEBjojsANreO8pPwoAmmP/ExVZo0nXJ3Ovdqd8oKkbc
6OYvaZJnVUfaUf7AAeBrhjudGymJaEIIQnEN4juF/89dg7mqrn0VLmRsuHAc5BBB
TQw=
=mDJM
-----END PGP MESSAGE-----
fp: 03ADBCD31737EB474A4BD235CE615FAF927A2054
- created_at: "2025-02-26T16:39:44Z"
enc: |-
-----BEGIN PGP MESSAGE-----
wcDMA/IHQvF5YsEcAQv+N5Gij2qXj4gjhGxSjpORDuuS9HMyAWKfpshnvYWjr3QT
Wdu2FdaYv1XHWbTMcVH0+B3Pe4INQboGiB2wY/DCzA0vuverBR5DciZ8vN78k05W
+IqDYLfljeclaoJlXnlaPOtWKI2OWm2fsg8/4lBqGnsJ92Y9Bc9qXbmaBgPvgtkU
B3dYDNZ1W6Mo/wm6Pk7QMVdZoJDGvs7PpOhJ3D83koSx4BmZvrDrPpwRtWwkbEl/
zSvpL4qXv4NWQjc71Djj9/uAtlXIt2QPWeCCqsoyIMHd7Vr7qrCAcZpQH2GYnn0v
5Tb3ZcCH6CNRrKQMS5Fn7OH6aeLuKHja012jJxnplYHzpp8hdL/ysqLlgw/wKxS4
rOThNHDAPtCoS2jDRmaNJgiSvOwtwPq1E2JkaInjaB4xciuaK/r5R1CwE7J/uKRp
j86JEC4hLgBwZX4llzMgk575O4CN+5HX/ywHMH3JXozkBPh3EBun79Il7AogUVRa
2uoMm5cHhHZ9M077YLOd0lEB4+qgj4o/p60cx/oiaGEQcQzjcYn8aoFyT023xuta
qrk4sKQDKQo58tQ2FzbKxQeKE7Ehadv05dRevc9j3+MwcbyEDCzbZ1k/zb+41mCj
agA=
=2PBo
-----END PGP MESSAGE-----
fp: 04ECA5DBF73E98EBCC1FF0B018F2D15CC56BE487
- created_at: "2025-02-26T16:39:44Z"
enc: |-
-----BEGIN PGP MESSAGE-----
wcFMA/fyan/DNa3uAQ/+NqLkSl9GWeDK81+MPPeU0bkbOSw5Awv2xn3eWjFKIjEJ
V5H1Hr0a3b/Vx0WdVM5eat0LpH5/OFWljx0QNJfqFBz25TFgBqHT4Ew3eCtZFxJC
ElwDpI6eCCkZ3w4Zk3BsBwCwcRt0kCHXJb7STFJ8OGg6qb0mB9Weg5Rauh8jArao
VhxrE//oqzLjdDCNE7jXb68/6Rw75TsKZ+nAAtecK4qtliU+gP3o78utoWb4+8hJ
0hhbLjQpKcwPEZWi8/O2gzePHNudHHPwVHOKcIs5kuXF5TPd9skiqZoNTtuiVtGA
LjM04k73fUm4JlqvtlSLXN+epm6ekTQWabslDK6yxXIovu8ycBS1EVudRjMdpBX0
5bi7nKKOsELiZ2k/bULmbLoKmiKSO9F9ulmQGTeH+dOZHPcPxDkRa8waBw6vpM47
4UDEbOcadc8+mAsMVoEiyGl7HNFWyVp6W0V4wWTtICZdty0AK6nyOp5VF9loUewE
jDbk/swUsILWdgDNbquwGkgVbbGWTuGt8wsc9i5erX5dkgeM2ghKpW6PgtDveDkD
uSINtgk6n8izcBOmiw7igD8+5e/bYpiwrHHvgX2d2FqnlK6zoTI8wjsekp+Cy2ps
VO4blkqYmdkbK3DCEQoVrShrRezlSU43wq4dZ6qi97Nk70qjbDoVuXpzNWhP3cvS
UQGmUKqjHRzV9c8lBKZEWdeJlyiWoaFrSoQvdE4an7QptYUjaN62HhG7Fwr3mzxJ
Hbewf3rU3gHC539+2W8wZ3teF0eik0gDU/ug36xun3uZ8Q==
=eVAS
-----END PGP MESSAGE-----
fp: 9E561083EACDE14694C73A323A2F6C1D153D753F
- created_at: "2025-02-26T16:39:44Z"
enc: |-
-----BEGIN PGP MESSAGE-----
wcDMA+cOSmNXMUmKAQv/YwIURSy3K/pt9nr7Et4FJmjQVh5M95bMlve/7w1W1C89
bFVodCQJuKKH99o4pC/FlIb0DWmHY3AsHnTyC61QJ0Mlca0W5eosye4vzeI02KE8
9FvYZRLGJ5f1XwIQTh25K3dGMS2J0CMVZDYXBm1EyqqdxoPuHZZOvas29j1CL5Of
Y6KRbgV323N8xXkuP6THBjVnDwyQUjnrLLlMAJX8bxBgKMtGcON5xqRji4SaWHeN
6ZbZMVjk6o3i00XOQe5xqDcEnBNtpzPXPbIf6enyEKfbzZBMoS28M/4gMkFtmaaF
gMqdH0qwfu4ZsrMCNUf3bQZawPm7JUxOVhzHpz/J22AxODF3CpH2romgJX2igtZ0
XX6nU2iFJDPCfpIIqJAq3D1+DajXDSOlkSmRU9yWUPI4PS2kj03xgR11YDE38m6a
1uvjDJ3p6CfFpBOs+gL5DrAspW6kbOyi309OmfQ+WWUDvSBloCePTbzdbFDPR7Qe
j8lu3vwkT/lOmR/haIN60lEBtig7+AKnJ5218r9So/4WOyl33hYikRj2G/sjrbPs
IktUHFgWuALcNgSCYPkx48WjHUcpjTDjAxkmgup4t5t9wupl+Hf82CkcBrffbnza
8sI=
=vFuE
-----END PGP MESSAGE-----
fp: 9F08DA9D42379AFE6610E9E615CCEC6801DBA02E
- created_at: "2025-02-26T16:39:44Z"
enc: |-
-----BEGIN PGP MESSAGE-----
wcDMA5bUWViEqX+EAQv+P4bjeVEVjMrI6tv/HaJd7PI0jDjaHLIKvH+EzamJWM+d
+bf5dh/ChV11bqNn+ziqTQKROOnjaWb4dKmZWU+12/kWUOVrpB2KcOj9QPOAZra0
z13iQzZy/aMSMHLZbOMzEpNaqvNSEuqnl3UgeEnjTav7JuMzw9Y13lWw68i0Bfog
QHQQ5HecuP1vnBfnY7j5rSx+ivKQZ368idArmcVuivbE3yTnigx9EJgw74TOlmOz
p9KuUQzAoTMQ9xle7Af41Q7vgI3sZndWvi/8dtcfPGe9BJocxK536ZumwXmwMZh9
htPA3s2JhnCUwwU+vnFRZsqBkbYRxgTlpE3FLWsfFXlRoqaWs9tz88mtS6ColxqF
035i3L68U6gZpXCNJEtUhjx4WHHDnjKi6bvQGx5jCLmhnHsUU0dTuSa9fr7SY1Jl
2G3nwfpCLaJE9IH6t00GRqgVWtniQLZ9b2UJ+uFbqgRRuZ9EWOCZWQvk1oq1M1te
QCg3Cbu7b7jZjTkk1GKf0lEBZUWHLxamR+THxo4I3nZq5J52P+7UB1V1sylO7vYk
1MFrHazA+51LZrhn57FIkMUCQ/Y4f/jOSFVtFVCIHD0j5+SfNnWFC2AQYoKH42Rl
Tcs=
=7YdW
-----END PGP MESSAGE-----
fp: BA6328948D50175F196AAB5111F324603D12DD56
- created_at: "2025-02-26T16:39:44Z"
enc: |-
-----BEGIN PGP MESSAGE-----
wcFMA8YZutSkx0fiAQ//XrSOZC3C1b53P7Nxq4lpJvkjFX07YwoQ2uKXFFQLcSC7
PufLzJvRxceytIUhEhCUbx5hASxLfCrupKSQ33FCTEg90eMRpAX3G0eHC+0bj0Ma
qAwAZJnG+vnLHc6W1no1NsijmGXvRzBRUdUk3yWt/pa+9qW09opJPsyrRwye3ZIL
IJk3rmm6d13TA7RVRHmy94TCIGSGHqfDUXLM405oxHQoMpOvXg+hl/YXqBNU8Lrv
610dKzKElS7cksyVdZ2LNL1Th9IvwxnN+l8+UKkq+aj0x5CtuYNjdQnucEthgZ4z
pwayS74uW0vmpCP3RgOhQ4hTdr4ZKxuQbaPeE3/V2dbTwT5XxUSTSOJxf8/Qu6kS
3T6hgIufirl3sIRGVsLn/dB0GRvgdrmBwuxyR1pvJocRdpnlYHciszZFnFrhGihH
qPF+p+UweYQi4CtIDOC2/eswhA1snA0oEvvMShCWrACGVLiGgDc2c8dI97zwfHvu
UQ9RGk0deal1egcQ5FshMVJU9b/onW8dw8q3nqTkaThTZgUJChQ/1pglmjb4Umk3
mwXuK7lX2hIguVDrgPu9EMN9ujiV1uE6ozLRjKDPYlirVx+6t8IKS7gEMv0K2gPM
yn6BUUID/13Gr8jcGQW/0snIpG50J9xRSLP2XtR5kPhrv0uBH8ACvX3dRUAPdorS
UQE6Q4EsrEFWxB8HMb6bQ9DsgkFJyDC044nd9HKpFI9IZUlhP/nAuTmqSxCMErVd
dPG0PyY0nUUA8kqnWtHpFhm4CCgl00uGr0rMwqM/ienspg==
=YQEC
-----END PGP MESSAGE-----
fp: B643A5D780A01F24E95AA100DE6F8E2C149C3748
- created_at: "2025-02-26T16:39:44Z"
enc: |-
-----BEGIN PGP MESSAGE-----
wcDMA9vEXLVODHp3AQv9FIxXFwDFg9EQ0bwdrxPWzrZnVIXKOpQeHtv+aA+iT8Hi
pMz8kimNlgHjWUthJsmAujMcoyvH04PTDlJUd6UTzVbqSRaSh4+8DrSsINyofjgn
0l/Wy1w1Bv3zmcAZwHD4/sITjgCZ8ETYOFSsZ67JoDz5MZ5jqCSSrvtFOrDIwlvl
sZTSY0gU/J27GJ7Oc2FJ8mxKeWN+ZO5HEpRZgoyo3rj6SKIgqw1mYAVPwl/e9eVv
hzhNe2A9bpSWFjLUKSrAW0VAai23HrdSHup45rKOEv28iAOSOTXon3jZNSH1au7K
KbBKahvvdJ679F0/9zH/iZAsnrY5oW+shgIjpi0moWnih0YPwZNu1mzDkcfLDQDH
F60eoZrg3iieS9DWcxOksbprqWdyF+B1d0AxrxR57Ru2nFdR+ST/8oIcGue6I+uK
kVSJFeArdbAjEI8ldB9WCqkLeKXVTPGLcOOQxSWLNFsHuuOpZWnWpOAMF06HtBcY
eSZHsqGHgyvndIgQ8FLv0lEBXgxpZl826/2gkLVSdNveCLF90yxaagrtdbTEfm3T
7LIUdeZlRhYe+eg0diWcAyDG1XWgXiCUfocFR0yfQRPHWEVXGh98zZ3MFndPYrfB
Rf8=
=J4gG
-----END PGP MESSAGE-----
fp: C19A7D807525CE24443CA9A49372E896B41FE700
- created_at: "2025-02-26T16:39:44Z"
enc: |-
-----BEGIN PGP MESSAGE-----
wcFMAztV5cc72nziARAAieRTsV9FNm/utlda05oE5ALMM+clIAtxw5grfaNBWeUo
Xy4fgkzBQx6UhKDwsIsAi9wUBS1Dzd9HG4tUmmAf85TEbX0llKdc8KD9gdzt22C3
Roq2oCQKHdomA41qVpnwudPQXDakc6pltLZy+TAuziypgzLHc+7nY5Kd9wFhzeGf
f4sIQj+ctHSd3ghGu7UQpJW6W6zfmkBjSHMnbZj/pQndg9i0+2YtxUJ9yqij5q0P
zIWVZgNt4dLoik3Q/CDDtw7u4IWPHN0vSNJRgeylUTMuF8Q5oVQgGcq6TTUYtrxp
76h/e8kFdxfif4fQkHVUB+kDvrGO7CUUjtgk+tMop5165BlkdZx3NpcPaAuk0ZP1
9CtCDRnGBRCz5sjjailmjEGXagUci/nWD7qT02I+ZWzqwnHPYA75FBmIrb6qeUNm
KsrAVsTNqb3o/iuLHaBAdEvncOQnV9ne9QAyp/i0KbLyTQ/zJaZEAIANY2GspRb3
gsCnL0vr2knofMVQD618WByyiTt2fqTnbfb47Jadp9otsim6OVNCzxYxFKX3VpYr
ncZxuxn+FxIFMn2tyFC6Og+xYPe4eBtbb5rqaEZdmzwYW2FMP6U2b4YyLqpQE3hw
miwo9ODNgtUpbVBrb5pRM9DVvsEVmibAEPV7uWzpPXFBCaLIlWSuHGtvWttEcQzS
UQFATosYmOKZjPcjPAFfr4avz2co17LZ38N98JQ5posfLFMlLMxqGfFcDZmQdxep
EGSR4dkO4xzzZ7ORcCdxhZaOtcB092kIJKtM9RCFegXo8g==
=LPni
-----END PGP MESSAGE-----
fp: 0DB51A7E90AC6418B7DB83724D38970874850C33
- created_at: "2025-02-26T16:39:44Z"
enc: |-
-----BEGIN PGP MESSAGE-----
hF4DxQtfVaqAjfQSAQdAL9rn+f5L3cPwmS+NM7vM5tR0VCDD/p+b43nFFhAnVQow
XcSpJNehn1E4JmgY5ixYk5hysPoDTanHvytXKSSOJF8XpybUz1K1BF06iiCnq6X9
1GgBCQIQfTwPQePieWp4usWB7gkimOQ5KeSmMRQ8vqRTa9i5AXzWlGruP/i5OSHq
FAKhcdcihAt9jFwxsAymbmrpqWOTA/H7uu5385cRiCRvJUydmz1AjD+e9U96dYT2
Z9OHZM/x7057bQ==
=dZEu
-----END PGP MESSAGE-----
fp: BF3D5CEA36DB58AAE1063D0BB341078652D87924
- created_at: "2025-02-26T16:39:44Z"
enc: |-
-----BEGIN PGP MESSAGE-----
wV4DZmNQj/lmIGsSAQdAMt1u6obZgLUjVDQFJ4cXDfUwzy+n9p+rZa/5DtAC+R8w
ChbOqargvUW+TPuA2KAOrH0m7QrzpUAXvk0grF5qewdTwvp9zRArCobkz/xwF6jS
0lEB/rra99F+Wp4c7IBque3HnyWbcNhw06BfK8jhmwjLcy/pNZkj93WYcvannZLJ
Lc93MffCPEK6eMXW4cFHT9BacyGTHKtvagwLPOo7yryKwTs=
=nYcx
-----END PGP MESSAGE-----
fp: 73C2C9954D1BC94DC6682525D2FA233B52AEC75C
- created_at: "2025-02-26T16:39:44Z"
enc: |-
-----BEGIN PGP MESSAGE-----
wcDMAxSu/PO9GEkJAQv+KGOnoZBVSEuWqR+m1npSR59qdPU/XQpkfRKTKK1XQOo5
0IU+uAnqweIXaScnI5+Mf+k9QbVPUw00w5ot5JlXfupBL2dS/0ZE9a8iESEu/hIR
d9FWSMGs2BwUlCVIlZPSz/p/+5NaxsckWKORDP8QBABHQ1c8bzq4yUWCt74gkjgc
oK2idCXqkvRaPe9ihiyq8fSWtKUPq84aSlQ2cVJMQ1C36xdbQi8Q4gQnTGr9Ncup
qUbryHejG7Zd1lUtgUZDPVdHnuDck2OUjD6R6DOe4U8dH+5/aH8mk1u7iBoxjAw+
InWUubfQJzEwpBZ3bojfz8oWKoEpToln3sJGfnlb/ssPFhh+AiH0sBfRgqbSf5Jh
pCMDKN3X8sy53pJdcaENwSRbZ4LcgsgyR9XihG9ZxGgPhXpDzMxZ65pTrvDjz3Kp
UloSzEjSSrQNE+z5sYbbjRTNk8YOkVW6FpzU5gmKrnsOFLARd96fdAvgyI/9Etuu
g45uD4GnNrq/wd6DLN4y0lEBUGFCPdh/eLsgsss830UVtCs2lQPfj5C1Q3yxhxjP
Hae+f/+bTfpOepMrd8huxQ0tB8qKDW94mdsikowJewX+5wV5EdErQ/4G2YtJZTOA
oTE=
=3jwE
-----END PGP MESSAGE-----
fp: 13E169CF5C35EF164628764AAA2EECBDE0B38CF7
- created_at: "2025-02-26T16:39:44Z"
enc: |-
-----BEGIN PGP MESSAGE-----
wcDMA8b/EJ6Mi55zAQv9GnVQNxIAnrBOPolTnJ46tHKs/Ny5QCn8i0pJ0cmj8LR3
l6eCrrvb5aEZRYV1DIMzP9BR9D6G0ShZBsqcBgpm3DgNjQDD1eWAAZxMuWMLMFlQ
0KDG1myVgRNzGXP2cpiJHPJPcdXTd9aNntMDzc/xpHlfW2UXCmdNwl9Q0NrMHR1w
xjcf2UIg6BN3yjxUOxxUbDPw5cRWlIUiIPC52R6V8hHBzh1UUhdj06Hln4e1aVp7
Wpa4K9KJd2n6q9XkF9aRuYyGybOvnPJlL6OHaR9PgIfGCZ+pufI5AneQeahrc+EU
/DkQZNILkBEW8hnwkrXGk53CnS7GQPg8s1kiab7nIuQNF21dpSA4RATMlESC3i0t
BmTfI0HuIaZMxbnRvPSfQwGiMhf6ea+02QwTIjuN59lDuFyQBa3CwJXpiAAtFyyz
9Lu+HAsnRmheK48PXtLkZ8ldNH2r/IYFjtjbANKKzn5HelmPWwLmHo3N4YIerKrj
znhdrY/9imhtA7CiC89Y0lEB6Y/cfSWKtoOLkx4RKknkQURvyJfAMjRhVVFxGZCc
LerDo0Ofmm0z7ZgibYiL1W7Rv4TsnZXr1KDbSrJzD9PcSZt0rTQ9Rw7MpqBeHH/f
/CI=
=L8LE
-----END PGP MESSAGE-----
fp: 13D7771BDE5241293F3EDA442AB3335ECC93DDDC
- created_at: "2025-02-26T16:39:44Z"
enc: |-
-----BEGIN PGP MESSAGE-----
wV4DAQRCY74qADkSAQdAERj1PfuanNLwdZgD+mojcIflbrh8NbXqU0kBicBH6SYw
3ZBhzUo72iPSpfHOzk3uZhLw1gIxyEwIAfg35k+2GQUlIO2yFjNGMrNXEXnd2QKz
0lEBhmYkMPlKeRXHAbaq2jF72GoHss5LxBVi88L68QkXeUSlITa4dN/YLcpn1rIA
1DvGGEEyrRt5zbYkOZsMxXgbAtarBf/HxDB2nHKmKUo38GE=
=xzey
-----END PGP MESSAGE-----
fp: 0C136F7514100470AD3EC8D37BF1FAEDB2ACCA9A
- created_at: "2025-02-26T16:39:44Z"
enc: |-
-----BEGIN PGP MESSAGE-----
wcFMA5KoiSc3R+FfAQ//WNYLuMOvDNX4ieMs8YwYFnti9ZBjuopwrPSMvNY+k+yt
AhAFN4DX2m5s76U2ILjyJbd2c2ZRUxa2Pmv2BjVhxjHt+Jkh1AznWCqX9BxcBlxd
q8kQAp2ciznmYePrPnZAOQkyu8Ik1A5/W0W9shL2LAfjRuwcqeOGEhoVSsXRQdi3
knGTqbCF52MX1yN/Z/AU6jJwFUPz7aUzpkJwW2Y8vWV+ndiyjZjyu8jT3qJtlH2z
JlaTBBTH0GWtXtlY2v+W9szfL70IzJVaKciqY7y6G767i+jPYLFXVOgudsAAfnPe
JMp0uvm6Hw1po9qgpoWGmiG8NPp8Sl4BCRQsk4oiddm/+34yfBDq+ra9hmeqiVan
9z0oFnpBY18L8MVimwoeAJOoihuq2/8k3OlpJoqRkLV+ez6o65qoPd8rS17+iOqb
ZDQwmqUf8lgYI1Sfq9qwWk88UYgBnwhRmyw7eZuqeCoaKv4x605cIydgKfd25xr8
SeeuLzrWeXGQF3friqj2LujtTOAH34O5XkQVp0TKHXoVyL0wqgVPPIGBs14HOh3M
FQQH2knRmoQDARIzlN8+UCf29VVG73BxRG71XbZnVTL54mwDjBYYOW7T9A9DpBGK
wo+8zzptoXc4bhIYCYz06mvuEyH3IMe2o4/MQ38fboU7Qsi3no9jqxRb/H6ye0PS
UQGRIaDP3cKXh34JQLT6XTpuVDpDpVw/F5xy92VDDafvD1PiSiFhQzWD15zEw1tw
2QcLcJZI3pUGf5aV9HK+thjzZKMTOtDog02GkIblov1EWA==
=sR1k
-----END PGP MESSAGE-----
fp: 57F93F2A6585CF2DF9A3B31F13B9F45E122698D5
unencrypted_suffix: _unencrypted
version: 3.9.3

@ -0,0 +1,98 @@
#!/usr/bin/env bash
# PURPOSE: Test to verify update_sops.sh script
set -ueo pipefail
test_dir="$(realpath $(dirname "${BASH_SOURCE[0]}"))"
cd "${test_dir}"
# opinionated: keys located in current repo, one level up
keys_dir="$(dirname "${test_dir}")"
# deliberate: just "dot" for current dir
sops_cfg_dir=.
secrets_file="mock_secrets.yaml"
# prerequisite: for verification of sops config, idempotent create file with a mock secret, src: https://bash-org-archive.com/?244321
test -e "${secrets_file}" || (yq -n '.demo.credentials.secret = "hunter2"' > "${secrets_file}" && sops -e -i "${secrets_file}" )
# Special Case: Add caveat header
cat <<EOM > .sops.yaml.tmp
# PURPOSE: BLUEPRINT for .sops.yaml config
# CAVEAT: DO NOT USE THIS FILE AS-IS in another project; copy it and remove the unauthorised users
$( cat .sops.yaml )
EOM
mv .sops.yaml.tmp .sops.yaml
# TESTCASES
# define "fixture"
repo_root="$(git rev-parse --show-toplevel)"
# ---
if [[ 1 -eq 1 ]]; then
>&2 echo -e "# ---\n# TEST: create sops cfg in default dir: ${repo_root}"
set -x
rm "${PWD}/.sops.yaml" || :
# note: fail if for any reason sops config defined at top level; this repo should not have this!
test ! -e "${repo_root}/.sops.yaml"
../bin/update_sops.sh -s "${secrets_file}" > /dev/null 2>&1
test ! -e "${PWD}/.sops.yaml"
test -e "${repo_root}/.sops.yaml"
set +x
# teardown
# enmesh: restore, since this particular one is checked in
git checkout "${PWD}/.sops.yaml" > /dev/null 2>&1
rm "${repo_root}/.sops.yaml"
else
>&2 echo "# INFO: skipping ...."
fi
# ---
if [[ 1 -eq 1 ]]; then
>&2 echo -e "# ---\n# TEST: create sops cfg in curdir: ${PWD}"
set -x
# note: fail if for any reason sops config defined at top level; this repo should not have this!
test ! -e "${repo_root}/.sops.yaml"
rm "${PWD}/.sops.yaml" || :
# minimal operation: update .sops.yaml, update keys in encrypted file
../bin/update_sops.sh -c "${PWD}" "${secrets_file}" > /dev/null 2>&1
test -e "${PWD}/.sops.yaml"
test ! -e "${repo_root}/.sops.yaml"
set +x
# teardown
# not necessary, all tracked in git
# enmesh: restore, since this particular one is checked in
git checkout "${PWD}/.sops.yaml" > /dev/null 2>&1
else
>&2 echo "# INFO: skipping ...."
fi
# ---
if [[ 1 -eq 1 ]]; then
>&2 echo -e "# ---\n# TEST: Full Args: specify path to each, also for secrets, mix specified and positional params"
set -x
../bin/update_sops.sh -k "${keys_dir}" -c "${sops_cfg_dir}" -s "${secrets_file}" "${secrets_file}" > /dev/null 2>&1
set +x
# teardown
# not necessary, all tracked in git
# enmesh: restore, since this particular one is checked in
git checkout "${PWD}/.sops.yaml" > /dev/null 2>&1
else
>&2 echo "# INFO: skipping ...."
fi
# ---
if [[ 1 -eq 1 ]]; then
>&2 echo -e "# ---\n# TEST: induce error: invalid file"
# dev note: ':' is a noop operator; could also just temporarily disable strict errors
set -x
../bin/update_sops.sh "${secrets_file}" -s non_existing_secrets.yaml > /dev/null 2>&1 || :
set +x
# teardown
# not necessary, all tracked in git
# enmesh: restore, since this particular one is checked in
git checkout "${PWD}/.sops.yaml" > /dev/null 2>&1
else
>&2 echo "# INFO: skipping ...."
fi
# ---
echo "TESTCASES PASSED"
exit 0

@ -0,0 +1,25 @@
#!/usr/bin/env bash
set -ueo pipefail
# PURPOSE: Allows User to verify their local SOPS configuration using a sample SOPS config and SOPS-encrypted file
# Usage: 1. Existing User: upon adding key, run this script to update the SOPS Config and encrypted file
# 2. New User: 'Existing User' has added key, run this script to confirm correct local configuration
cd "$(dirname "${BASH_SOURCE[0]}")"
secrets_file="mock_secrets.yaml"
# prerequisite: for verification of sops config, idempotent create file with a mock secret, src: https://bash-org-archive.com/?244321
test -e "${secrets_file}" || (yq -n '.demo.credentials.secret = "hunter2"' > "${secrets_file}" && sops -e -i "${secrets_file}" )
set -x
# within current dir: update .sops.yaml, update keys in encrypted file
../bin/update_sops.sh -c "${PWD}" "${secrets_file}"
# Special Case: Add caveat header
cat <<EOM > .sops.yaml.tmp
# PURPOSE: BLUEPRINT for .sops.yaml config
# CAVEAT: DO NOT USE THIS FILE AS-IS in another project; copy it and remove the unauthorised users
$( cat .sops.yaml )
EOM
mv .sops.yaml.tmp .sops.yaml
# if reached this far, is success, due to bash strict mode. I.e. script would have failed by now.
echo "SUCESS"
Loading…
Cancel
Save