Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add backend support for alternate base dir (subdir/subpath) hosting #868

Merged
merged 4 commits into from
Feb 22, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 2 additions & 3 deletions docker/Dockerfile.j2
Original file line number Diff line number Diff line change
Expand Up @@ -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"

Expand All @@ -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"]
Expand Down
5 changes: 2 additions & 3 deletions docker/aarch64/mysql/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -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"]

Expand Down
5 changes: 2 additions & 3 deletions docker/aarch64/sqlite/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -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"]

Expand Down
5 changes: 2 additions & 3 deletions docker/amd64/mysql/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -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"]

Expand Down
5 changes: 2 additions & 3 deletions docker/amd64/mysql/Dockerfile.alpine
Original file line number Diff line number Diff line change
Expand Up @@ -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"]

Expand Down
5 changes: 2 additions & 3 deletions docker/amd64/postgresql/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -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"]

Expand Down
5 changes: 2 additions & 3 deletions docker/amd64/postgresql/Dockerfile.alpine
Original file line number Diff line number Diff line change
Expand Up @@ -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"]

Expand Down
5 changes: 2 additions & 3 deletions docker/amd64/sqlite/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -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"]

Expand Down
5 changes: 2 additions & 3 deletions docker/amd64/sqlite/Dockerfile.alpine
Original file line number Diff line number Diff line change
Expand Up @@ -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"]

Expand Down
5 changes: 2 additions & 3 deletions docker/armv6/mysql/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -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"]

Expand Down
5 changes: 2 additions & 3 deletions docker/armv6/sqlite/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -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"]

Expand Down
5 changes: 2 additions & 3 deletions docker/armv7/mysql/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -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"]

Expand Down
5 changes: 2 additions & 3 deletions docker/armv7/sqlite/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -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"]

Expand Down
16 changes: 11 additions & 5 deletions src/api/admin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<FlashMessage>) -> ApiResult<Html<String>> {
// 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)?;
Expand All @@ -76,7 +80,7 @@ fn post_admin_login(data: Form<LoginForm>, 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 {
Expand All @@ -85,14 +89,14 @@ fn post_admin_login(data: Form<LoginForm>, 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()))
}
}

Expand All @@ -111,6 +115,7 @@ struct AdminTemplateData {
config: Value,
can_backup: bool,
logged_in: bool,
urlpath: String,
}

impl AdminTemplateData {
Expand All @@ -122,6 +127,7 @@ impl AdminTemplateData {
config: CONFIG.prepare_json(),
can_backup: *CAN_BACKUP,
logged_in: true,
urlpath: CONFIG.domain_path(),
}
}

Expand Down Expand Up @@ -167,7 +173,7 @@ fn invite_user(data: Json<InviteData>, _token: AdminToken, conn: DbConn) -> Empt
#[get("/logout")]
fn logout(mut cookies: Cookies) -> Result<Redirect, ()> {
cookies.remove(Cookie::named(COOKIE_NAME));
Ok(Redirect::to(ADMIN_PATH))
Ok(Redirect::to(admin_path()))
}

#[get("/users")]
Expand Down
2 changes: 1 addition & 1 deletion src/api/core/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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: <a href=\"https://haveibeenpwned.com/account/{account}\" target=\"_blank\" rel=\"noopener\">https://haveibeenpwned.com/account/{account}</a> for a manual check.<br/><br/>HaveIBeenPwned API key not set!<br/>Go to <a href=\"https://haveibeenpwned.com/API/Key\" target=\"_blank\" rel=\"noopener\">https://haveibeenpwned.com/API/Key</a> to purchase an API key from HaveIBeenPwned.<br/><br/>", account=username),
"LogoPath": "/bwrs_static/hibp.png",
"LogoPath": "bwrs_static/hibp.png",
"PwnCount": 0,
"DataClasses": [
"Error - No API key set!"
Expand Down
14 changes: 12 additions & 2 deletions src/api/web.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,17 @@ fn app_id() -> Cached<Content<Json<Value>>> {
{
"version": { "major": 1, "minor": 0 },
"ids": [
&CONFIG.domain(),
// Per <https://fidoalliance.org/specs/fido-v2.0-id-20180227/fido-appid-and-facets-v2.0-id-20180227.html#determining-the-facetid-of-a-calling-application>:
//
// "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" ]
}]
Expand Down Expand Up @@ -75,6 +85,6 @@ fn static_files(filename: String) -> Result<Content<&'static [u8]>, 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)),
}
}
10 changes: 5 additions & 5 deletions src/auth.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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());
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I changed this to domain_origin() instead of domain() to avoid JWT encode/decode issues in case the installation is moved to a different base dir. Note that in the usual case where the backend is hosted at the root of the domain, domain_origin() and domain() should be identical.

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<u8> = match read_file(&CONFIG.private_rsa_key()) {
Ok(key) => key,
Err(e) => panic!("Error loading private RSA Key.\n Error: {}", e),
Expand Down
21 changes: 21 additions & 0 deletions src/config.rs
Original file line number Diff line number Diff line change
@@ -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};

Expand Down Expand Up @@ -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;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems like domain_set could be made auto as well, but maybe there's some subtlety I'm missing, so I left it as is.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

/// 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;

Expand Down Expand Up @@ -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<Self, Error> {
// Loading from env and file
Expand Down
Loading