SOPS secrets management integrated with Bitwarden for NixOS
This branch contains the impure implementation of sopsWarden that requires the --impure flag for NixOS evaluation. This implementation provides direct access to decrypted secret values in your configuration by reading them at evaluation time.
Key differences:
- β Direct secret access - Secrets available as actual values, not just paths
β οΈ Requires--impure- All NixOS rebuilds need the--impureflag- π Evaluation-time reading - Secrets are read during configuration evaluation
sopsWarden eliminates the pain of manual secret management in NixOS by automatically syncing secrets from your Bitwarden vault to encrypted SOPS files. No more editing encrypted YAML files by hand!
- π Bitwarden Integration - Use your existing Bitwarden vault as the source of truth
- π― Simple Configuration - Define secrets directly in your NixOS configuration
- π‘οΈ SOPS Encryption - Secrets encrypted at rest using age keys
- π‘ Direct Access - Secrets available as actual values in your configuration
- π Change Detection - Automatic warnings when secrets need re-syncing
β οΈ Impure Evaluation - Requires--impureflag for all operations
{
inputs.sopswarden.url = "github:pfassina/sopswarden/impure-implementation";
outputs = { nixpkgs, sopswarden, ... }: {
nixosConfigurations.myhost = nixpkgs.lib.nixosSystem {
modules = [
sopswarden.nixosModules.default
{
services.sopswarden = {
enable = true;
secrets = {
# Simple secrets - just specify the Bitwarden item name
wifi-password = "Home WiFi";
database-url = "Production Database";
# Complex secrets - specify user, type, or field
api-key = { name = "My Service"; user = "admin@example.com"; };
ssl-cert = { name = "Certificates"; type = "note"; field = "ssl_cert"; };
};
};
}
];
};
};
}# Generate age key
mkdir -p ~/.config/sops/age
age-keygen -o ~/.config/sops/age/keys.txt
# sopswarden will automatically create .sops.yaml in /var/lib/sopswarden/
# when you run sopswarden-sync for the first time# Login to Bitwarden
rbw login your-email@example.com
# For self-hosted Bitwarden
rbw config set base_url https://your-bitwarden-server.com
# Unlock vault
rbw unlock# Run initial sync
sopswarden-sync
# This creates encrypted files in /var/lib/sopswarden/# IMPORTANT: Always use --impure flag
sudo nixos-rebuild switch --flake .#myhost --impureWith the impure implementation, secrets are available as actual values in your configuration:
# Add 'secrets' to your module arguments
{ config, pkgs, secrets, ... }: {
# Define secrets once
services.sopswarden.secrets = {
wifi-password = "Home WiFi";
api-key = { name = "My Service"; user = "admin@example.com"; };
db-password = "Database Password";
};
# Use actual secret values directly
networking.wireless.networks."MyWiFi".psk = secrets.wifi-password; # Actual password string
services.postgresql.initialScript = pkgs.writeText "init.sql" ''
CREATE USER app WITH PASSWORD '${secrets.db-password}'; # Direct value
'';
services.myapp = {
apiKey = secrets.api-key; # Direct access to decrypted value
};
}# Impure implementation (this branch)
environment.etc."api-config".text = ''
API_KEY=${secrets.api-key} # Direct value: "sk-1234567890abcdef"
'';
# Pure implementation (main branch)
environment.etc."api-config".text = ''
API_KEY=$(cat ${secrets.api-key}) # Path: /run/secrets/api-key
'';The impure implementation uses builtins.readFile to read decrypted secrets during evaluation:
- Sync -
sopswarden-syncfetches from Bitwarden and creates encrypted files in/var/lib/sopswarden/ - Deploy -
nixos-rebuild switch --impureruns - Decrypt - SOPS decrypts secrets to
/run/secrets/* - Read - Configuration reads actual values using
builtins.readFile - Use - Secrets available as strings in your configuration
- Add to Bitwarden (web interface or
rbw) - Update your NixOS configuration:
services.sopswarden.secrets = { # ... existing secrets ... new-secret = "New Bitwarden Item"; };
- Sync secrets:
sopswarden-sync
- Deploy with --impure:
sudo nixos-rebuild switch --flake .#host --impure
The module will warn you when secrets need syncing:
β οΈ sopswarden: secrets configuration has changed since last sync. Run 'sopswarden-sync' to update encrypted secrets.
services.sopswarden = {
enable = true;
# Define your secrets (recommended approach)
secrets = {
api-key = "My API Service";
db-password = { name = "Database"; user = "admin@example.com"; };
};
# rbw configuration
rbwCommand = "${pkgs.rbw}/bin/rbw";
# Secret permissions
defaultOwner = "root";
defaultGroup = "root";
defaultMode = "0400";
# Features
enableChangeDetection = true; # Warn when sync needed
installPackages = true; # Install rbw, sops, age
installSyncCommand = true; # Install sopswarden-sync
};Benefits:
- β Direct access to secret values
- β Simpler syntax in configurations
- β
No need to use
cator file reading
Drawbacks:
β οΈ Requires--impureflag alwaysβ οΈ Secrets accessible during evaluationβ οΈ Can't use with restricted evaluation modes
- Never commit unencrypted secrets
- Keep your age keys secure
- Use strong Bitwarden master password
- Regularly rotate secrets
- Audit secret access in configurations
Create a deployment helper:
#!/usr/bin/env bash
# deploy.sh
set -e
echo "π Checking for secret changes..."
if sopswarden-sync; then
echo "β
Secrets synced"
fi
echo "π Deploying configuration..."
sudo nixos-rebuild switch --flake .#$(hostname) --impure# Create a custom deployment command
environment.systemPackages = [
(pkgs.writeShellScriptBin "nx-deploy" ''
# Auto-sync if rbw is unlocked
if rbw ls &>/dev/null; then
sopswarden-sync
else
echo "β οΈ rbw is locked, using existing secrets"
fi
# Deploy with impure flag
sudo nixos-rebuild switch --flake /etc/nixos#$(hostname) --impure
'')
];Error: Forgetting --impure flag
error: access to path '/run/secrets/...' is forbidden in pure eval mode
Solution: Always use --impure flag
Error: Secrets not yet synced
error: getting status of '/run/secrets/wifi-password': No such file or directory
Solution: Run sopswarden-sync first
Error: rbw not unlocked
Error: rbw is not authenticated. Please run 'rbw login' first.
Solution: rbw unlock
MIT License - see LICENSE for details.