diff --git a/docker/Dockerfile.j2 b/docker/Dockerfile.j2 index 7ce2d4e..cdf83ee 100644 --- a/docker/Dockerfile.j2 +++ b/docker/Dockerfile.j2 @@ -31,7 +31,7 @@ {% endif %} FROM {{ vault_stage_base_image }} as vault -ENV VAULT_VERSION "v2.12.0c" +ENV VAULT_VERSION "v2.12.0d" ENV URL "https://github.com/dani-garcia/bw_web_builds/releases/download/$VAULT_VERSION/bw_web_$VAULT_VERSION.tar.gz" @@ -42,8 +42,7 @@ RUN apk add --no-cache --upgrade curl tar ENV DEBIAN_FRONTEND=noninteractive LANG=C.UTF-8 TZ=UTC TERM=xterm-256color {% endif %} -RUN mkdir /web-vault -WORKDIR /web-vault +WORKDIR / {% if "alpine" in vault_stage_base_image %} SHELL ["/bin/ash", "-o", "nounset", "-o", "pipefail", "-o", "errexit", "-c"] diff --git a/docker/aarch64/mysql/Dockerfile b/docker/aarch64/mysql/Dockerfile index 58cdf3b..14a0cbf 100644 --- a/docker/aarch64/mysql/Dockerfile +++ b/docker/aarch64/mysql/Dockerfile @@ -7,15 +7,14 @@ ####################### VAULT BUILD IMAGE ####################### FROM rust:1.40 as vault -ENV VAULT_VERSION "v2.12.0c" +ENV VAULT_VERSION "v2.12.0d" ENV URL "https://github.com/dani-garcia/bw_web_builds/releases/download/$VAULT_VERSION/bw_web_$VAULT_VERSION.tar.gz" # Build time options to avoid dpkg warnings and help with reproducible builds. ENV DEBIAN_FRONTEND=noninteractive LANG=C.UTF-8 TZ=UTC TERM=xterm-256color -RUN mkdir /web-vault -WORKDIR /web-vault +WORKDIR / SHELL ["/bin/bash", "-o", "nounset", "-o", "pipefail", "-o", "errexit", "-c"] diff --git a/docker/aarch64/sqlite/Dockerfile b/docker/aarch64/sqlite/Dockerfile index b763715..0c2e7db 100644 --- a/docker/aarch64/sqlite/Dockerfile +++ b/docker/aarch64/sqlite/Dockerfile @@ -7,15 +7,14 @@ ####################### VAULT BUILD IMAGE ####################### FROM rust:1.40 as vault -ENV VAULT_VERSION "v2.12.0c" +ENV VAULT_VERSION "v2.12.0d" ENV URL "https://github.com/dani-garcia/bw_web_builds/releases/download/$VAULT_VERSION/bw_web_$VAULT_VERSION.tar.gz" # Build time options to avoid dpkg warnings and help with reproducible builds. ENV DEBIAN_FRONTEND=noninteractive LANG=C.UTF-8 TZ=UTC TERM=xterm-256color -RUN mkdir /web-vault -WORKDIR /web-vault +WORKDIR / SHELL ["/bin/bash", "-o", "nounset", "-o", "pipefail", "-o", "errexit", "-c"] diff --git a/docker/amd64/mysql/Dockerfile b/docker/amd64/mysql/Dockerfile index 4949f81..6e12db2 100644 --- a/docker/amd64/mysql/Dockerfile +++ b/docker/amd64/mysql/Dockerfile @@ -7,15 +7,14 @@ ####################### VAULT BUILD IMAGE ####################### FROM rust:1.40 as vault -ENV VAULT_VERSION "v2.12.0c" +ENV VAULT_VERSION "v2.12.0d" ENV URL "https://github.com/dani-garcia/bw_web_builds/releases/download/$VAULT_VERSION/bw_web_$VAULT_VERSION.tar.gz" # Build time options to avoid dpkg warnings and help with reproducible builds. ENV DEBIAN_FRONTEND=noninteractive LANG=C.UTF-8 TZ=UTC TERM=xterm-256color -RUN mkdir /web-vault -WORKDIR /web-vault +WORKDIR / SHELL ["/bin/bash", "-o", "nounset", "-o", "pipefail", "-o", "errexit", "-c"] diff --git a/docker/amd64/mysql/Dockerfile.alpine b/docker/amd64/mysql/Dockerfile.alpine index 6ea0f0a..9bc6a51 100644 --- a/docker/amd64/mysql/Dockerfile.alpine +++ b/docker/amd64/mysql/Dockerfile.alpine @@ -7,14 +7,13 @@ ####################### VAULT BUILD IMAGE ####################### FROM alpine:3.11 as vault -ENV VAULT_VERSION "v2.12.0c" +ENV VAULT_VERSION "v2.12.0d" ENV URL "https://github.com/dani-garcia/bw_web_builds/releases/download/$VAULT_VERSION/bw_web_$VAULT_VERSION.tar.gz" RUN apk add --no-cache --upgrade curl tar -RUN mkdir /web-vault -WORKDIR /web-vault +WORKDIR / SHELL ["/bin/ash", "-o", "nounset", "-o", "pipefail", "-o", "errexit", "-c"] diff --git a/docker/amd64/postgresql/Dockerfile b/docker/amd64/postgresql/Dockerfile index 2418cb6..8428de9 100644 --- a/docker/amd64/postgresql/Dockerfile +++ b/docker/amd64/postgresql/Dockerfile @@ -7,15 +7,14 @@ ####################### VAULT BUILD IMAGE ####################### FROM rust:1.40 as vault -ENV VAULT_VERSION "v2.12.0c" +ENV VAULT_VERSION "v2.12.0d" ENV URL "https://github.com/dani-garcia/bw_web_builds/releases/download/$VAULT_VERSION/bw_web_$VAULT_VERSION.tar.gz" # Build time options to avoid dpkg warnings and help with reproducible builds. ENV DEBIAN_FRONTEND=noninteractive LANG=C.UTF-8 TZ=UTC TERM=xterm-256color -RUN mkdir /web-vault -WORKDIR /web-vault +WORKDIR / SHELL ["/bin/bash", "-o", "nounset", "-o", "pipefail", "-o", "errexit", "-c"] diff --git a/docker/amd64/postgresql/Dockerfile.alpine b/docker/amd64/postgresql/Dockerfile.alpine index 72dd66d..12f2e52 100644 --- a/docker/amd64/postgresql/Dockerfile.alpine +++ b/docker/amd64/postgresql/Dockerfile.alpine @@ -7,14 +7,13 @@ ####################### VAULT BUILD IMAGE ####################### FROM alpine:3.11 as vault -ENV VAULT_VERSION "v2.12.0c" +ENV VAULT_VERSION "v2.12.0d" ENV URL "https://github.com/dani-garcia/bw_web_builds/releases/download/$VAULT_VERSION/bw_web_$VAULT_VERSION.tar.gz" RUN apk add --no-cache --upgrade curl tar -RUN mkdir /web-vault -WORKDIR /web-vault +WORKDIR / SHELL ["/bin/ash", "-o", "nounset", "-o", "pipefail", "-o", "errexit", "-c"] diff --git a/docker/amd64/sqlite/Dockerfile b/docker/amd64/sqlite/Dockerfile index ba09eec..e120c18 100644 --- a/docker/amd64/sqlite/Dockerfile +++ b/docker/amd64/sqlite/Dockerfile @@ -7,15 +7,14 @@ ####################### VAULT BUILD IMAGE ####################### FROM rust:1.40 as vault -ENV VAULT_VERSION "v2.12.0c" +ENV VAULT_VERSION "v2.12.0d" ENV URL "https://github.com/dani-garcia/bw_web_builds/releases/download/$VAULT_VERSION/bw_web_$VAULT_VERSION.tar.gz" # Build time options to avoid dpkg warnings and help with reproducible builds. ENV DEBIAN_FRONTEND=noninteractive LANG=C.UTF-8 TZ=UTC TERM=xterm-256color -RUN mkdir /web-vault -WORKDIR /web-vault +WORKDIR / SHELL ["/bin/bash", "-o", "nounset", "-o", "pipefail", "-o", "errexit", "-c"] diff --git a/docker/amd64/sqlite/Dockerfile.alpine b/docker/amd64/sqlite/Dockerfile.alpine index 99cab51..8cd9251 100644 --- a/docker/amd64/sqlite/Dockerfile.alpine +++ b/docker/amd64/sqlite/Dockerfile.alpine @@ -7,14 +7,13 @@ ####################### VAULT BUILD IMAGE ####################### FROM alpine:3.11 as vault -ENV VAULT_VERSION "v2.12.0c" +ENV VAULT_VERSION "v2.12.0d" ENV URL "https://github.com/dani-garcia/bw_web_builds/releases/download/$VAULT_VERSION/bw_web_$VAULT_VERSION.tar.gz" RUN apk add --no-cache --upgrade curl tar -RUN mkdir /web-vault -WORKDIR /web-vault +WORKDIR / SHELL ["/bin/ash", "-o", "nounset", "-o", "pipefail", "-o", "errexit", "-c"] diff --git a/docker/armv6/mysql/Dockerfile b/docker/armv6/mysql/Dockerfile index 427b03a..d55e12b 100644 --- a/docker/armv6/mysql/Dockerfile +++ b/docker/armv6/mysql/Dockerfile @@ -7,15 +7,14 @@ ####################### VAULT BUILD IMAGE ####################### FROM rust:1.40 as vault -ENV VAULT_VERSION "v2.12.0c" +ENV VAULT_VERSION "v2.12.0d" ENV URL "https://github.com/dani-garcia/bw_web_builds/releases/download/$VAULT_VERSION/bw_web_$VAULT_VERSION.tar.gz" # Build time options to avoid dpkg warnings and help with reproducible builds. ENV DEBIAN_FRONTEND=noninteractive LANG=C.UTF-8 TZ=UTC TERM=xterm-256color -RUN mkdir /web-vault -WORKDIR /web-vault +WORKDIR / SHELL ["/bin/bash", "-o", "nounset", "-o", "pipefail", "-o", "errexit", "-c"] diff --git a/docker/armv6/sqlite/Dockerfile b/docker/armv6/sqlite/Dockerfile index 8aa5827..ee6b590 100644 --- a/docker/armv6/sqlite/Dockerfile +++ b/docker/armv6/sqlite/Dockerfile @@ -7,15 +7,14 @@ ####################### VAULT BUILD IMAGE ####################### FROM rust:1.40 as vault -ENV VAULT_VERSION "v2.12.0c" +ENV VAULT_VERSION "v2.12.0d" ENV URL "https://github.com/dani-garcia/bw_web_builds/releases/download/$VAULT_VERSION/bw_web_$VAULT_VERSION.tar.gz" # Build time options to avoid dpkg warnings and help with reproducible builds. ENV DEBIAN_FRONTEND=noninteractive LANG=C.UTF-8 TZ=UTC TERM=xterm-256color -RUN mkdir /web-vault -WORKDIR /web-vault +WORKDIR / SHELL ["/bin/bash", "-o", "nounset", "-o", "pipefail", "-o", "errexit", "-c"] diff --git a/docker/armv7/mysql/Dockerfile b/docker/armv7/mysql/Dockerfile index fb718c0..c486595 100644 --- a/docker/armv7/mysql/Dockerfile +++ b/docker/armv7/mysql/Dockerfile @@ -7,15 +7,14 @@ ####################### VAULT BUILD IMAGE ####################### FROM rust:1.40 as vault -ENV VAULT_VERSION "v2.12.0c" +ENV VAULT_VERSION "v2.12.0d" ENV URL "https://github.com/dani-garcia/bw_web_builds/releases/download/$VAULT_VERSION/bw_web_$VAULT_VERSION.tar.gz" # Build time options to avoid dpkg warnings and help with reproducible builds. ENV DEBIAN_FRONTEND=noninteractive LANG=C.UTF-8 TZ=UTC TERM=xterm-256color -RUN mkdir /web-vault -WORKDIR /web-vault +WORKDIR / SHELL ["/bin/bash", "-o", "nounset", "-o", "pipefail", "-o", "errexit", "-c"] diff --git a/docker/armv7/sqlite/Dockerfile b/docker/armv7/sqlite/Dockerfile index d1b6aaf..7cce852 100644 --- a/docker/armv7/sqlite/Dockerfile +++ b/docker/armv7/sqlite/Dockerfile @@ -7,15 +7,14 @@ ####################### VAULT BUILD IMAGE ####################### FROM rust:1.40 as vault -ENV VAULT_VERSION "v2.12.0c" +ENV VAULT_VERSION "v2.12.0d" ENV URL "https://github.com/dani-garcia/bw_web_builds/releases/download/$VAULT_VERSION/bw_web_$VAULT_VERSION.tar.gz" # Build time options to avoid dpkg warnings and help with reproducible builds. ENV DEBIAN_FRONTEND=noninteractive LANG=C.UTF-8 TZ=UTC TERM=xterm-256color -RUN mkdir /web-vault -WORKDIR /web-vault +WORKDIR / SHELL ["/bin/bash", "-o", "nounset", "-o", "pipefail", "-o", "errexit", "-c"] diff --git a/src/api/admin.rs b/src/api/admin.rs index c4fad11..937783f 100644 --- a/src/api/admin.rs +++ b/src/api/admin.rs @@ -52,11 +52,15 @@ const ADMIN_PATH: &str = "/admin"; const BASE_TEMPLATE: &str = "admin/base"; const VERSION: Option<&str> = option_env!("GIT_VERSION"); +fn admin_path() -> String { + format!("{}{}", CONFIG.domain_path(), ADMIN_PATH) +} + #[get("/", rank = 2)] fn admin_login(flash: Option) -> ApiResult> { // If there is an error, show it let msg = flash.map(|msg| format!("{}: {}", msg.name(), msg.msg())); - let json = json!({"page_content": "admin/login", "version": VERSION, "error": msg}); + let json = json!({"page_content": "admin/login", "version": VERSION, "error": msg, "urlpath": CONFIG.domain_path()}); // Return the page let text = CONFIG.render_template(BASE_TEMPLATE, &json)?; @@ -76,7 +80,7 @@ fn post_admin_login(data: Form, mut cookies: Cookies, ip: ClientIp) - if !_validate_token(&data.token) { error!("Invalid admin token. IP: {}", ip.ip); Err(Flash::error( - Redirect::to(ADMIN_PATH), + Redirect::to(admin_path()), "Invalid admin token, please try again.", )) } else { @@ -85,14 +89,14 @@ fn post_admin_login(data: Form, mut cookies: Cookies, ip: ClientIp) - let jwt = encode_jwt(&claims); let cookie = Cookie::build(COOKIE_NAME, jwt) - .path(ADMIN_PATH) + .path(admin_path()) .max_age(chrono::Duration::minutes(20)) .same_site(SameSite::Strict) .http_only(true) .finish(); cookies.add(cookie); - Ok(Redirect::to(ADMIN_PATH)) + Ok(Redirect::to(admin_path())) } } @@ -111,6 +115,7 @@ struct AdminTemplateData { config: Value, can_backup: bool, logged_in: bool, + urlpath: String, } impl AdminTemplateData { @@ -122,6 +127,7 @@ impl AdminTemplateData { config: CONFIG.prepare_json(), can_backup: *CAN_BACKUP, logged_in: true, + urlpath: CONFIG.domain_path(), } } @@ -167,7 +173,7 @@ fn invite_user(data: Json, _token: AdminToken, conn: DbConn) -> Empt #[get("/logout")] fn logout(mut cookies: Cookies) -> Result { cookies.remove(Cookie::named(COOKIE_NAME)); - Ok(Redirect::to(ADMIN_PATH)) + Ok(Redirect::to(admin_path())) } #[get("/users")] diff --git a/src/api/core/mod.rs b/src/api/core/mod.rs index 55ed66d..550733c 100644 --- a/src/api/core/mod.rs +++ b/src/api/core/mod.rs @@ -172,7 +172,7 @@ fn hibp_breach(username: String) -> JsonResult { "BreachDate": "2019-08-18T00:00:00Z", "AddedDate": "2019-08-18T00:00:00Z", "Description": format!("Go to: https://haveibeenpwned.com/account/{account} for a manual check.

