Password Generation
Secure password generation and strength testing.
Password Generation Fundamentals
# ENTROPY SOURCES (ranked by quality)
# 1. /dev/random - blocking, highest entropy (use for keys)
# 2. /dev/urandom - non-blocking, sufficient for passwords
# 3. openssl rand - uses /dev/urandom internally
# 4. $RANDOM - AVOID! predictable PRNG
# OPENSSL GENERATION (most portable)
openssl rand -base64 32 # 32 bytes → 44 chars (with padding)
openssl rand -base64 32 | tr -d '/+=' | head -c 32 # URL-safe, exactly 32 chars
openssl rand -hex 32 # 32 bytes → 64 hex chars
# URANDOM GENERATION (no dependencies)
< /dev/urandom tr -dc 'A-Za-z0-9' | head -c 32 # Alphanumeric only
< /dev/urandom tr -dc 'A-Za-z0-9!@#$%' | head -c 32 # With symbols
# PYTHON GENERATION (strongest stdlib)
python -c "import secrets; print(secrets.token_urlsafe(32))" # URL-safe, 43 chars
python -c "import secrets; print(secrets.token_hex(32))" # 64 hex chars
python -c "import secrets; print(secrets.token_bytes(32).hex())" # Same
# ENTROPY CALCULATION
# Alphanumeric (62 chars): log2(62) = 5.95 bits/char
# 32 chars = 190 bits entropy (excellent)
# 16 chars = 95 bits entropy (minimum recommended)
# 8 chars = 48 bits entropy (too weak!)
# PASSWORD STRENGTH TIERS
# < 64 bits: Weak (brute-forceable)
# 64-80 bits: Acceptable (short-term)
# 80-128 bits: Strong (long-term)
# > 128 bits: Paranoid (cryptographic keys)
Gopass Password Management
# GENERATE AND STORE (one command)
gopass generate v3/domains/d000/services/new-service 32
# Generates 32-char password and stores it
# GENERATE WITH SYMBOLS
gopass generate -s v3/domains/d000/services/new-service 32
# -s includes symbols (!@#$%^&*)
# GENERATE WITHOUT SYMBOLS (API keys)
gopass generate -n v3/domains/d000/api-keys/github 40
# -n alphanumeric only
# GENERATE AND SHOW
gopass generate -c v3/domains/d000/services/new-service 32
# -c copies to clipboard instead of showing
# GENERATE FOR SPECIFIC CHARSET
gopass generate --symbols='!@#$%' v3/domains/d000/services/svc 32
# RETRIEVE PASSWORD
gopass show v3/domains/d000/services/new-service
gopass show -c v3/domains/d000/services/new-service # Copy to clipboard
gopass show -o v3/domains/d000/services/new-service # Password only (no metadata)
# RETRIEVE WITH TIMEOUT (auto-clear clipboard)
gopass show -c -C 45 v3/domains/d000/services/new-service
# Clears clipboard after 45 seconds
# SEARCH PASSWORDS
gopass find service-name # Search by name
gopass search "pattern" # Search content too
# LIST ALL ENTRIES
gopass ls # Tree view
gopass ls v3/domains/d000 # Subtree only
# INFRASTRUCTURE PASSWORD PATTERNS
# Naming convention: v3/domains/d000/{category}/{service}
gopass generate v3/domains/d000/network/pfsense-admin 32
gopass generate v3/domains/d000/identity/ise-admin 32
gopass generate v3/domains/d000/k8s/argocd-admin 32
gopass generate v3/domains/d000/observability/grafana-admin 32
# MULTILINE ENTRIES (structured secrets)
gopass edit v3/domains/d000/services/complex-service
# Opens editor for:
# password-line-1
# user: admin
# url: https://service.example.com
# api_key: xxxx
# notes: Additional information
# RETRIEVE SPECIFIC FIELD
gopass show v3/domains/d000/services/complex-service user
gopass show v3/domains/d000/services/complex-service api_key
Age Encryption for Secrets
# AGE: Modern encryption tool (replaces GPG for file encryption)
# https://github.com/FiloSottile/age
# GENERATE AGE KEYPAIR
age-keygen -o ~/.config/age/key.txt
# Output:
# Public key: age1abc123...
# VIEW PUBLIC KEY
age-keygen -y ~/.config/age/key.txt
# ENCRYPT FILE TO RECIPIENT
age -r age1abc123... plaintext.txt > encrypted.age
age -r age1abc123... -o encrypted.age plaintext.txt # Explicit output
# ENCRYPT TO MULTIPLE RECIPIENTS
age -r age1abc123... -r age1def456... secret.txt > secret.age
# ENCRYPT WITH PASSPHRASE (no keys needed)
age -p secret.txt > secret.age
# Prompts for passphrase
# DECRYPT WITH KEY
age -d -i ~/.config/age/key.txt encrypted.age > plaintext.txt
# DECRYPT WITH PASSPHRASE
age -d encrypted.age > plaintext.txt
# Prompts for passphrase
# PIPE-FRIENDLY ENCRYPTION
echo "secret-api-key" | age -r age1abc123... > api-key.age
age -d -i ~/.config/age/key.txt api-key.age
# ENCRYPT TARBALL
tar czf - sensitive-dir/ | age -r age1abc123... > sensitive-dir.tar.gz.age
# DECRYPT TARBALL
age -d -i ~/.config/age/key.txt sensitive-dir.tar.gz.age | tar xzf -
# INFRASTRUCTURE SECRET PATTERNS
# Encrypt environment file
age -r age1abc123... ~/.secrets/environments/domains/d000/dev/network.env \
> ~/.secrets/environments/domains/d000/dev/network.env.age
# Decrypt for use
age -d -i ~/.config/age/key.txt network.env.age > /tmp/network.env
source /tmp/network.env
rm /tmp/network.env # Cleanup!
# Verify encryption (should show binary/encrypted data)
file encrypted.age # Output: data
head -c 50 encrypted.age | xxd # Shows age header
# AGE VS GPG
# Age: Simpler, faster, smaller keys, no web of trust
# GPG: More features, wider support, complex key management
# Use age for: file encryption, secrets, automation
# Use GPG for: email signing, package signing, key servers
Age Operational Patterns
# MASTER KEY LOCATION (your setup)
AGE_KEY=~/.secrets/.metadata/keys/master.age.key
# ------------------------------------
# SEARCH ENCRYPTED FILES (pipe to grep)
# ------------------------------------
# Search for pattern in encrypted file
age -d -i $AGE_KEY document.adoc.age | grep -iE "client.*(id|secret)"
# Search with context
age -d -i $AGE_KEY document.adoc.age | grep -B2 -A2 "password"
# Search multiple encrypted files
for f in ~/.secrets/documents/notes/*.age; do
echo "=== $f ==="
age -d -i $AGE_KEY "$f" 2>/dev/null | grep -i "oauth" || true
done
# Find which encrypted file contains a pattern
find ~/.secrets -name "*.age" -exec sh -c '
age -d -i '"$AGE_KEY"' "$1" 2>/dev/null | grep -q "CLIENT_ID" && echo "$1"
' _ {} \;
# ------------------------------------
# VIEW ENCRYPTED FILES
# ------------------------------------
# View in pager
age -d -i $AGE_KEY document.age | less
# View with syntax highlighting (bat)
age -d -i $AGE_KEY document.adoc.age | bat --language=asciidoc
# View first N lines
age -d -i $AGE_KEY document.age | head -50
# Extract specific section
age -d -i $AGE_KEY notes.age | sed -n '/^## Setup/,/^## /p'
# ------------------------------------
# EDIT ENCRYPTED FILES
# ------------------------------------
# Edit in place with temp file (SECURE pattern)
TEMP=$(mktemp)
trap "rm -f $TEMP" EXIT
age -d -i $AGE_KEY document.age > "$TEMP"
$EDITOR "$TEMP"
age -r "$(age-keygen -y $AGE_KEY)" -o document.age "$TEMP"
# ------------------------------------
# BATCH OPERATIONS
# ------------------------------------
# Decrypt all .age files in directory to /tmp
for f in ~/.secrets/documents/notes/*.age; do
out="/tmp/$(basename "$f" .age)"
age -d -i $AGE_KEY "$f" > "$out"
done
# Re-encrypt after bulk edit
for f in /tmp/*.adoc; do
age -r "$(age-keygen -y $AGE_KEY)" -o "~/.secrets/documents/notes/$(basename "$f").age" "$f"
rm "$f" # Cleanup plaintext
done
# List encrypted files by size
find ~/.secrets -name "*.age" -exec ls -lh {} \; | awk '{print $5, $9}'
# ------------------------------------
# DSOURCE INTEGRATION
# ------------------------------------
# Load secrets via dsource (your dsec tool)
DSEC_EVAL_VERIFIED=true dsec source d000 dev/network
# Decrypt and source environment
age -d -i $AGE_KEY ~/.secrets/environments/domains/d000/dev/network.env.age | \
while IFS='=' read -r key value; do
export "$key=$value"
done
# Quick inline extraction (env files)
CLIENT_ID=$(age -d -i $AGE_KEY oauth.env.age | grep CLIENT_ID | cut -d= -f2)
# ------------------------------------
# ENCRYPTED JSON/YAML (age + jq/yq)
# ------------------------------------
# Extract fields from encrypted JSON
age -d -i $AGE_KEY tokens.json.age | jq -r '.client_id'
# Formatted extraction with labels
age -d -i $AGE_KEY tokens.json.age | jq -r '"Client ID: " + .client_id, "Client Secret: " + .client_secret'
# Extract and export to env vars
eval "$(age -d -i $AGE_KEY tokens.json.age | jq -r '@sh "CLIENT_ID=\(.client_id) CLIENT_SECRET=\(.client_secret)"')"
# Extract nested fields
age -d -i $AGE_KEY config.json.age | jq -r '.oauth.credentials.client_id'
# Extract array elements
age -d -i $AGE_KEY accounts.json.age | jq -r '.accounts[].email'
# Filter and extract
age -d -i $AGE_KEY services.json.age | jq -r '.services[] | select(.name=="gmail") | .token'
# Encrypted YAML with yq
age -d -i $AGE_KEY config.yaml.age | yq '.database.password'
# Transform encrypted data
age -d -i $AGE_KEY old-format.json.age | jq '{id: .client_id, secret: .client_secret}' | \
age -r "$(age-keygen -y $AGE_KEY)" -o new-format.json.age
# ------------------------------------
# BACKUP AND RECOVERY
# ------------------------------------
# Backup key securely (encrypted with passphrase)
age -p -o master.age.key.backup $AGE_KEY
# Verify backup decrypts
age -d master.age.key.backup | head -1 # Should show AGE-SECRET-KEY-...
# Re-encrypt everything with new key
NEW_KEY=~/.secrets/.metadata/keys/new-master.age.key
for f in ~/.secrets/**/*.age; do
age -d -i $AGE_KEY "$f" | age -r "$(age-keygen -y $NEW_KEY)" -o "${f}.new"
mv "${f}.new" "$f"
done
# ------------------------------------
# GOTCHAS
# ------------------------------------
# WRONG: Leaving plaintext on disk
age -d -i $AGE_KEY secret.age > /tmp/secret.txt
# Now secret.txt is on disk!
# CORRECT: Use temp file with trap or pipe directly
age -d -i $AGE_KEY secret.age | grep "pattern" # Never touches disk
# WRONG: Hardcoding key path
age -d -i ~/.config/age/key.txt file.age # Works on ONE machine
# CORRECT: Use variable or standard location
age -d -i "$AGE_KEY" file.age # Portable across systems
# WRONG: Storing passphrase-encrypted files for automation
age -p -o secret.age plaintext # Can't automate without human
# CORRECT: Use key-based encryption for automation
age -r age1... -o secret.age plaintext # Automatable
TOTP/2FA Secrets Management
# TOTP (Time-based One-Time Password)
# RFC 6238 - 6-digit codes rotating every 30 seconds
# GOPASS TOTP SUPPORT
# Store TOTP secret as 'totp' field
gopass edit v3/domains/d000/identity/github-2fa
# Add line: totp: JBSWY3DPEHPK3PXP
# Generate current TOTP code
gopass otp v3/domains/d000/identity/github-2fa
# Output: 123456
# TOTP FROM RAW SECRET (without gopass)
# Convert hex to base32 if needed
HEX_SECRET="48656c6c6f21"
echo "$HEX_SECRET" | xxd -r -p | base32
# Output: JBSWY3DPEHPK3PXP
# Generate TOTP with oathtool
oathtool --totp -b JBSWY3DPEHPK3PXP
# -b = base32 input
# Generate TOTP with specific time
oathtool --totp -b --now="2024-01-15 12:00:00 UTC" JBSWY3DPEHPK3PXP
# Python TOTP generation
python3 << 'EOF'
import hmac
import struct
import time
import base64
secret = base64.b32decode("JBSWY3DPEHPK3PXP")
counter = int(time.time()) // 30
msg = struct.pack(">Q", counter)
h = hmac.new(secret, msg, "sha1").digest()
offset = h[-1] & 0x0F
code = (struct.unpack(">I", h[offset:offset+4])[0] & 0x7FFFFFFF) % 1000000
print(f"{code:06d}")
EOF
# QR CODE FOR TOTP ENROLLMENT
# Format: otpauth://totp/LABEL?secret=BASE32SECRET&issuer=ISSUER
qrencode -o totp-qr.png \
"otpauth://totp/GitHub:evan@example.com?secret=JBSWY3DPEHPK3PXP&issuer=GitHub"
# BACKUP TOTP SECRETS
# Export all TOTP entries from gopass
gopass ls | while read entry; do
secret=$(gopass show "$entry" totp 2>/dev/null)
if [ -n "$secret" ]; then
echo "$entry: $secret"
fi
done > totp-backup.txt
# Encrypt the backup
age -r age1abc123... totp-backup.txt > totp-backup.txt.age
rm totp-backup.txt
HashiCorp Vault Secrets
# VAULT KV SECRETS ENGINE
# Path: kv/ (Key-Value secrets)
# Store secret
vault kv put kv/database/postgres password="$(openssl rand -base64 32)"
# Retrieve secret
vault kv get kv/database/postgres
vault kv get -field=password kv/database/postgres # Just the value
vault kv get -format=json kv/database/postgres | jq -r '.data.data.password'
# List secrets
vault kv list kv/
vault kv list kv/database/
# Delete secret (soft delete - recoverable)
vault kv delete kv/database/postgres
# Undelete (recover)
vault kv undelete -versions=1 kv/database/postgres
# Destroy (permanent)
vault kv destroy -versions=1 kv/database/postgres
# VERSION HISTORY
vault kv metadata get kv/database/postgres
# Shows all versions with timestamps
vault kv get -version=2 kv/database/postgres
# Retrieve specific version
# DYNAMIC SECRETS (database credentials)
vault read database/creds/readonly-role
# Output:
# lease_id database/creds/readonly-role/abc123
# lease_duration 1h
# username v-token-readonly-abc123
# password A1b2C3d4E5f6G7h8
# TRANSIT ENGINE (encryption as a service)
# Encrypt data
vault write transit/encrypt/my-key plaintext=$(echo "secret" | base64)
# Output: ciphertext: vault:v1:abc123...
# Decrypt data
vault write transit/decrypt/my-key ciphertext="vault:v1:abc123..."
# Output: plaintext: c2VjcmV0 (base64)
echo "c2VjcmV0" | base64 -d
# Output: secret
# INFRASTRUCTURE PATTERNS
# Store ISE credentials
vault kv put kv/network/ise \
username="admin" \
password="$(gopass show -o v3/domains/d000/identity/ise-admin)"
# Store database credentials
vault kv put kv/database/postgres \
username="app_user" \
password="$(openssl rand -base64 24)" \
host="postgres.inside.domusdigitalis.dev" \
port="5432"
# Retrieve for application
export PGPASSWORD=$(vault kv get -field=password kv/database/postgres)
export PGUSER=$(vault kv get -field=username kv/database/postgres)
export PGHOST=$(vault kv get -field=host kv/database/postgres)
# APPROLE AUTHENTICATION (for automation)
# Get role-id (static)
vault read auth/approle/role/netapi/role-id
# Get secret-id (dynamic, one-time use)
vault write -f auth/approle/role/netapi/secret-id
# Login with AppRole
vault write auth/approle/login \
role_id="abc123" \
secret_id="def456"
# Returns token for subsequent requests
SSH Key Management
# KEY GENERATION (modern - Ed25519)
ssh-keygen -t ed25519 -C "evan@domusdigitalis.dev"
# -C comment (usually email)
# Creates: ~/.ssh/id_ed25519 (private) and ~/.ssh/id_ed25519.pub (public)
# KEY GENERATION WITH CUSTOM PATH
ssh-keygen -t ed25519 -f ~/.ssh/id_ed25519_vault -C "vault-signing-key"
# Separate key for Vault SSH CA
# RSA KEY (legacy compatibility)
ssh-keygen -t rsa -b 4096 -C "evan@domusdigitalis.dev"
# -b 4096 for security (minimum 2048)
# ECDSA KEY
ssh-keygen -t ecdsa -b 521 -C "evan@domusdigitalis.dev"
# -b 256, 384, or 521 bits
# CHANGE PASSPHRASE
ssh-keygen -p -f ~/.ssh/id_ed25519
# Prompts for old and new passphrase
# VIEW KEY FINGERPRINT
ssh-keygen -lf ~/.ssh/id_ed25519.pub
# Output: 256 SHA256:abc123... evan@domusdigitalis.dev (ED25519)
# VIEW KEY IN DIFFERENT FORMATS
ssh-keygen -lf ~/.ssh/id_ed25519.pub -E md5 # MD5 format
ssh-keygen -lf ~/.ssh/id_ed25519.pub -E sha256 # SHA256 (default)
# SSH AGENT MANAGEMENT
eval $(ssh-agent -s) # Start agent
ssh-add ~/.ssh/id_ed25519 # Add key
ssh-add -l # List keys
ssh-add -L # List public keys
ssh-add -d ~/.ssh/id_ed25519 # Remove specific
ssh-add -D # Remove all
# YUBIKEY/FIDO2 SSH KEYS
ssh-keygen -t ed25519-sk -C "yubikey-nano" # FIDO2 resident key
# -sk = security key (requires touch)
# VIEW SSH CERTIFICATE
ssh-keygen -Lf ~/.ssh/id_ed25519_vault-cert.pub
# Shows: Type, Public key, Signing CA, Key ID, Serial, Valid, Principals, Extensions
# AUTHORIZED_KEYS MANAGEMENT
# Add key
cat ~/.ssh/id_ed25519.pub >> ~/.ssh/authorized_keys
# Verify permissions (critical!)
chmod 700 ~/.ssh
chmod 600 ~/.ssh/authorized_keys
chmod 600 ~/.ssh/id_ed25519
chmod 644 ~/.ssh/id_ed25519.pub
# Parse authorized_keys
awk '{print NR": "$3" ("$1")"}' ~/.ssh/authorized_keys
# Shows: line_number: comment (key_type)
# Remove duplicate keys
sort -u -k2 ~/.ssh/authorized_keys > /tmp/ak_clean
mv /tmp/ak_clean ~/.ssh/authorized_keys
# COUNT KEYS
wc -l < ~/.ssh/authorized_keys
# INFRASTRUCTURE PATTERNS
# Deploy key to multiple hosts
for host in vault-01 ise-01 bind-01; do
ssh-copy-id -i ~/.ssh/id_ed25519.pub "evanusmodestus@$host"
done
# Vault SSH CA signing
vault write -field=signed_key ssh/sign/domus-client \
public_key=@$HOME/.ssh/id_ed25519_vault.pub \
valid_principals="evanusmodestus,admin,root,ansible" \
> ~/.ssh/id_ed25519_vault-cert.pub
# Refresh SSH agent after signing
ssh-add -d ~/.ssh/id_ed25519_vault 2>/dev/null
ssh-add ~/.ssh/id_ed25519_vault
Infrastructure Secret Patterns
# DSEC/DSOURCE ENVIRONMENT LOADING
# Load development secrets
dsource d000 dev/network
# Exports: ISE_HOST, ISE_USER, ISE_PASS, WLC_HOST, etc.
# Load observability secrets
dsource d000 dev/observability
# Exports: WAZUH_API_URL, WAZUH_API_USER, WAZUH_API_PASSWORD, etc.
# Verify loaded variables
env | grep -E '^(ISE_|WLC_|WAZUH_|PFSENSE_)' | sort
# SECRET ROTATION WORKFLOW
# 1. Generate new password
NEW_PASS=$(openssl rand -base64 24)
# 2. Update in service (example: ISE)
# (Manual step or API call)
# 3. Update in gopass
echo "$NEW_PASS" | gopass insert -f v3/domains/d000/identity/ise-admin
# 4. Update age-encrypted env file
age -d -i ~/.config/age/key.txt ~/.secrets/environments/domains/d000/dev/network.env.age \
> /tmp/network.env
sed -i "s/ISE_PASS=.*/ISE_PASS='$NEW_PASS'/" /tmp/network.env
age -r $(age-keygen -y ~/.config/age/key.txt) /tmp/network.env \
> ~/.secrets/environments/domains/d000/dev/network.env.age
rm /tmp/network.env
# 5. Verify
dsource d000 dev/network
echo $ISE_PASS
# NETAPI CREDENTIAL PATTERNS
# ISE
dsource d000 dev/network
netapi ise mnt sessions
# pfSense
netapi pfsense dns list
# WLC
netapi wlc status
# SECRET AUDIT
# List all gopass entries with age
gopass ls | while read entry; do
modified=$(gopass show "$entry" 2>/dev/null | grep -i 'modified:' | head -1)
echo "$entry: $modified"
done
# Find secrets older than 90 days
# (Manual review - gopass doesn't track modification dates well)
# EMERGENCY SECRET COMPROMISE
# If credential leaked:
# 1. Rotate immediately
gopass generate v3/domains/d000/compromised-service 32
# 2. Update service
# (API or manual)
# 3. Audit access logs
# Check who used the old credential
# 4. Update encrypted files
# Re-encrypt any files using old credential
# 5. Document incident
# Log in security runbook
Password/Secret Gotchas
# WRONG: Using $RANDOM for security
password=$RANDOM$RANDOM$RANDOM
# $RANDOM is predictable! Only 32k possible values per call
# CORRECT: Use cryptographic source
password=$(openssl rand -base64 24)
# WRONG: Password in command history
mysql -u root -pMySecretPassword
# Password visible in: history, ps, /proc
# CORRECT: Use environment variable or prompt
mysql -u root -p
# Or:
MYSQL_PWD="$password" mysql -u root
# WRONG: Hardcoding secrets in scripts
API_KEY="sk_live_abc123"
# Committed to git, visible in logs
# CORRECT: Load from secure source
API_KEY=$(gopass show -o v3/domains/d000/api-keys/service)
# Or:
source <(dsource d000 dev/network)
# WRONG: Storing secrets in plain text
echo "password123" > ~/.secret
# Anyone with file access sees it
# CORRECT: Encrypt at rest
echo "password123" | age -r age1abc123... > ~/.secret.age
# WRONG: Base64 "encryption"
echo "secret" | base64 > encoded.txt
# Base64 is encoding, NOT encryption!
# CORRECT: Use actual encryption
echo "secret" | age -r age1abc123... > encrypted.age
# WRONG: Reusing passwords
# Same password for ISE, WLC, switches
# One compromise = total compromise
# CORRECT: Unique passwords per service
gopass generate v3/domains/d000/network/ise-admin 32
gopass generate v3/domains/d000/network/wlc-admin 32
gopass generate v3/domains/d000/network/switch-admin 32
# WRONG: Short TOTP secrets
# Some services provide short hex: "DEADBEEF"
# Only 32 bits of entropy!
# CORRECT: Minimum 128-bit TOTP secrets
# 26+ character base32 string
# WRONG: Leaving decrypted secrets in /tmp
age -d secret.age > /tmp/secret.txt
# Use it...
# Forget to delete!
# CORRECT: Trap for cleanup
cleanup() { rm -f /tmp/secret.txt; }
trap cleanup EXIT
age -d secret.age > /tmp/secret.txt
# Use it...
# Automatically cleaned up on exit
# WRONG: echo password (in logs)
echo "Password is: $PASSWORD"
# Goes to stdout, potentially logged
# CORRECT: Direct to stderr or suppress
printf "Password set for %s\n" "$USER" >&2
# Or just don't echo passwords at all
Quick Reference
# GENERATION
openssl rand -base64 32 | tr -d '/+=' | head -c 32 # URL-safe 32 chars
openssl rand -hex 32 # 64 hex chars
python -c "import secrets; print(secrets.token_urlsafe(32))"
# GOPASS
gopass generate path/to/secret 32 # Generate + store
gopass generate -s path/to/secret 32 # With symbols
gopass show -o path/to/secret # Password only
gopass show -c path/to/secret # Copy to clipboard
gopass otp path/to/secret # Generate TOTP
# AGE ENCRYPTION
age -r age1public... file > file.age # Encrypt
age -d -i ~/.config/age/key.txt file.age # Decrypt
age -p file > file.age # Passphrase mode
# VAULT
vault kv put kv/path key=value # Store
vault kv get -field=key kv/path # Retrieve
vault kv list kv/ # List
# SSH KEYS
ssh-keygen -t ed25519 -C "comment" # Generate
ssh-keygen -lf ~/.ssh/id_ed25519.pub # Fingerprint
ssh-keygen -Lf certificate.pub # View cert
ssh-add ~/.ssh/id_ed25519 # Add to agent
# ENVIRONMENT LOADING
dsource d000 dev/network # Load network secrets
dsource d000 dev/observability # Load monitoring secrets
env | grep -E '^ISE_|^WLC_' | sort # Verify loaded
# TOTP
oathtool --totp -b SECRET # Generate code
gopass otp path/to/entry # From gopass
# ENTROPY (bits per character)
# Alphanumeric (62): 5.95 bits/char
# +Symbols (95): 6.57 bits/char
# Minimum: 16 chars = ~95 bits
# Recommended: 24+ chars = ~143+ bits