Add Vaultwarden password manager role with PostgreSQL and SSO support

- Implement complete Vaultwarden deployment using Podman Quadlet
- PostgreSQL backend via Unix socket with 777 permissions
- Caddy reverse proxy with WebSocket support for live sync
- Control-node admin token hashing using argon2 (OWASP preset)
- Idempotent token hashing with deterministic salt generation
- Full Authentik SSO integration following official guide
- SMTP email configuration support (optional)
- Invitation-only user registration by default
- Comprehensive documentation with setup and troubleshooting guides

Technical Details:
- Container: vaultwarden/server:latest from Docker Hub
- Database: PostgreSQL via /var/run/postgresql socket
- Port: 8080 (localhost only, proxied by Caddy)
- Domain: vault.jnss.me
- Admin token: Hashed on control node with argon2id
- SSO: OpenID Connect with offline_access scope support

Role includes automatic argon2 installation on control node if needed.
This commit is contained in:
2025-12-21 16:13:25 +01:00
parent 89b43180fc
commit bfd6f22f0e
14 changed files with 1346 additions and 3 deletions

331
roles/vaultwarden/README.md Normal file
View File

@@ -0,0 +1,331 @@
# Vaultwarden Password Manager Role
Self-contained Vaultwarden (Bitwarden-compatible) password manager deployment using Podman and PostgreSQL.
## Overview
This role deploys Vaultwarden as a Podman Quadlet container with:
- **PostgreSQL backend** via Unix socket (777 permissions)
- **Caddy reverse proxy** with HTTPS and WebSocket support
- **SSO integration** ready (Authentik OpenID Connect)
- **SMTP support** for email notifications (optional)
- **Admin panel** for management
## Architecture
```
Internet → Caddy (HTTPS) → Vaultwarden Container → PostgreSQL (Unix socket)
/data volume
```
### Components
- **Container Image**: `vaultwarden/server:latest` (Docker Hub)
- **User**: System user `vaultwarden` (non-root)
- **Port**: 8080 (localhost only)
- **Domain**: `vault.jnss.me`
- **Database**: PostgreSQL via Unix socket at `/var/run/postgresql`
- **Data**: `/opt/vaultwarden/data`
## Dependencies
**Managed Hosts:**
- `postgresql` role (provides database and Unix socket)
- `caddy` role (provides reverse proxy)
**Control Node:**
- `argon2` command-line tool (automatically installed if not present)
- Used to hash the admin token securely on the control node
- Available in most package managers: `pacman -S argon2`, `apt install argon2`, etc.
## Configuration
### Required Variables
Must be defined in vault (e.g., `group_vars/homelab/vault.yml`):
```yaml
# Database password
vault_vaultwarden_db_password: "secure-database-password"
# Admin token (plain text - will be hashed automatically during deployment)
vault_vaultwarden_admin_token: "your-secure-admin-token"
# SMTP password (if using email)
vault_vaultwarden_smtp_password: "smtp-password" # optional
# SSO credentials (if using Authentik integration)
vault_vaultwarden_sso_client_id: "vaultwarden" # optional
vault_vaultwarden_sso_client_secret: "sso-secret" # optional
```
### Optional Variables
Override in `group_vars` or `host_vars`:
```yaml
# Domain
vaultwarden_domain: "vault.jnss.me"
# Container version
vaultwarden_version: "latest"
# Registration controls
vaultwarden_signups_allowed: false # Disable open registration
vaultwarden_invitations_allowed: true # Allow existing users to invite
# SMTP Configuration
vaultwarden_smtp_enabled: true
vaultwarden_smtp_host: "smtp.example.com"
vaultwarden_smtp_port: 587
vaultwarden_smtp_from: "vault@jnss.me"
vaultwarden_smtp_username: "smtp-user"
# SSO Configuration (Authentik)
vaultwarden_sso_enabled: true
vaultwarden_sso_authority: "https://auth.jnss.me"
```
## Usage
### Deploy Vaultwarden
```bash
# Full deployment
ansible-playbook rick-infra.yml --tags vaultwarden
# Or via site.yml
ansible-playbook site.yml --tags vaultwarden -l homelab
```
### Access Admin Panel
1. Set admin token in vault file (plain text):
```yaml
# Generate a secure token
vault_vaultwarden_admin_token: "$(openssl rand -base64 32)"
```
2. The role automatically hashes the token during deployment:
- Hashing occurs on the **control node** using `argon2` CLI
- Uses OWASP recommended settings (19MiB memory, 2 iterations, 1 thread)
- Idempotent: same token always produces the same hash
- The `argon2` package is automatically installed if not present
3. Access: `https://vault.jnss.me/admin` (use the plain text token from step 1)
### Configure SSO (Authentik Integration)
> ⚠️ **IMPORTANT: SSO Feature Status (as of December 2025)**
>
> SSO is currently **only available in `vaultwarden/server:testing` images**.
> The stable release (v1.34.3) does **NOT** include SSO functionality.
>
> **Current Deployment Status:**
> - This role is configured with SSO settings ready for when SSO reaches stable release
> - Using `vaultwarden_version: "latest"` (stable) - SSO will not appear
> - To test SSO now: Set `vaultwarden_version: "testing"` (not recommended for production)
> - To wait for stable: Keep current configuration, SSO will activate automatically when available
>
> **References:**
> - [Vaultwarden Wiki - SSO Documentation](https://github.com/dani-garcia/vaultwarden/wiki/Enabling-SSO-support-using-OpenId-Connect)
> - [Vaultwarden Testing Features](https://github.com/dani-garcia/vaultwarden/wiki#testing-features)
>
> **Decision:** This deployment keeps SSO configured but uses stable image until SSO feature is production-ready.
Following the [Authentik integration guide](https://integrations.goauthentik.io/security/vaultwarden/):
1. **In Authentik**: Create OAuth2/OpenID Provider
- **Name**: `Vaultwarden`
- **Client Type**: `Confidential`
- **Redirect URIs**: `https://vault.jnss.me/identity/connect/oidc-signin` (must be strict/exact match)
- **Scopes**: Under "Advanced protocol settings", ensure these scope mappings are selected:
- `authentik default OAuth Mapping: OpenID 'openid'`
- `authentik default OAuth Mapping: OpenID 'email'`
- `authentik default OAuth Mapping: OpenID 'profile'`
- `authentik default OAuth Mapping: OpenID 'offline_access'` ⚠️ **Required**
- **Access token validity**: Set to more than 5 minutes
- **Note the Client ID, Client Secret, and application slug** (from URL or provider settings)
2. **Update Vault Variables**:
```yaml
vault_vaultwarden_sso_client_id: "<client-id-from-authentik>"
vault_vaultwarden_sso_client_secret: "<client-secret-from-authentik>"
```
3. **Enable SSO and set authority** in `group_vars/homelab/main.yml`:
```yaml
vaultwarden_sso_enabled: true
# Replace 'vaultwarden' with your actual application slug
vaultwarden_sso_authority: "https://auth.jnss.me/application/o/vaultwarden/"
```
4. **Optional: SSO-Only Mode** (disable password login):
```yaml
vaultwarden_sso_only: true # Requires SSO, disables email+password
```
5. **Redeploy**:
```bash
ansible-playbook rick-infra.yml --tags vaultwarden
```
6. **Test**: Log out, enter a verified email on login page, click "Use single sign-on"
- **Note**: With `vaultwarden_version: "latest"`, SSO button will not appear (feature not in stable yet)
## Security Considerations
### Database Access
- Uses PostgreSQL Unix socket with **777 permissions**
- Security maintained via password authentication (scram-sha-256)
- See: `docs/socket-permissions-architecture.md`
### Admin Token
- **Never commit plain admin token to git**
- Use Ansible Vault for `vault_vaultwarden_admin_token`
- Rotate periodically via admin panel
### User Registration
- Default: **Disabled** (`vaultwarden_signups_allowed: false`)
- Users must be invited by existing users or created via admin panel
- Prevents unauthorized account creation
## Maintenance
### Backup
Backup the following:
```bash
# Database backup (via PostgreSQL role)
sudo -u postgres pg_dump vaultwarden > vaultwarden-backup.sql
# Data directory (attachments, icons, etc.)
tar -czf vaultwarden-data-backup.tar.gz /opt/vaultwarden/data
```
### Update Container
```bash
# Pull new image and restart
ansible-playbook rick-infra.yml --tags vaultwarden
```
### View Logs
```bash
# Service logs
journalctl -u vaultwarden -f
# Container logs
podman logs vaultwarden -f
```
### Restart Service
```bash
systemctl restart vaultwarden
```
## Troubleshooting
### Container won't start
```bash
# Check container status
systemctl status vaultwarden
# Check container directly
podman ps -a
podman logs vaultwarden
# Verify database connectivity
sudo -u vaultwarden psql -h /var/run/postgresql -U vaultwarden -d vaultwarden -c "SELECT 1;"
```
### Database connection errors
1. Verify PostgreSQL is running: `systemctl status postgresql`
2. Check socket exists: `ls -la /var/run/postgresql/.s.PGSQL.5432`
3. Verify socket permissions: Should be `srwxrwxrwx` (777)
4. Test connection as vaultwarden user (see above)
### Can't access admin panel
1. Verify admin token is set in vault file (plain text)
2. Check that the token was hashed successfully during deployment
3. Ensure you're using the plain text token to log in
4. Redeploy to regenerate hash if needed
### SSO not appearing / not working
**Most Common Issue: Using Stable Image**
SSO is only available in testing images. Check your deployment:
```bash
# Check current image version
podman inspect vaultwarden --format '{{.ImageName}}'
# Check API config for SSO support
curl -s http://127.0.0.1:8080/api/config | grep -o '"sso":"[^"]*"'
# Empty string "" = SSO not available in this image
# URL present = SSO is available
```
**If using `vaultwarden_version: "latest"`**: SSO will not appear (feature not in stable yet)
- **To test SSO**: Set `vaultwarden_version: "testing"` in role defaults or group/host vars
- **For production**: Wait for SSO to reach stable release (recommended)
**If using `vaultwarden_version: "testing"` and SSO still not working**:
1. Verify Authentik provider configuration:
- Check that `offline_access` scope mapping is added
- Verify redirect URI matches exactly: `https://vault.jnss.me/identity/connect/oidc-signin`
- Ensure access token validity is > 5 minutes
2. Verify SSO authority URL includes full path with slug:
- Should be: `https://auth.jnss.me/application/o/<your-slug>/`
- Not just: `https://auth.jnss.me`
3. Check client ID and secret in vault match Authentik
4. Verify all required scopes: `openid email profile offline_access`
5. Check Vaultwarden logs for SSO-related errors:
```bash
podman logs vaultwarden 2>&1 | grep -i sso
```
6. Test SSO flow: Log out, enter verified email, click "Use single sign-on"
## File Structure
```
roles/vaultwarden/
├── defaults/
│ └── main.yml # Default variables
├── handlers/
│ └── main.yml # Service restart handlers
├── meta/
│ └── main.yml # Role dependencies
├── tasks/
│ ├── main.yml # Main orchestration
│ ├── user.yml # User and directory setup
│ └── database.yml # PostgreSQL setup
├── templates/
│ ├── vaultwarden.container # Quadlet container definition
│ ├── vaultwarden.env.j2 # Environment configuration
│ └── vaultwarden.caddy.j2 # Caddy reverse proxy config
└── README.md # This file
```
## References
- [Vaultwarden Documentation](https://github.com/dani-garcia/vaultwarden/wiki)
- [PostgreSQL Backend Guide](https://github.com/dani-garcia/vaultwarden/wiki/Using-the-PostgreSQL-Backend)
- [SSO Configuration](https://github.com/dani-garcia/vaultwarden/wiki/Enabling-SSO-support-using-OpenId-Connect)
- [Socket Permissions Architecture](../../docs/socket-permissions-architecture.md)
## License
MIT

View File

@@ -0,0 +1,171 @@
# Vaultwarden Role - Required Vault Variables
This document lists all vault-encrypted variables required by the Vaultwarden role.
## Required Variables
These variables **must** be defined in your vault file (e.g., `group_vars/homelab/vault.yml`):
### Database Credentials
```yaml
# PostgreSQL database password for vaultwarden user
vault_vaultwarden_db_password: "your-secure-database-password-here"
```
**Generation**:
```bash
openssl rand -base64 32
```
### Admin Panel Access
```yaml
# Plain text admin token (will be hashed during deployment)
vault_vaultwarden_admin_token: "your-secret-admin-token"
```
**Generation**:
```bash
# Generate a secure random token
openssl rand -base64 32
```
**Important**:
- Store as **plain text** in vault (the role will hash it automatically)
- Use the same token to access `/admin` panel
- The token is automatically hashed on the **control node** using argon2id
- Hashing uses OWASP recommended settings (m=19456, t=2, p=1)
- Hashing is **idempotent**: same token always produces same hash
- The `argon2` package is automatically installed if not present on control node
- Never commit the vault file unencrypted to git
## Optional Variables
These variables are only needed if you enable specific features:
### SMTP Configuration
Required if `vaultwarden_smtp_enabled: true`:
```yaml
# SMTP password for sending emails
vault_vaultwarden_smtp_password: "smtp-password-here"
```
### SSO Integration (Authentik)
> ⚠️ **SSO Feature Status (December 2025)**
>
> SSO is only available in `vaultwarden/server:testing` images (not in stable yet).
> This role is configured with SSO ready for when it reaches stable release.
>
> Current deployment uses `vaultwarden_version: "latest"` (stable) - SSO credentials
> below are configured but SSO will not appear until feature reaches stable.
Required if `vaultwarden_sso_enabled: true`:
```yaml
# OAuth2 Client ID from Authentik
vault_vaultwarden_sso_client_id: "your-client-id-here"
# OAuth2 Client Secret from Authentik
vault_vaultwarden_sso_client_secret: "your-client-secret-here"
```
**Setup** (following [Authentik integration guide](https://integrations.goauthentik.io/security/vaultwarden/)):
1. Create OAuth2/OpenID Provider in Authentik:
- Redirect URI: `https://vault.jnss.me/identity/connect/oidc-signin` (exact match)
- Add scope mappings: `openid`, `email`, `profile`, `offline_access` (required)
- Access token validity: > 5 minutes
- Note the **application slug** from the provider URL
2. Copy Client ID and Secret from Authentik
3. Add credentials to vault file
4. Set the SSO authority URL in role configuration:
```yaml
vaultwarden_sso_enabled: true
vaultwarden_sso_authority: "https://auth.jnss.me/application/o/<your-slug>/"
```
5. Deploy the role
6. **Wait for SSO to reach stable**, or use `vaultwarden_version: "testing"` to test now
**Important**:
- The SSO authority must include the full path with application slug
- The `offline_access` scope mapping is **required** for Vaultwarden SSO
- Access token must be valid for more than 5 minutes
- SSO is configured and ready but will activate when stable release includes it
## Example Vault File
```yaml
---
# group_vars/homelab/vault.yml (encrypted with ansible-vault)
# Vaultwarden - Database
vault_vaultwarden_db_password: "xK9mP2nR5tY8wQ3vZ7cB6sA4dF1gH0jL"
# Vaultwarden - Admin Panel (plain text, will be hashed automatically)
vault_vaultwarden_admin_token: "MySecureAdminToken123!"
# Vaultwarden - SMTP (optional)
vault_vaultwarden_smtp_password: "smtp-app-password-here"
# Vaultwarden - SSO (optional)
vault_vaultwarden_sso_client_id: "vaultwarden"
vault_vaultwarden_sso_client_secret: "sso-secret-from-authentik"
```
## Vault File Management
### Encrypt Vault File
```bash
ansible-vault encrypt group_vars/homelab/vault.yml
```
### Edit Vault File
```bash
ansible-vault edit group_vars/homelab/vault.yml
```
### Decrypt Vault File (temporary)
```bash
ansible-vault decrypt group_vars/homelab/vault.yml
# Make changes
ansible-vault encrypt group_vars/homelab/vault.yml
```
## Security Best Practices
1. **Never commit unencrypted vault files**
2. **Use strong passwords** (32+ characters for database, admin token)
3. **Rotate credentials periodically** (especially admin token)
4. **Limit vault password access** (use password manager)
5. **Use separate passwords** for different services
6. **Back up vault password** (secure location, not in git)
## Verifying Variables
Test if variables are properly loaded:
```bash
ansible -m debug -a "var=vault_vaultwarden_db_password" homelab --ask-vault-pass
```
## Troubleshooting
### Variable not found error
- Ensure vault file is in correct location: `group_vars/homelab/vault.yml`
- Verify file is encrypted: `file group_vars/homelab/vault.yml`
- Check variable name matches exactly (case-sensitive)
- Provide vault password with `--ask-vault-pass`
### Admin token not working
- Verify the plain text token in vault matches what you're entering
- Check for extra whitespace in vault file
- Ensure the token was hashed successfully during deployment (check ansible output)
- Try redeploying the role to regenerate the hash

View File

@@ -0,0 +1,109 @@
---
# =================================================================
# Vaultwarden Password Manager Role - Default Variables
# =================================================================
# Self-contained Vaultwarden deployment with Podman and PostgreSQL
# =================================================================
# Service Configuration
# =================================================================
# Service user and directories
vaultwarden_user: vaultwarden
vaultwarden_group: vaultwarden
vaultwarden_home: /opt/vaultwarden
vaultwarden_data_dir: "{{ vaultwarden_home }}/data"
# Container configuration
# NOTE: SSO feature is only available in "testing" tag (as of Dec 2025)
# Using "latest" (stable) means SSO will not appear even if configured
# SSO settings below are configured and ready for when feature reaches stable
vaultwarden_version: "latest"
vaultwarden_image: "vaultwarden/server"
# Service management
vaultwarden_service_enabled: true
vaultwarden_service_state: "started"
# =================================================================
# Database Configuration (Self-managed)
# =================================================================
vaultwarden_db_name: "vaultwarden"
vaultwarden_db_user: "vaultwarden"
vaultwarden_db_password: "{{ vault_vaultwarden_db_password }}"
# =================================================================
# Network Configuration
# =================================================================
vaultwarden_domain: "vault.jnss.me"
vaultwarden_http_port: 8080
# =================================================================
# Vaultwarden Core Configuration
# =================================================================
# Admin panel access token (plain text, will be hashed during deployment)
vaultwarden_admin_token_plain: "{{ vault_vaultwarden_admin_token }}"
# Registration and invitation controls
vaultwarden_signups_allowed: false # Disable open registration
vaultwarden_invitations_allowed: true # Allow existing users to invite
vaultwarden_show_password_hint: false # Don't show password hints
# WebSocket support for live sync
vaultwarden_websocket_enabled: true
# =================================================================
# Email Configuration (Optional)
# =================================================================
vaultwarden_smtp_enabled: true
vaultwarden_smtp_host: "smtp.titan.email"
vaultwarden_smtp_port: 587
vaultwarden_smtp_from: "hello@jnss.me"
vaultwarden_smtp_username: "hello@jnss.me"
vaultwarden_smtp_password: "{{ vault_vaultwarden_smtp_password | default('') }}"
vaultwarden_smtp_security: "starttls" # Options: starttls, force_tls, off
# =================================================================
# SSO Configuration (Optional - Authentik Integration)
# =================================================================
vaultwarden_sso_enabled: false
# SSO Provider Configuration (Authentik)
vaultwarden_sso_client_id: "{{ vault_vaultwarden_sso_client_id | default('') }}"
vaultwarden_sso_client_secret: "{{ vault_vaultwarden_sso_client_secret | default('') }}"
# Authority must include full path with application slug
vaultwarden_sso_authority: "https://{{ authentik_domain }}/application/o/vaultwarden/"
vaultwarden_sso_scopes: "openid email profile offline_access"
# Additional SSO settings (per Authentik integration guide)
vaultwarden_sso_only: false # Set to true to disable email+password login and require SSO
vaultwarden_sso_signups_match_email: true # Match first SSO login to existing account by email
vaultwarden_sso_allow_unknown_email_verification: false
vaultwarden_sso_client_cache_expiration: 0
# Domain whitelist for SSO signups (comma-separated domains, empty = all)
vaultwarden_sso_signups_domains_whitelist: ""
# =================================================================
# Caddy Integration
# =================================================================
# Caddy configuration (assumes caddy role provides these variables)
caddy_sites_enabled_dir: "/etc/caddy/sites-enabled"
caddy_log_dir: "/var/log/caddy"
caddy_user: "caddy"
# =================================================================
# Infrastructure Dependencies (Read-only)
# =================================================================
# PostgreSQL socket configuration (managed by postgresql role)
postgresql_unix_socket_directories: "/var/run/postgresql"
postgresql_client_group: "postgres-clients"
postgresql_port: 5432
postgresql_unix_socket_enabled: true

View File

@@ -0,0 +1,16 @@
---
# Vaultwarden Password Manager - Service Handlers
- name: reload systemd
systemd:
daemon_reload: true
- name: restart vaultwarden
systemd:
name: vaultwarden
state: restarted
- name: reload caddy
systemd:
name: caddy
state: reloaded

View File

@@ -0,0 +1,25 @@
---
# Vaultwarden Password Manager - Role Metadata
dependencies:
- role: postgresql
- role: caddy
galaxy_info:
author: Rick Infrastructure Team
description: Vaultwarden password manager deployment with PostgreSQL and Caddy
license: MIT
min_ansible_version: "2.14"
platforms:
- name: ArchLinux
versions:
- all
galaxy_tags:
- vaultwarden
- bitwarden
- password-manager
- security
- postgresql
- podman

View File

@@ -0,0 +1,51 @@
---
# Database setup for Vaultwarden - PostgreSQL via Unix Socket
- name: Test PostgreSQL socket connectivity
postgresql_ping:
login_unix_socket: "{{ postgresql_unix_socket_directories }}"
login_user: "{{ vaultwarden_user }}"
become: true
become_user: "{{ vaultwarden_user }}"
- name: Create Vaultwarden database user via socket
postgresql_user:
name: "{{ vaultwarden_db_user }}"
password: "{{ vaultwarden_db_password }}"
login_unix_socket: "{{ postgresql_unix_socket_directories }}"
login_user: postgres
become: true
become_user: postgres
- name: Create Vaultwarden database via socket
postgresql_db:
name: "{{ vaultwarden_db_name }}"
owner: "{{ vaultwarden_db_user }}"
encoding: UTF8
template: template0
login_unix_socket: "{{ postgresql_unix_socket_directories }}"
login_user: postgres
become: true
become_user: postgres
- name: Grant Vaultwarden database privileges
postgresql_privs:
db: "{{ vaultwarden_db_name }}"
privs: ALL
type: database
role: "{{ vaultwarden_db_user }}"
login_unix_socket: "{{ postgresql_unix_socket_directories }}"
login_user: postgres
become: true
become_user: postgres
- name: Display database setup status
debug:
msg: |
Vaultwarden database setup complete!
Database: {{ vaultwarden_db_name }}
User: {{ vaultwarden_db_user }}
Connection: Unix socket ({{ postgresql_unix_socket_directories }})
Ready for Vaultwarden container deployment

View File

@@ -0,0 +1,57 @@
---
# Hash admin token on Ansible control node using argon2
- name: Check if argon2 is available on control node
command: which argon2
register: argon2_check
delegate_to: localhost
become: false
changed_when: false
failed_when: false
- name: Install argon2 on control node if not present
package:
name: argon2
state: present
delegate_to: localhost
become: false
when: argon2_check.rc != 0
run_once: true
- name: Generate deterministic salt from domain
set_fact:
vaultwarden_salt_source: "{{ vaultwarden_domain }}-{{ vaultwarden_sso_authority }}"
no_log: true
- name: Create base64-encoded salt for argon2
shell: echo -n "{{ vaultwarden_salt_source }}" | sha256sum | cut -d' ' -f1 | head -c 22
register: admin_token_salt
delegate_to: localhost
become: false
changed_when: false
no_log: true
- name: Hash admin token using argon2 (OWASP preset)
shell: echo -n "{{ vaultwarden_admin_token_plain }}" | argon2 "{{ admin_token_salt.stdout }}" -id -t 2 -k 19456 -p 1 -e
register: admin_token_hash_result
delegate_to: localhost
become: false
changed_when: false
no_log: true
- name: Extract hashed admin token
set_fact:
vaultwarden_admin_token_hashed: "{{ admin_token_hash_result.stdout | trim }}"
no_log: true
- name: Display token hash status
debug:
msg: |
Admin token hashed successfully on control node
Hash algorithm: argon2id
Preset: OWASP (m=19456, t=2, p=1)
Format: PHC string (Vaultwarden compatible)
Idempotent: Same token always produces same hash
The hashed token will be used in the environment configuration

View File

@@ -0,0 +1,108 @@
---
# Vaultwarden Password Manager Role - Main Tasks
# Self-contained deployment with Podman and Unix sockets
- name: Setup vaultwarden user and directories
include_tasks: user.yml
tags: [user, setup]
- name: Setup database access and permissions
include_tasks: database.yml
tags: [database, setup]
- name: Pull vaultwarden container image
containers.podman.podman_image:
name: "{{ vaultwarden_image }}:{{ vaultwarden_version }}"
state: present
tags: [containers, image-pull]
- name: Hash admin token on host
include_tasks: hash_admin_token.yml
tags: [config, admin-token]
- name: Deploy environment configuration
template:
src: vaultwarden.env.j2
dest: "{{ vaultwarden_home }}/.env"
owner: "{{ vaultwarden_user }}"
group: "{{ vaultwarden_group }}"
mode: '0600'
backup: true
notify:
- restart vaultwarden
tags: [config]
- name: Create Quadlet systemd directory
file:
path: /etc/containers/systemd
state: directory
mode: '0755'
- name: Deploy Quadlet container file
template:
src: vaultwarden.container
dest: /etc/containers/systemd/vaultwarden.container
mode: '0644'
notify:
- reload systemd
- restart vaultwarden
tags: [containers, deployment]
- name: Deploy Caddy configuration
template:
src: vaultwarden.caddy.j2
dest: "{{ caddy_sites_enabled_dir }}/vaultwarden.caddy"
owner: root
group: "{{ caddy_user }}"
mode: '0644'
backup: true
notify: reload caddy
tags: [caddy, reverse-proxy]
- name: Ensure PostgreSQL is running
systemd:
name: postgresql
state: started
- name: Wait for PostgreSQL socket to be ready
wait_for:
path: "{{ postgresql_unix_socket_directories }}/.s.PGSQL.{{ postgresql_port }}"
timeout: 30
when: postgresql_unix_socket_enabled
- name: Enable and start Vaultwarden service (system scope)
systemd:
name: vaultwarden
enabled: "{{ vaultwarden_service_enabled }}"
state: "{{ vaultwarden_service_state }}"
daemon_reload: true
tags: [containers, service]
- name: Wait for Vaultwarden to be ready
uri:
url: "http://127.0.0.1:{{ vaultwarden_http_port }}/"
method: GET
status_code: [200, 302]
timeout: 30
retries: 10
delay: 15
register: vaultwarden_health_check
tags: [verification, health-check]
- name: Display Vaultwarden deployment status
debug:
msg: |
Vaultwarden Password Manager deployed successfully!
Domain: {{ vaultwarden_domain }}
Database: {{ vaultwarden_db_name }} (Unix socket)
Container: {{ vaultwarden_image }}:{{ vaultwarden_version }}
Admin Panel: https://{{ vaultwarden_domain }}/admin
Ready for user registration and password management!
Next Steps:
- Access https://{{ vaultwarden_domain }}/admin with your admin token
- Configure additional settings (SMTP, SSO, etc.)
- Invite users or create accounts
tags: [verification]

View File

@@ -0,0 +1,28 @@
---
# Vaultwarden User Management - Service-Specific User Setup
- name: Create vaultwarden group
group:
name: "{{ vaultwarden_group }}"
system: true
- name: Create vaultwarden user
user:
name: "{{ vaultwarden_user }}"
group: "{{ vaultwarden_group }}"
system: true
shell: /bin/bash
home: "{{ vaultwarden_home }}"
create_home: true
comment: "Vaultwarden password manager service"
- name: Create vaultwarden directories
file:
path: "{{ item }}"
state: directory
owner: "{{ vaultwarden_user }}"
group: "{{ vaultwarden_group }}"
mode: '0755'
loop:
- "{{ vaultwarden_home }}"
- "{{ vaultwarden_data_dir }}"

View File

@@ -0,0 +1,35 @@
# Vaultwarden Password Manager
{{ vaultwarden_domain }} {
# Notifications endpoint (WebSocket for live sync)
@websocket {
path /notifications/hub
}
reverse_proxy @websocket http://127.0.0.1:{{ vaultwarden_http_port }} {
header_up Upgrade {http.request.header.Upgrade}
header_up Connection {http.request.header.Connection}
}
# Regular HTTP traffic
reverse_proxy http://127.0.0.1:{{ vaultwarden_http_port }} {
header_up Host {host}
header_up X-Real-IP {remote_host}
header_up X-Forwarded-Proto https
header_up X-Forwarded-For {remote_host}
}
# Security headers
header {
X-Frame-Options SAMEORIGIN
X-Content-Type-Options nosniff
X-XSS-Protection "1; mode=block"
Referrer-Policy strict-origin-when-cross-origin
Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"
}
# Logging
log {
output file {{ caddy_log_dir }}/vaultwarden.log
level INFO
format json
}
}

View File

@@ -0,0 +1,26 @@
[Unit]
Description=Vaultwarden Password Manager Container
After=network-online.target postgresql.service
Wants=network-online.target
[Container]
ContainerName=vaultwarden
Image={{ vaultwarden_image }}:{{ vaultwarden_version }}
EnvironmentFile={{ vaultwarden_home }}/.env
# Volume mounts
# Application data (includes database, attachments, sends, icons, etc.)
Volume={{ vaultwarden_data_dir }}:/data:Z
# Infrastructure socket (PostgreSQL access with 777 permissions on host)
Volume={{ postgresql_unix_socket_directories }}:{{ postgresql_unix_socket_directories }}:Z
# Expose HTTP port to localhost only (Caddy will reverse proxy)
PublishPort=127.0.0.1:{{ vaultwarden_http_port }}:80
[Service]
Restart=always
TimeoutStartSec=300
[Install]
WantedBy=multi-user.target

View File

@@ -0,0 +1,73 @@
# Vaultwarden Environment Configuration
# Generated by Ansible - DO NOT EDIT MANUALLY
# =================================================================
# Database Configuration (PostgreSQL via Unix Socket)
# =================================================================
DATABASE_URL=postgresql://{{ vaultwarden_db_user }}:{{ vaultwarden_db_password }}@/{{ vaultwarden_db_name }}?host={{ postgresql_unix_socket_directories }}
# =================================================================
# Domain Configuration
# =================================================================
DOMAIN=https://{{ vaultwarden_domain }}
# =================================================================
# Admin Configuration
# =================================================================
ADMIN_TOKEN={{ vaultwarden_admin_token_hashed }}
# =================================================================
# Registration and Invitation Controls
# =================================================================
SIGNUPS_ALLOWED={{ vaultwarden_signups_allowed | lower }}
INVITATIONS_ALLOWED={{ vaultwarden_invitations_allowed | lower }}
SHOW_PASSWORD_HINT={{ vaultwarden_show_password_hint | lower }}
# =================================================================
# WebSocket Configuration (for live sync)
# =================================================================
WEBSOCKET_ENABLED={{ vaultwarden_websocket_enabled | lower }}
# =================================================================
# SMTP Configuration (Optional)
# =================================================================
{% if vaultwarden_smtp_enabled %}
SMTP_HOST={{ vaultwarden_smtp_host }}
SMTP_PORT={{ vaultwarden_smtp_port }}
SMTP_FROM={{ vaultwarden_smtp_from }}
SMTP_SECURITY={{ vaultwarden_smtp_security }}
{% if vaultwarden_smtp_username %}
SMTP_USERNAME={{ vaultwarden_smtp_username }}
SMTP_PASSWORD={{ vaultwarden_smtp_password }}
{% endif %}
{% endif %}
# =================================================================
# SSO Configuration (Optional - Authentik Integration)
# =================================================================
{% if vaultwarden_sso_enabled %}
SSO_ENABLED=true
SSO_ONLY={{ vaultwarden_sso_only | lower }}
SSO_CLIENT_ID={{ vaultwarden_sso_client_id }}
SSO_CLIENT_SECRET={{ vaultwarden_sso_client_secret }}
SSO_AUTHORITY={{ vaultwarden_sso_authority }}
SSO_SCOPES="{{ vaultwarden_sso_scopes }}"
SSO_SIGNUPS_MATCH_EMAIL={{ vaultwarden_sso_signups_match_email | lower }}
SSO_ALLOW_UNKNOWN_EMAIL_VERIFICATION={{ vaultwarden_sso_allow_unknown_email_verification | lower }}
SSO_CLIENT_CACHE_EXPIRATION={{ vaultwarden_sso_client_cache_expiration }}
{% if vaultwarden_sso_signups_domains_whitelist %}
SSO_SIGNUPS_DOMAINS_WHITELIST={{ vaultwarden_sso_signups_domains_whitelist }}
{% endif %}
{% endif %}
# =================================================================
# Security and Performance
# =================================================================
# Disable user registration via email (use admin panel or invitations)
SIGNUPS_VERIFY=false
# Log level (trace, debug, info, warn, error, off)
LOG_LEVEL=info
# Rocket configuration
ROCKET_WORKERS=10