HaveIBeenPwned API key not set!
Go to https://haveibeenpwned.com/API/Key to purchase an API key from HaveIBeenPwned.

", account=username), - "LogoPath": "/bwrs_static/hibp.png", + "LogoPath": "bwrs_static/hibp.png", "PwnCount": 0, "DataClasses": [ "Error - No API key set!" diff --git a/src/api/web.rs b/src/api/web.rs index 408e38e..7f47ae7 100644 --- a/src/api/web.rs +++ b/src/api/web.rs @@ -37,7 +37,17 @@ fn app_id() -> Cached>> { { "version": { "major": 1, "minor": 0 }, "ids": [ - &CONFIG.domain(), + // Per : + // + // "In the Web case, the FacetID MUST be the Web Origin [RFC6454] + // of the web page triggering the FIDO operation, written as + // a URI with an empty path. Default ports are omitted and any + // path component is ignored." + // + // This leaves it unclear as to whether the path must be empty, + // or whether it can be non-empty and will be ignored. To be on + // the safe side, use a proper web origin (with empty path). + &CONFIG.domain_origin(), "ios:bundle-id:com.8bit.bitwarden", "android:apk-key-hash:dUGFzUzf3lmHSLBDBIv+WaFyZMI" ] }] @@ -75,6 +85,6 @@ fn static_files(filename: String) -> Result, Error> { "bootstrap-native-v4.js" => Ok(Content(ContentType::JavaScript, include_bytes!("../static/scripts/bootstrap-native-v4.js"))), "md5.js" => Ok(Content(ContentType::JavaScript, include_bytes!("../static/scripts/md5.js"))), "identicon.js" => Ok(Content(ContentType::JavaScript, include_bytes!("../static/scripts/identicon.js"))), - _ => err!("Image not found"), + _ => err!(format!("Static file not found: {}", filename)), } } diff --git a/src/auth.rs b/src/auth.rs index 2820498..cbcdb47 100644 --- a/src/auth.rs +++ b/src/auth.rs @@ -16,11 +16,11 @@ const JWT_ALGORITHM: Algorithm = Algorithm::RS256; lazy_static! { pub static ref DEFAULT_VALIDITY: Duration = Duration::hours(2); static ref JWT_HEADER: Header = Header::new(JWT_ALGORITHM); - pub static ref JWT_LOGIN_ISSUER: String = format!("{}|login", CONFIG.domain()); - pub static ref JWT_INVITE_ISSUER: String = format!("{}|invite", CONFIG.domain()); - pub static ref JWT_DELETE_ISSUER: String = format!("{}|delete", CONFIG.domain()); - pub static ref JWT_VERIFYEMAIL_ISSUER: String = format!("{}|verifyemail", CONFIG.domain()); - pub static ref JWT_ADMIN_ISSUER: String = format!("{}|admin", CONFIG.domain()); + pub static ref JWT_LOGIN_ISSUER: String = format!("{}|login", CONFIG.domain_origin()); + pub static ref JWT_INVITE_ISSUER: String = format!("{}|invite", CONFIG.domain_origin()); + pub static ref JWT_DELETE_ISSUER: String = format!("{}|delete", CONFIG.domain_origin()); + pub static ref JWT_VERIFYEMAIL_ISSUER: String = format!("{}|verifyemail", CONFIG.domain_origin()); + pub static ref JWT_ADMIN_ISSUER: String = format!("{}|admin", CONFIG.domain_origin()); static ref PRIVATE_RSA_KEY: Vec = match read_file(&CONFIG.private_rsa_key()) { Ok(key) => key, Err(e) => panic!("Error loading private RSA Key.\n Error: {}", e), diff --git a/src/config.rs b/src/config.rs index 2c53a34..a8e440b 100644 --- a/src/config.rs +++ b/src/config.rs @@ -1,6 +1,8 @@ use std::process::exit; use std::sync::RwLock; +use reqwest::Url; + use crate::error::Error; use crate::util::{get_env, get_env_bool}; @@ -240,6 +242,10 @@ make_config! { domain: String, true, def, "http://localhost".to_string(); /// Domain Set |> Indicates if the domain is set by the admin. Otherwise the default will be used. domain_set: bool, false, def, false; + /// Domain origin |> Domain URL origin (in https://example.com:8443/path, https://example.com:8443 is the origin) + domain_origin: String, false, auto, |c| extract_url_origin(&c.domain); + /// Domain path |> Domain URL path (in https://example.com:8443/path, /path is the path) + domain_path: String, false, auto, |c| extract_url_path(&c.domain); /// Enable web vault web_vault_enabled: bool, false, def, true; @@ -457,6 +463,21 @@ fn validate_config(cfg: &ConfigItems) -> Result<(), Error> { Ok(()) } +/// Extracts an RFC 6454 web origin from a URL. +fn extract_url_origin(url: &str) -> String { + let url = Url::parse(url).expect("valid URL"); + + url.origin().ascii_serialization() +} + +/// Extracts the path from a URL. +/// All trailing '/' chars are trimmed, even if the path is a lone '/'. +fn extract_url_path(url: &str) -> String { + let url = Url::parse(url).expect("valid URL"); + + url.path().trim_end_matches('/').to_string() +} + impl Config { pub fn load() -> Result { // Loading from env and file diff --git a/src/main.rs b/src/main.rs index 7c9d026..3c12063 100644 --- a/src/main.rs +++ b/src/main.rs @@ -255,18 +255,20 @@ mod migrations { } fn launch_rocket(extra_debug: bool) { - // Create Rocket object, this stores current log level and sets it's own + // Create Rocket object, this stores current log level and sets its own let rocket = rocket::ignite(); - // If addding more base paths here, consider also adding them to + let basepath = &CONFIG.domain_path(); + + // If adding more paths here, consider also adding them to // crate::utils::LOGGED_ROUTES to make sure they appear in the log let rocket = rocket - .mount("/", api::web_routes()) - .mount("/api", api::core_routes()) - .mount("/admin", api::admin_routes()) - .mount("/identity", api::identity_routes()) - .mount("/icons", api::icons_routes()) - .mount("/notifications", api::notifications_routes()) + .mount(&[basepath, "/"].concat(), api::web_routes()) + .mount(&[basepath, "/api"].concat(), api::core_routes()) + .mount(&[basepath, "/admin"].concat(), api::admin_routes()) + .mount(&[basepath, "/identity"].concat(), api::identity_routes()) + .mount(&[basepath, "/icons"].concat(), api::icons_routes()) + .mount(&[basepath, "/notifications"].concat(), api::notifications_routes()) .manage(db::init_pool()) .manage(api::start_notification_server()) .attach(util::AppHeaders()) diff --git a/src/static/templates/admin/base.hbs b/src/static/templates/admin/base.hbs index f61ae6a..e3948d0 100644 --- a/src/static/templates/admin/base.hbs +++ b/src/static/templates/admin/base.hbs @@ -6,10 +6,10 @@ Bitwarden_rs Admin Panel - - - - + + + +