diff --git a/README.md b/README.md index 0351a30..f78e15b 100644 --- a/README.md +++ b/README.md @@ -11,13 +11,14 @@ _*Note, that this project is not associated with the [Bitwarden](https://bitward - [Updating the bitwarden image](#updating-the-bitwarden-image) - [Configuring bitwarden service](#configuring-bitwarden-service) - [Disable registration of new users](#disable-registration-of-new-users) + - [Enabling HTTPS](#enabling-https) + - [Enabling U2F authentication](#enabling-u2f-authentication) - [Changing persistent data location](#changing-persistent-data-location) - [/data prefix:](#data-prefix) - [database name and location](#database-name-and-location) - [attachments location](#attachments-location) - [icons cache](#icons-cache) - [Changing the API request size limit](#changing-the-api-request-size-limit) - - [Enabling HTTPS](#enabling-https) - [Other configuration](#other-configuration) - [Building your own image](#building-your-own-image) - [Building binary](#building-binary) @@ -41,6 +42,14 @@ Basically full implementation of Bitwarden API is provided including: * Vault API support * Serving the static files for Vault interface * Website icons API + * Authenticator and U2F support + +## Missing features +* Email confirmation +* Other two-factor systems: + * YubiKey OTP (if your key supports U2F, you can use that) + * Duo + * Email codes ## Docker image usage @@ -109,6 +118,44 @@ docker run -d --name bitwarden \ mprasil/bitwarden:latest ``` +### Enabling HTTPS +To enable HTTPS, you need to configure the `ROCKET_TLS`. + +The values to the option must follow the format: +``` +ROCKET_TLS={certs="/path/to/certs.pem",key="/path/to/key.pem"} +``` +Where: +- certs: a path to a certificate chain in PEM format +- key: a path to a private key file in PEM format for the certificate in certs + +```sh +docker run -d --name bitwarden \ + -e ROCKET_TLS={certs='"/ssl/certs.pem",key="/ssl/key.pem"}' \ + -v /ssl/keys/:/ssl/ \ + -v /bw-data/:/data/ \ + -v /icon_cache/ \ + -p 443:443 \ + mprasil/bitwarden:latest +``` +Note that you need to mount ssl files and you need to forward appropriate port. + +### Enabling U2F authentication +To enable U2F authentication, you must be serving bitwarden_rs from an HTTPS domain with a valid certificate (Either using the included +HTTPS options or with a reverse proxy). We recommend using a free certificate from Let's Encrypt. + +After that, you need to set the `DOMAIN` environment variable to the same address from where bitwarden_rs is being served: + +```sh +docker run -d --name bitwarden \ + -e DOMAIN=https://bw.domain.tld \ + -v /bw-data/:/data/ \ + -p 80:80 \ + mprasil/bitwarden:latest +``` + +Note that the value has to include the `https://` and it may include a port at the end (in the format of `https://bw.domain.tld:port`) when not using `443`. + ### Changing persistent data location #### /data prefix: @@ -184,28 +231,6 @@ docker run -d --name bitwarden \ mprasil/bitwarden:latest ``` -### Enabling HTTPS -To enable HTTPS, you need to configure the `ROCKET_TLS`. - -The values to the option must follow the format: -``` -ROCKET_TLS={certs="/path/to/certs.pem",key="/path/to/key.pem"} -``` -Where: -- certs: a path to a certificate chain in PEM format -- key: a path to a private key file in PEM format for the certificate in certs - -```sh -docker run -d --name bitwarden \ - -e ROCKET_TLS={certs='"/ssl/certs.pem",key="/ssl/key.pem"}' \ - -v /ssl/keys/:/ssl/ \ - -v /bw-data/:/data/ \ - -v /icon_cache/ \ - -p 443:443 \ - mprasil/bitwarden:latest -``` -Note that you need to mount ssl files and you need to forward appropriate port. - ### Other configuration Though this is unlikely to be required in small deployment, you can fine-tune some other settings like number of workers using environment variables that are processed by [Rocket](https://rocket.rs), please see details in [documentation](https://rocket.rs/guide/configuration/#environment-variables). diff --git a/src/api/core/two_factor.rs b/src/api/core/two_factor.rs index 031711f..420362a 100644 --- a/src/api/core/two_factor.rs +++ b/src/api/core/two_factor.rs @@ -217,6 +217,10 @@ lazy_static! { #[post("/two-factor/get-u2f", data = "")] fn generate_u2f(data: JsonUpcase, headers: Headers, conn: DbConn) -> JsonResult { + if !CONFIG.domain_set { + err!("`DOMAIN` environment variable is not set. U2F disabled") + } + let data: PasswordData = data.into_inner().data; if !headers.user.check_valid_password(&data.MasterPasswordHash) { @@ -299,8 +303,6 @@ fn activate_u2f(data: JsonUpcase, headers: Headers, conn: DbConn) .delete(&conn) .expect("Error deleting U2F register challenge"); - println!("RegisterResponse {:#?}", &data.DeviceResponse); - let response_copy: RegisterResponseCopy = serde_json::from_str(&data.DeviceResponse).expect("Can't parse RegisterResponse data"); @@ -388,7 +390,6 @@ impl Into for RegistrationCopy { } fn _parse_registrations(registations: &str) -> Vec { - println!("Registrations {:#?}", registations); let registrations_copy: Vec = serde_json::from_str(registations).expect("Can't parse RegistrationCopy data"); @@ -418,8 +419,6 @@ pub fn validate_u2f_login(user_uuid: &str, response: &str, conn: &DbConn) -> Api let challenge = match tf_challenge { Some(tf_challenge) => { - println!("challenge {:#?}", &tf_challenge.data); - let challenge: Challenge = serde_json::from_str(&tf_challenge.data) .expect("Can't parse U2fLoginChallenge data"); tf_challenge @@ -437,8 +436,6 @@ pub fn validate_u2f_login(user_uuid: &str, response: &str, conn: &DbConn) -> Api let registrations = _parse_registrations(&twofactor.data); - println!("response {:#?}", response); - let response: SignResponse = serde_json::from_str(response).expect("Can't parse SignResponse data"); diff --git a/src/api/identity.rs b/src/api/identity.rs index b14c198..fb6120e 100644 --- a/src/api/identity.rs +++ b/src/api/identity.rs @@ -14,6 +14,8 @@ use util::{self, JsonMap}; use api::{ApiResult, JsonResult}; +use CONFIG; + pub fn routes() -> Vec { routes![login] } @@ -216,7 +218,7 @@ fn _json_err_twofactor(providers: &[i32], user_uuid: &str, conn: &DbConn) -> Api match TwoFactorType::from_i32(*provider) { Some(TwoFactorType::Authenticator) => { /* Nothing to do for TOTP */ } - Some(TwoFactorType::U2f) => { + Some(TwoFactorType::U2f) if CONFIG.domain_set => { let request = two_factor::generate_u2f_login(user_uuid, conn)?; let mut challenge_list = Vec::new(); diff --git a/src/util.rs b/src/util.rs index 78d169a..4a18680 100644 --- a/src/util.rs +++ b/src/util.rs @@ -3,7 +3,8 @@ /// #[macro_export] macro_rules! err { - ($err:expr, $err_desc:expr, $msg:expr) => { + ($err:expr, $err_desc:expr, $msg:expr) => {{ + println!("ERROR: {}", $msg); err_json!(json!({ "error": $err, "error_description": $err_desc, @@ -13,14 +14,13 @@ macro_rules! err { "Object": "error" } })) - }; + }}; ($msg:expr) => { err!("default_error", "default_error_description", $msg) } } #[macro_export] macro_rules! err_json { ($expr:expr) => {{ - println!("ERROR: {}", $expr); return Err($crate::rocket::response::status::BadRequest(Some($crate::rocket_contrib::Json($expr)))); }} }