Skip to content

Commit

Permalink
Merge pull request #34 from astoria-tech/azlyth/gpg-secrets
Browse files Browse the repository at this point in the history
Add GPG encryption for secrets & the first encrypted file: gpg-secrets/staging.env.gpg
  • Loading branch information
azlyth authored Feb 24, 2025
2 parents f29a27a + 6a774df commit 2b5e7cb
Show file tree
Hide file tree
Showing 6 changed files with 230 additions and 19 deletions.
3 changes: 2 additions & 1 deletion .cursorignore
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
.envrc
.envrc
gpg-secrets/*
7 changes: 7 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,8 @@
volumes

# GPG secrets directory - only track .gpg files
gpg-secrets/*
!gpg-secrets/*.gpg

# GPG key IDs file
.localmart-github-key-ids
15 changes: 15 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ clean:

clean-data:
rm -rf ./volumes
rm -rf frontend/.next

deploy-frontend:
cd frontend && fly deploy
Expand Down Expand Up @@ -42,3 +43,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)"
85 changes: 67 additions & 18 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Binary file added gpg-secrets/staging.env.gpg
Binary file not shown.
139 changes: 139 additions & 0 deletions utils/gpg_manager.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
#!/bin/bash

# Function to display usage information
show_usage() {
echo "Usage:"
echo " $0 import-keys # Import GPG keys from GITHUB_USERS env var"
echo " $0 encrypt FILE # Encrypt a file using imported GPG keys"
echo " $0 decrypt FILE # Decrypt a GPG encrypted file"
exit 1
}

# Function to get key ID from GitHub username
get_key_id() {
local username=$1
local key_data
key_data=$(curl -s "https://github.com/$username.gpg")
if [ -n "$key_data" ]; then
echo "$key_data" | gpg --batch --import 2>/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 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
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

0 comments on commit 2b5e7cb

Please sign in to comment.