Securely load and manage encrypted environment variables
Project description
Encrypted Environment Loader
A secure Python package for managing encrypted environment variables. Load sensitive configuration from encrypted .env files using Fernet encryption, with convenient keyring integration for local development.
Features
- Secure encryption using Fernet (AES 128 in CBC mode with HMAC-SHA256)
- Keyring integration for secure local key storage (macOS Keychain, Windows Credential Manager, Linux Secret Service)
- Profile support for multiple environments (dev, prod, test, etc.)
- CLI tools for file management and subprocess execution
- Python API with context managers and decorators
- Shell integration for fish, bash, and zsh
- Safe editing with backup and validation
- CI/CD friendly with quiet modes and secret masking
- Modern Python with type hints and proper packaging
Installation
pip install encrypted-env-loader
Quick Start
1. Initialize a new encrypted environment file
# Generate new encrypted file with random key
encrypted-env init
# Output: Encryption key: gAAAAABh...
# Output: Set your encryption key: export ENCRYPTED_ENV_KEY='gAAAAABh...'
# Set the key in your shell
export ENCRYPTED_ENV_KEY='gAAAAABh...'
2. Edit your environment variables
# Opens decrypted content in $EDITOR, re-encrypts on save
encrypted-env edit
3. Run commands with encrypted environment
# Run any command with encrypted env loaded
encrypted-env run -- python app.py
encrypted-env run -- ./deploy.sh
Or use shell integration
# Load into current shell (fish)
eval (encrypted-env load)
# Load into current shell (bash/zsh)
eval $(encrypted-env load)
Keyring Integration (New in v0.2.0)
For local development, store encryption keys securely in your system keyring instead of environment variables:
1. Store key in keyring
# Generate and store key for default profile
encrypted-env keyring set-key
# Store key for specific environment
encrypted-env keyring set-key --profile dev
encrypted-env keyring set-key --profile prod
2. Use encrypted environment with keyring
# Automatically uses keyring-stored key
encrypted-env run --profile dev -- python app.py
encrypted-env load --profile dev
encrypted-env edit --profile dev
3. Production/CI usage
# Force environment variable usage (skip keyring)
encrypted-env run --profile prod --no-keyring -- ./deploy.sh
export ENCRYPTED_ENV_KEY="your-key-here"
encrypted-env load --profile prod --no-keyring
4. Manage keyring keys
# List stored keys
encrypted-env keyring list-keys
# Retrieve a key
encrypted-env keyring get-key --profile dev
# Delete a key
encrypted-env keyring delete-key --profile dev
Key Benefits:
- 🔐 Secure: Keys stored in OS-native keyring (never in plaintext)
- 🚀 Convenient: No need to manage environment variables locally
- 🔄 Flexible: Fallback to environment variables for production/CI
- 📋 Profile-aware: Separate keys per environment automatically
Security Features
CI/CD Safe Operations
All commands support --quiet mode for CI environments and have secure defaults:
# Generate keys silently (CI-safe)
KEY=$(encrypted-env generate-key --quiet)
# Show variable names only (never exposes values)
encrypted-env show --names-only
# Validate files without output (exit codes only)
encrypted-env validate --quiet
Secret Masking
By default, commands mask sensitive values:
# Safe - shows masked values
encrypted-env show
# Output: DATABASE_URL=***
# API_KEY=***
# Requires explicit flag to show values
encrypted-env show --show-values # WARNING: exposes secrets
Examples and Testing
Run the comprehensive demo to see all features:
# Interactive demo with full output
./examples/demo.sh
# CI-friendly mode (no secrets exposed)
./examples/demo.sh --ci
# View usage examples
./examples/basic_usage.sh
CLI Reference
Core Operations
run - Execute commands with encrypted environment
encrypted-env run [--file FILE] [--profile PROFILE] [--no-keyring] -- <command>
# Examples
encrypted-env run -- python app.py
encrypted-env run --profile prod -- ./deploy.sh
encrypted-env run --file .env.custom.encrypted -- npm start
encrypted-env run --profile dev --no-keyring -- python app.py # force env var usage
load - Generate shell commands to load environment
encrypted-env load [--file FILE] [--profile PROFILE] [--shell SHELL] [--quiet] [--no-keyring]
# Usage in fish shell
eval (encrypted-env load)
eval (encrypted-env load --profile dev)
eval (encrypted-env load --profile prod --no-keyring) # force env var usage
# Usage in bash/zsh
eval $(encrypted-env load)
eval $(encrypted-env load --profile prod)
clear - Generate shell commands to clear environment
encrypted-env clear [--file FILE] [--profile PROFILE] [--shell SHELL] [--quiet] [--no-keyring]
# Usage
eval (encrypted-env clear) # fish
eval $(encrypted-env clear) # bash/zsh
File Management
init - Create new encrypted environment file
encrypted-env init [--file FILE] [--profile PROFILE] [--key-file KEYFILE] [--quiet]
# Examples
encrypted-env init # creates .env.encrypted
encrypted-env init --profile dev # creates .env.dev.encrypted
encrypted-env init --key-file .env.key # saves key to file
encrypted-env init --quiet # CI-friendly (key only)
encrypt - Encrypt existing .env file
encrypted-env encrypt <source> [--output OUTPUT] [--profile PROFILE] [--quiet]
# Examples
encrypted-env encrypt .env # creates .env.encrypted
encrypted-env encrypt .env.dev --profile dev # creates .env.dev.encrypted
encrypted-env encrypt .env --output custom.encrypted
decrypt - Decrypt to filesystem
encrypted-env decrypt [--file FILE] [--output OUTPUT] [--profile PROFILE] [--quiet]
# Examples
encrypted-env decrypt # decrypts to .env
encrypted-env decrypt --profile dev # decrypts .env.dev.encrypted to .env
encrypted-env decrypt --output .env.backup # custom output file
edit - Safely edit encrypted files
encrypted-env edit [--file FILE] [--profile PROFILE] [--quiet]
# Opens in $EDITOR (vi by default)
# Creates backup before editing
# Validates .env format before re-encrypting
encrypted-env edit --profile prod
Key Management
generate-key - Generate new encryption key
encrypted-env generate-key [--quiet]
# Interactive mode
encrypted-env generate-key
# Output: Generated key: gAAAAABh...
# CI mode
encrypted-env generate-key --quiet
# Output: gAAAAABh...
rekey - Change encryption key
encrypted-env rekey [--file FILE] [--profile PROFILE] [--old-key-env VAR] [--new-key-env VAR] [--quiet]
# Examples
ENCRYPTED_ENV_KEY="old_key" NEW_KEY="new_key" encrypted-env rekey --new-key-env NEW_KEY
encrypted-env rekey --quiet # generates new random key silently
Keyring Management (New in v0.2.0)
keyring set-key - Store encryption key in system keyring
encrypted-env keyring set-key [--profile PROFILE] [--key KEY] [--quiet]
# Examples
encrypted-env keyring set-key # generates key for default profile
encrypted-env keyring set-key --profile dev # generates key for dev profile
encrypted-env keyring set-key --key "ABC123" # store specific key
encrypted-env keyring set-key --quiet # CI-friendly output
keyring get-key - Retrieve encryption key from keyring
encrypted-env keyring get-key [--profile PROFILE] [--quiet]
# Examples
encrypted-env keyring get-key # get default profile key
encrypted-env keyring get-key --profile dev # get dev profile key
encrypted-env keyring get-key --quiet # key only, no messages
keyring delete-key - Remove encryption key from keyring
encrypted-env keyring delete-key [--profile PROFILE] [--quiet]
# Examples
encrypted-env keyring delete-key # delete default profile key
encrypted-env keyring delete-key --profile dev # delete dev profile key
encrypted-env keyring delete-key --quiet # no confirmation prompt
keyring list-keys - List all profiles with stored keys
encrypted-env keyring list-keys [--quiet]
# Example output:
# Found keys for 3 profiles:
# - default
# - dev
# - prod
# Quiet mode (CI-friendly)
encrypted-env keyring list-keys --quiet
# Output: default
# dev
# prod
Keyring Key Storage:
- Service name: Git repository name (e.g.,
encrypted-env-loader) - Username format:
{profile}-key(e.g.,dev-key,default-key) - Automatic detection: Git repo name used for consistent keyring service naming
Utilities
status - Show file information and variables
encrypted-env status [--file FILE] [--profile PROFILE] [--no-keyring]
# Example output with keyring:
# File: .env.encrypted
# Exists: True
# Size: 1024 bytes
# Key source: keyring (profile: default)
# Status: Valid (contains 5 variables)
# Variables:
# - DATABASE_URL
# - SECRET_KEY
# Example output with environment variable:
# File: .env.encrypted
# Exists: True
# Size: 1024 bytes
# Key source: $ENCRYPTED_ENV_KEY
# Status: Valid (contains 5 variables)
# Variables:
# - DATABASE_URL
# - SECRET_KEY
# With --no-keyring flag:
# Key source: $ENCRYPTED_ENV_KEY (not found)
# Status: Cannot validate (no key)
validate - Check if file can be decrypted
encrypted-env validate [--file FILE] [--profile PROFILE] [--quiet] [--no-keyring]
# Exit code 0 if valid, 1 if invalid
show - Display variables (with security options)
encrypted-env show [--file FILE] [--profile PROFILE] [--names-only] [--show-values] [--no-keyring]
# Safe default (masks values)
encrypted-env show
# Output: DATABASE_URL=***
# CI-safe (names only)
encrypted-env show --names-only
# Output: DATABASE_URL
# API_KEY
# Explicit flag required to show values
encrypted-env show --show-values # WARNING: exposes secrets
Python API
Basic Usage
from encrypted_env_loader import load_encrypted_env
# Load with default settings (.env.encrypted, keyring or ENCRYPTED_ENV_KEY)
env_vars = load_encrypted_env()
# Load with specific parameters
env_vars = load_encrypted_env(
key="base64-encoded-key",
file_path=".env.prod.encrypted",
profile="prod",
change_os_env=True, # Updates os.environ
use_keyring=True # Try keyring first (default: True)
)
# Force environment variable usage (skip keyring)
env_vars = load_encrypted_env(
profile="prod",
use_keyring=False
)
Context Manager
from encrypted_env_loader import encrypted_env_context
import os
with encrypted_env_context(profile="dev"):
# Environment variables loaded here (uses keyring automatically)
database_url = os.getenv("DATABASE_URL")
secret_key = os.getenv("SECRET_KEY")
# Environment automatically restored when exiting context
# Force environment variable usage
with encrypted_env_context(profile="prod", use_keyring=False):
# Uses ENCRYPTED_ENV_KEY environment variable
pass
Decorator
from encrypted_env_loader import with_encrypted_env
import os
@with_encrypted_env(profile="prod")
def deploy_application():
# Function runs with encrypted env loaded (keyring first, then env var)
api_key = os.getenv("API_KEY")
database_url = os.getenv("DATABASE_URL")
# Environment restored after function returns
@with_encrypted_env(profile="prod", use_keyring=False)
def ci_deploy():
# Force environment variable usage for CI/production
pass
deploy_application()
Keyring Functions (New in v0.2.0)
from encrypted_env_loader import (
get_key_from_keyring,
set_key_in_keyring,
delete_key_from_keyring,
list_keyring_profiles,
get_git_repo_name
)
# Get git repository name (used for keyring service name)
repo_name = get_git_repo_name() # e.g., "my-project"
# Store key in keyring
success = set_key_in_keyring("my-secret-key", profile="dev")
# Retrieve key from keyring
key = get_key_from_keyring(profile="dev")
# List profiles with stored keys
profiles = list_keyring_profiles() # ["default", "dev", "prod"]
# Delete key from keyring
success = delete_key_from_keyring(profile="dev")
Utility Functions
from encrypted_env_loader import (
generate_key,
encrypt_env_file,
decrypt_env_file,
validate_encrypted_file
)
# Generate encryption key
key = generate_key()
# Encrypt a file (supports keyring)
encrypt_env_file(".env", ".env.encrypted", key)
encrypt_env_file(".env", ".env.encrypted", profile="dev") # uses keyring
encrypt_env_file(".env", ".env.encrypted", profile="dev", use_keyring=False) # force env var
# Decrypt and get variables (supports keyring)
env_vars = decrypt_env_file(".env.encrypted", key=key)
env_vars = decrypt_env_file(".env.encrypted", profile="dev") # uses keyring
env_vars = decrypt_env_file(".env.encrypted", profile="dev", use_keyring=False) # force env var
# Validate file (supports keyring)
is_valid = validate_encrypted_file(".env.encrypted", key=key)
is_valid = validate_encrypted_file(".env.encrypted", profile="dev") # uses keyring
is_valid = validate_encrypted_file(".env.encrypted", profile="dev", use_keyring=False) # force env var
Profiles
Profiles allow managing multiple environment configurations:
# Profile-based file naming
.env.encrypted # default profile
.env.dev.encrypted # dev profile
.env.prod.encrypted # prod profile
.env.test.encrypted # test profile
# Usage
encrypted-env init --profile dev
encrypted-env run --profile prod -- python app.py
encrypted-env edit --profile test
Shell Integration
Fish Shell
# Load environment
eval (encrypted-env load --profile dev)
# Clear environment
eval (encrypted-env clear --profile dev)
# One-liner with auto-clear
encrypted-env run --profile dev -- python app.py
Bash/Zsh
# Load environment
eval $(encrypted-env load --profile dev)
# Clear environment
eval $(encrypted-env clear --profile dev)
# Use in loops (eval once, use many times)
eval $(encrypted-env load)
for i in {1..100}; do
curl -H "Authorization: $SECRET_TOKEN" api.example.com/data/$i
done
eval $(encrypted-env clear)
CI/CD Integration
GitHub Actions Example
- name: Setup encrypted environment
run: |
# Generate or retrieve key securely
echo "${{ secrets.ENCRYPTED_ENV_KEY }}" > .env.key
export ENCRYPTED_ENV_KEY=$(cat .env.key)
# Validate encrypted file
encrypted-env validate --quiet
# Run tests with encrypted environment
encrypted-env run -- pytest
Security Best Practices
# Never expose secrets in CI logs
encrypted-env show --names-only # ✅ Safe
encrypted-env validate --quiet # ✅ Safe
encrypted-env generate-key --quiet # ✅ Safe
# Avoid these in CI
encrypted-env show --show-values # ❌ Exposes secrets
encrypted-env status # ❌ May expose info
Security Considerations
- Key Storage: Never commit encryption keys to version control
- Key Rotation: Regularly rotate encryption keys using
rekeycommand - File Permissions: Ensure encrypted files have appropriate permissions
- Backup Strategy: Keep secure backups of both encrypted files and keys
- Environment Isolation: Use profiles to separate dev/staging/prod secrets
- CI/CD Safety: Use
--quietand--names-onlyflags in automated environments
Error Handling
The package provides specific exception types:
from encrypted_env_loader import EncryptedEnvError, DecryptionError, KeyError
try:
load_encrypted_env()
except KeyError:
print("Encryption key missing or invalid")
except DecryptionError:
print("File cannot be decrypted - wrong key or corrupted data")
except EncryptedEnvError:
print("General error with encrypted environment operations")
Development
Setup
git clone https://github.com/igutekunst/encrypted-env-loader
cd encrypted-env-loader
pip install -e ".[dev]"
Testing
# Run unit tests
pytest
# Run full demo/integration tests
./examples/demo.sh
# Run CI-safe tests
./examples/demo.sh --ci
# Test coverage
pytest --cov=encrypted_env_loader
Code Quality
black src tests
isort src tests
flake8 src tests
mypy src
License
MIT License - see LICENSE file.
Contributing
- Fork the repository
- Create a feature branch
- Add tests for new functionality
- Ensure all tests pass
- Submit a pull request
Changelog
0.2.0
- Keyring integration for secure local key storage
- Automatic git repo detection for keyring service naming
- Key resolution priority: explicit key → keyring → environment variable
- New CLI command group:
keyringwithset-key,get-key,delete-key,list-keys - Enhanced commands: All file operations support
--no-keyringflag - Improved status reporting: Shows key source (keyring vs environment variable)
- Python API extensions: All functions support
use_keyringparameter - Production/CI compatibility: Graceful fallback to environment variables
0.1.0
- Initial release
- Basic encryption/decryption functionality
- CLI with all core commands
- Python API with context managers and decorators
- Profile support
- Shell integration
- CI/CD safety features with quiet modes and secret masking
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file encrypted_env_loader-0.3.0.tar.gz.
File metadata
- Download URL: encrypted_env_loader-0.3.0.tar.gz
- Upload date:
- Size: 22.8 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.11.10
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
a4cee67631f61ab433de3c4d70eb1266e7240fbcae27aa00b00cdebe0c22d9cc
|
|
| MD5 |
a557794a8b1ec470f4c599cb0e9ef166
|
|
| BLAKE2b-256 |
90170413a0a3a254e8ae287fed0fa4d45ddabb8a062bd084f20109e6372242db
|
File details
Details for the file encrypted_env_loader-0.3.0-py3-none-any.whl.
File metadata
- Download URL: encrypted_env_loader-0.3.0-py3-none-any.whl
- Upload date:
- Size: 17.0 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.11.10
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
ee96b025674bca2ed44843944f1bb1c0de289642e5aede6efe31dc08d53491b7
|
|
| MD5 |
8a720b4bc943f5df0c56bc73991b437f
|
|
| BLAKE2b-256 |
e938b9c659092ffddcd3f416c4975d18556ed2039b89079522b9625acc2dfdc3
|