Security Guide¶
This guide covers secure authentication methods for fnb, moving beyond plain-text password storage in .env files.
Overview¶
By default, fnb supports SSH password authentication via .env files, but this approach stores passwords in plain text. This guide presents more secure alternatives and best practices.
Authentication Methods (Recommended Order)¶
1. SSH Key Authentication (Most Secure)¶
SSH key authentication eliminates the need for password storage entirely.
Setup SSH Keys¶
# Generate SSH key pair (creates private/public key files)
ssh-keygen -t ed25519 -f ~/.ssh/fnb_key
# Copy public key to remote server (adds to ~/.ssh/authorized_keys)
ssh-copy-id -i ~/.ssh/fnb_key.pub user@server.com
# Configure SSH to use the key
echo "Host server.com
IdentityFile ~/.ssh/fnb_key
IdentitiesOnly yes" >> ~/.ssh/config
Command Explanations:
ssh-keygen: Creates a new SSH key pair (private key + public key)-t ed25519: Uses modern Ed25519 algorithm (recommended)-f ~/.ssh/fnb_key: Specifies output file path (default:id_ed25519andid_ed25519.pub)
ssh-copy-id: Copies your public key to the remote server-i ~/.ssh/fnb_key.pub: Specifies which public key to copy (default to~/.ssh/id_ed25519.pub)- Automatically adds the key to
~/.ssh/authorized_keyson remote server
Benefits¶
- No password storage - Most secure option
- Automatic authentication - No manual intervention needed
- Key rotation - Easy to revoke and replace keys
- Standard practice - Widely adopted security standard
Configuration¶
When using SSH keys, remove password entries from your .env file:
# Remove these lines from .env
# FNB_PASSWORD_USER_SERVER_COM=mypassword
# FNB_PASSWORD_DEFAULT=defaultpassword
2. macOS Keychain Integration¶
On macOS, fnb can read SSH passwords directly from the Keychain instead of plain-text .env files. This is built in — no extra configuration or code changes needed, just an item in your Keychain.
fnb looks up a generic password item whose service name is fnb-{host}, where {host} is exactly the host value from your fnb.toml task (e.g. user@server.com). The lookup matches on service name only, so the account name can be anything you like.
Setup Keychain Storage¶
# Store a password for host "user@server.com" (the host value from fnb.toml)
security add-generic-password \
-s "fnb-user@server.com" \
-a "user@server.com" \
-w "your-password"
# Verify fnb can find it
security find-generic-password \
-s "fnb-user@server.com" \
-w
Command Explanations:
security add-generic-password: Stores a password in macOS Keychain-s "fnb-user@server.com": Service name — must befnb-followed by the exacthoststring from your config-a "user@server.com": Account name — fnb doesn't check this, so any value works, but reusing the host here keeps the entry easy to identify in Keychain Access.app-w "your-password": Password to store (can also prompt interactively without this option)
security find-generic-password: Retrieves a password from Keychain (this is what fnb runs internally)-s "fnb-user@server.com": Service name to search for-w: Output only the password (without this, shows all metadata)
You can also add the item via Keychain Access.app: create a new password item, set "Keychain Item Name" to fnb-user@server.com, and set any account name and password.
Lookup Order¶
fnb fetch/backup/upload/sync retrieve passwords in this order:
- Host-specific environment variable (
FNB_PASSWORD_{NORMALIZED_HOST}) - macOS Keychain item
fnb-{host}(this section; silently skipped on non-macOS) FNB_PASSWORD_DEFAULTenvironment variable- Interactive SSH password prompt
A Keychain item is only consulted if no host-specific environment variable is set for that host.
3. dotenvx Encryption¶
Use dotenvx to encrypt .env files while maintaining compatibility with existing fnb workflows.
Setup dotenvx¶
# Install dotenvx
npm install -g @dotenvx/dotenvx
# Or using other package managers
brew install dotenvx/brew/dotenvx
curl -fsS https://dotenvx.sh/install.sh | sh
Encrypt Existing .env File¶
# Encrypt your existing .env file
dotenvx encrypt
# This encrypts the .env file in-place and creates .env.keys
# The original .env content is now encrypted
File Structure After Encryption¶
Usage with fnb¶
# Run fnb with encrypted environment
dotenvx run -- fnb fetch backup-server
# Or set the decryption key in environment
export DOTENV_KEY="dotenv://:key_1234...@dotenvx.com/vault/.env.vault?environment=production"
fnb fetch backup-server
# Decrypt for manual inspection (reference only)
dotenvx decrypt
Benefits¶
- Backward compatible - Works with existing fnb implementation
- Encrypted storage - Passwords encrypted at rest
- Environment separation - Different keys for dev/staging/production
- Version control safe - Encrypted
.envcan be safely committed
Git Configuration¶
# Add to .gitignore
echo ".env.keys" >> .gitignore
# Encrypted .env CAN be committed (it's encrypted)
git add .env
4. GPG Encryption¶
fnb can read SSH passwords from GPG-encrypted files instead of plain-text .env files. This is built in and works on any platform with the gpg command-line tool installed — no extra fnb configuration needed, just files in the right place.
fnb looks for encrypted files under ~/.config/fnb/passwords/, checking a host-specific file first and falling back to a shared default:
~/.config/fnb/passwords/
├── user_server_com.gpg # Password for host "user@server.com"
└── default.gpg # Fallback for any host without its own file
The filename is built the same way as the FNB_PASSWORD_* environment variable (replace @, ., - with _), but lowercase and with a .gpg extension: user@server.com → user_server_com.gpg.
Setup GPG Encryption¶
mkdir -p ~/.config/fnb/passwords
# Create a host-specific encrypted password file (symmetric/passphrase-based)
echo -n "your-password" | gpg --symmetric -o ~/.config/fnb/passwords/user_server_com.gpg
# Or a shared default file, used for any host without its own file
echo -n "your-password" | gpg --symmetric -o ~/.config/fnb/passwords/default.gpg
# Set restrictive permissions (fnb warns, but doesn't enforce, if this is skipped)
chmod 600 ~/.config/fnb/passwords/*.gpg
# Verify fnb can decrypt it (uses the same command fnb runs internally)
gpg --decrypt --quiet --batch ~/.config/fnb/passwords/user_server_com.gpg
Command Explanations:
gpg --symmetric: Encrypts data using a passphrase (no public/private key pair needed)- Prompts for the passphrase interactively when run without
-opiping like above -o <file>: Writes the encrypted output to a specific path
- Prompts for the passphrase interactively when run without
gpg --decrypt --quiet --batch: Decrypts a file (this is what fnb runs internally)--batchdisables interactive prompts, so decryption only succeeds if yourgpg-agentalready has the passphrase cached (or, for public-key encrypted files, your secret key is available) — otherwise it fails fast rather than hanging- Public-key encrypted files (
gpg --encrypt -r you@example.com) work too, sincegpg --decryptauto-detects the encryption type
Notes¶
- Both symmetric (passphrase) and public-key encrypted files work, since fnb just runs
gpg --decryptand lets GPG figure out how to decrypt it - If a host-specific file exists but fails to decrypt (wrong passphrase not cached, corrupted file, etc.), fnb still falls back to
default.gpgif present - fnb logs a warning (but doesn't block) if a password file is readable by group or other — run
chmod 600on it - If
gpgisn't installed, or no matching file exists, this source is silently skipped
5. --ask-password (One-off, No Setup Required)¶
For a one-off connection where setting up SSH keys, Keychain, or a GPG file isn't worth it, use --ask-password instead of --ssh-password. fnb prompts for the password interactively with input hidden, so it's never typed on the command line and never ends up in shell history.
Avoid --ssh-password PASSWORD for anything beyond quick local experiments: the shell records the full command line — including the password — to its history file the moment you press Enter, regardless of what fnb does with the value afterward. --ssh-password and --ask-password are mutually exclusive; fnb errors out if both are given.
6. Interactive Password Input¶
fnb automatically falls back to interactive password input when no stored passwords are found.
Current Behavior¶
When no password is found in environment variables or other sources, fnb automatically falls back to interactive password input:
- fnb attempts to retrieve password from configured sources
- If no password is found (
ssh_password = None) - rsync executes without password automation
- SSH prompts user for password in terminal
- User enters password interactively
Benefits¶
- No configuration required - Works out of the box
- Highest security - No stored passwords
- Standard SSH behavior - Familiar user experience
- Fallback mechanism - Always available when other methods fail
Usage Example¶
# No .env file or password configuration
fnb fetch backup-server
# Output:
# Fetching backup-server from user@server:~/data/ to ./backup/
# user@server's password: [user types password]
# Fetch completed successfully: backup-server
When This Mode Activates¶
- No
.envfile exists - Environment variables
FNB_PASSWORD_*are not set - Other password sources (keychain, GPG) are not configured or fail
- SSH key authentication is not set up
- Neither
--ssh-passwordnor--ask-passwordwas given
Current .env File Security¶
If you must continue using .env files, follow these security practices:
File Permissions¶
# Set restrictive permissions
chmod 600 .env
chmod 600 ~/.config/fnb/.env
# Verify permissions
ls -la .env
# Should show: -rw------- (600)
Environment Variable Format¶
# Host-specific passwords (recommended)
FNB_PASSWORD_USER_EXAMPLE_COM=hostspecificpassword
# Default password (less secure)
FNB_PASSWORD_DEFAULT=defaultpassword
Git Security¶
# Ensure .env is in .gitignore
echo ".env" >> .gitignore
echo "*.env" >> .gitignore
# Remove .env from git history if accidentally committed
git rm --cached .env
git commit -m "Remove .env from version control"
Security Best Practices¶
1. Principle of Least Privilege¶
- Use SSH keys with specific access scopes
- Avoid shared or default passwords
- Regularly rotate credentials
2. Environment Isolation¶
- Use separate SSH keys for different environments
- Maintain environment-specific
.envfiles with restricted access - Never commit
.envfiles to version control
3. Monitoring and Auditing¶
- Monitor SSH key usage through server logs
- Regular security audits of stored credentials
- Document all authentication methods used
4. Backup Security¶
- Encrypt backup destinations when possible
- Secure backup storage locations
- Regular backup integrity verification
Migration from Plain-text Passwords¶
Step 1: Audit Current Setup¶
Step 2: Implement SSH Keys¶
# For each host in your configuration
ssh-keygen -t ed25519 -f ~/.ssh/fnb_key_hostname
ssh-copy-id -i ~/.ssh/fnb_key_hostname.pub user@hostname
Step 3: Update SSH Configuration¶
# Add to ~/.ssh/config
Host hostname1
IdentityFile ~/.ssh/fnb_key_hostname1
IdentitiesOnly yes
Host hostname2
IdentityFile ~/.ssh/fnb_key_hostname2
IdentitiesOnly yes
Step 4: Test and Cleanup¶
# Test connections
ssh user@hostname1
ssh user@hostname2
# Remove passwords from .env files
# Keep files for other environment variables if needed
Alternative: Migrating to macOS Keychain¶
If SSH keys aren't an option for some hosts, move their passwords to the Keychain instead of leaving them in .env (macOS only):
# 1. Read the existing password from .env, then store it in the Keychain
# (replace user@server.com with the host value from fnb.toml)
security add-generic-password -s "fnb-user@server.com" -a "user@server.com" -w "the-password-from-env"
# 2. Verify fnb can retrieve it
security find-generic-password -s "fnb-user@server.com" -w
# 3. Remove the corresponding line from .env, then confirm fnb still works
# (FNB_PASSWORD_USER_SERVER_COM=... in this example)
fnb fetch some-label --dry-run
Repeat for each host, then delete .env (or the now-empty FNB_PASSWORD_* lines) once every host is confirmed working.
Alternative: Migrating to GPG-Encrypted Files¶
Cross-platform alternative to the Keychain — works anywhere gpg is installed:
# 1. Read the existing password from .env, then encrypt it into a file
# (replace user@server.com with the host value from fnb.toml)
mkdir -p ~/.config/fnb/passwords
echo -n "the-password-from-env" | gpg --symmetric -o ~/.config/fnb/passwords/user_server_com.gpg
chmod 600 ~/.config/fnb/passwords/user_server_com.gpg
# 2. Verify fnb can decrypt it
gpg --decrypt --quiet --batch ~/.config/fnb/passwords/user_server_com.gpg
# 3. Remove the corresponding line from .env, then confirm fnb still works
# (FNB_PASSWORD_USER_SERVER_COM=... in this example)
fnb fetch some-label --dry-run
Repeat for each host, then delete .env (or the now-empty FNB_PASSWORD_* lines) once every host is confirmed working.
Troubleshooting¶
SSH Key Issues¶
# Check SSH agent
ssh-add -l
# Add key to agent if needed
ssh-add ~/.ssh/fnb_key
# Test connection
ssh -v user@server.com
Permission Issues¶
# Fix SSH directory permissions
chmod 700 ~/.ssh
chmod 600 ~/.ssh/config
chmod 600 ~/.ssh/fnb_key*
chmod 644 ~/.ssh/fnb_key*.pub
Keychain Issues (macOS)¶
# List all fnb-related Keychain entries
security dump-keychain | grep "fnb-"
# Update the stored password for host "user@server.com"
security delete-generic-password -s "fnb-user@server.com"
security add-generic-password -s "fnb-user@server.com" -a "user@server.com" -w "newpassword"
# Confirm fnb can retrieve it
security find-generic-password -s "fnb-user@server.com" -w
GPG Issues¶
# List password files fnb will check
ls -la ~/.config/fnb/passwords/
# Fix overly permissive file permissions (fnb only warns, doesn't block)
chmod 600 ~/.config/fnb/passwords/*.gpg
# Test decryption directly with the exact command fnb runs
gpg --decrypt --quiet --batch ~/.config/fnb/passwords/user_server_com.gpg
# If this hangs or fails with an agent/passphrase error, your gpg-agent
# doesn't have the passphrase cached (or the secret key isn't available
# for public-key encrypted files) — `--batch` intentionally fails fast
# instead of prompting, so fnb never hangs waiting for input
# Replace a password file
echo -n "newpassword" | gpg --symmetric -o ~/.config/fnb/passwords/user_server_com.gpg
chmod 600 ~/.config/fnb/passwords/user_server_com.gpg
See Also¶
- Configuration Guide - Basic fnb configuration
- Examples - Configuration examples with security considerations
- Contributor Guide - Security considerations for development