From c44c9a0988760eedf4b163a883686f589001ebb5 Mon Sep 17 00:00:00 2001 From: Peter Valdez Date: Sun, 23 Feb 2025 22:40:11 -0500 Subject: [PATCH 1/2] Add gpg files --- .cursorignore | 3 +- .gitignore | 4 ++ Makefile | 14 ++++ gpg-secrets/staging.env.gpg | Bin 0 -> 871 bytes utils/gpg_manager.sh | 140 ++++++++++++++++++++++++++++++++++++ 5 files changed, 160 insertions(+), 1 deletion(-) create mode 100644 gpg-secrets/staging.env.gpg create mode 100755 utils/gpg_manager.sh diff --git a/.cursorignore b/.cursorignore index 1761c01..b8e022b 100644 --- a/.cursorignore +++ b/.cursorignore @@ -1 +1,2 @@ -.envrc \ No newline at end of file +.envrc +gpg-secrets/* diff --git a/.gitignore b/.gitignore index 6995804..c8e5722 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,5 @@ volumes + +# GPG secrets directory - only track .gpg files +gpg-secrets/* +!gpg-secrets/*.gpg diff --git a/Makefile b/Makefile index 0addab2..d5d69f6 100644 --- a/Makefile +++ b/Makefile @@ -42,3 +42,17 @@ deploy-search-prod: cd search && fly deploy --config fly.prod.toml deploy-all-prod: deploy-frontend-prod deploy-backend-prod deploy-pocketbase-prod deploy-meilisearch-prod deploy-search-prod + +# GPG key management +GITHUB_USERS := azlyth + +import-gpg-keys: + @GITHUB_USERS="$(GITHUB_USERS)" ./utils/gpg_manager.sh import-keys + +encrypt-file: + @if [ -z "$(FILE)" ]; then echo "Usage: make encrypt-file FILE=path/to/file"; exit 1; fi + @GITHUB_USERS="$(GITHUB_USERS)" ./utils/gpg_manager.sh encrypt "$(FILE)" + +decrypt-file: + @if [ -z "$(FILE)" ]; then echo "Usage: make decrypt-file FILE=gpg-secrets/file.gpg"; exit 1; fi + @./utils/gpg_manager.sh decrypt "$(FILE)" diff --git a/gpg-secrets/staging.env.gpg b/gpg-secrets/staging.env.gpg new file mode 100644 index 0000000000000000000000000000000000000000..4dd91f2c6729faa413f501a76908c8b21aa83d29 GIT binary patch literal 871 zcmV-t1DO1U0gMAnwv=3JUY%C~3;r*gS^tmz%gZ&hu9b}@r|GpOej&DHub{dxU6x?EnvGA+* z1#x{UQRg~aCYQycw#S^w3p8^ZzQezW({R~) z62XVNO5h%N^3e6#-(s;`V9_PJ?W)~ z_&jtppuK`S{9q@!4N(i6B}UymN=~3o_&U>jWhX#;xzGGC4b{dS1Uh`%{AfC6)q^Y| ze%F|L&cB%pPJ%?PK?8LX^<}JpL7bb_b(h?B*zsepuTJg($#5x;uhk-#U;k(LS*$oL zlt`sWz@<70O~7}UOu9}0JQ0pN!Crjt_vF92LOS;L^*;v~!iNsTvt=0T6VFq@zH%^e zTW~6-bcKY#>Y%Ba_e!GH9B0)2%;`2{AHOaAAOU&v_1yo~2UbTeV56WYGvTY)vt$oqzOzB1FmnHvq;Eh-UnNPwpgL*ZfklWshVa72F;FPp8(DHbZE;{^80(; zuy|?syk(M%|8|`va#azYQn^+VbG{SV!#AXekMLDR+s1IBt2y^ISn_O>v1E`cc%gk| zX)P$?Jv2*^pF|mF`*s<*gVZGN6gST;HL+iE@?@-x-^@oo2F;7wVcthJZoySlTy=Ii x9W)s{1KuhF)l{SNZOwViRAh7y$(>w|^&Z`JgltjcDO$@>r_PdZfO?r/dev/null + # Get the key ID of the most recently imported key + echo "$key_data" | gpg --batch --with-colons --show-keys 2>/dev/null | grep '^pub' | cut -d: -f5 + fi +} + +# Function to import GPG keys from GitHub +import_keys() { + if [ -z "$GITHUB_USERS" ]; then + echo "Error: GITHUB_USERS environment variable not set" + echo "Please set GITHUB_USERS as a comma-separated list of GitHub usernames" + exit 1 + fi + + # Convert comma-separated list to array + IFS=',' read -ra USERS <<< "$GITHUB_USERS" + + # Create a temporary file to store key IDs + key_ids_file=$(mktemp) + + for username in "${USERS[@]}"; do + # Remove any leading/trailing whitespace and @ symbol + username=$(echo "$username" | tr -d '[:space:]' | sed 's/^@//') + if [ -n "$username" ]; then + echo "Importing GPG key for GitHub user: $username" + key_id=$(get_key_id "$username") + if [ -n "$key_id" ]; then + echo "$key_id" >> "$key_ids_file" + else + echo "Warning: Could not import key for user $username" + fi + fi + done + + # Store the key IDs in a more permanent location for later use + mkdir -p ~/.localmart + mv "$key_ids_file" ~/.localmart/github_key_ids + echo "Imported keys have been stored in ~/.localmart/github_key_ids" +} + +# Function to encrypt a file +encrypt_file() { + local input_file=$1 + if [ ! -f "$input_file" ]; then + echo "Error: Input file not found: $input_file" + exit 1 + fi + + # Check if we have stored key IDs + if [ ! -f ~/.localmart/github_key_ids ]; then + echo "Error: No GitHub keys found. Please run import-keys first." + exit 1 + fi + + # Build recipient arguments from stored key IDs + recipient_args="" + while IFS= read -r key_id; do + if [ -n "$key_id" ]; then + recipient_args="$recipient_args -r $key_id" + fi + done < ~/.localmart/github_key_ids + + if [ -z "$recipient_args" ]; then + echo "Error: No valid recipient keys found" + exit 1 + fi + + # Create output filename + mkdir -p gpg-secrets + output_file="gpg-secrets/$(basename "$input_file").gpg" + + # Encrypt the file for all recipients + gpg --batch --yes --trust-model always $recipient_args --encrypt --output "$output_file" "$input_file" 2>/dev/null + echo "File encrypted: $output_file" +} + +# Function to decrypt a file +decrypt_file() { + local input_file=$1 + if [ ! -f "$input_file" ]; then + echo "Error: Input file not found: $input_file" + exit 1 + fi + + if [[ ! "$input_file" =~ \.gpg$ ]]; then + echo "Error: Input file must have .gpg extension" + exit 1 + fi + + if [[ ! "$input_file" =~ ^gpg-secrets/ ]]; then + echo "Error: Can only decrypt files from the gpg-secrets directory" + exit 1 + fi + + # Create output filename (remove .gpg extension) + output_file="${input_file%.gpg}" + + # Decrypt the file + gpg --batch --yes --decrypt --output "$output_file" "$input_file" 2>/dev/null + echo "File decrypted: $output_file" +} + +# Main script logic +case "$1" in + "import-keys") + import_keys + ;; + "encrypt") + if [ -z "$2" ]; then + show_usage + fi + encrypt_file "$2" + ;; + "decrypt") + if [ -z "$2" ]; then + show_usage + fi + decrypt_file "$2" + ;; + *) + show_usage + ;; +esac \ No newline at end of file From 6a774dfc317ae230cf31a9b7e83bcac74469f8a6 Mon Sep 17 00:00:00 2001 From: Peter Valdez Date: Sun, 23 Feb 2025 23:01:20 -0500 Subject: [PATCH 2/2] Update README, use repo-local key id file --- .gitignore | 3 ++ Makefile | 1 + README.md | 85 ++++++++++++++++++++++++++++++++++---------- utils/gpg_manager.sh | 11 +++--- 4 files changed, 76 insertions(+), 24 deletions(-) diff --git a/.gitignore b/.gitignore index c8e5722..9862a22 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,6 @@ volumes # GPG secrets directory - only track .gpg files gpg-secrets/* !gpg-secrets/*.gpg + +# GPG key IDs file +.localmart-github-key-ids diff --git a/Makefile b/Makefile index d5d69f6..15bcd98 100644 --- a/Makefile +++ b/Makefile @@ -10,6 +10,7 @@ clean: clean-data: rm -rf ./volumes + rm -rf frontend/.next deploy-frontend: cd frontend && fly deploy diff --git a/README.md b/README.md index 432ca91..3fb0175 100644 --- a/README.md +++ b/README.md @@ -2,32 +2,81 @@ localmart is a platform for local businesses to sell their products and services to local customers. -## Prerequisites -Docker, Docker Compose +## Development -## Usage +### Prerequisites + +For development: +- Docker +- Docker Compose + +### Usage To start the next.js frontend, the Python backend, and the database, run: ```bash -docker compose --build up +make ``` -To clear the database +To clear the database and the frontend next.js cache: ```bash -rm -rf ./volumes +make clean-data ``` -## Debugging Tips -If the app is not loading porperly on your machine, try clear the cache and restart. +### Debugging Tips +1. If the app is not loading porperly on your machine, try: + - clearing the data with `make clean-data` + - restarting with `make` -To rebuild the backend: -```bash -make -``` -To rebuild the frontend, if NextJS is giving errors: -```bash -cd frontend/ -rm -rf .next -make -``` +## Secret Management + +We use GPG encryption to securely store sensitive files. The encrypted files are stored in the `gpg-secrets/` directory and can only be decrypted by authorized team members. + +### Prerequisites + +1. Install GPG on your system: + ```bash + # macOS + brew install gnupg + + # Ubuntu/Debian + sudo apt-get install gnupg + ``` + +2. Have your own GPG key pair and upload the public key to your GitHub profile + ([GitHub guide on adding GPG keys](https://docs.github.com/en/authentication/managing-commit-signature-verification/adding-a-gpg-key-to-your-github-account)) + +### Managing Secrets + +The following commands are available: + +1. Import team members' GPG keys: + ```bash + # Import team members' GPG keys + make import-gpg-keys + ``` + +2. Encrypt a file: + ```bash + # The file will be encrypted and stored in gpg-secrets/ + make encrypt-file FILE=path/to/your/file + + # Example: Encrypt staging environment variables + make encrypt-file FILE=gpg-secrets/staging.env + ``` + +3. Decrypt a file: + ```bash + # Only works with files in the gpg-secrets/ directory + make decrypt-file FILE=gpg-secrets/your-file.gpg + + # Example: Decrypt staging environment variables + make decrypt-file FILE=gpg-secrets/staging.env.gpg + ``` + +### Notes + +- Only files in the `gpg-secrets/` directory with the `.gpg` extension are tracked in Git +- Decrypted files are automatically placed in the `gpg-secrets/` directory +- You must have your private key to decrypt files +- The list of team members is maintained in the Makefile's `GITHUB_USERS` variable \ No newline at end of file diff --git a/utils/gpg_manager.sh b/utils/gpg_manager.sh index 1b143a7..1cd2965 100755 --- a/utils/gpg_manager.sh +++ b/utils/gpg_manager.sh @@ -49,10 +49,9 @@ import_keys() { fi done - # Store the key IDs in a more permanent location for later use - mkdir -p ~/.localmart - mv "$key_ids_file" ~/.localmart/github_key_ids - echo "Imported keys have been stored in ~/.localmart/github_key_ids" + # Store the key IDs in the repository + mv "$key_ids_file" .localmart-github-key-ids + echo "Imported keys have been stored in .localmart-github-key-ids" } # Function to encrypt a file @@ -64,7 +63,7 @@ encrypt_file() { fi # Check if we have stored key IDs - if [ ! -f ~/.localmart/github_key_ids ]; then + if [ ! -f .localmart-github-key-ids ]; then echo "Error: No GitHub keys found. Please run import-keys first." exit 1 fi @@ -75,7 +74,7 @@ encrypt_file() { if [ -n "$key_id" ]; then recipient_args="$recipient_args -r $key_id" fi - done < ~/.localmart/github_key_ids + done < .localmart-github-key-ids if [ -z "$recipient_args" ]; then echo "Error: No valid recipient keys found"