SOPS Integration - Team Sharing & CI/CD
Overview
SOPS (Secrets OPerationS) extends dsec with multi-recipient encryption capabilities, enabling:
-
Team Sharing - Share secrets with family, friends, collaborators using multiple age keys
-
CI/CD Integration - Use cloud KMS (AWS, GCP, Azure) for pipeline secrets
-
Backward Compatibility - Existing dsec workflow unchanged
Architecture
%%{init: {'theme': 'dark', 'themeVariables': { 'primaryColor': '#313244', 'primaryTextColor': '#cdd6f4', 'primaryBorderColor': '#89b4fa', 'lineColor': '#89b4fa', 'secondaryColor': '#45475a', 'tertiaryColor': '#181825', 'background': '#1e1e2e', 'mainBkg': '#313244', 'nodeBorder': '#89b4fa', 'clusterBkg': '#181825', 'clusterBorder': '#585b70', 'titleColor': '#cdd6f4', 'fontFamily': 'JetBrains Mono, monospace'}}}%%
flowchart TB
subgraph Personal["Personal Secrets (dsec/age)"]
D000["d000/"]
D001["d001+"]
MASTER["master.age.key"]
end
subgraph Shared["Team Secrets (dsec+sops)"]
TEAMS["teams/"]
REGISTRY["registry.yaml"]
SOPS_YAML[".sops.yaml"]
end
subgraph Keys["Key Management"]
AGE["Age Keys<br/>(recipients)"]
AWS["AWS KMS"]
GCP["GCP KMS"]
AZURE["Azure KV"]
end
MASTER --> D000 & D001
AGE --> TEAMS
AWS & GCP & AZURE --> TEAMS
REGISTRY --> TEAMS
SOPS_YAML --> TEAMS
style Personal fill:#a6e3a1,color:#1e1e2e
style Shared fill:#89b4fa,color:#1e1e2e
style Keys fill:#f9e2af,color:#1e1e2e
Directory Structure
~/.secrets/
├── [EXISTING - UNCHANGED]
│ ├── environments/domains/{d000,d001,...}
│ ├── .metadata/keys/master.age.{key,pub}
│ └── bin/dsec
│
└── shared/ # NEW: SOPS-managed secrets
├── .sops.yaml # Global SOPS config
├── registry.yaml # Team registry (metadata)
├── keys/
│ ├── recipients/ # Team member public keys
│ │ └── owner.txt
│ └── kms/ # Cloud KMS ARNs
└── teams/
├── family/
│ ├── .sops.yaml # Team-specific config
│ └── home-automation.yaml # SOPS-encrypted secrets
├── homelab/
└── friends/
Key Management Tiers
| Tier | Keys | Location | Use Case |
|---|---|---|---|
Personal |
|
|
Your d000/d001+ domain secrets |
Team |
Multiple age public keys |
|
Family, friends, collaborators |
CI/CD |
AWS/GCP/Azure KMS |
Cloud provider |
GitHub Actions, GitLab CI, pipelines |
Commands Reference
Team Management
| Command | Description |
|---|---|
|
List all teams with member/secret counts |
|
Create a new team |
|
Add member to team |
|
Remove member from team |
|
Display all team public keys |
|
Re-encrypt all team secrets with current keys |
Shared Secrets
| Command | Description |
|---|---|
|
List shared secrets |
|
Output export statements for eval |
|
Clear shared secrets from environment |
|
Display decrypted secrets |
|
Edit secrets via SOPS (creates if new) |
|
Add secrets from YAML file |
Shell Wrappers
Add to ~/.zshrc (auto-included in shell-security.zsh):
# SOPS uses this for decryption
export SOPS_AGE_KEY_FILE="$HOME/.secrets/.metadata/keys/master.age.key"
# Team secrets loader
dsteam() { eval " $(dsec shared source "$@")"; }
dsteunsource() { eval " $(dsec shared unsource)"; }
# Short aliases
alias dst='dsteam'
alias dstu='dsteunsource'
Workflows
Creating a Team
# Initialize team
dsec team init family "Family Home Automation"
# View teams
dsec team list
Adding Team Members
# Member generates their key
age-keygen -o alice.key # They keep this private
age-keygen -y alice.key > alice.pub # They send you this
# You add them to the team
dsec team add-member family alice.pub --name "Alice"
# Re-encrypt existing secrets for new member
dsec team rotate family
Creating Shared Secrets
# Interactive edit (creates file if new)
dsec shared edit family home-automation
# Or from existing YAML
cat > /tmp/secrets.yaml << 'EOF'
MQTT_USER: homeassistant
MQTT_PASS: supersecret
ZIGBEE_KEY: 0x1234567890abcdef
EOF
dsec shared add family home-automation /tmp/secrets.yaml
shred -u /tmp/secrets.yaml
Team Member Decryption
Team members decrypt with their own age key:
# On Alice's machine
export SOPS_AGE_KEY_FILE=~/.age/alice.key
sops -d ~/.secrets/shared/teams/family/home-automation.yaml
# Or using dsec (if they have it)
dst family home-automation
CI/CD Pipeline Setup
# In your project directory
dsec cicd init . --github
# Add AWS KMS for production
dsec cicd add-kms . "arn:aws:kms:us-east-1:123456789:key/abc-123"
# Validate configuration
dsec cicd validate .
# Add secrets
sops secrets/prod.yaml
GitHub Actions Example
name: Deploy
on: push
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install SOPS
run: |
curl -LO https://github.com/getsops/sops/releases/download/v3.8.1/sops-v3.8.1.linux.amd64
chmod +x sops-v3.8.1.linux.amd64
sudo mv sops-v3.8.1.linux.amd64 /usr/local/bin/sops
- name: Decrypt secrets
env:
SOPS_AGE_KEY: ${{ secrets.AGE_SECRET_KEY }}
run: |
echo "$SOPS_AGE_KEY" > /tmp/age.key
export SOPS_AGE_KEY_FILE=/tmp/age.key
sops -d secrets/prod.yaml > .env
source .env
# ... deploy ...
rm /tmp/age.key .env
Security Model
Isolation
| Aspect | Personal (dsec) | Team (SOPS) |
|---|---|---|
Directory |
|
|
Encryption |
Single age key |
Multi-recipient age + KMS |
Access Control |
Domain lock (d000 vs d001+) |
Team membership |
Audit Log |
|
|
Key Rotation
When a team member leaves:
# Remove member
dsec team remove-member family alice
# Re-encrypt all secrets (excludes removed member)
dsec team rotate family
|
After removing a member, you should also rotate any secrets they had access to (change passwords, API keys, etc.) as they may have copies. |
Dependencies
| Tool | Purpose | Installation |
|---|---|---|
sops |
Multi-recipient encryption |
|
yq (v4+) |
YAML processing |
|
age |
Encryption backend |
|