POTE/docs/13_secrets_management.md
ilia 0c183fb28c 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
2025-12-15 15:47:12 -05:00

8.3 KiB

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

# 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:

[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:

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):

sudo mkdir -p /etc/pote
sudo nano /etc/pote/secrets

Content:

export SMTP_PASSWORD="your_password_here"
export DATABASE_PASSWORD="your_db_password_here"

Secure it:

sudo chmod 600 /etc/pote/secrets
sudo chown poteapp:poteapp /etc/pote/secrets

Update scripts to source it:

#!/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:

echo "your_smtp_password" | docker secret create smtp_password -
echo "your_db_password" | docker secret create db_password -

Update docker-compose.yml:

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:

# 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:
vault kv put secret/pote \
    smtp_password="your_password" \
    db_password="your_db_password"
  1. Update POTE to fetch from Vault:
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:

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:

# 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:

# 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

# 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

# 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:
    nano .env  # Change SMTP_PASSWORD
    
  3. Test:
    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):

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.