# secenv
**Secure Environment Variable Management with PGP Encryption**
`secenv` is a command-line tool for managing environment variables with PGP encryption support. It allows you to store sensitive configuration values encrypted in YAML files and decrypt them on-demand using your system's GPG keys.
## Features
- 🔐 **PGP Encryption**: Encrypt sensitive values using OpenPGP/GPG
- 📁 **Profile-Based Configuration**: Organize variables into different profiles (dev, staging, prod, etc.)
- 🔑 **System GPG Integration**: Uses your existing GPG keys and agent
- 🛡️ **Password Protection**: Supports password-protected PGP keys with secure prompting
- 📄 **Multiple Value Types**: Literal values, environment variables, files, and encrypted values
- ⚡ **Fast & Lightweight**: Written in Rust for performance and safety
## Installation
### From Source
```bash
git clone https://github.com/cchexcode/secenv
cd secenv
cargo build --release
```
The binary will be available at `target/release/secenv`.
## Quick Start
### 1. Create a Configuration File
Create a `secenv.yaml` file with your environment variables:
```yaml
# Define reusable anchors
.defaultkey: &defaultkey "PGP-KEY-FINGERPRINT"
profiles:
dev:
propagate: true
vars:
# Literal value
APP_NAME: !literal "myapp"
# Environment variable reference
HOME_DIR: !environment "HOME"
# File content
CONFIG_JSON_CONTENT: !file "/etc/myapp/config.json"
# PGP encrypted value
DATABASE_PASSWORD: !pgp
key: *defaultkey
value: |
-----BEGIN PGP MESSAGE-----
...
-----END PGP MESSAGE-----
production:
propagate: false
vars:
APP_NAME: !literal "myapp"
DATABASE_PASSWORD: !pgp
key: "PROD_KEY_FINGERPRINT"
value: |
-----BEGIN PGP MESSAGE-----
...
-----END PGP MESSAGE-----
```
### 2. Encrypt Values
To create encrypted values, use GPG to encrypt your secrets:
```bash
# Encrypt a value for a specific key
# Or encrypt for a specific key fingerprint
Copy the resulting PGP message (including the `-----BEGIN PGP MESSAGE-----` and `-----END PGP MESSAGE-----` lines) into your YAML file.
### 3. Unlock Variables
```bash
# Unlock variables from the default profile
secenv unlock
# Unlock variables from a specific profile
secenv unlock --profile production
# Use a different config file
secenv unlock --config /path/to/config.yaml --profile staging
```
Output format:
```
APP_NAME=MyApp
DATABASE_PASSWORD=my-secret-password
HOME_DIR=/Users/username
```
## Configuration Format
### Profile Structure
```yaml
profiles:
<profile-name>:
propagate: <boolean> # Whether to propagate to child processes
vars:
<variable-name>: <value-provider>
```
### Value Providers
#### Literal Values
```yaml
APP_NAME: !literal "MyApplication"
```
#### Environment Variables
```yaml
HOME_DIR: !environment "HOME"
USER_NAME: !environment "USER"
```
#### File Contents
```yaml
CONFIG_DATA: !file "/etc/myapp/config.json"
CERTIFICATE: !file "/etc/ssl/certs/app.crt"
```
#### PGP Encrypted Values
```yaml
SECRET_KEY: !pgp
key: "9CD9D9187E17BE27E2F838B7BA59BCD337CEEA1D" # PGP key fingerprint
value: |
-----BEGIN PGP MESSAGE-----
hQIMA/nYRg4ylYK0AQ/+K25a1jwIORvRnutLSR4vBuWA1mC8GHDrWRW3k7btX8DD
...
-----END PGP MESSAGE-----
```
### YAML Anchors and References
Use YAML anchors to avoid repeating key fingerprints:
```yaml
.defaultkey: &defaultkey "9CD9D9187E17BE27E2F838B7BA59BCD337CEEA1D"
.prodkey: &prodkey "ABCD1234567890ABCD1234567890ABCD12345678"
profiles:
default:
vars:
SECRET1: !pgp
key: *defaultkey
value: "..."
SECRET2: !pgp
key: *defaultkey
value: "..."
production:
vars:
SECRET1: !pgp
key: *prodkey
value: "..."
```
## Usage
### Commands
#### `unlock`
Decrypt and display environment variables from a profile.
```bash
secenv unlock [OPTIONS]
Options:
-c, --config <config> Configuration file path [default: secenv.yaml]
-p, --profile <profile> Profile to unlock [default: default]
-h, --help Print help
```
#### `man`
Generate manual pages.
```bash
#### `autocomplete`
Generate shell completion scripts.
```bash
### Integration Examples
#### Shell Integration
```bash
# Load variables into current shell
eval "$(secenv unlock --profile production)"
# Export to a file
secenv unlock --profile staging > .env
```
#### Docker Integration
```dockerfile
# In your Dockerfile
COPY secenv.yaml /app/
RUN secenv unlock --profile production > /app/.env
```
#### CI/CD Integration
```yaml
# GitHub Actions example
- name: Load secrets
run: |
secenv unlock --profile ci > $GITHUB_ENV
```
## Security Considerations
### PGP Key Management
- **Key Storage**: Store your PGP private keys securely using your system's keychain or GPG agent
- **Key Rotation**: Regularly rotate encryption keys and re-encrypt values
- **Access Control**: Use different PGP keys for different environments (dev, staging, prod)
### Best Practices
1. **Never commit unencrypted secrets** to version control
2. **Use different keys per environment** to limit blast radius
3. **Regularly audit** who has access to decryption keys
4. **Use strong passphrases** for PGP keys
5. **Enable GPG agent** for convenient key management
6. **Backup your keys** securely
### Password-Protected Keys
When using password-protected PGP keys, `secenv` will:
1. Attempt decryption without a password first
2. If that fails, prompt securely for the password
3. Allow up to 3 retry attempts
4. Integrate with GPG agent for cached passwords
## GPG Setup
### Generate a New Key
```bash
# Generate a new GPG key
gpg --full-generate-key
# List your keys
gpg --list-secret-keys --keyid-format LONG
# Export public key (for sharing)
gpg --armor --export your@email.com
```
### Import Existing Keys
```bash
# Import a private key
gpg --import private-key.asc
# Import a public key
gpg --import public-key.asc
# Trust a key
gpg --edit-key your@email.com
# In GPG prompt: trust -> 5 -> y -> quit
```
## Troubleshooting
### Common Issues
#### "GPG decryption failed"
- Ensure the correct PGP key is installed: `gpg --list-secret-keys`
- Verify the key fingerprint matches the one in your config
- Check if the key requires a password and GPG agent is running
#### "Profile not found"
- Verify the profile name exists in your config file
- Check YAML syntax with `yamllint secenv.yaml`
#### "Failed to read config"
- Ensure the config file exists and is readable
- Verify YAML syntax is correct
- Check file permissions
### Debug Mode
For verbose output, set the `RUST_LOG` environment variable:
```bash
RUST_LOG=debug secenv unlock
```
## Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
### Development Setup
```bash
git clone https://github.com/cchexcode/secenv
cd secenv
cargo build
cargo test
```
### Running Tests
```bash
# Run all tests
cargo test
# Run with output
cargo test -- --nocapture
# Run specific test
cargo test test_name
```
## License
This project is licensed under the MIT License - see the [LICENSE](../LICENSE) file for details.
## Changelog
### v0.0.0 (Current)
- Initial implementation
- PGP encryption support
- Profile-based configuration
- Multiple value providers (literal, environment, file, pgp)
- GPG integration with password support
- Shell completion and manual generation
## Related Projects
- [sops](https://github.com/mozilla/sops) - Secrets OPerationS
- [age](https://github.com/FiloSottile/age) - Simple, modern encryption tool
- [pass](https://www.passwordstore.org/) - The standard unix password manager