Skip to content

[BUG] SWAG’s Vaultwarden subdomain.conf.sample does not remove X-Frame-Options and Content-Security-Policy for 2FA API calls #551

Open
@H2OKing89

Description

@H2OKing89

Is there an existing issue for this?

  • I have searched the existing issues

Current Behavior

Current Behavior

Vaultwarden’s diagnostics page reports that X-Frame-Options and
Content-Security-Policy are present when they should not be for 2FA-related API calls.

Issue Details

  • These headers persist on /api/two-factor despite using proxy_hide_header.
  • This causes iframe-based authentication methods (such as Duo 2FA) to fail.
  • The issue is consistently reported in the Vaultwarden diagnostics page.

Example Output from Vaultwarden Diagnostics:

2FA Connector calls:
Header: 'x-frame-options' is present while it should not
Header: 'content-security-policy' is present while it should not



### Expected Behavior

## Expected Behavior

The `vaultwarden.subdomain.conf.sample` file should properly handle the removal of  
`X-Frame-Options` and `Content-Security-Policy` for all **2FA-related API calls**  
to ensure iframe-based authentication (e.g., Duo 2FA) functions correctly.

### Why This Matters
- Some authentication providers require iframes to function properly.
- These headers prevent **iframe-based 2FA login methods from working**.
- Vaultwarden’s **diagnostics page** confirms these headers **should not be present**.
- The issue persists **even after applying `proxy_hide_header` on known 2FA endpoints**.



### Steps To Reproduce

