From 0f95bdc9bbf7949ca8bf436c088d5e09ea2c6e82 Mon Sep 17 00:00:00 2001 From: BlackDex Date: Sun, 17 Jul 2022 16:21:03 +0200 Subject: [PATCH 1/2] Fix issue with CSP and icon redirects When using anything else but the `internal` icon service it would trigger an CSP block because the redirects were not allowed. This PR fixes #2623 by dynamically adding the needed CSP strings. This should also work with custom services. For Google i needed to add an extra check because that does a redirect it self to there gstatic.com domain. --- src/api/icons.rs | 24 +++--------------------- src/config.rs | 32 ++++++++++++++++++++++++++++++++ src/util.rs | 23 ++++++++++++----------- 3 files changed, 47 insertions(+), 32 deletions(-) diff --git a/src/api/icons.rs b/src/api/icons.rs index 6d4d3a5a..c343df14 100644 --- a/src/api/icons.rs +++ b/src/api/icons.rs @@ -30,10 +30,7 @@ use crate::{ pub fn routes() -> Vec { match CONFIG.icon_service().as_str() { "internal" => routes![icon_internal], - "bitwarden" => routes![icon_bitwarden], - "duckduckgo" => routes![icon_duckduckgo], - "google" => routes![icon_google], - _ => routes![icon_custom], + _ => routes![icon_external], } } @@ -100,23 +97,8 @@ async fn icon_redirect(domain: &str, template: &str) -> Option { } #[get("//icon.png")] -async fn icon_custom(domain: String) -> Option { - icon_redirect(&domain, &CONFIG.icon_service()).await -} - -#[get("//icon.png")] -async fn icon_bitwarden(domain: String) -> Option { - icon_redirect(&domain, "https://icons.bitwarden.net/{}/icon.png").await -} - -#[get("//icon.png")] -async fn icon_duckduckgo(domain: String) -> Option { - icon_redirect(&domain, "https://icons.duckduckgo.com/ip3/{}.ico").await -} - -#[get("//icon.png")] -async fn icon_google(domain: String) -> Option { - icon_redirect(&domain, "https://www.google.com/s2/favicons?domain={}&sz=32").await +async fn icon_external(domain: String) -> Option { + icon_redirect(&domain, &CONFIG._icon_service_url()).await } #[get("//icon.png")] diff --git a/src/config.rs b/src/config.rs index 8cc96f56..b8f3246b 100644 --- a/src/config.rs +++ b/src/config.rs @@ -463,6 +463,10 @@ make_config! { /// service is set, an icon request to Vaultwarden will return an HTTP redirect to the /// corresponding icon at the external service. icon_service: String, false, def, "internal".to_string(); + /// Internal + _icon_service_url: String, false, gen, |c| generate_icon_service_url(&c.icon_service); + /// Internal + _icon_service_csp: String, false, gen, |c| generate_icon_service_csp(&c.icon_service, &c._icon_service_url); /// Icon redirect code |> The HTTP status code to use for redirects to an external icon service. /// The supported codes are 301 (legacy permanent), 302 (legacy temporary), 307 (temporary), and 308 (permanent). /// Temporary redirects are useful while testing different icon services, but once a service @@ -748,6 +752,34 @@ fn extract_url_path(url: &str) -> String { } } +/// Generate the correct URL for the icon service. +/// This will be used within icons.rs to call the external icon service. +fn generate_icon_service_url(icon_service: &str) -> String { + match icon_service { + "internal" => "".to_string(), + "bitwarden" => "https://icons.bitwarden.net/{}/icon.png".to_string(), + "duckduckgo" => "https://icons.duckduckgo.com/ip3/{}.ico".to_string(), + "google" => "https://www.google.com/s2/favicons?domain={}&sz=32".to_string(), + _ => icon_service.to_string(), + } +} + +/// Generate the CSP string needed to allow redirected icon fetching +fn generate_icon_service_csp(icon_service: &str, icon_service_url: &str) -> String { + // We split on the first '{', since that is the variable delimiter for an icon service URL. + // Everything up until the first '{' should be fixed and can be used as an CSP string. + let csp_string = match icon_service_url.split_once('{') { + Some((c, _)) => c.to_string(), + None => "".to_string(), + }; + + // Because Google does a second redirect to there gstatic.com domain, we need to add an extra csp string. + match icon_service { + "google" => csp_string + " https://*.gstatic.com/favicon", + _ => csp_string, + } +} + /// Convert the old SMTP_SSL and SMTP_EXPLICIT_TLS options fn smtp_convert_deprecated_ssl_options(smtp_ssl: Option, smtp_explicit_tls: Option) -> String { if smtp_explicit_tls.is_some() || smtp_ssl.is_some() { diff --git a/src/util.rs b/src/util.rs index 9bf697ba..ca3667e1 100644 --- a/src/util.rs +++ b/src/util.rs @@ -38,18 +38,18 @@ impl Fairing for AppHeaders { let req_uri_path = req.uri().path(); - // Check if we are requesting an admin page, if so, allow unsafe-inline for scripts. - // TODO: In the future maybe we need to see if we can generate a sha256 hash or have no scripts inline at all. - let admin_path = format!("{}/admin", CONFIG.domain_path()); - let mut script_src = ""; - if req_uri_path.starts_with(admin_path.as_str()) { - script_src = " 'unsafe-inline'"; - } - // Do not send the Content-Security-Policy (CSP) Header and X-Frame-Options for the *-connector.html files. // This can cause issues when some MFA requests needs to open a popup or page within the clients like WebAuthn, or Duo. // This is the same behaviour as upstream Bitwarden. if !req_uri_path.ends_with("connector.html") { + // Check if we are requesting an admin page, if so, allow unsafe-inline for scripts. + // TODO: In the future maybe we need to see if we can generate a sha256 hash or have no scripts inline at all. + let admin_path = format!("{}/admin", CONFIG.domain_path()); + let mut script_src = ""; + if req_uri_path.starts_with(admin_path.as_str()) { + script_src = " 'unsafe-inline'"; + } + // # Frame Ancestors: // Chrome Web Store: https://chrome.google.com/webstore/detail/bitwarden-free-password-m/nngceckbapebfimnlniiiahkandclblb // Edge Add-ons: https://microsoftedge.microsoft.com/addons/detail/bitwarden-free-password/jbkfoedolllekgbhcbcoahefnbanhhlh?hl=en-US @@ -65,13 +65,14 @@ impl Fairing for AppHeaders { "default-src 'self'; \ script-src 'self'{script_src}; \ style-src 'self' 'unsafe-inline'; \ - img-src 'self' data: https://haveibeenpwned.com/ https://www.gravatar.com; \ + img-src 'self' data: https://haveibeenpwned.com/ https://www.gravatar.com {icon_service_csp}; \ child-src 'self' https://*.duosecurity.com https://*.duofederal.com; \ frame-src 'self' https://*.duosecurity.com https://*.duofederal.com; \ connect-src 'self' https://api.pwnedpasswords.com/range/ https://2fa.directory/api/ https://app.simplelogin.io/api/ https://app.anonaddy.com/api/ https://relay.firefox.com/api/; \ object-src 'self' blob:; \ - frame-ancestors 'self' chrome-extension://nngceckbapebfimnlniiiahkandclblb chrome-extension://jbkfoedolllekgbhcbcoahefnbanhhlh moz-extension://* {};", - CONFIG.allowed_iframe_ancestors() + frame-ancestors 'self' chrome-extension://nngceckbapebfimnlniiiahkandclblb chrome-extension://jbkfoedolllekgbhcbcoahefnbanhhlh moz-extension://* {allowed_iframe_ancestors};", + icon_service_csp=CONFIG._icon_service_csp(), + allowed_iframe_ancestors=CONFIG.allowed_iframe_ancestors() ); res.set_raw_header("Content-Security-Policy", csp); res.set_raw_header("X-Frame-Options", "SAMEORIGIN"); From 9a787dd105edeea5fdb309a50f2b47155937c5ed Mon Sep 17 00:00:00 2001 From: BlackDex Date: Tue, 19 Jul 2022 18:39:42 +0200 Subject: [PATCH 2/2] Fix persistent folder check within containers The previous persistent folder check worked by checking if a file exists. If you used a bind-mount, then this file is not there. But when using a docker/podman volume those files are copied, and caused the container to not start. This change checks the `/proc/self/mountinfo` for a specific patern to see if the data folder is persistent or not. Fixes #2622 --- docker/Dockerfile.j2 | 9 ----- docker/amd64/Dockerfile | 9 ----- docker/amd64/Dockerfile.alpine | 9 ----- docker/amd64/Dockerfile.buildx | 9 ----- docker/amd64/Dockerfile.buildx.alpine | 9 ----- docker/arm64/Dockerfile | 9 ----- docker/arm64/Dockerfile.alpine | 9 ----- docker/arm64/Dockerfile.buildx | 9 ----- docker/arm64/Dockerfile.buildx.alpine | 9 ----- docker/armv6/Dockerfile | 9 ----- docker/armv6/Dockerfile.alpine | 9 ----- docker/armv6/Dockerfile.buildx | 9 ----- docker/armv6/Dockerfile.buildx.alpine | 9 ----- docker/armv7/Dockerfile | 9 ----- docker/armv7/Dockerfile.alpine | 9 ----- docker/armv7/Dockerfile.buildx | 9 ----- docker/armv7/Dockerfile.buildx.alpine | 9 ----- src/main.rs | 48 ++++++++++++++++++++++++--- 18 files changed, 43 insertions(+), 158 deletions(-) diff --git a/docker/Dockerfile.j2 b/docker/Dockerfile.j2 index 3778513e..4a4a98d5 100644 --- a/docker/Dockerfile.j2 +++ b/docker/Dockerfile.j2 @@ -181,14 +181,6 @@ RUN touch src/main.rs # hadolint ignore=DL3059 RUN {{ mount_rust_cache -}} cargo build --features ${DB} --release{{ package_arch_target_param }} -# Create a special empty file which we check within the application. -# If this file exists, then we exit Vaultwarden to prevent data loss when someone forgets to use volumes. -# If you really really want to use volatile storage you can set the env `I_REALLY_WANT_VOLATILE_STORAGE=true` -# This file should disappear if a volume is mounted on-top of this using a docker volume. -# We run this in the build image and copy it over, because the runtime image could be missing some executables. -# hadolint ignore=DL3059 -RUN touch /vaultwarden_docker_persistent_volume_check - ######################## RUNTIME IMAGE ######################## # Create a new stage with a minimal image # because we already have a binary built @@ -250,7 +242,6 @@ EXPOSE 3012 # and the binary from the "build" stage to the current stage WORKDIR / COPY --from=vault /web-vault ./web-vault -COPY --from=build /vaultwarden_docker_persistent_volume_check /data/vaultwarden_docker_persistent_volume_check {% if package_arch_target is defined %} COPY --from=build /app/target/{{ package_arch_target }}/release/vaultwarden . {% else %} diff --git a/docker/amd64/Dockerfile b/docker/amd64/Dockerfile index fba481a1..34891a7e 100644 --- a/docker/amd64/Dockerfile +++ b/docker/amd64/Dockerfile @@ -84,14 +84,6 @@ RUN touch src/main.rs # hadolint ignore=DL3059 RUN cargo build --features ${DB} --release -# Create a special empty file which we check within the application. -# If this file exists, then we exit Vaultwarden to prevent data loss when someone forgets to use volumes. -# If you really really want to use volatile storage you can set the env `I_REALLY_WANT_VOLATILE_STORAGE=true` -# This file should disappear if a volume is mounted on-top of this using a docker volume. -# We run this in the build image and copy it over, because the runtime image could be missing some executables. -# hadolint ignore=DL3059 -RUN touch /vaultwarden_docker_persistent_volume_check - ######################## RUNTIME IMAGE ######################## # Create a new stage with a minimal image # because we already have a binary built @@ -124,7 +116,6 @@ EXPOSE 3012 # and the binary from the "build" stage to the current stage WORKDIR / COPY --from=vault /web-vault ./web-vault -COPY --from=build /vaultwarden_docker_persistent_volume_check /data/vaultwarden_docker_persistent_volume_check COPY --from=build /app/target/release/vaultwarden . COPY docker/healthcheck.sh /healthcheck.sh diff --git a/docker/amd64/Dockerfile.alpine b/docker/amd64/Dockerfile.alpine index ef4631a9..9d36fd3f 100644 --- a/docker/amd64/Dockerfile.alpine +++ b/docker/amd64/Dockerfile.alpine @@ -78,14 +78,6 @@ RUN touch src/main.rs # hadolint ignore=DL3059 RUN cargo build --features ${DB} --release --target=x86_64-unknown-linux-musl -# Create a special empty file which we check within the application. -# If this file exists, then we exit Vaultwarden to prevent data loss when someone forgets to use volumes. -# If you really really want to use volatile storage you can set the env `I_REALLY_WANT_VOLATILE_STORAGE=true` -# This file should disappear if a volume is mounted on-top of this using a docker volume. -# We run this in the build image and copy it over, because the runtime image could be missing some executables. -# hadolint ignore=DL3059 -RUN touch /vaultwarden_docker_persistent_volume_check - ######################## RUNTIME IMAGE ######################## # Create a new stage with a minimal image # because we already have a binary built @@ -116,7 +108,6 @@ EXPOSE 3012 # and the binary from the "build" stage to the current stage WORKDIR / COPY --from=vault /web-vault ./web-vault -COPY --from=build /vaultwarden_docker_persistent_volume_check /data/vaultwarden_docker_persistent_volume_check COPY --from=build /app/target/x86_64-unknown-linux-musl/release/vaultwarden . COPY docker/healthcheck.sh /healthcheck.sh diff --git a/docker/amd64/Dockerfile.buildx b/docker/amd64/Dockerfile.buildx index 883869de..6c66d59c 100644 --- a/docker/amd64/Dockerfile.buildx +++ b/docker/amd64/Dockerfile.buildx @@ -84,14 +84,6 @@ RUN touch src/main.rs # hadolint ignore=DL3059 RUN --mount=type=cache,target=/root/.cargo/git --mount=type=cache,target=/root/.cargo/registry cargo build --features ${DB} --release -# Create a special empty file which we check within the application. -# If this file exists, then we exit Vaultwarden to prevent data loss when someone forgets to use volumes. -# If you really really want to use volatile storage you can set the env `I_REALLY_WANT_VOLATILE_STORAGE=true` -# This file should disappear if a volume is mounted on-top of this using a docker volume. -# We run this in the build image and copy it over, because the runtime image could be missing some executables. -# hadolint ignore=DL3059 -RUN touch /vaultwarden_docker_persistent_volume_check - ######################## RUNTIME IMAGE ######################## # Create a new stage with a minimal image # because we already have a binary built @@ -124,7 +116,6 @@ EXPOSE 3012 # and the binary from the "build" stage to the current stage WORKDIR / COPY --from=vault /web-vault ./web-vault -COPY --from=build /vaultwarden_docker_persistent_volume_check /data/vaultwarden_docker_persistent_volume_check COPY --from=build /app/target/release/vaultwarden . COPY docker/healthcheck.sh /healthcheck.sh diff --git a/docker/amd64/Dockerfile.buildx.alpine b/docker/amd64/Dockerfile.buildx.alpine index c59044e9..b2ecabc4 100644 --- a/docker/amd64/Dockerfile.buildx.alpine +++ b/docker/amd64/Dockerfile.buildx.alpine @@ -78,14 +78,6 @@ RUN touch src/main.rs # hadolint ignore=DL3059 RUN --mount=type=cache,target=/root/.cargo/git --mount=type=cache,target=/root/.cargo/registry cargo build --features ${DB} --release --target=x86_64-unknown-linux-musl -# Create a special empty file which we check within the application. -# If this file exists, then we exit Vaultwarden to prevent data loss when someone forgets to use volumes. -# If you really really want to use volatile storage you can set the env `I_REALLY_WANT_VOLATILE_STORAGE=true` -# This file should disappear if a volume is mounted on-top of this using a docker volume. -# We run this in the build image and copy it over, because the runtime image could be missing some executables. -# hadolint ignore=DL3059 -RUN touch /vaultwarden_docker_persistent_volume_check - ######################## RUNTIME IMAGE ######################## # Create a new stage with a minimal image # because we already have a binary built @@ -116,7 +108,6 @@ EXPOSE 3012 # and the binary from the "build" stage to the current stage WORKDIR / COPY --from=vault /web-vault ./web-vault -COPY --from=build /vaultwarden_docker_persistent_volume_check /data/vaultwarden_docker_persistent_volume_check COPY --from=build /app/target/x86_64-unknown-linux-musl/release/vaultwarden . COPY docker/healthcheck.sh /healthcheck.sh diff --git a/docker/arm64/Dockerfile b/docker/arm64/Dockerfile index 145b6586..6fa6bfae 100644 --- a/docker/arm64/Dockerfile +++ b/docker/arm64/Dockerfile @@ -104,14 +104,6 @@ RUN touch src/main.rs # hadolint ignore=DL3059 RUN cargo build --features ${DB} --release --target=aarch64-unknown-linux-gnu -# Create a special empty file which we check within the application. -# If this file exists, then we exit Vaultwarden to prevent data loss when someone forgets to use volumes. -# If you really really want to use volatile storage you can set the env `I_REALLY_WANT_VOLATILE_STORAGE=true` -# This file should disappear if a volume is mounted on-top of this using a docker volume. -# We run this in the build image and copy it over, because the runtime image could be missing some executables. -# hadolint ignore=DL3059 -RUN touch /vaultwarden_docker_persistent_volume_check - ######################## RUNTIME IMAGE ######################## # Create a new stage with a minimal image # because we already have a binary built @@ -148,7 +140,6 @@ EXPOSE 3012 # and the binary from the "build" stage to the current stage WORKDIR / COPY --from=vault /web-vault ./web-vault -COPY --from=build /vaultwarden_docker_persistent_volume_check /data/vaultwarden_docker_persistent_volume_check COPY --from=build /app/target/aarch64-unknown-linux-gnu/release/vaultwarden . COPY docker/healthcheck.sh /healthcheck.sh diff --git a/docker/arm64/Dockerfile.alpine b/docker/arm64/Dockerfile.alpine index f19a142b..b998c7c4 100644 --- a/docker/arm64/Dockerfile.alpine +++ b/docker/arm64/Dockerfile.alpine @@ -78,14 +78,6 @@ RUN touch src/main.rs # hadolint ignore=DL3059 RUN cargo build --features ${DB} --release --target=aarch64-unknown-linux-musl -# Create a special empty file which we check within the application. -# If this file exists, then we exit Vaultwarden to prevent data loss when someone forgets to use volumes. -# If you really really want to use volatile storage you can set the env `I_REALLY_WANT_VOLATILE_STORAGE=true` -# This file should disappear if a volume is mounted on-top of this using a docker volume. -# We run this in the build image and copy it over, because the runtime image could be missing some executables. -# hadolint ignore=DL3059 -RUN touch /vaultwarden_docker_persistent_volume_check - ######################## RUNTIME IMAGE ######################## # Create a new stage with a minimal image # because we already have a binary built @@ -120,7 +112,6 @@ EXPOSE 3012 # and the binary from the "build" stage to the current stage WORKDIR / COPY --from=vault /web-vault ./web-vault -COPY --from=build /vaultwarden_docker_persistent_volume_check /data/vaultwarden_docker_persistent_volume_check COPY --from=build /app/target/aarch64-unknown-linux-musl/release/vaultwarden . COPY docker/healthcheck.sh /healthcheck.sh diff --git a/docker/arm64/Dockerfile.buildx b/docker/arm64/Dockerfile.buildx index 4ace6f45..1f582af8 100644 --- a/docker/arm64/Dockerfile.buildx +++ b/docker/arm64/Dockerfile.buildx @@ -104,14 +104,6 @@ RUN touch src/main.rs # hadolint ignore=DL3059 RUN --mount=type=cache,target=/root/.cargo/git --mount=type=cache,target=/root/.cargo/registry cargo build --features ${DB} --release --target=aarch64-unknown-linux-gnu -# Create a special empty file which we check within the application. -# If this file exists, then we exit Vaultwarden to prevent data loss when someone forgets to use volumes. -# If you really really want to use volatile storage you can set the env `I_REALLY_WANT_VOLATILE_STORAGE=true` -# This file should disappear if a volume is mounted on-top of this using a docker volume. -# We run this in the build image and copy it over, because the runtime image could be missing some executables. -# hadolint ignore=DL3059 -RUN touch /vaultwarden_docker_persistent_volume_check - ######################## RUNTIME IMAGE ######################## # Create a new stage with a minimal image # because we already have a binary built @@ -148,7 +140,6 @@ EXPOSE 3012 # and the binary from the "build" stage to the current stage WORKDIR / COPY --from=vault /web-vault ./web-vault -COPY --from=build /vaultwarden_docker_persistent_volume_check /data/vaultwarden_docker_persistent_volume_check COPY --from=build /app/target/aarch64-unknown-linux-gnu/release/vaultwarden . COPY docker/healthcheck.sh /healthcheck.sh diff --git a/docker/arm64/Dockerfile.buildx.alpine b/docker/arm64/Dockerfile.buildx.alpine index 86281045..358e5001 100644 --- a/docker/arm64/Dockerfile.buildx.alpine +++ b/docker/arm64/Dockerfile.buildx.alpine @@ -78,14 +78,6 @@ RUN touch src/main.rs # hadolint ignore=DL3059 RUN --mount=type=cache,target=/root/.cargo/git --mount=type=cache,target=/root/.cargo/registry cargo build --features ${DB} --release --target=aarch64-unknown-linux-musl -# Create a special empty file which we check within the application. -# If this file exists, then we exit Vaultwarden to prevent data loss when someone forgets to use volumes. -# If you really really want to use volatile storage you can set the env `I_REALLY_WANT_VOLATILE_STORAGE=true` -# This file should disappear if a volume is mounted on-top of this using a docker volume. -# We run this in the build image and copy it over, because the runtime image could be missing some executables. -# hadolint ignore=DL3059 -RUN touch /vaultwarden_docker_persistent_volume_check - ######################## RUNTIME IMAGE ######################## # Create a new stage with a minimal image # because we already have a binary built @@ -120,7 +112,6 @@ EXPOSE 3012 # and the binary from the "build" stage to the current stage WORKDIR / COPY --from=vault /web-vault ./web-vault -COPY --from=build /vaultwarden_docker_persistent_volume_check /data/vaultwarden_docker_persistent_volume_check COPY --from=build /app/target/aarch64-unknown-linux-musl/release/vaultwarden . COPY docker/healthcheck.sh /healthcheck.sh diff --git a/docker/armv6/Dockerfile b/docker/armv6/Dockerfile index 94375eb6..0bbaeca7 100644 --- a/docker/armv6/Dockerfile +++ b/docker/armv6/Dockerfile @@ -104,14 +104,6 @@ RUN touch src/main.rs # hadolint ignore=DL3059 RUN cargo build --features ${DB} --release --target=arm-unknown-linux-gnueabi -# Create a special empty file which we check within the application. -# If this file exists, then we exit Vaultwarden to prevent data loss when someone forgets to use volumes. -# If you really really want to use volatile storage you can set the env `I_REALLY_WANT_VOLATILE_STORAGE=true` -# This file should disappear if a volume is mounted on-top of this using a docker volume. -# We run this in the build image and copy it over, because the runtime image could be missing some executables. -# hadolint ignore=DL3059 -RUN touch /vaultwarden_docker_persistent_volume_check - ######################## RUNTIME IMAGE ######################## # Create a new stage with a minimal image # because we already have a binary built @@ -153,7 +145,6 @@ EXPOSE 3012 # and the binary from the "build" stage to the current stage WORKDIR / COPY --from=vault /web-vault ./web-vault -COPY --from=build /vaultwarden_docker_persistent_volume_check /data/vaultwarden_docker_persistent_volume_check COPY --from=build /app/target/arm-unknown-linux-gnueabi/release/vaultwarden . COPY docker/healthcheck.sh /healthcheck.sh diff --git a/docker/armv6/Dockerfile.alpine b/docker/armv6/Dockerfile.alpine index 033fa5f0..ef4a198f 100644 --- a/docker/armv6/Dockerfile.alpine +++ b/docker/armv6/Dockerfile.alpine @@ -80,14 +80,6 @@ RUN touch src/main.rs # hadolint ignore=DL3059 RUN cargo build --features ${DB} --release --target=arm-unknown-linux-musleabi -# Create a special empty file which we check within the application. -# If this file exists, then we exit Vaultwarden to prevent data loss when someone forgets to use volumes. -# If you really really want to use volatile storage you can set the env `I_REALLY_WANT_VOLATILE_STORAGE=true` -# This file should disappear if a volume is mounted on-top of this using a docker volume. -# We run this in the build image and copy it over, because the runtime image could be missing some executables. -# hadolint ignore=DL3059 -RUN touch /vaultwarden_docker_persistent_volume_check - ######################## RUNTIME IMAGE ######################## # Create a new stage with a minimal image # because we already have a binary built @@ -122,7 +114,6 @@ EXPOSE 3012 # and the binary from the "build" stage to the current stage WORKDIR / COPY --from=vault /web-vault ./web-vault -COPY --from=build /vaultwarden_docker_persistent_volume_check /data/vaultwarden_docker_persistent_volume_check COPY --from=build /app/target/arm-unknown-linux-musleabi/release/vaultwarden . COPY docker/healthcheck.sh /healthcheck.sh diff --git a/docker/armv6/Dockerfile.buildx b/docker/armv6/Dockerfile.buildx index 1feee4f7..17d5b8da 100644 --- a/docker/armv6/Dockerfile.buildx +++ b/docker/armv6/Dockerfile.buildx @@ -104,14 +104,6 @@ RUN touch src/main.rs # hadolint ignore=DL3059 RUN --mount=type=cache,target=/root/.cargo/git --mount=type=cache,target=/root/.cargo/registry cargo build --features ${DB} --release --target=arm-unknown-linux-gnueabi -# Create a special empty file which we check within the application. -# If this file exists, then we exit Vaultwarden to prevent data loss when someone forgets to use volumes. -# If you really really want to use volatile storage you can set the env `I_REALLY_WANT_VOLATILE_STORAGE=true` -# This file should disappear if a volume is mounted on-top of this using a docker volume. -# We run this in the build image and copy it over, because the runtime image could be missing some executables. -# hadolint ignore=DL3059 -RUN touch /vaultwarden_docker_persistent_volume_check - ######################## RUNTIME IMAGE ######################## # Create a new stage with a minimal image # because we already have a binary built @@ -153,7 +145,6 @@ EXPOSE 3012 # and the binary from the "build" stage to the current stage WORKDIR / COPY --from=vault /web-vault ./web-vault -COPY --from=build /vaultwarden_docker_persistent_volume_check /data/vaultwarden_docker_persistent_volume_check COPY --from=build /app/target/arm-unknown-linux-gnueabi/release/vaultwarden . COPY docker/healthcheck.sh /healthcheck.sh diff --git a/docker/armv6/Dockerfile.buildx.alpine b/docker/armv6/Dockerfile.buildx.alpine index f1240f72..ff93af8a 100644 --- a/docker/armv6/Dockerfile.buildx.alpine +++ b/docker/armv6/Dockerfile.buildx.alpine @@ -80,14 +80,6 @@ RUN touch src/main.rs # hadolint ignore=DL3059 RUN --mount=type=cache,target=/root/.cargo/git --mount=type=cache,target=/root/.cargo/registry cargo build --features ${DB} --release --target=arm-unknown-linux-musleabi -# Create a special empty file which we check within the application. -# If this file exists, then we exit Vaultwarden to prevent data loss when someone forgets to use volumes. -# If you really really want to use volatile storage you can set the env `I_REALLY_WANT_VOLATILE_STORAGE=true` -# This file should disappear if a volume is mounted on-top of this using a docker volume. -# We run this in the build image and copy it over, because the runtime image could be missing some executables. -# hadolint ignore=DL3059 -RUN touch /vaultwarden_docker_persistent_volume_check - ######################## RUNTIME IMAGE ######################## # Create a new stage with a minimal image # because we already have a binary built @@ -122,7 +114,6 @@ EXPOSE 3012 # and the binary from the "build" stage to the current stage WORKDIR / COPY --from=vault /web-vault ./web-vault -COPY --from=build /vaultwarden_docker_persistent_volume_check /data/vaultwarden_docker_persistent_volume_check COPY --from=build /app/target/arm-unknown-linux-musleabi/release/vaultwarden . COPY docker/healthcheck.sh /healthcheck.sh diff --git a/docker/armv7/Dockerfile b/docker/armv7/Dockerfile index ac35c663..25c91be1 100644 --- a/docker/armv7/Dockerfile +++ b/docker/armv7/Dockerfile @@ -104,14 +104,6 @@ RUN touch src/main.rs # hadolint ignore=DL3059 RUN cargo build --features ${DB} --release --target=armv7-unknown-linux-gnueabihf -# Create a special empty file which we check within the application. -# If this file exists, then we exit Vaultwarden to prevent data loss when someone forgets to use volumes. -# If you really really want to use volatile storage you can set the env `I_REALLY_WANT_VOLATILE_STORAGE=true` -# This file should disappear if a volume is mounted on-top of this using a docker volume. -# We run this in the build image and copy it over, because the runtime image could be missing some executables. -# hadolint ignore=DL3059 -RUN touch /vaultwarden_docker_persistent_volume_check - ######################## RUNTIME IMAGE ######################## # Create a new stage with a minimal image # because we already have a binary built @@ -148,7 +140,6 @@ EXPOSE 3012 # and the binary from the "build" stage to the current stage WORKDIR / COPY --from=vault /web-vault ./web-vault -COPY --from=build /vaultwarden_docker_persistent_volume_check /data/vaultwarden_docker_persistent_volume_check COPY --from=build /app/target/armv7-unknown-linux-gnueabihf/release/vaultwarden . COPY docker/healthcheck.sh /healthcheck.sh diff --git a/docker/armv7/Dockerfile.alpine b/docker/armv7/Dockerfile.alpine index 4aec831a..d737b1f4 100644 --- a/docker/armv7/Dockerfile.alpine +++ b/docker/armv7/Dockerfile.alpine @@ -78,14 +78,6 @@ RUN touch src/main.rs # hadolint ignore=DL3059 RUN cargo build --features ${DB} --release --target=armv7-unknown-linux-musleabihf -# Create a special empty file which we check within the application. -# If this file exists, then we exit Vaultwarden to prevent data loss when someone forgets to use volumes. -# If you really really want to use volatile storage you can set the env `I_REALLY_WANT_VOLATILE_STORAGE=true` -# This file should disappear if a volume is mounted on-top of this using a docker volume. -# We run this in the build image and copy it over, because the runtime image could be missing some executables. -# hadolint ignore=DL3059 -RUN touch /vaultwarden_docker_persistent_volume_check - ######################## RUNTIME IMAGE ######################## # Create a new stage with a minimal image # because we already have a binary built @@ -120,7 +112,6 @@ EXPOSE 3012 # and the binary from the "build" stage to the current stage WORKDIR / COPY --from=vault /web-vault ./web-vault -COPY --from=build /vaultwarden_docker_persistent_volume_check /data/vaultwarden_docker_persistent_volume_check COPY --from=build /app/target/armv7-unknown-linux-musleabihf/release/vaultwarden . COPY docker/healthcheck.sh /healthcheck.sh diff --git a/docker/armv7/Dockerfile.buildx b/docker/armv7/Dockerfile.buildx index d75dd847..7bbded88 100644 --- a/docker/armv7/Dockerfile.buildx +++ b/docker/armv7/Dockerfile.buildx @@ -104,14 +104,6 @@ RUN touch src/main.rs # hadolint ignore=DL3059 RUN --mount=type=cache,target=/root/.cargo/git --mount=type=cache,target=/root/.cargo/registry cargo build --features ${DB} --release --target=armv7-unknown-linux-gnueabihf -# Create a special empty file which we check within the application. -# If this file exists, then we exit Vaultwarden to prevent data loss when someone forgets to use volumes. -# If you really really want to use volatile storage you can set the env `I_REALLY_WANT_VOLATILE_STORAGE=true` -# This file should disappear if a volume is mounted on-top of this using a docker volume. -# We run this in the build image and copy it over, because the runtime image could be missing some executables. -# hadolint ignore=DL3059 -RUN touch /vaultwarden_docker_persistent_volume_check - ######################## RUNTIME IMAGE ######################## # Create a new stage with a minimal image # because we already have a binary built @@ -148,7 +140,6 @@ EXPOSE 3012 # and the binary from the "build" stage to the current stage WORKDIR / COPY --from=vault /web-vault ./web-vault -COPY --from=build /vaultwarden_docker_persistent_volume_check /data/vaultwarden_docker_persistent_volume_check COPY --from=build /app/target/armv7-unknown-linux-gnueabihf/release/vaultwarden . COPY docker/healthcheck.sh /healthcheck.sh diff --git a/docker/armv7/Dockerfile.buildx.alpine b/docker/armv7/Dockerfile.buildx.alpine index e90df821..ffa5dcf2 100644 --- a/docker/armv7/Dockerfile.buildx.alpine +++ b/docker/armv7/Dockerfile.buildx.alpine @@ -78,14 +78,6 @@ RUN touch src/main.rs # hadolint ignore=DL3059 RUN --mount=type=cache,target=/root/.cargo/git --mount=type=cache,target=/root/.cargo/registry cargo build --features ${DB} --release --target=armv7-unknown-linux-musleabihf -# Create a special empty file which we check within the application. -# If this file exists, then we exit Vaultwarden to prevent data loss when someone forgets to use volumes. -# If you really really want to use volatile storage you can set the env `I_REALLY_WANT_VOLATILE_STORAGE=true` -# This file should disappear if a volume is mounted on-top of this using a docker volume. -# We run this in the build image and copy it over, because the runtime image could be missing some executables. -# hadolint ignore=DL3059 -RUN touch /vaultwarden_docker_persistent_volume_check - ######################## RUNTIME IMAGE ######################## # Create a new stage with a minimal image # because we already have a binary built @@ -120,7 +112,6 @@ EXPOSE 3012 # and the binary from the "build" stage to the current stage WORKDIR / COPY --from=vault /web-vault ./web-vault -COPY --from=build /vaultwarden_docker_persistent_volume_check /data/vaultwarden_docker_persistent_volume_check COPY --from=build /app/target/armv7-unknown-linux-musleabihf/release/vaultwarden . COPY docker/healthcheck.sh /healthcheck.sh diff --git a/src/main.rs b/src/main.rs index 20f40bc5..ad47f3c5 100644 --- a/src/main.rs +++ b/src/main.rs @@ -61,6 +61,11 @@ use std::{ thread, }; +use tokio::{ + fs::File, + io::{AsyncBufReadExt, BufReader}, +}; + #[macro_use] mod error; mod api; @@ -89,7 +94,7 @@ async fn main() -> Result<(), Error> { let extra_debug = matches!(level, LF::Trace | LF::Debug); - check_data_folder(); + check_data_folder().await; check_rsa_keys().unwrap_or_else(|_| { error!("Error creating keys, exiting..."); exit(1); @@ -286,7 +291,7 @@ fn create_dir(path: &str, description: &str) { create_dir_all(path).expect(&err_msg); } -fn check_data_folder() { +async fn check_data_folder() { let data_folder = &CONFIG.data_folder(); let path = Path::new(data_folder); if !path.exists() { @@ -299,9 +304,10 @@ fn check_data_folder() { exit(1); } - let persistent_volume_check_file = format!("{data_folder}/vaultwarden_docker_persistent_volume_check"); - let check_file = Path::new(&persistent_volume_check_file); - if check_file.exists() && std::env::var("I_REALLY_WANT_VOLATILE_STORAGE").is_err() { + if is_running_in_docker() + && std::env::var("I_REALLY_WANT_VOLATILE_STORAGE").is_err() + && !docker_data_folder_is_persistent(data_folder).await + { error!( "No persistent volume!\n\ ########################################################################################\n\ @@ -314,6 +320,38 @@ fn check_data_folder() { } } +/// Detect when using Docker or Podman the DATA_FOLDER is either a bind-mount or a volume created manually. +/// If not created manually, then the data will not be persistent. +/// A none persistent volume in either Docker or Podman is represented by a 64 alphanumerical string. +/// If we detect this string, we will alert about not having a persistent self defined volume. +/// This probably means that someone forgot to add `-v /path/to/vaultwarden_data/:/data` +async fn docker_data_folder_is_persistent(data_folder: &str) -> bool { + if let Ok(mountinfo) = File::open("/proc/self/mountinfo").await { + // Since there can only be one mountpoint to the DATA_FOLDER + // We do a basic check for this mountpoint surrounded by a space. + let data_folder_match = if data_folder.starts_with('/') { + format!(" {data_folder} ") + } else { + format!(" /{data_folder} ") + }; + let mut lines = BufReader::new(mountinfo).lines(); + while let Some(line) = lines.next_line().await.unwrap_or_default() { + // Only execute a regex check if we find the base match + if line.contains(&data_folder_match) { + let re = regex::Regex::new(r"/volumes/[a-z0-9]{64}/_data /").unwrap(); + if re.is_match(&line) { + return false; + } + // If we did found a match for the mountpoint, but not the regex, then still stop searching. + break; + } + } + } + // In all other cases, just assume a true. + // This is just an informative check to try and prevent data loss. + true +} + fn check_rsa_keys() -> Result<(), crate::error::Error> { // If the RSA keys don't exist, try to create them let priv_path = CONFIG.private_rsa_key();