Using Pass for Secrets

Set up the Unix password manager (pass) as Gordon's secrets backend for secure credential storage.

What You'll Learn

  • Installing and initializing pass
  • Storing Gordon secrets securely
  • Configuring Gordon to use pass
  • Team workflows with shared GPG keys
  • Troubleshooting common issues

Prerequisites

  • Linux or macOS system
  • GPG installed
  • Basic understanding of GPG keys

Why Pass?

Pass is a simple, Unix-philosophy password manager that:

  • Stores secrets as GPG-encrypted files
  • Integrates with Git for version control
  • Works with standard Unix tools
  • Supports multiple GPG keys for team access

Installation

Ubuntu/Debian

sudo apt install pass gnupg

macOS

brew install pass gnupg

Arch Linux

sudo pacman -S pass gnupg

Initial Setup

1. Generate a GPG Key (if needed)

# Generate a new GPG key
gpg --gen-key

# Follow prompts:
# - Real name: Gordon Server
# - Email: [email protected]
# - Passphrase: (use a strong passphrase or leave empty for automation)

List your keys to find the key ID:

gpg --list-keys

Output:

pub   ed25519 2024-01-15 [SC]
      ABC123DEF456...  <-- This is your key ID
uid           [ultimate] Gordon Server <[email protected]>

2. Initialize Pass Store

pass init ABC123DEF456...  # Use your GPG key ID

This creates ~/.password-store/.

3. Create Gordon Secrets

# Registry password hash (bcrypt)
# Generate with: htpasswd -nbB admin yourpassword | cut -d: -f2
pass insert gordon/auth/password_hash

# Token secret (random 32+ chars for JWT signing)
pass generate gordon/auth/token_secret 32

Gordon Configuration

Enable Pass Backend

[auth]
enabled = true
secrets_backend = "pass"
token_secret = "gordon/auth/token_secret"
# Optional: enable password auth for interactive login
# username = "admin"
# password_hash = "gordon/auth/password_hash"

Using Route Secrets

With the pass backend, per-domain secrets are stored in pass, not .env files.

# Store secrets for a domain
gordon secrets set app.mydomain.com DATABASE_URL "postgresql://user:pass@postgres:5432/app"
gordon secrets set app.mydomain.com API_KEY "your-api-key"

Gordon migrates existing .env files on startup and renames them to .env.migrated.

Organizing Secrets

Recommended directory structure in pass:

~/.password-store/
├── gordon/
│   └── auth/
│       ├── password_hash.gpg
│       └── token_secret.gpg
├── myapp/
│   ├── db/
│   │   └── password.gpg
│   ├── api/
│   │   └── key.gpg
│   └── jwt/
│       └── secret.gpg
└── production/
    └── stripe/
        └── secret_key.gpg

Team Workflows

Multiple GPG Keys

Allow multiple team members to decrypt secrets:

# Initialize with multiple keys
pass init KEY_ID_1 KEY_ID_2 KEY_ID_3

# Or add keys later
pass init --path=gordon KEY_ID_1 KEY_ID_2

Git Integration

Version control your encrypted secrets:

# Initialize git in pass store
pass git init

# Add remote
pass git remote add origin [email protected]:yourorg/secrets.git

# Commit and push
pass git push -u origin main

Pull Secrets on New Server

# Clone the pass store
git clone [email protected]:yourorg/secrets.git ~/.password-store

# Import team GPG keys
gpg --import team-member.pub

Automation (Passphrase-less Keys)

For servers running Gordon automatically, use a GPG key without a passphrase:

# Generate key without passphrase
gpg --batch --gen-key <<EOF
Key-Type: EdDSA
Key-Curve: ed25519
Name-Real: Gordon Automation
Name-Email: [email protected]
Expire-Date: 0
%no-protection
%commit
EOF

Security Note: Protect access to the server itself when using passphrase-less keys.

Running in Containers

See Running Gordon in a Container for container-specific setup.

Quick summary:

docker run -d \
  --name gordon \
  -v $HOME/.gnupg:/home/gordon/.gnupg:ro \
  -v $HOME/.password-store:/home/gordon/.password-store:ro \
  gordon-with-pass

Troubleshooting

"gpg: decryption failed: No secret key"

Your GPG private key isn't available.

# List available secret keys
gpg --list-secret-keys

# If empty, import your key
gpg --import your-private-key.asc

"pass: command not found"

Pass isn't installed.

# Ubuntu/Debian
sudo apt install pass

# macOS
brew install pass

"Error: password store is empty"

Initialize pass first:

pass init YOUR_GPG_KEY_ID

"gpg: public key decryption failed: Inappropriate ioctl for device"

GPG needs a TTY for passphrase entry:

export GPG_TTY=$(tty)

Add to ~/.bashrc for persistence.

"gpg-agent: no running gpg-agent"

Start the GPG agent:

gpg-agent --daemon

Permissions Issues

Ensure correct permissions:

chmod 700 ~/.gnupg
chmod 600 ~/.gnupg/*
chmod 700 ~/.password-store

Security Best Practices

  1. Protect GPG keys: Back up securely, use strong passphrases for interactive use
  2. Limit access: Use separate GPG keys per environment (dev, staging, prod)
  3. Rotate secrets: Periodically update passwords and tokens
  4. Audit access: Review who has GPG keys for each environment
  5. Use Git: Track changes to secrets with pass git integration