1. Set up Vaultwarden behind SWAG using the default `vaultwarden.subdomain.conf.sample`.
2. Enable 2FA (Duo or other iframe-based authentication methods).
3. Try to authenticate using 2FA.
4. Check Vaultwarden’s diagnostics page:  
   - **URL:** [https://vaultwarden.domain.com/admin/diagnostics](https://vaultwarden.domain.com/admin/diagnostics)  
   - **It shows:**
   
   ```plaintext
   2FA Connector calls:
   Header: 'x-frame-options' is present while it should not
   Header: 'content-security-policy' is present while it should not
  1. Run curl to verify headers:

    curl -I https://vaultwarden.domain.com/api/two-factor
  2. Expected Behavior:

    • X-Frame-Options and Content-Security-Policy should be removed.
  3. Actual Behavior:

    • The headers persist, breaking 2FA authentication.

Your environment (Generated via diagnostics page)

  • Vaultwarden version: v1.33.2
  • Web-vault version: v2025.1.1
  • OS/Arch: linux/x86_64
  • Running within a container: true (Base: Debian)
  • Database type: SQLite
  • Database version: 3.48.0
  • Environment settings overridden!: true
  • Uses a reverse proxy: true
  • IP Header check: true (X-Real-IP)
  • Internet access: true
  • Internet access via a proxy: false
  • DNS Check: true
  • Browser/Server Time Check: true
  • Server/NTP Time Check: true
  • Domain Configuration Check: true
  • HTTPS Check: true
  • Websocket Check: true
  • HTTP Response Checks: false

Config & Details (Generated via diagnostics page)

Show Config & Details

Environment settings which are overridden: SIGNUPS_ALLOWED, INVITATIONS_ALLOWED, ADMIN_TOKEN

Failed HTTP Checks:

2FA Connector calls:
Header: 'x-frame-options' is present while it should not
Header: 'content-security-policy' is present while it should not

Config:

{
  "_duo_akey": "***",
  "_enable_duo": true,
  "_enable_email_2fa": false,
  "_enable_smtp": true,
  "_enable_yubico": true,
  "_icon_service_csp": "",
  "_icon_service_url": "",
  "_ip_header_enabled": true,
  "_max_note_size": 10000,
  "_smtp_img_src": "***:",
  "admin_ratelimit_max_burst": 3,
  "admin_ratelimit_seconds": 300,
  "admin_session_lifetime": 20,
  "admin_token": "***",
  "allowed_connect_src": "",
  "allowed_iframe_ancestors": "",
  "attachments_folder": "/attachments",
  "auth_request_purge_schedule": "30 * * * * *",
  "authenticator_disable_time_drift": false,
  "data_folder": "data",
  "database_conn_init": "",
  "database_max_conns": 10,
  "database_timeout": 30,
  "database_url": "***************",
  "db_connection_retries": 15,
  "disable_2fa_remember": false,
  "disable_admin_token": false,
  "disable_icon_download": false,
  "domain": "*****://**************************",
  "domain_origin": "*****://**************************",
  "domain_path": "",
  "domain_set": true,
  "duo_context_purge_schedule": "30 * * * * *",
  "duo_host": "api-22fc8a48.duosecurity.com",
  "duo_ikey": "DIXORW9SNE6071KVE7NL",
  "duo_skey": "***",
  "duo_use_iframe": false,
  "email_2fa_auto_fallback": false,
  "email_2fa_enforce_on_verified_invite": false,
  "email_attempts_limit": 3,
  "email_change_allowed": true,
  "email_expiration_time": 600,
  "email_token_size": 6,
  "emergency_access_allowed": true,
  "emergency_notification_reminder_schedule": "0 3 * * * *",
  "emergency_request_timeout_schedule": "0 7 * * * *",
  "enable_db_wal": true,
  "enable_websocket": true,
  "enforce_single_org_with_reset_pw_policy": false,
  "event_cleanup_schedule": "0 10 0 * * *",
  "events_days_retain": null,
  "experimental_client_feature_flags": "fido2-vault-credentials",
  "extended_logging": true,
  "helo_name": null,
  "hibp_api_key": null,
  "http_request_block_non_global_ips": true,
  "http_request_block_regex": null,
  "icon_blacklist_non_global_ips": true,
  "icon_blacklist_regex": null,
  "icon_cache_folder": "/icon_cache",
  "icon_cache_negttl": 259200,
  "icon_cache_ttl": 2592000,
  "icon_download_timeout": 10,
  "icon_redirect_code": 302,
  "icon_service": "internal",
  "incomplete_2fa_schedule": "30 * * * * *",
  "incomplete_2fa_time_limit": 3,
  "increase_note_size_limit": false,
  "invitation_expiration_hours": 120,
  "invitation_org_name": "King Paging",
  "invitations_allowed": true,
  "ip_header": "X-Real-IP",
  "job_poll_interval_ms": 30000,
  "log_file": "/logs/vaultwarden.log",
  "log_level": "info",
  "log_timestamp_format": "%Y-%m-%d %H:%M:%S.%3f",
  "login_ratelimit_max_burst": 10,
  "login_ratelimit_seconds": 60,
  "org_attachment_limit": 5242880,
  "org_creation_users": "",
  "org_events_enabled": false,
  "org_groups_enabled": false,
  "password_hints_allowed": true,
  "password_iterations": 600000,
  "push_enabled": false,
  "push_identity_uri": "https://identity.bitwarden.com",
  "push_installation_id": "***",
  "push_installation_key": "***",
  "push_relay_uri": "https://push.bitwarden.com",
  "reload_templates": false,
  "require_device_email": true,
  "rsa_key_filename": "data/rsa_key",
  "send_purge_schedule": "0 5 * * * *",
  "sendmail_command": null,
  "sends_allowed": true,
  "sends_folder": "data/sends",
  "show_password_hint": false,
  "signups_allowed": false,
  "signups_domains_whitelist": "",
  "signups_verify": true,
  "signups_verify_resend_limit": 6,
  "signups_verify_resend_time": 3600,
  "smtp_accept_invalid_certs": false,
  "smtp_accept_invalid_hostnames": false,
  "smtp_auth_mechanism": null,
  "smtp_debug": false,
  "smtp_embed_images": true,
  "smtp_explicit_tls": null,
  "smtp_from": "*************************",
  "smtp_from_name": "King's Vaultwarden",
  "smtp_host": "**************",
  "smtp_password": "***",
  "smtp_port": 465,
  "smtp_security": "force_tls",
  "smtp_ssl": null,
  "smtp_timeout": 15,
  "smtp_username": "*************************",
  "templates_folder": "data/templates",
  "tmp_folder": "data/tmp",
  "trash_auto_delete_days": 30,
  "trash_purge_schedule": "0 5 0 * * *",
  "use_sendmail": false,
  "use_syslog": false,
  "user_attachment_limit": 1048576,
  "user_send_limit": 524288,
  "web_vault_enabled": true,
  "web_vault_folder": "web-vault/",
  "yubico_client_id": "72859",
  "yubico_secret_key": "***",
  "yubico_server": null
}

Environment

OS: unraid 7.0.1
How Docker service was installed: Unraid Community Apps
SWAG Version: 3.3.0
Vaultwarden Version: v1.33.2
Cloudflare Proxy: Yes

CPU architecture

x86-64

Docker creation

docker run \
  -d \
  --name='swag' \
  --net='proxynet' \
  --pids-limit 2048 \
  -e TZ="America/Chicago" \
  -e HOST_OS="Unraid" \
  -e HOST_HOSTNAME="UNRAID" \
  -e HOST_CONTAINERNAME="swag" \
  -e 'URL'='kingpaging.com' \
  -e 'VALIDATION'='dns' \
  -e 'SUBDOMAINS'='wildcard' \
  -e 'CERTPROVIDER'='' \
  -e 'DNSPLUGIN'='cloudflare' \
  -e 'PROPAGATION'='' \
  -e 'EMAIL'='[REDACTED]' \
  -e 'ONLY_SUBDOMAINS'='false' \
  -e 'EXTRA_DOMAINS'='' \
  -e 'STAGING'='false' \
  -e 'DISABLE_F2B'='' \
  -e 'SWAG_AUTORELOAD'='' \
  -e 'SWAG_AUTORELOAD_WATCHLIST'='' \
  -e 'DOCKER_MODS'='linuxserver/mods:swag-maxmind|linuxserver/mods:swag-cloudflare-real-ip' \
  -e 'MAXMINDDB_USER_ID'='[REDACTED]' \
  -e 'PUID'='99' \
  -e 'PGID'='100' \
  -e 'UMASK'='022' \
  -e 'MAXMINDDB_LICENSE_KEY'='[REDACTED]' \
  -p '443:443/tcp' \
  -p '80:80/tcp' \
  -p '8089:8089/tcp' \
  -p '8003:8003/tcp' \
  -v '/mnt/cache/appdata/swag':'/config':'rw' \
  --cap-add=NET_ADMIN 'lscr.io/linuxserver/swag'

Container logs

[mod-init] Running Docker Modification Logic
[mod-init] Adding linuxserver/mods:swag-maxmind to container
[mod-init] Downloading linuxserver/mods:swag-maxmind from lscr.io
[mod-init] Installing linuxserver/mods:swag-maxmind
[mod-init] linuxserver/mods:swag-maxmind applied to container
[mod-init] Adding linuxserver/mods:swag-cloudflare-real-ip to container
[mod-init] Downloading linuxserver/mods:swag-cloudflare-real-ip from lscr.io
[mod-init] Installing linuxserver/mods:swag-cloudflare-real-ip
[mod-init] linuxserver/mods:swag-cloudflare-real-ip applied to container
[migrations] started
[migrations] 01-nginx-site-confs-default: skipped
[migrations] 02-swag-old-certbot-paths: skipped
[migrations] done
───────────────────────────────────────

      ██╗     ███████╗██╗ ██████╗
      ██║     ██╔════╝██║██╔═══██╗
      ██║     ███████╗██║██║   ██║
      ██║     ╚════██║██║██║   ██║
      ███████╗███████║██║╚██████╔╝
      ╚══════╝╚══════╝╚═╝ ╚═════╝

   Brought to you by linuxserver.io
───────────────────────────────────────

To support the app dev(s) visit:
Certbot: https://supporters.eff.org/donate/support-work-on-certbot

To support LSIO projects visit:
https://www.linuxserver.io/donate/

───────────────────────────────────────
GID/UID
───────────────────────────────────────

User UID:    99
User GID:    100
───────────────────────────────────────
Linuxserver.io version: 3.3.0-ls369
Build-date: 2025-03-11T17:21:22+00:00
───────────────────────────────────────
    
using keys found in /config/keys
Variables set:
PUID=99
PGID=100
TZ=America/Chicago
URL=[Redacted]
SUBDOMAINS=wildcard
EXTRA_DOMAINS=
ONLY_SUBDOMAINS=false
VALIDATION=dns
CERTPROVIDER=
DNSPLUGIN=cloudflare
EMAIL=[Redacted]@gmail.com
STAGING=false

Using Let's Encrypt as the cert provider
SUBDOMAINS entered, processing
Wildcard cert for [Redacted] will be requested
E-mail address entered: [Redacted]@gmail.com
dns validation via cloudflare plugin is selected
Certificate exists; parameters unchanged; starting nginx
The cert does not expire within the next day. Letting the cron script handle the renewal attempts overnight (2:08am).
**** adding libmaxminddb to package install list ****
[pkg-install-init] **** Installing all mod packages ****
fetch http://dl-cdn.alpinelinux.org/alpine/v3.21/main/x86_64/APKINDEX.tar.gz
fetch http://dl-cdn.alpinelinux.org/alpine/v3.21/community/x86_64/APKINDEX.tar.gz
(1/1) Installing libmaxminddb (1.9.1-r0)
Executing busybox-1.37.0-r12.trigger
OK: 182 MiB in 223 packages
Applying the maxmind mod...
Applied the maxmind mod
[custom-init] No custom files found, skipping...
[ls.io-init] done.
Server ready

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    • Status

      Issues

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions