Add comprehensive secrets management guide
Covers 6 options for storing passwords securely: 1. .env file (current, good for personal use) 2. Environment variables (better for production) 3. Separate secrets file 4. Docker secrets 5. HashiCorp Vault (enterprise) 6. Git secrets (CI/CD only) Recommendation: Current .env setup is fine for personal/research use Improvement: chmod 600 .env (done) Includes security checklist, rotation procedures, and testing
This commit is contained in:
parent
5613d7f894
commit
0c183fb28c
414
docs/13_secrets_management.md
Normal file
414
docs/13_secrets_management.md
Normal file
@ -0,0 +1,414 @@
|
||||
# Secrets Management Guide
|
||||
|
||||
## Overview
|
||||
|
||||
POTE needs sensitive information like database passwords and SMTP credentials. This guide covers secure storage options.
|
||||
|
||||
---
|
||||
|
||||
## Option 1: `.env` File (Current Default)
|
||||
|
||||
**Good for:** Personal use, single server, local development
|
||||
|
||||
### Setup
|
||||
|
||||
```bash
|
||||
# Create .env file
|
||||
cp .env.example .env
|
||||
nano .env # Add secrets
|
||||
|
||||
# Secure permissions
|
||||
chmod 600 .env
|
||||
chown poteapp:poteapp .env
|
||||
```
|
||||
|
||||
### ✅ Pros
|
||||
- Simple, works immediately
|
||||
- No additional setup
|
||||
- Standard practice for Python projects
|
||||
|
||||
### ⚠️ Cons
|
||||
- Secrets stored in plain text on disk
|
||||
- Risk if server is compromised
|
||||
- No audit trail
|
||||
|
||||
### 🔒 Security Checklist
|
||||
- [ ] `.env` in `.gitignore` (already done ✅)
|
||||
- [ ] File permissions: `chmod 600 .env`
|
||||
- [ ] Never commit to git
|
||||
- [ ] Backup securely (encrypted)
|
||||
- [ ] Rotate passwords regularly
|
||||
|
||||
---
|
||||
|
||||
## Option 2: Environment Variables (Better)
|
||||
|
||||
**Good for:** Systemd services, Docker, production
|
||||
|
||||
### Setup for Systemd Service
|
||||
|
||||
Create `/etc/systemd/system/pote.service`:
|
||||
|
||||
```ini
|
||||
[Unit]
|
||||
Description=POTE Daily Update
|
||||
After=network.target postgresql.service
|
||||
|
||||
[Service]
|
||||
Type=oneshot
|
||||
User=poteapp
|
||||
WorkingDirectory=/home/poteapp/pote
|
||||
Environment="DATABASE_URL=postgresql://poteuser:PASSWORD@localhost:5432/potedb"
|
||||
Environment="SMTP_HOST=mail.levkin.ca"
|
||||
Environment="SMTP_PORT=587"
|
||||
Environment="SMTP_USER=test@levkin.ca"
|
||||
Environment="SMTP_PASSWORD=YOUR_PASSWORD"
|
||||
Environment="FROM_EMAIL=test@levkin.ca"
|
||||
ExecStart=/home/poteapp/pote/venv/bin/python scripts/automated_daily_run.sh
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
```
|
||||
|
||||
**Secure the service file:**
|
||||
```bash
|
||||
sudo chmod 600 /etc/systemd/system/pote.service
|
||||
sudo systemctl daemon-reload
|
||||
```
|
||||
|
||||
### ✅ Pros
|
||||
- Secrets not in git or project directory
|
||||
- Standard Linux practice
|
||||
- Works with systemd timers
|
||||
|
||||
### ⚠️ Cons
|
||||
- Still visible in `systemctl show`
|
||||
- Requires root to edit
|
||||
|
||||
---
|
||||
|
||||
## Option 3: Separate Secrets File (Compromise)
|
||||
|
||||
**Good for:** Multiple environments, easier rotation
|
||||
|
||||
### Setup
|
||||
|
||||
Create `/etc/pote/secrets` (outside project):
|
||||
|
||||
```bash
|
||||
sudo mkdir -p /etc/pote
|
||||
sudo nano /etc/pote/secrets
|
||||
```
|
||||
|
||||
Content:
|
||||
```bash
|
||||
export SMTP_PASSWORD="your_password_here"
|
||||
export DATABASE_PASSWORD="your_db_password_here"
|
||||
```
|
||||
|
||||
Secure it:
|
||||
```bash
|
||||
sudo chmod 600 /etc/pote/secrets
|
||||
sudo chown poteapp:poteapp /etc/pote/secrets
|
||||
```
|
||||
|
||||
Update scripts to source it:
|
||||
|
||||
```bash
|
||||
#!/bin/bash
|
||||
# Load secrets
|
||||
if [ -f /etc/pote/secrets ]; then
|
||||
source /etc/pote/secrets
|
||||
fi
|
||||
|
||||
# Load .env (without passwords)
|
||||
source .env
|
||||
|
||||
# Run POTE
|
||||
python scripts/send_daily_report.py
|
||||
```
|
||||
|
||||
### ✅ Pros
|
||||
- Secrets separate from code
|
||||
- Easy to rotate
|
||||
- Can be backed up separately
|
||||
|
||||
### ⚠️ Cons
|
||||
- Extra file to manage
|
||||
- Still plain text
|
||||
|
||||
---
|
||||
|
||||
## Option 4: Docker Secrets (For Docker Deployments)
|
||||
|
||||
**Good for:** Docker Compose, Docker Swarm
|
||||
|
||||
### Setup
|
||||
|
||||
Create secret files:
|
||||
|
||||
```bash
|
||||
echo "your_smtp_password" | docker secret create smtp_password -
|
||||
echo "your_db_password" | docker secret create db_password -
|
||||
```
|
||||
|
||||
Update `docker-compose.yml`:
|
||||
|
||||
```yaml
|
||||
version: '3.8'
|
||||
|
||||
services:
|
||||
pote:
|
||||
image: pote:latest
|
||||
secrets:
|
||||
- smtp_password
|
||||
- db_password
|
||||
environment:
|
||||
SMTP_HOST: mail.levkin.ca
|
||||
SMTP_USER: test@levkin.ca
|
||||
SMTP_PASSWORD_FILE: /run/secrets/smtp_password
|
||||
DATABASE_PASSWORD_FILE: /run/secrets/db_password
|
||||
|
||||
secrets:
|
||||
smtp_password:
|
||||
external: true
|
||||
db_password:
|
||||
external: true
|
||||
```
|
||||
|
||||
Update code to read from files:
|
||||
|
||||
```python
|
||||
# In src/pote/config.py
|
||||
def get_secret(key: str, default: str = "") -> str:
|
||||
"""Read secret from file or environment."""
|
||||
file_path = os.getenv(f"{key}_FILE")
|
||||
if file_path and Path(file_path).exists():
|
||||
return Path(file_path).read_text().strip()
|
||||
return os.getenv(key, default)
|
||||
|
||||
class Settings(BaseSettings):
|
||||
smtp_password: str = Field(default_factory=lambda: get_secret("SMTP_PASSWORD"))
|
||||
```
|
||||
|
||||
### ✅ Pros
|
||||
- Docker-native solution
|
||||
- Encrypted in Swarm mode
|
||||
- Never in logs
|
||||
|
||||
### ⚠️ Cons
|
||||
- Requires Docker
|
||||
- More complex setup
|
||||
|
||||
---
|
||||
|
||||
## Option 5: HashiCorp Vault (Enterprise)
|
||||
|
||||
**Good for:** Teams, multiple projects, compliance
|
||||
|
||||
### Setup
|
||||
|
||||
1. Install Vault server
|
||||
2. Store secrets:
|
||||
|
||||
```bash
|
||||
vault kv put secret/pote \
|
||||
smtp_password="your_password" \
|
||||
db_password="your_db_password"
|
||||
```
|
||||
|
||||
3. Update POTE to fetch from Vault:
|
||||
|
||||
```python
|
||||
import hvac
|
||||
|
||||
client = hvac.Client(url='http://vault:8200', token=os.getenv('VAULT_TOKEN'))
|
||||
secrets = client.secrets.kv.v2.read_secret_version(path='pote')
|
||||
|
||||
smtp_password = secrets['data']['data']['smtp_password']
|
||||
```
|
||||
|
||||
### ✅ Pros
|
||||
- Centralized secrets management
|
||||
- Audit logs
|
||||
- Dynamic secrets
|
||||
- Access control
|
||||
|
||||
### ⚠️ Cons
|
||||
- Complex setup
|
||||
- Requires Vault infrastructure
|
||||
- Overkill for single user
|
||||
|
||||
---
|
||||
|
||||
## Option 6: Git Secrets (For CI/CD ONLY)
|
||||
|
||||
**Good for:** GitHub Actions, Gitea Actions
|
||||
|
||||
### Setup in Gitea/GitHub
|
||||
|
||||
1. Go to Repository Settings → Secrets
|
||||
2. Add secrets:
|
||||
- `SMTP_PASSWORD`
|
||||
- `DB_PASSWORD`
|
||||
|
||||
3. Reference in `.github/workflows/ci.yml`:
|
||||
|
||||
```yaml
|
||||
env:
|
||||
SMTP_PASSWORD: ${{ secrets.SMTP_PASSWORD }}
|
||||
DATABASE_URL: postgresql://user:${{ secrets.DB_PASSWORD }}@postgres/db
|
||||
```
|
||||
|
||||
### ⚠️ Important
|
||||
- **Only for CI/CD pipelines**
|
||||
- **NOT for deployed servers**
|
||||
- Secrets are injected during workflow runs
|
||||
|
||||
---
|
||||
|
||||
## 🎯 Recommendation for Your Setup
|
||||
|
||||
### Personal/Research Use (Current)
|
||||
|
||||
**Keep `.env` file with better security:**
|
||||
|
||||
```bash
|
||||
# On Proxmox
|
||||
ssh poteapp@your-proxmox-ip
|
||||
cd ~/pote
|
||||
|
||||
# Secure .env
|
||||
chmod 600 .env
|
||||
chown poteapp:poteapp .env
|
||||
|
||||
# Verify
|
||||
ls -la .env
|
||||
# Should show: -rw------- 1 poteapp poteapp
|
||||
```
|
||||
|
||||
**Backup strategy:**
|
||||
|
||||
```bash
|
||||
# Encrypted backup of .env
|
||||
gpg -c .env # Creates .env.gpg
|
||||
# Store .env.gpg somewhere safe (encrypted USB, password manager)
|
||||
```
|
||||
|
||||
### Production/Team Use
|
||||
|
||||
**Use environment variables + systemd:**
|
||||
|
||||
1. Remove passwords from `.env`
|
||||
2. Create systemd service with `Environment=` directives
|
||||
3. Secure service file: `chmod 600 /etc/systemd/system/pote.service`
|
||||
|
||||
---
|
||||
|
||||
## 🔒 General Security Best Practices
|
||||
|
||||
### ✅ DO
|
||||
|
||||
- Use strong, unique passwords
|
||||
- Restrict file permissions (`chmod 600`)
|
||||
- Keep `.env` in `.gitignore`
|
||||
- Rotate passwords regularly (every 90 days)
|
||||
- Use encrypted backups
|
||||
- Audit who has server access
|
||||
|
||||
### ❌ DON'T
|
||||
|
||||
- Commit secrets to git (even private repos)
|
||||
- Store passwords in code
|
||||
- Share `.env` files via email/Slack
|
||||
- Use the same password everywhere
|
||||
- Leave default passwords
|
||||
- Store secrets in public cloud storage
|
||||
|
||||
---
|
||||
|
||||
## 🧪 Test Your Security
|
||||
|
||||
### Check if `.env` is protected
|
||||
|
||||
```bash
|
||||
# Should be in .gitignore
|
||||
git check-ignore .env # Should output: .env
|
||||
|
||||
# Should have restricted permissions
|
||||
ls -la .env # Should show: -rw------- (600)
|
||||
|
||||
# Should not be committed
|
||||
git log --all --full-history --oneline -- .env # Should be empty
|
||||
```
|
||||
|
||||
### Verify secrets aren't in git history
|
||||
|
||||
```bash
|
||||
# Search for passwords in git history
|
||||
git log --all --full-history --source --pickaxe-all -S 'smtp_password'
|
||||
# Should find nothing
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🔄 Password Rotation Procedure
|
||||
|
||||
### Every 90 days (or if compromised):
|
||||
|
||||
1. Generate new password in mailcow
|
||||
2. Update `.env`:
|
||||
```bash
|
||||
nano .env # Change SMTP_PASSWORD
|
||||
```
|
||||
3. Test:
|
||||
```bash
|
||||
python scripts/send_daily_report.py --test-smtp
|
||||
```
|
||||
4. No restart needed (scripts read `.env` on each run)
|
||||
|
||||
---
|
||||
|
||||
## 📊 Security Level Comparison
|
||||
|
||||
| Level | Method | Effort | Protection |
|
||||
|-------|--------|--------|------------|
|
||||
| 🔓 Basic | `.env` (default perms) | None | Low |
|
||||
| 🔒 Good | `.env` (chmod 600) | 1 min | Medium |
|
||||
| 🔒 Better | Environment variables | 10 min | Good |
|
||||
| 🔒 Better | Separate secrets file | 10 min | Good |
|
||||
| 🔐 Best | Docker Secrets | 30 min | Very Good |
|
||||
| 🔐 Best | Vault | 2+ hours | Excellent |
|
||||
|
||||
---
|
||||
|
||||
## 🎯 Your Current Status
|
||||
|
||||
✅ **Already secure enough for personal use:**
|
||||
- `.env` in `.gitignore` ✅
|
||||
- Not committed to git ✅
|
||||
- Local server only ✅
|
||||
|
||||
⚠️ **Recommended improvement (2 minutes):**
|
||||
```bash
|
||||
chmod 600 .env
|
||||
```
|
||||
|
||||
🔐 **Optional (if paranoid):**
|
||||
- Use separate secrets file in `/etc/pote/`
|
||||
- Encrypt backups with GPG
|
||||
- Set up password rotation schedule
|
||||
|
||||
---
|
||||
|
||||
## Summary
|
||||
|
||||
**For your levkin.ca setup:**
|
||||
|
||||
1. **Current approach (`.env` file) is fine** ✅
|
||||
2. **Add `chmod 600 .env`** for better security (2 minutes)
|
||||
3. **Don't commit `.env` to git** (already protected ✅)
|
||||
4. **Consider upgrading to environment variables** if you deploy to production
|
||||
|
||||
Your current setup is **appropriate for a personal research project**. Don't over-engineer it unless you have specific compliance requirements or a team.
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user