mirror of
https://github.com/dani-garcia/vaultwarden.git
synced 2025-09-10 18:55:57 +03:00
Compare commits
66 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
70f3ab8ec3 | ||
|
b6612e90ca | ||
|
161cccca30 | ||
|
84dc2eda1f | ||
|
390d10d656 | ||
|
1f775f4414 | ||
|
cc404b4edc | ||
|
536672ac1b | ||
|
e41e7c07db | ||
|
f1d3b03c60 | ||
|
2ebff958a4 | ||
|
edfdda86ae | ||
|
97fb7b5b96 | ||
|
f6de144cbb | ||
|
5a974c7b94 | ||
|
5f61607419 | ||
|
cd8907542a | ||
|
8a5450e830 | ||
|
ad9f2b2d8e | ||
|
2f4a9865e1 | ||
|
0a3008e753 | ||
|
29a0795219 | ||
|
63459c5f72 | ||
|
916e96b143 | ||
|
325039c316 | ||
|
c5b97f4146 | ||
|
03233429f4 | ||
|
0a72c4b6db | ||
|
8867626de8 | ||
|
f5916ec396 | ||
|
ebb36235a7 | ||
|
def174a517 | ||
|
2798f623d4 | ||
|
480ba933fa | ||
|
3d1ee9ef62 | ||
|
5352321fe1 | ||
|
c4101162d6 | ||
|
632d55265b | ||
|
e277f7d1c1 | ||
|
ff7b4a3d38 | ||
|
d212dfe735 | ||
|
84ed185579 | ||
|
c0ba3406ef | ||
|
e196ba6e86 | ||
|
76743aee48 | ||
|
9ebca99290 | ||
|
a734ad2d36 | ||
|
baf7d1be4e | ||
|
31bcd1bf7c | ||
|
a3b30ed65a | ||
|
402c857d17 | ||
|
def858854b | ||
|
f6761ac30e | ||
|
f8e49ea3f4 | ||
|
f6a4a2127b | ||
|
446fc3f1f8 | ||
|
146525db91 | ||
|
1698b43f9b | ||
|
078b21db85 | ||
|
43adcde094 | ||
|
7a0bb18dcf | ||
|
47a5a4e1fc | ||
|
0f0e5876ae | ||
|
43aa75dc89 | ||
|
8280d200ea | ||
|
f250c54813 |
1
.github/FUNDING.yml
vendored
1
.github/FUNDING.yml
vendored
@@ -1 +1,2 @@
|
|||||||
github: dani-garcia
|
github: dani-garcia
|
||||||
|
custom: ["https://paypal.me/DaniGG"]
|
||||||
|
@@ -1,3 +1,12 @@
|
|||||||
|
---
|
||||||
|
name: Bug report
|
||||||
|
about: Create a report to help us improve
|
||||||
|
title: ''
|
||||||
|
labels: ''
|
||||||
|
assignees: ''
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
<!--
|
<!--
|
||||||
Please fill out the following template to make solving your problem easier and faster for us.
|
Please fill out the following template to make solving your problem easier and faster for us.
|
||||||
This is only a guideline. If you think that parts are unneccessary for your issue, feel free to remove them.
|
This is only a guideline. If you think that parts are unneccessary for your issue, feel free to remove them.
|
11
.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal file
11
.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
---
|
||||||
|
name: Feature request
|
||||||
|
about: Suggest an idea for this project
|
||||||
|
title: ''
|
||||||
|
labels: better for forum
|
||||||
|
assignees: ''
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# Please submit all your feature requests to the forum
|
||||||
|
Link: https://bitwardenrs.discourse.group/c/feature-requests
|
11
.github/ISSUE_TEMPLATE/help-with-installation-configuration.md
vendored
Normal file
11
.github/ISSUE_TEMPLATE/help-with-installation-configuration.md
vendored
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
---
|
||||||
|
name: Help with installation/configuration
|
||||||
|
about: Any questions about the setup of bitwarden_rs
|
||||||
|
title: ''
|
||||||
|
labels: better for forum
|
||||||
|
assignees: ''
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# Please submit all your third party help requests to the forum
|
||||||
|
Link: https://bitwardenrs.discourse.group/c/help
|
11
.github/ISSUE_TEMPLATE/help-with-proxy-database-nas-setup.md
vendored
Normal file
11
.github/ISSUE_TEMPLATE/help-with-proxy-database-nas-setup.md
vendored
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
---
|
||||||
|
name: Help with proxy/database/NAS setup
|
||||||
|
about: Any questions about third party software
|
||||||
|
title: ''
|
||||||
|
labels: better for forum
|
||||||
|
assignees: ''
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# Please submit all your third party help requests to the forum
|
||||||
|
Link: https://bitwardenrs.discourse.group/c/third-party-help
|
70
.github/workflows/rust-win.yml.disabled
vendored
70
.github/workflows/rust-win.yml.disabled
vendored
@@ -1,70 +0,0 @@
|
|||||||
name: build-windows
|
|
||||||
|
|
||||||
on: [push, pull_request]
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
build:
|
|
||||||
|
|
||||||
runs-on: windows-latest
|
|
||||||
|
|
||||||
strategy:
|
|
||||||
matrix:
|
|
||||||
db-backend: [sqlite, mysql, postgresql]
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v1
|
|
||||||
|
|
||||||
- name: Cache choco cache
|
|
||||||
uses: actions/cache@v1.0.3
|
|
||||||
with:
|
|
||||||
path: ~\AppData\Local\Temp\chocolatey
|
|
||||||
key: ${{ runner.os }}-choco-cache
|
|
||||||
|
|
||||||
- name: Install dependencies
|
|
||||||
run: choco install openssl sqlite postgresql12 mysql
|
|
||||||
|
|
||||||
- name: Cache cargo registry
|
|
||||||
uses: actions/cache@v1.0.3
|
|
||||||
with:
|
|
||||||
path: ~/.cargo/registry
|
|
||||||
key: ${{ runner.os }}-cargo-registry-${{ hashFiles('**/Cargo.lock') }}
|
|
||||||
- name: Cache cargo index
|
|
||||||
uses: actions/cache@v1.0.3
|
|
||||||
with:
|
|
||||||
path: ~/.cargo/git
|
|
||||||
key: ${{ runner.os }}-cargo-index-${{ hashFiles('**/Cargo.lock') }}
|
|
||||||
- name: Cache cargo build
|
|
||||||
uses: actions/cache@v1.0.3
|
|
||||||
with:
|
|
||||||
path: target
|
|
||||||
key: ${{ runner.os }}-cargo-build-target-${{ hashFiles('**/Cargo.lock') }}
|
|
||||||
|
|
||||||
- name: Install latest nightly
|
|
||||||
uses: actions-rs/toolchain@v1
|
|
||||||
with:
|
|
||||||
toolchain: nightly
|
|
||||||
override: true
|
|
||||||
profile: minimal
|
|
||||||
target: x86_64-pc-windows-msvc
|
|
||||||
|
|
||||||
- name: Build
|
|
||||||
run: cargo.exe build --verbose --features ${{ matrix.db-backend }} --release --target x86_64-pc-windows-msvc
|
|
||||||
env:
|
|
||||||
OPENSSL_DIR: C:\Program Files\OpenSSL-Win64\
|
|
||||||
|
|
||||||
- name: Run tests
|
|
||||||
run: cargo test --features ${{ matrix.db-backend }}
|
|
||||||
|
|
||||||
- name: Upload windows artifact
|
|
||||||
uses: actions/upload-artifact@v1.0.0
|
|
||||||
with:
|
|
||||||
name: x86_64-pc-windows-msvc-${{ matrix.db-backend }}-bitwarden_rs
|
|
||||||
path: target/release/bitwarden_rs.exe
|
|
||||||
|
|
||||||
- name: Release
|
|
||||||
uses: Shopify/upload-to-release@1.0.0
|
|
||||||
if: startsWith(github.ref, 'refs/tags/')
|
|
||||||
with:
|
|
||||||
name: x86_64-pc-windows-msvc-${{ matrix.db-backend }}-bitwarden_rs
|
|
||||||
path: target/release/bitwarden_rs.exe
|
|
||||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
|
19
.github/workflows/workspace.yml
vendored
19
.github/workflows/workspace.yml
vendored
@@ -4,9 +4,9 @@ on:
|
|||||||
push:
|
push:
|
||||||
paths-ignore:
|
paths-ignore:
|
||||||
- "**.md"
|
- "**.md"
|
||||||
pull_request:
|
#pull_request:
|
||||||
paths-ignore:
|
# paths-ignore:
|
||||||
- "**.md"
|
# - "**.md"
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
@@ -18,7 +18,7 @@ jobs:
|
|||||||
target:
|
target:
|
||||||
- x86_64-unknown-linux-gnu
|
- x86_64-unknown-linux-gnu
|
||||||
# - x86_64-unknown-linux-musl
|
# - x86_64-unknown-linux-musl
|
||||||
- x86_64-apple-darwin
|
# - x86_64-apple-darwin
|
||||||
# - x86_64-pc-windows-msvc
|
# - x86_64-pc-windows-msvc
|
||||||
include:
|
include:
|
||||||
- target: x86_64-unknown-linux-gnu
|
- target: x86_64-unknown-linux-gnu
|
||||||
@@ -27,9 +27,9 @@ jobs:
|
|||||||
# - target: x86_64-unknown-linux-musl
|
# - target: x86_64-unknown-linux-musl
|
||||||
# os: ubuntu-latest
|
# os: ubuntu-latest
|
||||||
# ext:
|
# ext:
|
||||||
- target: x86_64-apple-darwin
|
# - target: x86_64-apple-darwin
|
||||||
os: macOS-latest
|
# os: macOS-latest
|
||||||
ext:
|
# ext:
|
||||||
# - target: x86_64-pc-windows-msvc
|
# - target: x86_64-pc-windows-msvc
|
||||||
# os: windows-latest
|
# os: windows-latest
|
||||||
# ext: .exe
|
# ext: .exe
|
||||||
@@ -110,10 +110,9 @@ jobs:
|
|||||||
key: ${{ runner.os }}-${{matrix.db-backend}}-cargo-build-target-${{ hashFiles('**/Cargo.lock') }}
|
key: ${{ runner.os }}-${{matrix.db-backend}}-cargo-build-target-${{ hashFiles('**/Cargo.lock') }}
|
||||||
|
|
||||||
- name: Install latest nightly
|
- name: Install latest nightly
|
||||||
uses: actions-rs/toolchain@v1
|
uses: actions-rs/toolchain@v1.0.5
|
||||||
with:
|
with:
|
||||||
toolchain: nightly
|
# Uses rust-toolchain to determine version
|
||||||
override: true
|
|
||||||
profile: minimal
|
profile: minimal
|
||||||
target: ${{ matrix.target }}
|
target: ${{ matrix.target }}
|
||||||
|
|
||||||
|
2622
Cargo.lock
generated
2622
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
47
Cargo.toml
47
Cargo.toml
@@ -14,7 +14,7 @@ build = "build.rs"
|
|||||||
# Empty to keep compatibility, prefer to set USE_SYSLOG=true
|
# Empty to keep compatibility, prefer to set USE_SYSLOG=true
|
||||||
enable_syslog = []
|
enable_syslog = []
|
||||||
mysql = ["diesel/mysql", "diesel_migrations/mysql"]
|
mysql = ["diesel/mysql", "diesel_migrations/mysql"]
|
||||||
postgresql = ["diesel/postgres", "diesel_migrations/postgres", "openssl"]
|
postgresql = ["diesel/postgres", "diesel_migrations/postgres"]
|
||||||
sqlite = ["diesel/sqlite", "diesel_migrations/sqlite", "libsqlite3-sys"]
|
sqlite = ["diesel/sqlite", "diesel_migrations/sqlite", "libsqlite3-sys"]
|
||||||
|
|
||||||
[target."cfg(not(windows))".dependencies]
|
[target."cfg(not(windows))".dependencies]
|
||||||
@@ -35,7 +35,7 @@ multipart = { version = "0.16.1", features = ["server"], default-features = fals
|
|||||||
ws = "0.9.1"
|
ws = "0.9.1"
|
||||||
|
|
||||||
# MessagePack library
|
# MessagePack library
|
||||||
rmpv = "0.4.3"
|
rmpv = "0.4.4"
|
||||||
|
|
||||||
# Concurrent hashmap implementation
|
# Concurrent hashmap implementation
|
||||||
chashmap = "2.2.2"
|
chashmap = "2.2.2"
|
||||||
@@ -43,11 +43,11 @@ chashmap = "2.2.2"
|
|||||||
# A generic serialization/deserialization framework
|
# A generic serialization/deserialization framework
|
||||||
serde = "1.0.104"
|
serde = "1.0.104"
|
||||||
serde_derive = "1.0.104"
|
serde_derive = "1.0.104"
|
||||||
serde_json = "1.0.44"
|
serde_json = "1.0.48"
|
||||||
|
|
||||||
# Logging
|
# Logging
|
||||||
log = "0.4.8"
|
log = "0.4.8"
|
||||||
fern = { version = "0.5.9", features = ["syslog-4"] }
|
fern = { version = "0.6.0", features = ["syslog-4"] }
|
||||||
|
|
||||||
# A safe, extensible ORM and Query builder
|
# A safe, extensible ORM and Query builder
|
||||||
diesel = { version = "1.4.3", features = [ "chrono", "r2d2"] }
|
diesel = { version = "1.4.3", features = [ "chrono", "r2d2"] }
|
||||||
@@ -63,19 +63,19 @@ ring = "0.14.6"
|
|||||||
uuid = { version = "0.8.1", features = ["v4"] }
|
uuid = { version = "0.8.1", features = ["v4"] }
|
||||||
|
|
||||||
# Date and time library for Rust
|
# Date and time library for Rust
|
||||||
chrono = "0.4.10"
|
chrono = "0.4.11"
|
||||||
|
|
||||||
# TOTP library
|
# TOTP library
|
||||||
oath = "0.10.2"
|
oath = "0.10.2"
|
||||||
|
|
||||||
# Data encoding library
|
# Data encoding library
|
||||||
data-encoding = "2.1.2"
|
data-encoding = "2.2.0"
|
||||||
|
|
||||||
# JWT library
|
# JWT library
|
||||||
jsonwebtoken = "6.0.1"
|
jsonwebtoken = "6.0.1"
|
||||||
|
|
||||||
# U2F library
|
# U2F library
|
||||||
u2f = "0.1.6"
|
u2f = "0.2.0"
|
||||||
|
|
||||||
# Yubico Library
|
# Yubico Library
|
||||||
yubico = { version = "0.7.1", features = ["online-tokio"], default-features = false }
|
yubico = { version = "0.7.1", features = ["online-tokio"], default-features = false }
|
||||||
@@ -83,35 +83,39 @@ yubico = { version = "0.7.1", features = ["online-tokio"], default-features = fa
|
|||||||
# A `dotenv` implementation for Rust
|
# A `dotenv` implementation for Rust
|
||||||
dotenv = { version = "0.15.0", default-features = false }
|
dotenv = { version = "0.15.0", default-features = false }
|
||||||
|
|
||||||
# Lazy static macro
|
# Lazy initialization
|
||||||
lazy_static = "1.4.0"
|
once_cell = "1.3.1"
|
||||||
|
|
||||||
# More derives
|
# More derives
|
||||||
derive_more = "0.99.2"
|
derive_more = "0.99.3"
|
||||||
|
|
||||||
# Numerical libraries
|
# Numerical libraries
|
||||||
num-traits = "0.2.10"
|
num-traits = "0.2.11"
|
||||||
num-derive = "0.3.0"
|
num-derive = "0.3.0"
|
||||||
|
|
||||||
# Email libraries
|
# Email libraries
|
||||||
lettre = "0.9.2"
|
lettre = "0.10.0-pre"
|
||||||
lettre_email = "0.9.2"
|
native-tls = "0.2.4"
|
||||||
native-tls = "0.2.3"
|
quoted_printable = "0.4.2"
|
||||||
quoted_printable = "0.4.1"
|
|
||||||
|
|
||||||
# Template library
|
# Template library
|
||||||
handlebars = "=2.0.2"
|
handlebars = { version = "3.0.1", features = ["dir_source"] }
|
||||||
|
|
||||||
# For favicon extraction from main website
|
# For favicon extraction from main website
|
||||||
soup = "0.4.1"
|
soup = "0.5.0"
|
||||||
regex = "1.3.1"
|
regex = "1.3.4"
|
||||||
data-url = "0.1.0"
|
data-url = "0.1.0"
|
||||||
|
|
||||||
# Required for SSL support for PostgreSQL
|
# Used by U2F, JWT and Postgres
|
||||||
openssl = { version = "0.10.26", optional = true }
|
openssl = "0.10.28"
|
||||||
|
|
||||||
# URL encoding library
|
# URL encoding library
|
||||||
percent-encoding = "2.1.0"
|
percent-encoding = "2.1.0"
|
||||||
|
# Punycode conversion
|
||||||
|
idna = "0.2.0"
|
||||||
|
|
||||||
|
# CLI argument parsing
|
||||||
|
structopt = "0.3.11"
|
||||||
|
|
||||||
[patch.crates-io]
|
[patch.crates-io]
|
||||||
# Use newest ring
|
# Use newest ring
|
||||||
@@ -119,8 +123,7 @@ rocket = { git = 'https://github.com/SergioBenitez/Rocket', rev = 'b95b6765e1cc8
|
|||||||
rocket_contrib = { git = 'https://github.com/SergioBenitez/Rocket', rev = 'b95b6765e1cc8be7c1e7eaef8a9d9ad940b0ac13' }
|
rocket_contrib = { git = 'https://github.com/SergioBenitez/Rocket', rev = 'b95b6765e1cc8be7c1e7eaef8a9d9ad940b0ac13' }
|
||||||
|
|
||||||
# Use git version for timeout fix #706
|
# Use git version for timeout fix #706
|
||||||
lettre = { git = 'https://github.com/lettre/lettre', rev = '24d694db3be017d82b1cdc8bf9da601420b31bb0' }
|
lettre = { git = 'https://github.com/lettre/lettre', rev = '245c600c82ee18b766e8729f005ff453a55dce34' }
|
||||||
lettre_email = { git = 'https://github.com/lettre/lettre', rev = '24d694db3be017d82b1cdc8bf9da601420b31bb0' }
|
|
||||||
|
|
||||||
# For favicon extraction from main website
|
# For favicon extraction from main website
|
||||||
data-url = { git = 'https://github.com/servo/rust-url', package="data-url", rev = '7f1bd6ce1c2fde599a757302a843a60e714c5f72' }
|
data-url = { git = 'https://github.com/servo/rust-url', package="data-url", rev = '7f1bd6ce1c2fde599a757302a843a60e714c5f72' }
|
||||||
|
11
README.md
11
README.md
@@ -13,7 +13,7 @@ Image is based on [Rust implementation of Bitwarden API](https://github.com/dani
|
|||||||
|
|
||||||
**This project is not associated with the [Bitwarden](https://bitwarden.com/) project nor 8bit Solutions LLC.**
|
**This project is not associated with the [Bitwarden](https://bitwarden.com/) project nor 8bit Solutions LLC.**
|
||||||
|
|
||||||
#### ⚠️**IMPORTANT**⚠️: When using this server, please report any Bitwarden related bug-reports or suggestions [here](https://github.com/dani-garcia/bitwarden_rs/issues/new), regardless of whatever clients you are using (mobile, desktop, browser...). DO NOT use the official support channels.
|
#### ⚠️**IMPORTANT**⚠️: When using this server, please report any bugs or suggestions to us directly (look at the bottom of this page for ways to get in touch), regardless of whatever clients you are using (mobile, desktop, browser...). DO NOT use the official support channels.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -21,14 +21,14 @@ Image is based on [Rust implementation of Bitwarden API](https://github.com/dani
|
|||||||
|
|
||||||
Basically full implementation of Bitwarden API is provided including:
|
Basically full implementation of Bitwarden API is provided including:
|
||||||
|
|
||||||
* Basic single user functionality
|
* Single user functionality
|
||||||
* Organizations support
|
* Organizations support
|
||||||
* Attachments
|
* Attachments
|
||||||
* Vault API support
|
* Vault API support
|
||||||
* Serving the static files for Vault interface
|
* Serving the static files for Vault interface
|
||||||
* Website icons API
|
* Website icons API
|
||||||
* Authenticator and U2F support
|
* Authenticator and U2F support
|
||||||
* YubiKey OTP
|
* YubiKey and Duo support
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
Pull the docker image and mount a volume from the host for persistent storage:
|
Pull the docker image and mount a volume from the host for persistent storage:
|
||||||
@@ -49,12 +49,13 @@ If you have an available domain name, you can get HTTPS certificates with [Let's
|
|||||||
See the [bitwarden_rs wiki](https://github.com/dani-garcia/bitwarden_rs/wiki) for more information on how to configure and run the bitwarden_rs server.
|
See the [bitwarden_rs wiki](https://github.com/dani-garcia/bitwarden_rs/wiki) for more information on how to configure and run the bitwarden_rs server.
|
||||||
|
|
||||||
## Get in touch
|
## Get in touch
|
||||||
|
To ask a question, offer suggestions or new features or to get help configuring or installing the software, please [use the forum](https://bitwardenrs.discourse.group/).
|
||||||
|
|
||||||
To ask a question, [raising an issue](https://github.com/dani-garcia/bitwarden_rs/issues/new) is fine. Please also report any bugs spotted here.
|
If you spot any bugs or crashes with bitwarden_rs itself, please [create an issue](https://github.com/dani-garcia/bitwarden_rs/issues/). Make sure there aren't any similar issues open, though!
|
||||||
|
|
||||||
If you prefer to chat, we're usually hanging around at [#bitwarden_rs:matrix.org](https://matrix.to/#/#bitwarden_rs:matrix.org) room on Matrix. Feel free to join us!
|
If you prefer to chat, we're usually hanging around at [#bitwarden_rs:matrix.org](https://matrix.to/#/#bitwarden_rs:matrix.org) room on Matrix. Feel free to join us!
|
||||||
|
|
||||||
### Sponsors
|
### Sponsors
|
||||||
Thanks for your contribution to the project!
|
Thanks for your contribution to the project!
|
||||||
|
|
||||||
- [@Skaronator](https://github.com/Skaronator)
|
- [@ChonoN](https://github.com/ChonoN)
|
||||||
|
295
docker/Dockerfile.j2
Normal file
295
docker/Dockerfile.j2
Normal file
@@ -0,0 +1,295 @@
|
|||||||
|
# This file was generated using a Jinja2 template.
|
||||||
|
# Please make your changes in `Dockerfile.j2` and then `make` the individual Dockerfile's.
|
||||||
|
|
||||||
|
{% set build_stage_base_image = "rust:1.40" %}
|
||||||
|
{% if "alpine" in target_file %}
|
||||||
|
{% set build_stage_base_image = "clux/muslrust:nightly-2020-03-09" %}
|
||||||
|
{% set runtime_stage_base_image = "alpine:3.11" %}
|
||||||
|
{% set package_arch_name = "" %}
|
||||||
|
{% elif "amd64" in target_file %}
|
||||||
|
{% set runtime_stage_base_image = "debian:buster-slim" %}
|
||||||
|
{% set package_arch_name = "" %}
|
||||||
|
{% elif "aarch64" in target_file %}
|
||||||
|
{% set runtime_stage_base_image = "balenalib/aarch64-debian:buster" %}
|
||||||
|
{% set package_arch_name = "arm64" %}
|
||||||
|
{% elif "armv6" in target_file %}
|
||||||
|
{% set runtime_stage_base_image = "balenalib/rpi-debian:buster" %}
|
||||||
|
{% set package_arch_name = "armel" %}
|
||||||
|
{% elif "armv7" in target_file %}
|
||||||
|
{% set runtime_stage_base_image = "balenalib/armv7hf-debian:buster" %}
|
||||||
|
{% set package_arch_name = "armhf" %}
|
||||||
|
{% endif %}
|
||||||
|
{% set package_arch_prefix = ":" + package_arch_name %}
|
||||||
|
{% if package_arch_name == "" %}
|
||||||
|
{% set package_arch_prefix = "" %}
|
||||||
|
{% endif %}
|
||||||
|
# Using multistage build:
|
||||||
|
# https://docs.docker.com/develop/develop-images/multistage-build/
|
||||||
|
# https://whitfin.io/speeding-up-rust-docker-builds/
|
||||||
|
####################### VAULT BUILD IMAGE #######################
|
||||||
|
{% set vault_image_hash = "sha256:ce56b3f5e538351411785ac45e9b4b913259c3508b1323d62e8fa0f30717dd1c" %}
|
||||||
|
{% raw %}
|
||||||
|
# This hash is extracted from the docker web-vault builds and it's prefered over a simple tag because it's immutable.
|
||||||
|
# It can be viewed in multiple ways:
|
||||||
|
# - From the https://hub.docker.com/repository/docker/bitwardenrs/web-vault/tags page, click the tag name and the digest should be there.
|
||||||
|
# - From the console, with the following commands:
|
||||||
|
# docker pull bitwardenrs/web-vault:v2.12.0e
|
||||||
|
# docker image inspect --format "{{.RepoDigests}}" bitwardenrs/web-vault:v2.12.0e
|
||||||
|
#
|
||||||
|
# - To do the opposite, and get the tag from the hash, you can do:
|
||||||
|
# docker image inspect --format "{{.RepoTags}}" bitwardenrs/web-vault@sha256:ce56b3f5e538351411785ac45e9b4b913259c3508b1323d62e8fa0f30717dd1c
|
||||||
|
{% endraw %}
|
||||||
|
FROM bitwardenrs/web-vault@{{ vault_image_hash }} as vault
|
||||||
|
|
||||||
|
########################## BUILD IMAGE ##########################
|
||||||
|
{% if "musl" in build_stage_base_image %}
|
||||||
|
# Musl build image for statically compiled binary
|
||||||
|
{% else %}
|
||||||
|
# We need to use the Rust build image, because
|
||||||
|
# we need the Rust compiler and Cargo tooling
|
||||||
|
{% endif %}
|
||||||
|
FROM {{ build_stage_base_image }} as build
|
||||||
|
|
||||||
|
{% if "sqlite" in target_file %}
|
||||||
|
# set sqlite as default for DB ARG for backward compatibility
|
||||||
|
ARG DB=sqlite
|
||||||
|
|
||||||
|
{% elif "mysql" in target_file %}
|
||||||
|
# set mysql backend
|
||||||
|
ARG DB=mysql
|
||||||
|
|
||||||
|
{% elif "postgresql" in target_file %}
|
||||||
|
# set postgresql backend
|
||||||
|
ARG DB=postgresql
|
||||||
|
|
||||||
|
{% endif %}
|
||||||
|
# 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
|
||||||
|
|
||||||
|
# Don't download rust docs
|
||||||
|
RUN rustup set profile minimal
|
||||||
|
|
||||||
|
{% if "alpine" in target_file %}
|
||||||
|
ENV USER "root"
|
||||||
|
|
||||||
|
{% elif "aarch64" in target_file or "armv" in target_file %}
|
||||||
|
# Install required build libs for {{ package_arch_name }} architecture.
|
||||||
|
RUN sed 's/^deb/deb-src/' /etc/apt/sources.list > \
|
||||||
|
/etc/apt/sources.list.d/deb-src.list \
|
||||||
|
&& dpkg --add-architecture {{ package_arch_name }} \
|
||||||
|
&& apt-get update \
|
||||||
|
&& apt-get install -y \
|
||||||
|
--no-install-recommends \
|
||||||
|
libssl-dev{{ package_arch_prefix }} \
|
||||||
|
libc6-dev{{ package_arch_prefix }}
|
||||||
|
|
||||||
|
{% endif -%}
|
||||||
|
{% if "aarch64" in target_file %}
|
||||||
|
RUN apt-get update \
|
||||||
|
&& apt-get install -y \
|
||||||
|
--no-install-recommends \
|
||||||
|
gcc-aarch64-linux-gnu \
|
||||||
|
&& mkdir -p ~/.cargo \
|
||||||
|
&& echo '[target.aarch64-unknown-linux-gnu]' >> ~/.cargo/config \
|
||||||
|
&& echo 'linker = "aarch64-linux-gnu-gcc"' >> ~/.cargo/config
|
||||||
|
|
||||||
|
ENV CARGO_HOME "/root/.cargo"
|
||||||
|
ENV USER "root"
|
||||||
|
|
||||||
|
{% elif "armv6" in target_file %}
|
||||||
|
RUN apt-get update \
|
||||||
|
&& apt-get install -y \
|
||||||
|
--no-install-recommends \
|
||||||
|
gcc-arm-linux-gnueabi \
|
||||||
|
&& mkdir -p ~/.cargo \
|
||||||
|
&& echo '[target.arm-unknown-linux-gnueabi]' >> ~/.cargo/config \
|
||||||
|
&& echo 'linker = "arm-linux-gnueabi-gcc"' >> ~/.cargo/config
|
||||||
|
|
||||||
|
ENV CARGO_HOME "/root/.cargo"
|
||||||
|
ENV USER "root"
|
||||||
|
|
||||||
|
{% elif "armv6" in target_file %}
|
||||||
|
RUN apt-get update \
|
||||||
|
&& apt-get install -y \
|
||||||
|
--no-install-recommends \
|
||||||
|
gcc-arm-linux-gnueabihf \
|
||||||
|
&& mkdir -p ~/.cargo \
|
||||||
|
&& echo '[target.armv7-unknown-linux-gnueabihf]' >> ~/.cargo/config \
|
||||||
|
&& echo 'linker = "arm-linux-gnueabihf-gcc"' >> ~/.cargo/config
|
||||||
|
|
||||||
|
ENV CARGO_HOME "/root/.cargo"
|
||||||
|
ENV USER "root"
|
||||||
|
|
||||||
|
{% elif "armv7" in target_file %}
|
||||||
|
RUN apt-get update \
|
||||||
|
&& apt-get install -y \
|
||||||
|
--no-install-recommends \
|
||||||
|
gcc-arm-linux-gnueabihf \
|
||||||
|
&& mkdir -p ~/.cargo \
|
||||||
|
&& echo '[target.armv7-unknown-linux-gnueabihf]' >> ~/.cargo/config \
|
||||||
|
&& echo 'linker = "arm-linux-gnueabihf-gcc"' >> ~/.cargo/config
|
||||||
|
|
||||||
|
ENV CARGO_HOME "/root/.cargo"
|
||||||
|
ENV USER "root"
|
||||||
|
|
||||||
|
{% endif %}
|
||||||
|
{% if "mysql" in target_file %}
|
||||||
|
# Install MySQL package
|
||||||
|
RUN apt-get update && apt-get install -y \
|
||||||
|
--no-install-recommends \
|
||||||
|
{% if "musl" in build_stage_base_image %}
|
||||||
|
libmysqlclient-dev{{ package_arch_prefix }} \
|
||||||
|
{% else %}
|
||||||
|
libmariadb-dev{{ package_arch_prefix }} \
|
||||||
|
{% endif %}
|
||||||
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
|
{% elif "postgresql" in target_file %}
|
||||||
|
# Install PostgreSQL package
|
||||||
|
RUN apt-get update && apt-get install -y \
|
||||||
|
--no-install-recommends \
|
||||||
|
libpq-dev{{ package_arch_prefix }} \
|
||||||
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
|
{% endif %}
|
||||||
|
# Creates a dummy project used to grab dependencies
|
||||||
|
RUN USER=root cargo new --bin /app
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
# Copies over *only* your manifests and build files
|
||||||
|
COPY ./Cargo.* ./
|
||||||
|
COPY ./rust-toolchain ./rust-toolchain
|
||||||
|
COPY ./build.rs ./build.rs
|
||||||
|
|
||||||
|
{% if "aarch64" in target_file %}
|
||||||
|
ENV CC_aarch64_unknown_linux_gnu="/usr/bin/aarch64-linux-gnu-gcc"
|
||||||
|
ENV CROSS_COMPILE="1"
|
||||||
|
ENV OPENSSL_INCLUDE_DIR="/usr/include/aarch64-linux-gnu"
|
||||||
|
ENV OPENSSL_LIB_DIR="/usr/lib/aarch64-linux-gnu"
|
||||||
|
{% elif "armv6" in target_file %}
|
||||||
|
ENV CC_arm_unknown_linux_gnueabi="/usr/bin/arm-linux-gnueabi-gcc"
|
||||||
|
ENV CROSS_COMPILE="1"
|
||||||
|
ENV OPENSSL_INCLUDE_DIR="/usr/include/arm-linux-gnueabi"
|
||||||
|
ENV OPENSSL_LIB_DIR="/usr/lib/arm-linux-gnueabi"
|
||||||
|
{% elif "armv7" in target_file %}
|
||||||
|
ENV CC_armv7_unknown_linux_gnueabihf="/usr/bin/arm-linux-gnueabihf-gcc"
|
||||||
|
ENV CROSS_COMPILE="1"
|
||||||
|
ENV OPENSSL_INCLUDE_DIR="/usr/include/arm-linux-gnueabihf"
|
||||||
|
ENV OPENSSL_LIB_DIR="/usr/lib/arm-linux-gnueabihf"
|
||||||
|
{% endif -%}
|
||||||
|
|
||||||
|
{% if "alpine" in target_file %}
|
||||||
|
RUN rustup target add x86_64-unknown-linux-musl
|
||||||
|
|
||||||
|
{% elif "aarch64" in target_file %}
|
||||||
|
RUN rustup target add aarch64-unknown-linux-gnu
|
||||||
|
|
||||||
|
{% elif "armv6" in target_file %}
|
||||||
|
RUN rustup target add arm-unknown-linux-gnueabi
|
||||||
|
|
||||||
|
{% elif "armv7" in target_file %}
|
||||||
|
RUN rustup target add armv7-unknown-linux-gnueabihf
|
||||||
|
{% endif %}
|
||||||
|
# Builds your dependencies and removes the
|
||||||
|
# dummy project, except the target folder
|
||||||
|
# This folder contains the compiled dependencies
|
||||||
|
RUN cargo build --features ${DB} --release
|
||||||
|
RUN find . -not -path "./target*" -delete
|
||||||
|
|
||||||
|
# Copies the complete project
|
||||||
|
# To avoid copying unneeded files, use .dockerignore
|
||||||
|
COPY . .
|
||||||
|
|
||||||
|
# Make sure that we actually build the project
|
||||||
|
RUN touch src/main.rs
|
||||||
|
|
||||||
|
# Builds again, this time it'll just be
|
||||||
|
# your actual source files being built
|
||||||
|
{% if "amd64" in target_file %}
|
||||||
|
RUN cargo build --features ${DB} --release
|
||||||
|
{% elif "aarch64" in target_file %}
|
||||||
|
RUN cargo build --features ${DB} --release --target=aarch64-unknown-linux-gnu
|
||||||
|
{% elif "armv6" in target_file %}
|
||||||
|
RUN cargo build --features ${DB} --release --target=arm-unknown-linux-gnueabi
|
||||||
|
{% elif "armv7" in target_file %}
|
||||||
|
RUN cargo build --features ${DB} --release --target=armv7-unknown-linux-gnueabihf
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
######################## RUNTIME IMAGE ########################
|
||||||
|
# Create a new stage with a minimal image
|
||||||
|
# because we already have a binary built
|
||||||
|
FROM {{ runtime_stage_base_image }}
|
||||||
|
|
||||||
|
ENV ROCKET_ENV "staging"
|
||||||
|
ENV ROCKET_PORT=80
|
||||||
|
ENV ROCKET_WORKERS=10
|
||||||
|
{% if "alpine" in runtime_stage_base_image %}
|
||||||
|
ENV SSL_CERT_DIR=/etc/ssl/certs
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% if "amd64" not in target_file %}
|
||||||
|
RUN [ "cross-build-start" ]
|
||||||
|
|
||||||
|
{% endif %}
|
||||||
|
# Install needed libraries
|
||||||
|
{% if "alpine" in runtime_stage_base_image %}
|
||||||
|
RUN apk add --no-cache \
|
||||||
|
openssl \
|
||||||
|
curl \
|
||||||
|
{% if "sqlite" in target_file %}
|
||||||
|
sqlite \
|
||||||
|
{% elif "mysql" in target_file %}
|
||||||
|
mariadb-connector-c \
|
||||||
|
{% elif "postgresql" in target_file %}
|
||||||
|
postgresql-libs \
|
||||||
|
{% endif %}
|
||||||
|
ca-certificates
|
||||||
|
{% else %}
|
||||||
|
RUN apt-get update && apt-get install -y \
|
||||||
|
--no-install-recommends \
|
||||||
|
openssl \
|
||||||
|
ca-certificates \
|
||||||
|
curl \
|
||||||
|
{% if "sqlite" in target_file %}
|
||||||
|
sqlite3 \
|
||||||
|
{% elif "mysql" in target_file %}
|
||||||
|
libmariadbclient-dev \
|
||||||
|
{% elif "postgresql" in target_file %}
|
||||||
|
libpq5 \
|
||||||
|
{% endif %}
|
||||||
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
RUN mkdir /data
|
||||||
|
{% if "amd64" not in target_file %}
|
||||||
|
|
||||||
|
RUN [ "cross-build-end" ]
|
||||||
|
|
||||||
|
{% endif %}
|
||||||
|
VOLUME /data
|
||||||
|
EXPOSE 80
|
||||||
|
EXPOSE 3012
|
||||||
|
|
||||||
|
# Copies the files from the context (Rocket.toml file and web-vault)
|
||||||
|
# and the binary from the "build" stage to the current stage
|
||||||
|
COPY Rocket.toml .
|
||||||
|
COPY --from=vault /web-vault ./web-vault
|
||||||
|
{% if "alpine" in target_file %}
|
||||||
|
COPY --from=build /app/target/x86_64-unknown-linux-musl/release/bitwarden_rs .
|
||||||
|
{% elif "aarch64" in target_file %}
|
||||||
|
COPY --from=build /app/target/aarch64-unknown-linux-gnu/release/bitwarden_rs .
|
||||||
|
{% elif "armv6" in target_file %}
|
||||||
|
COPY --from=build /app/target/arm-unknown-linux-gnueabi/release/bitwarden_rs .
|
||||||
|
{% elif "armv7" in target_file %}
|
||||||
|
COPY --from=build /app/target/armv7-unknown-linux-gnueabihf/release/bitwarden_rs .
|
||||||
|
{% else %}
|
||||||
|
COPY --from=build app/target/release/bitwarden_rs .
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
COPY docker/healthcheck.sh ./healthcheck.sh
|
||||||
|
|
||||||
|
HEALTHCHECK --interval=30s --timeout=3s CMD sh healthcheck.sh || exit 1
|
||||||
|
|
||||||
|
# Configures the startup!
|
||||||
|
WORKDIR /
|
||||||
|
CMD ["/bitwarden_rs"]
|
9
docker/Makefile
Normal file
9
docker/Makefile
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
OBJECTS := $(shell find -mindepth 2 -name 'Dockerfile*')
|
||||||
|
|
||||||
|
all: $(OBJECTS)
|
||||||
|
|
||||||
|
%/Dockerfile: Dockerfile.j2 render_template
|
||||||
|
./render_template "$<" "{\"target_file\":\"$@\"}" > "$@"
|
||||||
|
|
||||||
|
%/Dockerfile.alpine: Dockerfile.j2 render_template
|
||||||
|
./render_template "$<" "{\"target_file\":\"$@\"}" > "$@"
|
@@ -1,24 +1,21 @@
|
|||||||
|
# This file was generated using a Jinja2 template.
|
||||||
|
# Please make your changes in `Dockerfile.j2` and then `make` the individual Dockerfile's.
|
||||||
|
|
||||||
# Using multistage build:
|
# Using multistage build:
|
||||||
# https://docs.docker.com/develop/develop-images/multistage-build/
|
# https://docs.docker.com/develop/develop-images/multistage-build/
|
||||||
# https://whitfin.io/speeding-up-rust-docker-builds/
|
# https://whitfin.io/speeding-up-rust-docker-builds/
|
||||||
####################### VAULT BUILD IMAGE #######################
|
####################### VAULT BUILD IMAGE #######################
|
||||||
FROM alpine:3.11 as vault
|
|
||||||
|
|
||||||
ENV VAULT_VERSION "v2.12.0b"
|
# This hash is extracted from the docker web-vault builds and it's prefered over a simple tag because it's immutable.
|
||||||
|
# It can be viewed in multiple ways:
|
||||||
ENV URL "https://github.com/dani-garcia/bw_web_builds/releases/download/$VAULT_VERSION/bw_web_$VAULT_VERSION.tar.gz"
|
# - From the https://hub.docker.com/repository/docker/bitwardenrs/web-vault/tags page, click the tag name and the digest should be there.
|
||||||
|
# - From the console, with the following commands:
|
||||||
RUN apk add --no-cache --upgrade \
|
# docker pull bitwardenrs/web-vault:v2.12.0e
|
||||||
curl \
|
# docker image inspect --format "{{.RepoDigests}}" bitwardenrs/web-vault:v2.12.0e
|
||||||
tar
|
#
|
||||||
|
# - To do the opposite, and get the tag from the hash, you can do:
|
||||||
RUN mkdir /web-vault
|
# docker image inspect --format "{{.RepoTags}}" bitwardenrs/web-vault@sha256:ce56b3f5e538351411785ac45e9b4b913259c3508b1323d62e8fa0f30717dd1c
|
||||||
WORKDIR /web-vault
|
FROM bitwardenrs/web-vault@sha256:ce56b3f5e538351411785ac45e9b4b913259c3508b1323d62e8fa0f30717dd1c as vault
|
||||||
|
|
||||||
SHELL ["/bin/ash", "-eo", "pipefail", "-c"]
|
|
||||||
|
|
||||||
RUN curl -L $URL | tar xz
|
|
||||||
RUN ls
|
|
||||||
|
|
||||||
########################## BUILD IMAGE ##########################
|
########################## BUILD IMAGE ##########################
|
||||||
# We need to use the Rust build image, because
|
# We need to use the Rust build image, because
|
||||||
@@ -28,9 +25,22 @@ FROM rust:1.40 as build
|
|||||||
# set mysql backend
|
# set mysql backend
|
||||||
ARG DB=mysql
|
ARG DB=mysql
|
||||||
|
|
||||||
|
# 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
|
||||||
|
|
||||||
# Don't download rust docs
|
# Don't download rust docs
|
||||||
RUN rustup set profile minimal
|
RUN rustup set profile minimal
|
||||||
|
|
||||||
|
# Install required build libs for arm64 architecture.
|
||||||
|
RUN sed 's/^deb/deb-src/' /etc/apt/sources.list > \
|
||||||
|
/etc/apt/sources.list.d/deb-src.list \
|
||||||
|
&& dpkg --add-architecture arm64 \
|
||||||
|
&& apt-get update \
|
||||||
|
&& apt-get install -y \
|
||||||
|
--no-install-recommends \
|
||||||
|
libssl-dev:arm64 \
|
||||||
|
libc6-dev:arm64
|
||||||
|
|
||||||
RUN apt-get update \
|
RUN apt-get update \
|
||||||
&& apt-get install -y \
|
&& apt-get install -y \
|
||||||
--no-install-recommends \
|
--no-install-recommends \
|
||||||
@@ -42,30 +52,42 @@ RUN apt-get update \
|
|||||||
ENV CARGO_HOME "/root/.cargo"
|
ENV CARGO_HOME "/root/.cargo"
|
||||||
ENV USER "root"
|
ENV USER "root"
|
||||||
|
|
||||||
|
# Install MySQL package
|
||||||
|
RUN apt-get update && apt-get install -y \
|
||||||
|
--no-install-recommends \
|
||||||
|
libmariadb-dev:arm64 \
|
||||||
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
|
# Creates a dummy project used to grab dependencies
|
||||||
|
RUN USER=root cargo new --bin /app
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
|
|
||||||
# Prepare openssl arm64 libs
|
# Copies over *only* your manifests and build files
|
||||||
RUN sed 's/^deb/deb-src/' /etc/apt/sources.list > \
|
COPY ./Cargo.* ./
|
||||||
/etc/apt/sources.list.d/deb-src.list \
|
COPY ./rust-toolchain ./rust-toolchain
|
||||||
&& dpkg --add-architecture arm64 \
|
COPY ./build.rs ./build.rs
|
||||||
&& apt-get update \
|
|
||||||
&& apt-get install -y \
|
|
||||||
--no-install-recommends \
|
|
||||||
libssl-dev:arm64 \
|
|
||||||
libc6-dev:arm64 \
|
|
||||||
libmariadb-dev:arm64
|
|
||||||
|
|
||||||
ENV CC_aarch64_unknown_linux_gnu="/usr/bin/aarch64-linux-gnu-gcc"
|
ENV CC_aarch64_unknown_linux_gnu="/usr/bin/aarch64-linux-gnu-gcc"
|
||||||
ENV CROSS_COMPILE="1"
|
ENV CROSS_COMPILE="1"
|
||||||
ENV OPENSSL_INCLUDE_DIR="/usr/include/aarch64-linux-gnu"
|
ENV OPENSSL_INCLUDE_DIR="/usr/include/aarch64-linux-gnu"
|
||||||
ENV OPENSSL_LIB_DIR="/usr/lib/aarch64-linux-gnu"
|
ENV OPENSSL_LIB_DIR="/usr/lib/aarch64-linux-gnu"
|
||||||
|
RUN rustup target add aarch64-unknown-linux-gnu
|
||||||
|
|
||||||
|
# Builds your dependencies and removes the
|
||||||
|
# dummy project, except the target folder
|
||||||
|
# This folder contains the compiled dependencies
|
||||||
|
RUN cargo build --features ${DB} --release
|
||||||
|
RUN find . -not -path "./target*" -delete
|
||||||
|
|
||||||
# Copies the complete project
|
# Copies the complete project
|
||||||
# To avoid copying unneeded files, use .dockerignore
|
# To avoid copying unneeded files, use .dockerignore
|
||||||
COPY . .
|
COPY . .
|
||||||
|
|
||||||
# Build
|
# Make sure that we actually build the project
|
||||||
RUN rustup target add aarch64-unknown-linux-gnu
|
RUN touch src/main.rs
|
||||||
|
|
||||||
|
# Builds again, this time it'll just be
|
||||||
|
# your actual source files being built
|
||||||
RUN cargo build --features ${DB} --release --target=aarch64-unknown-linux-gnu
|
RUN cargo build --features ${DB} --release --target=aarch64-unknown-linux-gnu
|
||||||
|
|
||||||
######################## RUNTIME IMAGE ########################
|
######################## RUNTIME IMAGE ########################
|
||||||
@@ -86,7 +108,7 @@ RUN apt-get update && apt-get install -y \
|
|||||||
ca-certificates \
|
ca-certificates \
|
||||||
curl \
|
curl \
|
||||||
libmariadbclient-dev \
|
libmariadbclient-dev \
|
||||||
&& rm -rf /var/lib/apt/lists/*
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
RUN mkdir /data
|
RUN mkdir /data
|
||||||
|
|
||||||
@@ -94,6 +116,7 @@ RUN [ "cross-build-end" ]
|
|||||||
|
|
||||||
VOLUME /data
|
VOLUME /data
|
||||||
EXPOSE 80
|
EXPOSE 80
|
||||||
|
EXPOSE 3012
|
||||||
|
|
||||||
# Copies the files from the context (Rocket.toml file and web-vault)
|
# Copies the files from the context (Rocket.toml file and web-vault)
|
||||||
# and the binary from the "build" stage to the current stage
|
# and the binary from the "build" stage to the current stage
|
||||||
|
@@ -1,36 +1,46 @@
|
|||||||
|
# This file was generated using a Jinja2 template.
|
||||||
|
# Please make your changes in `Dockerfile.j2` and then `make` the individual Dockerfile's.
|
||||||
|
|
||||||
# Using multistage build:
|
# Using multistage build:
|
||||||
# https://docs.docker.com/develop/develop-images/multistage-build/
|
# https://docs.docker.com/develop/develop-images/multistage-build/
|
||||||
# https://whitfin.io/speeding-up-rust-docker-builds/
|
# https://whitfin.io/speeding-up-rust-docker-builds/
|
||||||
####################### VAULT BUILD IMAGE #######################
|
####################### VAULT BUILD IMAGE #######################
|
||||||
FROM alpine:3.11 as vault
|
|
||||||
|
|
||||||
ENV VAULT_VERSION "v2.12.0b"
|
# This hash is extracted from the docker web-vault builds and it's prefered over a simple tag because it's immutable.
|
||||||
|
# It can be viewed in multiple ways:
|
||||||
ENV URL "https://github.com/dani-garcia/bw_web_builds/releases/download/$VAULT_VERSION/bw_web_$VAULT_VERSION.tar.gz"
|
# - From the https://hub.docker.com/repository/docker/bitwardenrs/web-vault/tags page, click the tag name and the digest should be there.
|
||||||
|
# - From the console, with the following commands:
|
||||||
RUN apk add --no-cache --upgrade \
|
# docker pull bitwardenrs/web-vault:v2.12.0e
|
||||||
curl \
|
# docker image inspect --format "{{.RepoDigests}}" bitwardenrs/web-vault:v2.12.0e
|
||||||
tar
|
#
|
||||||
|
# - To do the opposite, and get the tag from the hash, you can do:
|
||||||
RUN mkdir /web-vault
|
# docker image inspect --format "{{.RepoTags}}" bitwardenrs/web-vault@sha256:ce56b3f5e538351411785ac45e9b4b913259c3508b1323d62e8fa0f30717dd1c
|
||||||
WORKDIR /web-vault
|
FROM bitwardenrs/web-vault@sha256:ce56b3f5e538351411785ac45e9b4b913259c3508b1323d62e8fa0f30717dd1c as vault
|
||||||
|
|
||||||
SHELL ["/bin/ash", "-eo", "pipefail", "-c"]
|
|
||||||
|
|
||||||
RUN curl -L $URL | tar xz
|
|
||||||
RUN ls
|
|
||||||
|
|
||||||
########################## BUILD IMAGE ##########################
|
########################## BUILD IMAGE ##########################
|
||||||
# We need to use the Rust build image, because
|
# We need to use the Rust build image, because
|
||||||
# we need the Rust compiler and Cargo tooling
|
# we need the Rust compiler and Cargo tooling
|
||||||
FROM rust:1.40 as build
|
FROM rust:1.40 as build
|
||||||
|
|
||||||
# set sqlite as default for DB ARG for backward comaptibility
|
# set sqlite as default for DB ARG for backward compatibility
|
||||||
ARG DB=sqlite
|
ARG DB=sqlite
|
||||||
|
|
||||||
|
# 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
|
||||||
|
|
||||||
# Don't download rust docs
|
# Don't download rust docs
|
||||||
RUN rustup set profile minimal
|
RUN rustup set profile minimal
|
||||||
|
|
||||||
|
# Install required build libs for arm64 architecture.
|
||||||
|
RUN sed 's/^deb/deb-src/' /etc/apt/sources.list > \
|
||||||
|
/etc/apt/sources.list.d/deb-src.list \
|
||||||
|
&& dpkg --add-architecture arm64 \
|
||||||
|
&& apt-get update \
|
||||||
|
&& apt-get install -y \
|
||||||
|
--no-install-recommends \
|
||||||
|
libssl-dev:arm64 \
|
||||||
|
libc6-dev:arm64
|
||||||
|
|
||||||
RUN apt-get update \
|
RUN apt-get update \
|
||||||
&& apt-get install -y \
|
&& apt-get install -y \
|
||||||
--no-install-recommends \
|
--no-install-recommends \
|
||||||
@@ -42,29 +52,36 @@ RUN apt-get update \
|
|||||||
ENV CARGO_HOME "/root/.cargo"
|
ENV CARGO_HOME "/root/.cargo"
|
||||||
ENV USER "root"
|
ENV USER "root"
|
||||||
|
|
||||||
|
# Creates a dummy project used to grab dependencies
|
||||||
|
RUN USER=root cargo new --bin /app
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
|
|
||||||
# Prepare openssl arm64 libs
|
# Copies over *only* your manifests and build files
|
||||||
RUN sed 's/^deb/deb-src/' /etc/apt/sources.list > \
|
COPY ./Cargo.* ./
|
||||||
/etc/apt/sources.list.d/deb-src.list \
|
COPY ./rust-toolchain ./rust-toolchain
|
||||||
&& dpkg --add-architecture arm64 \
|
COPY ./build.rs ./build.rs
|
||||||
&& apt-get update \
|
|
||||||
&& apt-get install -y \
|
|
||||||
--no-install-recommends \
|
|
||||||
libssl-dev:arm64 \
|
|
||||||
libc6-dev:arm64
|
|
||||||
|
|
||||||
ENV CC_aarch64_unknown_linux_gnu="/usr/bin/aarch64-linux-gnu-gcc"
|
ENV CC_aarch64_unknown_linux_gnu="/usr/bin/aarch64-linux-gnu-gcc"
|
||||||
ENV CROSS_COMPILE="1"
|
ENV CROSS_COMPILE="1"
|
||||||
ENV OPENSSL_INCLUDE_DIR="/usr/include/aarch64-linux-gnu"
|
ENV OPENSSL_INCLUDE_DIR="/usr/include/aarch64-linux-gnu"
|
||||||
ENV OPENSSL_LIB_DIR="/usr/lib/aarch64-linux-gnu"
|
ENV OPENSSL_LIB_DIR="/usr/lib/aarch64-linux-gnu"
|
||||||
|
RUN rustup target add aarch64-unknown-linux-gnu
|
||||||
|
|
||||||
|
# Builds your dependencies and removes the
|
||||||
|
# dummy project, except the target folder
|
||||||
|
# This folder contains the compiled dependencies
|
||||||
|
RUN cargo build --features ${DB} --release
|
||||||
|
RUN find . -not -path "./target*" -delete
|
||||||
|
|
||||||
# Copies the complete project
|
# Copies the complete project
|
||||||
# To avoid copying unneeded files, use .dockerignore
|
# To avoid copying unneeded files, use .dockerignore
|
||||||
COPY . .
|
COPY . .
|
||||||
|
|
||||||
# Build
|
# Make sure that we actually build the project
|
||||||
RUN rustup target add aarch64-unknown-linux-gnu
|
RUN touch src/main.rs
|
||||||
|
|
||||||
|
# Builds again, this time it'll just be
|
||||||
|
# your actual source files being built
|
||||||
RUN cargo build --features ${DB} --release --target=aarch64-unknown-linux-gnu
|
RUN cargo build --features ${DB} --release --target=aarch64-unknown-linux-gnu
|
||||||
|
|
||||||
######################## RUNTIME IMAGE ########################
|
######################## RUNTIME IMAGE ########################
|
||||||
@@ -85,7 +102,7 @@ RUN apt-get update && apt-get install -y \
|
|||||||
ca-certificates \
|
ca-certificates \
|
||||||
curl \
|
curl \
|
||||||
sqlite3 \
|
sqlite3 \
|
||||||
&& rm -rf /var/lib/apt/lists/*
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
RUN mkdir /data
|
RUN mkdir /data
|
||||||
|
|
||||||
@@ -93,6 +110,7 @@ RUN [ "cross-build-end" ]
|
|||||||
|
|
||||||
VOLUME /data
|
VOLUME /data
|
||||||
EXPOSE 80
|
EXPOSE 80
|
||||||
|
EXPOSE 3012
|
||||||
|
|
||||||
# Copies the files from the context (Rocket.toml file and web-vault)
|
# Copies the files from the context (Rocket.toml file and web-vault)
|
||||||
# and the binary from the "build" stage to the current stage
|
# and the binary from the "build" stage to the current stage
|
||||||
|
@@ -1,24 +1,21 @@
|
|||||||
|
# This file was generated using a Jinja2 template.
|
||||||
|
# Please make your changes in `Dockerfile.j2` and then `make` the individual Dockerfile's.
|
||||||
|
|
||||||
# Using multistage build:
|
# Using multistage build:
|
||||||
# https://docs.docker.com/develop/develop-images/multistage-build/
|
# https://docs.docker.com/develop/develop-images/multistage-build/
|
||||||
# https://whitfin.io/speeding-up-rust-docker-builds/
|
# https://whitfin.io/speeding-up-rust-docker-builds/
|
||||||
####################### VAULT BUILD IMAGE #######################
|
####################### VAULT BUILD IMAGE #######################
|
||||||
FROM alpine:3.11 as vault
|
|
||||||
|
|
||||||
ENV VAULT_VERSION "v2.12.0b"
|
# This hash is extracted from the docker web-vault builds and it's prefered over a simple tag because it's immutable.
|
||||||
|
# It can be viewed in multiple ways:
|
||||||
ENV URL "https://github.com/dani-garcia/bw_web_builds/releases/download/$VAULT_VERSION/bw_web_$VAULT_VERSION.tar.gz"
|
# - From the https://hub.docker.com/repository/docker/bitwardenrs/web-vault/tags page, click the tag name and the digest should be there.
|
||||||
|
# - From the console, with the following commands:
|
||||||
RUN apk add --no-cache --upgrade \
|
# docker pull bitwardenrs/web-vault:v2.12.0e
|
||||||
curl \
|
# docker image inspect --format "{{.RepoDigests}}" bitwardenrs/web-vault:v2.12.0e
|
||||||
tar
|
#
|
||||||
|
# - To do the opposite, and get the tag from the hash, you can do:
|
||||||
RUN mkdir /web-vault
|
# docker image inspect --format "{{.RepoTags}}" bitwardenrs/web-vault@sha256:ce56b3f5e538351411785ac45e9b4b913259c3508b1323d62e8fa0f30717dd1c
|
||||||
WORKDIR /web-vault
|
FROM bitwardenrs/web-vault@sha256:ce56b3f5e538351411785ac45e9b4b913259c3508b1323d62e8fa0f30717dd1c as vault
|
||||||
|
|
||||||
SHELL ["/bin/ash", "-eo", "pipefail", "-c"]
|
|
||||||
|
|
||||||
RUN curl -L $URL | tar xz
|
|
||||||
RUN ls
|
|
||||||
|
|
||||||
########################## BUILD IMAGE ##########################
|
########################## BUILD IMAGE ##########################
|
||||||
# We need to use the Rust build image, because
|
# We need to use the Rust build image, because
|
||||||
@@ -28,6 +25,9 @@ FROM rust:1.40 as build
|
|||||||
# set mysql backend
|
# set mysql backend
|
||||||
ARG DB=mysql
|
ARG DB=mysql
|
||||||
|
|
||||||
|
# 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
|
||||||
|
|
||||||
# Don't download rust docs
|
# Don't download rust docs
|
||||||
RUN rustup set profile minimal
|
RUN rustup set profile minimal
|
||||||
|
|
||||||
@@ -38,7 +38,7 @@ RUN apt-get update && apt-get install -y \
|
|||||||
&& rm -rf /var/lib/apt/lists/*
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
# Creates a dummy project used to grab dependencies
|
# Creates a dummy project used to grab dependencies
|
||||||
RUN USER=root cargo new --bin app
|
RUN USER=root cargo new --bin /app
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
|
|
||||||
# Copies over *only* your manifests and build files
|
# Copies over *only* your manifests and build files
|
||||||
|
@@ -1,24 +1,21 @@
|
|||||||
|
# This file was generated using a Jinja2 template.
|
||||||
|
# Please make your changes in `Dockerfile.j2` and then `make` the individual Dockerfile's.
|
||||||
|
|
||||||
# Using multistage build:
|
# Using multistage build:
|
||||||
# https://docs.docker.com/develop/develop-images/multistage-build/
|
# https://docs.docker.com/develop/develop-images/multistage-build/
|
||||||
# https://whitfin.io/speeding-up-rust-docker-builds/
|
# https://whitfin.io/speeding-up-rust-docker-builds/
|
||||||
####################### VAULT BUILD IMAGE #######################
|
####################### VAULT BUILD IMAGE #######################
|
||||||
FROM alpine:3.11 as vault
|
|
||||||
|
|
||||||
ENV VAULT_VERSION "v2.12.0b"
|
# This hash is extracted from the docker web-vault builds and it's prefered over a simple tag because it's immutable.
|
||||||
|
# It can be viewed in multiple ways:
|
||||||
ENV URL "https://github.com/dani-garcia/bw_web_builds/releases/download/$VAULT_VERSION/bw_web_$VAULT_VERSION.tar.gz"
|
# - From the https://hub.docker.com/repository/docker/bitwardenrs/web-vault/tags page, click the tag name and the digest should be there.
|
||||||
|
# - From the console, with the following commands:
|
||||||
RUN apk add --no-cache --upgrade \
|
# docker pull bitwardenrs/web-vault:v2.12.0e
|
||||||
curl \
|
# docker image inspect --format "{{.RepoDigests}}" bitwardenrs/web-vault:v2.12.0e
|
||||||
tar
|
#
|
||||||
|
# - To do the opposite, and get the tag from the hash, you can do:
|
||||||
RUN mkdir /web-vault
|
# docker image inspect --format "{{.RepoTags}}" bitwardenrs/web-vault@sha256:ce56b3f5e538351411785ac45e9b4b913259c3508b1323d62e8fa0f30717dd1c
|
||||||
WORKDIR /web-vault
|
FROM bitwardenrs/web-vault@sha256:ce56b3f5e538351411785ac45e9b4b913259c3508b1323d62e8fa0f30717dd1c as vault
|
||||||
|
|
||||||
SHELL ["/bin/ash", "-eo", "pipefail", "-c"]
|
|
||||||
|
|
||||||
RUN curl -L $URL | tar xz
|
|
||||||
RUN ls
|
|
||||||
|
|
||||||
########################## BUILD IMAGE ##########################
|
########################## BUILD IMAGE ##########################
|
||||||
# Musl build image for statically compiled binary
|
# Musl build image for statically compiled binary
|
||||||
@@ -27,29 +24,46 @@ FROM clux/muslrust:nightly-2019-12-19 as build
|
|||||||
# set mysql backend
|
# set mysql backend
|
||||||
ARG DB=mysql
|
ARG DB=mysql
|
||||||
|
|
||||||
|
# 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
|
||||||
|
|
||||||
# Don't download rust docs
|
# Don't download rust docs
|
||||||
RUN rustup set profile minimal
|
RUN rustup set profile minimal
|
||||||
|
|
||||||
ENV USER "root"
|
ENV USER "root"
|
||||||
|
|
||||||
# Install needed libraries
|
# Install MySQL package
|
||||||
RUN apt-get update && apt-get install -y \
|
RUN apt-get update && apt-get install -y \
|
||||||
--no-install-recommends \
|
--no-install-recommends \
|
||||||
libmysqlclient-dev \
|
libmysqlclient-dev \
|
||||||
&& rm -rf /var/lib/apt/lists/*
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
|
# Creates a dummy project used to grab dependencies
|
||||||
|
RUN USER=root cargo new --bin /app
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
|
|
||||||
|
# Copies over *only* your manifests and build files
|
||||||
|
COPY ./Cargo.* ./
|
||||||
|
COPY ./rust-toolchain ./rust-toolchain
|
||||||
|
COPY ./build.rs ./build.rs
|
||||||
|
|
||||||
|
RUN rustup target add x86_64-unknown-linux-musl
|
||||||
|
|
||||||
|
# Builds your dependencies and removes the
|
||||||
|
# dummy project, except the target folder
|
||||||
|
# This folder contains the compiled dependencies
|
||||||
|
RUN cargo build --features ${DB} --release
|
||||||
|
RUN find . -not -path "./target*" -delete
|
||||||
|
|
||||||
# Copies the complete project
|
# Copies the complete project
|
||||||
# To avoid copying unneeded files, use .dockerignore
|
# To avoid copying unneeded files, use .dockerignore
|
||||||
COPY . .
|
COPY . .
|
||||||
|
|
||||||
RUN rustup target add x86_64-unknown-linux-musl
|
|
||||||
|
|
||||||
# Make sure that we actually build the project
|
# Make sure that we actually build the project
|
||||||
RUN touch src/main.rs
|
RUN touch src/main.rs
|
||||||
|
|
||||||
# Build
|
# Builds again, this time it'll just be
|
||||||
|
# your actual source files being built
|
||||||
RUN cargo build --features ${DB} --release
|
RUN cargo build --features ${DB} --release
|
||||||
|
|
||||||
######################## RUNTIME IMAGE ########################
|
######################## RUNTIME IMAGE ########################
|
||||||
@@ -65,8 +79,8 @@ ENV SSL_CERT_DIR=/etc/ssl/certs
|
|||||||
# Install needed libraries
|
# Install needed libraries
|
||||||
RUN apk add --no-cache \
|
RUN apk add --no-cache \
|
||||||
openssl \
|
openssl \
|
||||||
mariadb-connector-c \
|
|
||||||
curl \
|
curl \
|
||||||
|
mariadb-connector-c \
|
||||||
ca-certificates
|
ca-certificates
|
||||||
|
|
||||||
RUN mkdir /data
|
RUN mkdir /data
|
||||||
|
@@ -1,50 +1,44 @@
|
|||||||
|
# This file was generated using a Jinja2 template.
|
||||||
|
# Please make your changes in `Dockerfile.j2` and then `make` the individual Dockerfile's.
|
||||||
|
|
||||||
# Using multistage build:
|
# Using multistage build:
|
||||||
# https://docs.docker.com/develop/develop-images/multistage-build/
|
# https://docs.docker.com/develop/develop-images/multistage-build/
|
||||||
# https://whitfin.io/speeding-up-rust-docker-builds/
|
# https://whitfin.io/speeding-up-rust-docker-builds/
|
||||||
####################### VAULT BUILD IMAGE #######################
|
####################### VAULT BUILD IMAGE #######################
|
||||||
FROM alpine:3.11 as vault
|
|
||||||
|
|
||||||
ENV VAULT_VERSION "v2.12.0b"
|
# This hash is extracted from the docker web-vault builds and it's prefered over a simple tag because it's immutable.
|
||||||
|
# It can be viewed in multiple ways:
|
||||||
ENV URL "https://github.com/dani-garcia/bw_web_builds/releases/download/$VAULT_VERSION/bw_web_$VAULT_VERSION.tar.gz"
|
# - From the https://hub.docker.com/repository/docker/bitwardenrs/web-vault/tags page, click the tag name and the digest should be there.
|
||||||
|
# - From the console, with the following commands:
|
||||||
RUN apk add --no-cache --upgrade \
|
# docker pull bitwardenrs/web-vault:v2.12.0e
|
||||||
curl \
|
# docker image inspect --format "{{.RepoDigests}}" bitwardenrs/web-vault:v2.12.0e
|
||||||
tar
|
#
|
||||||
|
# - To do the opposite, and get the tag from the hash, you can do:
|
||||||
RUN mkdir /web-vault
|
# docker image inspect --format "{{.RepoTags}}" bitwardenrs/web-vault@sha256:ce56b3f5e538351411785ac45e9b4b913259c3508b1323d62e8fa0f30717dd1c
|
||||||
WORKDIR /web-vault
|
FROM bitwardenrs/web-vault@sha256:ce56b3f5e538351411785ac45e9b4b913259c3508b1323d62e8fa0f30717dd1c as vault
|
||||||
|
|
||||||
SHELL ["/bin/ash", "-eo", "pipefail", "-c"]
|
|
||||||
|
|
||||||
RUN curl -L $URL | tar xz
|
|
||||||
RUN ls
|
|
||||||
|
|
||||||
########################## BUILD IMAGE ##########################
|
########################## BUILD IMAGE ##########################
|
||||||
# We need to use the Rust build image, because
|
# We need to use the Rust build image, because
|
||||||
# we need the Rust compiler and Cargo tooling
|
# we need the Rust compiler and Cargo tooling
|
||||||
FROM rust:1.40 as build
|
FROM rust:1.40 as build
|
||||||
|
|
||||||
# set mysql backend
|
# set postgresql backend
|
||||||
ARG DB=postgresql
|
ARG DB=postgresql
|
||||||
|
|
||||||
|
# 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
|
||||||
|
|
||||||
# Don't download rust docs
|
# Don't download rust docs
|
||||||
RUN rustup set profile minimal
|
RUN rustup set profile minimal
|
||||||
|
|
||||||
# Using bundled SQLite, no need to install it
|
# Install PostgreSQL package
|
||||||
# RUN apt-get update && apt-get install -y\
|
|
||||||
# --no-install-recommends \
|
|
||||||
# sqlite3\
|
|
||||||
# && rm -rf /var/lib/apt/lists/*
|
|
||||||
|
|
||||||
# Install MySQL package
|
|
||||||
RUN apt-get update && apt-get install -y \
|
RUN apt-get update && apt-get install -y \
|
||||||
--no-install-recommends \
|
--no-install-recommends \
|
||||||
libpq-dev \
|
libpq-dev \
|
||||||
&& rm -rf /var/lib/apt/lists/*
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
# Creates a dummy project used to grab dependencies
|
# Creates a dummy project used to grab dependencies
|
||||||
RUN USER=root cargo new --bin app
|
RUN USER=root cargo new --bin /app
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
|
|
||||||
# Copies over *only* your manifests and build files
|
# Copies over *only* your manifests and build files
|
||||||
@@ -84,7 +78,6 @@ RUN apt-get update && apt-get install -y \
|
|||||||
openssl \
|
openssl \
|
||||||
ca-certificates \
|
ca-certificates \
|
||||||
curl \
|
curl \
|
||||||
sqlite3 \
|
|
||||||
libpq5 \
|
libpq5 \
|
||||||
&& rm -rf /var/lib/apt/lists/*
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
|
@@ -1,24 +1,21 @@
|
|||||||
|
# This file was generated using a Jinja2 template.
|
||||||
|
# Please make your changes in `Dockerfile.j2` and then `make` the individual Dockerfile's.
|
||||||
|
|
||||||
# Using multistage build:
|
# Using multistage build:
|
||||||
# https://docs.docker.com/develop/develop-images/multistage-build/
|
# https://docs.docker.com/develop/develop-images/multistage-build/
|
||||||
# https://whitfin.io/speeding-up-rust-docker-builds/
|
# https://whitfin.io/speeding-up-rust-docker-builds/
|
||||||
####################### VAULT BUILD IMAGE #######################
|
####################### VAULT BUILD IMAGE #######################
|
||||||
FROM alpine:3.11 as vault
|
|
||||||
|
|
||||||
ENV VAULT_VERSION "v2.12.0b"
|
# This hash is extracted from the docker web-vault builds and it's prefered over a simple tag because it's immutable.
|
||||||
|
# It can be viewed in multiple ways:
|
||||||
ENV URL "https://github.com/dani-garcia/bw_web_builds/releases/download/$VAULT_VERSION/bw_web_$VAULT_VERSION.tar.gz"
|
# - From the https://hub.docker.com/repository/docker/bitwardenrs/web-vault/tags page, click the tag name and the digest should be there.
|
||||||
|
# - From the console, with the following commands:
|
||||||
RUN apk add --no-cache --upgrade \
|
# docker pull bitwardenrs/web-vault:v2.12.0e
|
||||||
curl \
|
# docker image inspect --format "{{.RepoDigests}}" bitwardenrs/web-vault:v2.12.0e
|
||||||
tar
|
#
|
||||||
|
# - To do the opposite, and get the tag from the hash, you can do:
|
||||||
RUN mkdir /web-vault
|
# docker image inspect --format "{{.RepoTags}}" bitwardenrs/web-vault@sha256:ce56b3f5e538351411785ac45e9b4b913259c3508b1323d62e8fa0f30717dd1c
|
||||||
WORKDIR /web-vault
|
FROM bitwardenrs/web-vault@sha256:ce56b3f5e538351411785ac45e9b4b913259c3508b1323d62e8fa0f30717dd1c as vault
|
||||||
|
|
||||||
SHELL ["/bin/ash", "-eo", "pipefail", "-c"]
|
|
||||||
|
|
||||||
RUN curl -L $URL | tar xz
|
|
||||||
RUN ls
|
|
||||||
|
|
||||||
########################## BUILD IMAGE ##########################
|
########################## BUILD IMAGE ##########################
|
||||||
# Musl build image for statically compiled binary
|
# Musl build image for statically compiled binary
|
||||||
@@ -27,29 +24,46 @@ FROM clux/muslrust:nightly-2019-12-19 as build
|
|||||||
# set postgresql backend
|
# set postgresql backend
|
||||||
ARG DB=postgresql
|
ARG DB=postgresql
|
||||||
|
|
||||||
|
# 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
|
||||||
|
|
||||||
# Don't download rust docs
|
# Don't download rust docs
|
||||||
RUN rustup set profile minimal
|
RUN rustup set profile minimal
|
||||||
|
|
||||||
ENV USER "root"
|
ENV USER "root"
|
||||||
|
|
||||||
# Install needed libraries
|
# Install PostgreSQL package
|
||||||
RUN apt-get update && apt-get install -y \
|
RUN apt-get update && apt-get install -y \
|
||||||
--no-install-recommends \
|
--no-install-recommends \
|
||||||
libpq-dev \
|
libpq-dev \
|
||||||
&& rm -rf /var/lib/apt/lists/*
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
|
# Creates a dummy project used to grab dependencies
|
||||||
|
RUN USER=root cargo new --bin /app
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
|
|
||||||
|
# Copies over *only* your manifests and build files
|
||||||
|
COPY ./Cargo.* ./
|
||||||
|
COPY ./rust-toolchain ./rust-toolchain
|
||||||
|
COPY ./build.rs ./build.rs
|
||||||
|
|
||||||
|
RUN rustup target add x86_64-unknown-linux-musl
|
||||||
|
|
||||||
|
# Builds your dependencies and removes the
|
||||||
|
# dummy project, except the target folder
|
||||||
|
# This folder contains the compiled dependencies
|
||||||
|
RUN cargo build --features ${DB} --release
|
||||||
|
RUN find . -not -path "./target*" -delete
|
||||||
|
|
||||||
# Copies the complete project
|
# Copies the complete project
|
||||||
# To avoid copying unneeded files, use .dockerignore
|
# To avoid copying unneeded files, use .dockerignore
|
||||||
COPY . .
|
COPY . .
|
||||||
|
|
||||||
RUN rustup target add x86_64-unknown-linux-musl
|
|
||||||
|
|
||||||
# Make sure that we actually build the project
|
# Make sure that we actually build the project
|
||||||
RUN touch src/main.rs
|
RUN touch src/main.rs
|
||||||
|
|
||||||
# Build
|
# Builds again, this time it'll just be
|
||||||
|
# your actual source files being built
|
||||||
RUN cargo build --features ${DB} --release
|
RUN cargo build --features ${DB} --release
|
||||||
|
|
||||||
######################## RUNTIME IMAGE ########################
|
######################## RUNTIME IMAGE ########################
|
||||||
@@ -65,9 +79,8 @@ ENV SSL_CERT_DIR=/etc/ssl/certs
|
|||||||
# Install needed libraries
|
# Install needed libraries
|
||||||
RUN apk add --no-cache \
|
RUN apk add --no-cache \
|
||||||
openssl \
|
openssl \
|
||||||
postgresql-libs \
|
|
||||||
curl \
|
curl \
|
||||||
sqlite \
|
postgresql-libs \
|
||||||
ca-certificates
|
ca-certificates
|
||||||
|
|
||||||
RUN mkdir /data
|
RUN mkdir /data
|
||||||
|
@@ -1,38 +1,38 @@
|
|||||||
|
# This file was generated using a Jinja2 template.
|
||||||
|
# Please make your changes in `Dockerfile.j2` and then `make` the individual Dockerfile's.
|
||||||
|
|
||||||
# Using multistage build:
|
# Using multistage build:
|
||||||
# https://docs.docker.com/develop/develop-images/multistage-build/
|
# https://docs.docker.com/develop/develop-images/multistage-build/
|
||||||
# https://whitfin.io/speeding-up-rust-docker-builds/
|
# https://whitfin.io/speeding-up-rust-docker-builds/
|
||||||
####################### VAULT BUILD IMAGE #######################
|
####################### VAULT BUILD IMAGE #######################
|
||||||
FROM alpine:3.11 as vault
|
|
||||||
|
|
||||||
ENV VAULT_VERSION "v2.12.0b"
|
# This hash is extracted from the docker web-vault builds and it's prefered over a simple tag because it's immutable.
|
||||||
|
# It can be viewed in multiple ways:
|
||||||
ENV URL "https://github.com/dani-garcia/bw_web_builds/releases/download/$VAULT_VERSION/bw_web_$VAULT_VERSION.tar.gz"
|
# - From the https://hub.docker.com/repository/docker/bitwardenrs/web-vault/tags page, click the tag name and the digest should be there.
|
||||||
|
# - From the console, with the following commands:
|
||||||
RUN apk add --no-cache --upgrade \
|
# docker pull bitwardenrs/web-vault:v2.12.0e
|
||||||
curl \
|
# docker image inspect --format "{{.RepoDigests}}" bitwardenrs/web-vault:v2.12.0e
|
||||||
tar
|
#
|
||||||
|
# - To do the opposite, and get the tag from the hash, you can do:
|
||||||
RUN mkdir /web-vault
|
# docker image inspect --format "{{.RepoTags}}" bitwardenrs/web-vault@sha256:ce56b3f5e538351411785ac45e9b4b913259c3508b1323d62e8fa0f30717dd1c
|
||||||
WORKDIR /web-vault
|
FROM bitwardenrs/web-vault@sha256:ce56b3f5e538351411785ac45e9b4b913259c3508b1323d62e8fa0f30717dd1c as vault
|
||||||
|
|
||||||
SHELL ["/bin/ash", "-eo", "pipefail", "-c"]
|
|
||||||
|
|
||||||
RUN curl -L $URL | tar xz
|
|
||||||
RUN ls
|
|
||||||
|
|
||||||
########################## BUILD IMAGE ##########################
|
########################## BUILD IMAGE ##########################
|
||||||
# We need to use the Rust build image, because
|
# We need to use the Rust build image, because
|
||||||
# we need the Rust compiler and Cargo tooling
|
# we need the Rust compiler and Cargo tooling
|
||||||
FROM rust:1.40 as build
|
FROM rust:1.40 as build
|
||||||
|
|
||||||
# set sqlite as default for DB ARG for backward comaptibility
|
# set sqlite as default for DB ARG for backward compatibility
|
||||||
ARG DB=sqlite
|
ARG DB=sqlite
|
||||||
|
|
||||||
|
# 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
|
||||||
|
|
||||||
# Don't download rust docs
|
# Don't download rust docs
|
||||||
RUN rustup set profile minimal
|
RUN rustup set profile minimal
|
||||||
|
|
||||||
# Creates a dummy project used to grab dependencies
|
# Creates a dummy project used to grab dependencies
|
||||||
RUN USER=root cargo new --bin app
|
RUN USER=root cargo new --bin /app
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
|
|
||||||
# Copies over *only* your manifests and build files
|
# Copies over *only* your manifests and build files
|
||||||
|
@@ -1,49 +1,63 @@
|
|||||||
|
# This file was generated using a Jinja2 template.
|
||||||
|
# Please make your changes in `Dockerfile.j2` and then `make` the individual Dockerfile's.
|
||||||
|
|
||||||
# Using multistage build:
|
# Using multistage build:
|
||||||
# https://docs.docker.com/develop/develop-images/multistage-build/
|
# https://docs.docker.com/develop/develop-images/multistage-build/
|
||||||
# https://whitfin.io/speeding-up-rust-docker-builds/
|
# https://whitfin.io/speeding-up-rust-docker-builds/
|
||||||
####################### VAULT BUILD IMAGE #######################
|
####################### VAULT BUILD IMAGE #######################
|
||||||
FROM alpine:3.11 as vault
|
|
||||||
|
|
||||||
ENV VAULT_VERSION "v2.12.0b"
|
# This hash is extracted from the docker web-vault builds and it's prefered over a simple tag because it's immutable.
|
||||||
|
# It can be viewed in multiple ways:
|
||||||
ENV URL "https://github.com/dani-garcia/bw_web_builds/releases/download/$VAULT_VERSION/bw_web_$VAULT_VERSION.tar.gz"
|
# - From the https://hub.docker.com/repository/docker/bitwardenrs/web-vault/tags page, click the tag name and the digest should be there.
|
||||||
|
# - From the console, with the following commands:
|
||||||
RUN apk add --no-cache --upgrade \
|
# docker pull bitwardenrs/web-vault:v2.12.0e
|
||||||
curl \
|
# docker image inspect --format "{{.RepoDigests}}" bitwardenrs/web-vault:v2.12.0e
|
||||||
tar
|
#
|
||||||
|
# - To do the opposite, and get the tag from the hash, you can do:
|
||||||
RUN mkdir /web-vault
|
# docker image inspect --format "{{.RepoTags}}" bitwardenrs/web-vault@sha256:ce56b3f5e538351411785ac45e9b4b913259c3508b1323d62e8fa0f30717dd1c
|
||||||
WORKDIR /web-vault
|
FROM bitwardenrs/web-vault@sha256:ce56b3f5e538351411785ac45e9b4b913259c3508b1323d62e8fa0f30717dd1c as vault
|
||||||
|
|
||||||
SHELL ["/bin/ash", "-eo", "pipefail", "-c"]
|
|
||||||
|
|
||||||
RUN curl -L $URL | tar xz
|
|
||||||
RUN ls
|
|
||||||
|
|
||||||
########################## BUILD IMAGE ##########################
|
########################## BUILD IMAGE ##########################
|
||||||
# Musl build image for statically compiled binary
|
# Musl build image for statically compiled binary
|
||||||
FROM clux/muslrust:nightly-2019-12-19 as build
|
FROM clux/muslrust:nightly-2019-12-19 as build
|
||||||
|
|
||||||
# set sqlite as default for DB ARG for backward comaptibility
|
# set sqlite as default for DB ARG for backward compatibility
|
||||||
ARG DB=sqlite
|
ARG DB=sqlite
|
||||||
|
|
||||||
|
# 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
|
||||||
|
|
||||||
# Don't download rust docs
|
# Don't download rust docs
|
||||||
RUN rustup set profile minimal
|
RUN rustup set profile minimal
|
||||||
|
|
||||||
ENV USER "root"
|
ENV USER "root"
|
||||||
|
|
||||||
|
# Creates a dummy project used to grab dependencies
|
||||||
|
RUN USER=root cargo new --bin /app
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
|
|
||||||
|
# Copies over *only* your manifests and build files
|
||||||
|
COPY ./Cargo.* ./
|
||||||
|
COPY ./rust-toolchain ./rust-toolchain
|
||||||
|
COPY ./build.rs ./build.rs
|
||||||
|
|
||||||
|
RUN rustup target add x86_64-unknown-linux-musl
|
||||||
|
|
||||||
|
# Builds your dependencies and removes the
|
||||||
|
# dummy project, except the target folder
|
||||||
|
# This folder contains the compiled dependencies
|
||||||
|
RUN cargo build --features ${DB} --release
|
||||||
|
RUN find . -not -path "./target*" -delete
|
||||||
|
|
||||||
# Copies the complete project
|
# Copies the complete project
|
||||||
# To avoid copying unneeded files, use .dockerignore
|
# To avoid copying unneeded files, use .dockerignore
|
||||||
COPY . .
|
COPY . .
|
||||||
|
|
||||||
RUN rustup target add x86_64-unknown-linux-musl
|
|
||||||
|
|
||||||
# Make sure that we actually build the project
|
# Make sure that we actually build the project
|
||||||
RUN touch src/main.rs
|
RUN touch src/main.rs
|
||||||
|
|
||||||
# Build
|
# Builds again, this time it'll just be
|
||||||
|
# your actual source files being built
|
||||||
RUN cargo build --features ${DB} --release
|
RUN cargo build --features ${DB} --release
|
||||||
|
|
||||||
######################## RUNTIME IMAGE ########################
|
######################## RUNTIME IMAGE ########################
|
||||||
@@ -78,7 +92,6 @@ COPY docker/healthcheck.sh ./healthcheck.sh
|
|||||||
|
|
||||||
HEALTHCHECK --interval=30s --timeout=3s CMD sh healthcheck.sh || exit 1
|
HEALTHCHECK --interval=30s --timeout=3s CMD sh healthcheck.sh || exit 1
|
||||||
|
|
||||||
|
|
||||||
# Configures the startup!
|
# Configures the startup!
|
||||||
WORKDIR /
|
WORKDIR /
|
||||||
CMD ["/bitwarden_rs"]
|
CMD ["/bitwarden_rs"]
|
||||||
|
@@ -1,24 +1,21 @@
|
|||||||
|
# This file was generated using a Jinja2 template.
|
||||||
|
# Please make your changes in `Dockerfile.j2` and then `make` the individual Dockerfile's.
|
||||||
|
|
||||||
# Using multistage build:
|
# Using multistage build:
|
||||||
# https://docs.docker.com/develop/develop-images/multistage-build/
|
# https://docs.docker.com/develop/develop-images/multistage-build/
|
||||||
# https://whitfin.io/speeding-up-rust-docker-builds/
|
# https://whitfin.io/speeding-up-rust-docker-builds/
|
||||||
####################### VAULT BUILD IMAGE #######################
|
####################### VAULT BUILD IMAGE #######################
|
||||||
FROM alpine:3.11 as vault
|
|
||||||
|
|
||||||
ENV VAULT_VERSION "v2.12.0b"
|
# This hash is extracted from the docker web-vault builds and it's prefered over a simple tag because it's immutable.
|
||||||
|
# It can be viewed in multiple ways:
|
||||||
ENV URL "https://github.com/dani-garcia/bw_web_builds/releases/download/$VAULT_VERSION/bw_web_$VAULT_VERSION.tar.gz"
|
# - From the https://hub.docker.com/repository/docker/bitwardenrs/web-vault/tags page, click the tag name and the digest should be there.
|
||||||
|
# - From the console, with the following commands:
|
||||||
RUN apk add --no-cache --upgrade \
|
# docker pull bitwardenrs/web-vault:v2.12.0e
|
||||||
curl \
|
# docker image inspect --format "{{.RepoDigests}}" bitwardenrs/web-vault:v2.12.0e
|
||||||
tar
|
#
|
||||||
|
# - To do the opposite, and get the tag from the hash, you can do:
|
||||||
RUN mkdir /web-vault
|
# docker image inspect --format "{{.RepoTags}}" bitwardenrs/web-vault@sha256:ce56b3f5e538351411785ac45e9b4b913259c3508b1323d62e8fa0f30717dd1c
|
||||||
WORKDIR /web-vault
|
FROM bitwardenrs/web-vault@sha256:ce56b3f5e538351411785ac45e9b4b913259c3508b1323d62e8fa0f30717dd1c as vault
|
||||||
|
|
||||||
SHELL ["/bin/ash", "-eo", "pipefail", "-c"]
|
|
||||||
|
|
||||||
RUN curl -L $URL | tar xz
|
|
||||||
RUN ls
|
|
||||||
|
|
||||||
########################## BUILD IMAGE ##########################
|
########################## BUILD IMAGE ##########################
|
||||||
# We need to use the Rust build image, because
|
# We need to use the Rust build image, because
|
||||||
@@ -28,9 +25,22 @@ FROM rust:1.40 as build
|
|||||||
# set mysql backend
|
# set mysql backend
|
||||||
ARG DB=mysql
|
ARG DB=mysql
|
||||||
|
|
||||||
|
# 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
|
||||||
|
|
||||||
# Don't download rust docs
|
# Don't download rust docs
|
||||||
RUN rustup set profile minimal
|
RUN rustup set profile minimal
|
||||||
|
|
||||||
|
# Install required build libs for armel architecture.
|
||||||
|
RUN sed 's/^deb/deb-src/' /etc/apt/sources.list > \
|
||||||
|
/etc/apt/sources.list.d/deb-src.list \
|
||||||
|
&& dpkg --add-architecture armel \
|
||||||
|
&& apt-get update \
|
||||||
|
&& apt-get install -y \
|
||||||
|
--no-install-recommends \
|
||||||
|
libssl-dev:armel \
|
||||||
|
libc6-dev:armel
|
||||||
|
|
||||||
RUN apt-get update \
|
RUN apt-get update \
|
||||||
&& apt-get install -y \
|
&& apt-get install -y \
|
||||||
--no-install-recommends \
|
--no-install-recommends \
|
||||||
@@ -42,30 +52,42 @@ RUN apt-get update \
|
|||||||
ENV CARGO_HOME "/root/.cargo"
|
ENV CARGO_HOME "/root/.cargo"
|
||||||
ENV USER "root"
|
ENV USER "root"
|
||||||
|
|
||||||
|
# Install MySQL package
|
||||||
|
RUN apt-get update && apt-get install -y \
|
||||||
|
--no-install-recommends \
|
||||||
|
libmariadb-dev:armel \
|
||||||
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
|
# Creates a dummy project used to grab dependencies
|
||||||
|
RUN USER=root cargo new --bin /app
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
|
|
||||||
# Prepare openssl armel libs
|
# Copies over *only* your manifests and build files
|
||||||
RUN sed 's/^deb/deb-src/' /etc/apt/sources.list > \
|
COPY ./Cargo.* ./
|
||||||
/etc/apt/sources.list.d/deb-src.list \
|
COPY ./rust-toolchain ./rust-toolchain
|
||||||
&& dpkg --add-architecture armel \
|
COPY ./build.rs ./build.rs
|
||||||
&& apt-get update \
|
|
||||||
&& apt-get install -y \
|
|
||||||
--no-install-recommends \
|
|
||||||
libssl-dev:armel \
|
|
||||||
libc6-dev:armel \
|
|
||||||
libmariadb-dev:armel
|
|
||||||
|
|
||||||
ENV CC_arm_unknown_linux_gnueabi="/usr/bin/arm-linux-gnueabi-gcc"
|
ENV CC_arm_unknown_linux_gnueabi="/usr/bin/arm-linux-gnueabi-gcc"
|
||||||
ENV CROSS_COMPILE="1"
|
ENV CROSS_COMPILE="1"
|
||||||
ENV OPENSSL_INCLUDE_DIR="/usr/include/arm-linux-gnueabi"
|
ENV OPENSSL_INCLUDE_DIR="/usr/include/arm-linux-gnueabi"
|
||||||
ENV OPENSSL_LIB_DIR="/usr/lib/arm-linux-gnueabi"
|
ENV OPENSSL_LIB_DIR="/usr/lib/arm-linux-gnueabi"
|
||||||
|
RUN rustup target add arm-unknown-linux-gnueabi
|
||||||
|
|
||||||
|
# Builds your dependencies and removes the
|
||||||
|
# dummy project, except the target folder
|
||||||
|
# This folder contains the compiled dependencies
|
||||||
|
RUN cargo build --features ${DB} --release
|
||||||
|
RUN find . -not -path "./target*" -delete
|
||||||
|
|
||||||
# Copies the complete project
|
# Copies the complete project
|
||||||
# To avoid copying unneeded files, use .dockerignore
|
# To avoid copying unneeded files, use .dockerignore
|
||||||
COPY . .
|
COPY . .
|
||||||
|
|
||||||
# Build
|
# Make sure that we actually build the project
|
||||||
RUN rustup target add arm-unknown-linux-gnueabi
|
RUN touch src/main.rs
|
||||||
|
|
||||||
|
# Builds again, this time it'll just be
|
||||||
|
# your actual source files being built
|
||||||
RUN cargo build --features ${DB} --release --target=arm-unknown-linux-gnueabi
|
RUN cargo build --features ${DB} --release --target=arm-unknown-linux-gnueabi
|
||||||
|
|
||||||
######################## RUNTIME IMAGE ########################
|
######################## RUNTIME IMAGE ########################
|
||||||
@@ -94,6 +116,7 @@ RUN [ "cross-build-end" ]
|
|||||||
|
|
||||||
VOLUME /data
|
VOLUME /data
|
||||||
EXPOSE 80
|
EXPOSE 80
|
||||||
|
EXPOSE 3012
|
||||||
|
|
||||||
# Copies the files from the context (Rocket.toml file and web-vault)
|
# Copies the files from the context (Rocket.toml file and web-vault)
|
||||||
# and the binary from the "build" stage to the current stage
|
# and the binary from the "build" stage to the current stage
|
||||||
|
@@ -1,36 +1,46 @@
|
|||||||
|
# This file was generated using a Jinja2 template.
|
||||||
|
# Please make your changes in `Dockerfile.j2` and then `make` the individual Dockerfile's.
|
||||||
|
|
||||||
# Using multistage build:
|
# Using multistage build:
|
||||||
# https://docs.docker.com/develop/develop-images/multistage-build/
|
# https://docs.docker.com/develop/develop-images/multistage-build/
|
||||||
# https://whitfin.io/speeding-up-rust-docker-builds/
|
# https://whitfin.io/speeding-up-rust-docker-builds/
|
||||||
####################### VAULT BUILD IMAGE #######################
|
####################### VAULT BUILD IMAGE #######################
|
||||||
FROM alpine:3.11 as vault
|
|
||||||
|
|
||||||
ENV VAULT_VERSION "v2.12.0b"
|
# This hash is extracted from the docker web-vault builds and it's prefered over a simple tag because it's immutable.
|
||||||
|
# It can be viewed in multiple ways:
|
||||||
ENV URL "https://github.com/dani-garcia/bw_web_builds/releases/download/$VAULT_VERSION/bw_web_$VAULT_VERSION.tar.gz"
|
# - From the https://hub.docker.com/repository/docker/bitwardenrs/web-vault/tags page, click the tag name and the digest should be there.
|
||||||
|
# - From the console, with the following commands:
|
||||||
RUN apk add --no-cache --upgrade \
|
# docker pull bitwardenrs/web-vault:v2.12.0e
|
||||||
curl \
|
# docker image inspect --format "{{.RepoDigests}}" bitwardenrs/web-vault:v2.12.0e
|
||||||
tar
|
#
|
||||||
|
# - To do the opposite, and get the tag from the hash, you can do:
|
||||||
RUN mkdir /web-vault
|
# docker image inspect --format "{{.RepoTags}}" bitwardenrs/web-vault@sha256:ce56b3f5e538351411785ac45e9b4b913259c3508b1323d62e8fa0f30717dd1c
|
||||||
WORKDIR /web-vault
|
FROM bitwardenrs/web-vault@sha256:ce56b3f5e538351411785ac45e9b4b913259c3508b1323d62e8fa0f30717dd1c as vault
|
||||||
|
|
||||||
SHELL ["/bin/ash", "-eo", "pipefail", "-c"]
|
|
||||||
|
|
||||||
RUN curl -L $URL | tar xz
|
|
||||||
RUN ls
|
|
||||||
|
|
||||||
########################## BUILD IMAGE ##########################
|
########################## BUILD IMAGE ##########################
|
||||||
# We need to use the Rust build image, because
|
# We need to use the Rust build image, because
|
||||||
# we need the Rust compiler and Cargo tooling
|
# we need the Rust compiler and Cargo tooling
|
||||||
FROM rust:1.40 as build
|
FROM rust:1.40 as build
|
||||||
|
|
||||||
# set sqlite as default for DB ARG for backward comaptibility
|
# set sqlite as default for DB ARG for backward compatibility
|
||||||
ARG DB=sqlite
|
ARG DB=sqlite
|
||||||
|
|
||||||
|
# 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
|
||||||
|
|
||||||
# Don't download rust docs
|
# Don't download rust docs
|
||||||
RUN rustup set profile minimal
|
RUN rustup set profile minimal
|
||||||
|
|
||||||
|
# Install required build libs for armel architecture.
|
||||||
|
RUN sed 's/^deb/deb-src/' /etc/apt/sources.list > \
|
||||||
|
/etc/apt/sources.list.d/deb-src.list \
|
||||||
|
&& dpkg --add-architecture armel \
|
||||||
|
&& apt-get update \
|
||||||
|
&& apt-get install -y \
|
||||||
|
--no-install-recommends \
|
||||||
|
libssl-dev:armel \
|
||||||
|
libc6-dev:armel
|
||||||
|
|
||||||
RUN apt-get update \
|
RUN apt-get update \
|
||||||
&& apt-get install -y \
|
&& apt-get install -y \
|
||||||
--no-install-recommends \
|
--no-install-recommends \
|
||||||
@@ -42,29 +52,36 @@ RUN apt-get update \
|
|||||||
ENV CARGO_HOME "/root/.cargo"
|
ENV CARGO_HOME "/root/.cargo"
|
||||||
ENV USER "root"
|
ENV USER "root"
|
||||||
|
|
||||||
|
# Creates a dummy project used to grab dependencies
|
||||||
|
RUN USER=root cargo new --bin /app
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
|
|
||||||
# Prepare openssl armel libs
|
# Copies over *only* your manifests and build files
|
||||||
RUN sed 's/^deb/deb-src/' /etc/apt/sources.list > \
|
COPY ./Cargo.* ./
|
||||||
/etc/apt/sources.list.d/deb-src.list \
|
COPY ./rust-toolchain ./rust-toolchain
|
||||||
&& dpkg --add-architecture armel \
|
COPY ./build.rs ./build.rs
|
||||||
&& apt-get update \
|
|
||||||
&& apt-get install -y \
|
|
||||||
--no-install-recommends \
|
|
||||||
libssl-dev:armel \
|
|
||||||
libc6-dev:armel
|
|
||||||
|
|
||||||
ENV CC_arm_unknown_linux_gnueabi="/usr/bin/arm-linux-gnueabi-gcc"
|
ENV CC_arm_unknown_linux_gnueabi="/usr/bin/arm-linux-gnueabi-gcc"
|
||||||
ENV CROSS_COMPILE="1"
|
ENV CROSS_COMPILE="1"
|
||||||
ENV OPENSSL_INCLUDE_DIR="/usr/include/arm-linux-gnueabi"
|
ENV OPENSSL_INCLUDE_DIR="/usr/include/arm-linux-gnueabi"
|
||||||
ENV OPENSSL_LIB_DIR="/usr/lib/arm-linux-gnueabi"
|
ENV OPENSSL_LIB_DIR="/usr/lib/arm-linux-gnueabi"
|
||||||
|
RUN rustup target add arm-unknown-linux-gnueabi
|
||||||
|
|
||||||
|
# Builds your dependencies and removes the
|
||||||
|
# dummy project, except the target folder
|
||||||
|
# This folder contains the compiled dependencies
|
||||||
|
RUN cargo build --features ${DB} --release
|
||||||
|
RUN find . -not -path "./target*" -delete
|
||||||
|
|
||||||
# Copies the complete project
|
# Copies the complete project
|
||||||
# To avoid copying unneeded files, use .dockerignore
|
# To avoid copying unneeded files, use .dockerignore
|
||||||
COPY . .
|
COPY . .
|
||||||
|
|
||||||
# Build
|
# Make sure that we actually build the project
|
||||||
RUN rustup target add arm-unknown-linux-gnueabi
|
RUN touch src/main.rs
|
||||||
|
|
||||||
|
# Builds again, this time it'll just be
|
||||||
|
# your actual source files being built
|
||||||
RUN cargo build --features ${DB} --release --target=arm-unknown-linux-gnueabi
|
RUN cargo build --features ${DB} --release --target=arm-unknown-linux-gnueabi
|
||||||
|
|
||||||
######################## RUNTIME IMAGE ########################
|
######################## RUNTIME IMAGE ########################
|
||||||
@@ -93,6 +110,7 @@ RUN [ "cross-build-end" ]
|
|||||||
|
|
||||||
VOLUME /data
|
VOLUME /data
|
||||||
EXPOSE 80
|
EXPOSE 80
|
||||||
|
EXPOSE 3012
|
||||||
|
|
||||||
# Copies the files from the context (Rocket.toml file and web-vault)
|
# Copies the files from the context (Rocket.toml file and web-vault)
|
||||||
# and the binary from the "build" stage to the current stage
|
# and the binary from the "build" stage to the current stage
|
||||||
|
@@ -1,24 +1,21 @@
|
|||||||
|
# This file was generated using a Jinja2 template.
|
||||||
|
# Please make your changes in `Dockerfile.j2` and then `make` the individual Dockerfile's.
|
||||||
|
|
||||||
# Using multistage build:
|
# Using multistage build:
|
||||||
# https://docs.docker.com/develop/develop-images/multistage-build/
|
# https://docs.docker.com/develop/develop-images/multistage-build/
|
||||||
# https://whitfin.io/speeding-up-rust-docker-builds/
|
# https://whitfin.io/speeding-up-rust-docker-builds/
|
||||||
####################### VAULT BUILD IMAGE #######################
|
####################### VAULT BUILD IMAGE #######################
|
||||||
FROM alpine:3.11 as vault
|
|
||||||
|
|
||||||
ENV VAULT_VERSION "v2.12.0b"
|
# This hash is extracted from the docker web-vault builds and it's prefered over a simple tag because it's immutable.
|
||||||
|
# It can be viewed in multiple ways:
|
||||||
ENV URL "https://github.com/dani-garcia/bw_web_builds/releases/download/$VAULT_VERSION/bw_web_$VAULT_VERSION.tar.gz"
|
# - From the https://hub.docker.com/repository/docker/bitwardenrs/web-vault/tags page, click the tag name and the digest should be there.
|
||||||
|
# - From the console, with the following commands:
|
||||||
RUN apk add --no-cache --upgrade \
|
# docker pull bitwardenrs/web-vault:v2.12.0e
|
||||||
curl \
|
# docker image inspect --format "{{.RepoDigests}}" bitwardenrs/web-vault:v2.12.0e
|
||||||
tar
|
#
|
||||||
|
# - To do the opposite, and get the tag from the hash, you can do:
|
||||||
RUN mkdir /web-vault
|
# docker image inspect --format "{{.RepoTags}}" bitwardenrs/web-vault@sha256:ce56b3f5e538351411785ac45e9b4b913259c3508b1323d62e8fa0f30717dd1c
|
||||||
WORKDIR /web-vault
|
FROM bitwardenrs/web-vault@sha256:ce56b3f5e538351411785ac45e9b4b913259c3508b1323d62e8fa0f30717dd1c as vault
|
||||||
|
|
||||||
SHELL ["/bin/ash", "-eo", "pipefail", "-c"]
|
|
||||||
|
|
||||||
RUN curl -L $URL | tar xz
|
|
||||||
RUN ls
|
|
||||||
|
|
||||||
########################## BUILD IMAGE ##########################
|
########################## BUILD IMAGE ##########################
|
||||||
# We need to use the Rust build image, because
|
# We need to use the Rust build image, because
|
||||||
@@ -28,9 +25,22 @@ FROM rust:1.40 as build
|
|||||||
# set mysql backend
|
# set mysql backend
|
||||||
ARG DB=mysql
|
ARG DB=mysql
|
||||||
|
|
||||||
|
# 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
|
||||||
|
|
||||||
# Don't download rust docs
|
# Don't download rust docs
|
||||||
RUN rustup set profile minimal
|
RUN rustup set profile minimal
|
||||||
|
|
||||||
|
# Install required build libs for armhf architecture.
|
||||||
|
RUN sed 's/^deb/deb-src/' /etc/apt/sources.list > \
|
||||||
|
/etc/apt/sources.list.d/deb-src.list \
|
||||||
|
&& dpkg --add-architecture armhf \
|
||||||
|
&& apt-get update \
|
||||||
|
&& apt-get install -y \
|
||||||
|
--no-install-recommends \
|
||||||
|
libssl-dev:armhf \
|
||||||
|
libc6-dev:armhf
|
||||||
|
|
||||||
RUN apt-get update \
|
RUN apt-get update \
|
||||||
&& apt-get install -y \
|
&& apt-get install -y \
|
||||||
--no-install-recommends \
|
--no-install-recommends \
|
||||||
@@ -42,31 +52,41 @@ RUN apt-get update \
|
|||||||
ENV CARGO_HOME "/root/.cargo"
|
ENV CARGO_HOME "/root/.cargo"
|
||||||
ENV USER "root"
|
ENV USER "root"
|
||||||
|
|
||||||
|
# Install MySQL package
|
||||||
|
RUN apt-get update && apt-get install -y \
|
||||||
|
--no-install-recommends \
|
||||||
|
libmariadb-dev:armhf \
|
||||||
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
|
# Creates a dummy project used to grab dependencies
|
||||||
|
RUN USER=root cargo new --bin /app
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
|
|
||||||
# Prepare openssl armhf libs
|
# Copies over *only* your manifests and build files
|
||||||
RUN sed 's/^deb/deb-src/' /etc/apt/sources.list > \
|
COPY ./Cargo.* ./
|
||||||
/etc/apt/sources.list.d/deb-src.list \
|
COPY ./rust-toolchain ./rust-toolchain
|
||||||
&& dpkg --add-architecture armhf \
|
COPY ./build.rs ./build.rs
|
||||||
&& apt-get update \
|
|
||||||
&& apt-get install -y \
|
|
||||||
--no-install-recommends \
|
|
||||||
libssl-dev:armhf \
|
|
||||||
libc6-dev:armhf \
|
|
||||||
libmariadb-dev:armhf
|
|
||||||
|
|
||||||
|
|
||||||
ENV CC_armv7_unknown_linux_gnueabihf="/usr/bin/arm-linux-gnueabihf-gcc"
|
ENV CC_armv7_unknown_linux_gnueabihf="/usr/bin/arm-linux-gnueabihf-gcc"
|
||||||
ENV CROSS_COMPILE="1"
|
ENV CROSS_COMPILE="1"
|
||||||
ENV OPENSSL_INCLUDE_DIR="/usr/include/arm-linux-gnueabihf"
|
ENV OPENSSL_INCLUDE_DIR="/usr/include/arm-linux-gnueabihf"
|
||||||
ENV OPENSSL_LIB_DIR="/usr/lib/arm-linux-gnueabihf"
|
ENV OPENSSL_LIB_DIR="/usr/lib/arm-linux-gnueabihf"
|
||||||
|
RUN rustup target add armv7-unknown-linux-gnueabihf
|
||||||
|
# Builds your dependencies and removes the
|
||||||
|
# dummy project, except the target folder
|
||||||
|
# This folder contains the compiled dependencies
|
||||||
|
RUN cargo build --features ${DB} --release
|
||||||
|
RUN find . -not -path "./target*" -delete
|
||||||
|
|
||||||
# Copies the complete project
|
# Copies the complete project
|
||||||
# To avoid copying unneeded files, use .dockerignore
|
# To avoid copying unneeded files, use .dockerignore
|
||||||
COPY . .
|
COPY . .
|
||||||
|
|
||||||
# Build
|
# Make sure that we actually build the project
|
||||||
RUN rustup target add armv7-unknown-linux-gnueabihf
|
RUN touch src/main.rs
|
||||||
|
|
||||||
|
# Builds again, this time it'll just be
|
||||||
|
# your actual source files being built
|
||||||
RUN cargo build --features ${DB} --release --target=armv7-unknown-linux-gnueabihf
|
RUN cargo build --features ${DB} --release --target=armv7-unknown-linux-gnueabihf
|
||||||
|
|
||||||
######################## RUNTIME IMAGE ########################
|
######################## RUNTIME IMAGE ########################
|
||||||
@@ -95,6 +115,7 @@ RUN [ "cross-build-end" ]
|
|||||||
|
|
||||||
VOLUME /data
|
VOLUME /data
|
||||||
EXPOSE 80
|
EXPOSE 80
|
||||||
|
EXPOSE 3012
|
||||||
|
|
||||||
# Copies the files from the context (Rocket.toml file and web-vault)
|
# Copies the files from the context (Rocket.toml file and web-vault)
|
||||||
# and the binary from the "build" stage to the current stage
|
# and the binary from the "build" stage to the current stage
|
||||||
|
@@ -1,36 +1,46 @@
|
|||||||
|
# This file was generated using a Jinja2 template.
|
||||||
|
# Please make your changes in `Dockerfile.j2` and then `make` the individual Dockerfile's.
|
||||||
|
|
||||||
# Using multistage build:
|
# Using multistage build:
|
||||||
# https://docs.docker.com/develop/develop-images/multistage-build/
|
# https://docs.docker.com/develop/develop-images/multistage-build/
|
||||||
# https://whitfin.io/speeding-up-rust-docker-builds/
|
# https://whitfin.io/speeding-up-rust-docker-builds/
|
||||||
####################### VAULT BUILD IMAGE #######################
|
####################### VAULT BUILD IMAGE #######################
|
||||||
FROM alpine:3.11 as vault
|
|
||||||
|
|
||||||
ENV VAULT_VERSION "v2.12.0b"
|
# This hash is extracted from the docker web-vault builds and it's prefered over a simple tag because it's immutable.
|
||||||
|
# It can be viewed in multiple ways:
|
||||||
ENV URL "https://github.com/dani-garcia/bw_web_builds/releases/download/$VAULT_VERSION/bw_web_$VAULT_VERSION.tar.gz"
|
# - From the https://hub.docker.com/repository/docker/bitwardenrs/web-vault/tags page, click the tag name and the digest should be there.
|
||||||
|
# - From the console, with the following commands:
|
||||||
RUN apk add --no-cache --upgrade \
|
# docker pull bitwardenrs/web-vault:v2.12.0e
|
||||||
curl \
|
# docker image inspect --format "{{.RepoDigests}}" bitwardenrs/web-vault:v2.12.0e
|
||||||
tar
|
#
|
||||||
|
# - To do the opposite, and get the tag from the hash, you can do:
|
||||||
RUN mkdir /web-vault
|
# docker image inspect --format "{{.RepoTags}}" bitwardenrs/web-vault@sha256:ce56b3f5e538351411785ac45e9b4b913259c3508b1323d62e8fa0f30717dd1c
|
||||||
WORKDIR /web-vault
|
FROM bitwardenrs/web-vault@sha256:ce56b3f5e538351411785ac45e9b4b913259c3508b1323d62e8fa0f30717dd1c as vault
|
||||||
|
|
||||||
SHELL ["/bin/ash", "-eo", "pipefail", "-c"]
|
|
||||||
|
|
||||||
RUN curl -L $URL | tar xz
|
|
||||||
RUN ls
|
|
||||||
|
|
||||||
########################## BUILD IMAGE ##########################
|
########################## BUILD IMAGE ##########################
|
||||||
# We need to use the Rust build image, because
|
# We need to use the Rust build image, because
|
||||||
# we need the Rust compiler and Cargo tooling
|
# we need the Rust compiler and Cargo tooling
|
||||||
FROM rust:1.40 as build
|
FROM rust:1.40 as build
|
||||||
|
|
||||||
# set sqlite as default for DB ARG for backward comaptibility
|
# set sqlite as default for DB ARG for backward compatibility
|
||||||
ARG DB=sqlite
|
ARG DB=sqlite
|
||||||
|
|
||||||
|
# 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
|
||||||
|
|
||||||
# Don't download rust docs
|
# Don't download rust docs
|
||||||
RUN rustup set profile minimal
|
RUN rustup set profile minimal
|
||||||
|
|
||||||
|
# Install required build libs for armhf architecture.
|
||||||
|
RUN sed 's/^deb/deb-src/' /etc/apt/sources.list > \
|
||||||
|
/etc/apt/sources.list.d/deb-src.list \
|
||||||
|
&& dpkg --add-architecture armhf \
|
||||||
|
&& apt-get update \
|
||||||
|
&& apt-get install -y \
|
||||||
|
--no-install-recommends \
|
||||||
|
libssl-dev:armhf \
|
||||||
|
libc6-dev:armhf
|
||||||
|
|
||||||
RUN apt-get update \
|
RUN apt-get update \
|
||||||
&& apt-get install -y \
|
&& apt-get install -y \
|
||||||
--no-install-recommends \
|
--no-install-recommends \
|
||||||
@@ -42,29 +52,35 @@ RUN apt-get update \
|
|||||||
ENV CARGO_HOME "/root/.cargo"
|
ENV CARGO_HOME "/root/.cargo"
|
||||||
ENV USER "root"
|
ENV USER "root"
|
||||||
|
|
||||||
|
# Creates a dummy project used to grab dependencies
|
||||||
|
RUN USER=root cargo new --bin /app
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
|
|
||||||
# Prepare openssl armhf libs
|
# Copies over *only* your manifests and build files
|
||||||
RUN sed 's/^deb/deb-src/' /etc/apt/sources.list > \
|
COPY ./Cargo.* ./
|
||||||
/etc/apt/sources.list.d/deb-src.list \
|
COPY ./rust-toolchain ./rust-toolchain
|
||||||
&& dpkg --add-architecture armhf \
|
COPY ./build.rs ./build.rs
|
||||||
&& apt-get update \
|
|
||||||
&& apt-get install -y \
|
|
||||||
--no-install-recommends \
|
|
||||||
libssl-dev:armhf \
|
|
||||||
libc6-dev:armhf
|
|
||||||
|
|
||||||
ENV CC_armv7_unknown_linux_gnueabihf="/usr/bin/arm-linux-gnueabihf-gcc"
|
ENV CC_armv7_unknown_linux_gnueabihf="/usr/bin/arm-linux-gnueabihf-gcc"
|
||||||
ENV CROSS_COMPILE="1"
|
ENV CROSS_COMPILE="1"
|
||||||
ENV OPENSSL_INCLUDE_DIR="/usr/include/arm-linux-gnueabihf"
|
ENV OPENSSL_INCLUDE_DIR="/usr/include/arm-linux-gnueabihf"
|
||||||
ENV OPENSSL_LIB_DIR="/usr/lib/arm-linux-gnueabihf"
|
ENV OPENSSL_LIB_DIR="/usr/lib/arm-linux-gnueabihf"
|
||||||
|
RUN rustup target add armv7-unknown-linux-gnueabihf
|
||||||
|
# Builds your dependencies and removes the
|
||||||
|
# dummy project, except the target folder
|
||||||
|
# This folder contains the compiled dependencies
|
||||||
|
RUN cargo build --features ${DB} --release
|
||||||
|
RUN find . -not -path "./target*" -delete
|
||||||
|
|
||||||
# Copies the complete project
|
# Copies the complete project
|
||||||
# To avoid copying unneeded files, use .dockerignore
|
# To avoid copying unneeded files, use .dockerignore
|
||||||
COPY . .
|
COPY . .
|
||||||
|
|
||||||
# Build
|
# Make sure that we actually build the project
|
||||||
RUN rustup target add armv7-unknown-linux-gnueabihf
|
RUN touch src/main.rs
|
||||||
|
|
||||||
|
# Builds again, this time it'll just be
|
||||||
|
# your actual source files being built
|
||||||
RUN cargo build --features ${DB} --release --target=armv7-unknown-linux-gnueabihf
|
RUN cargo build --features ${DB} --release --target=armv7-unknown-linux-gnueabihf
|
||||||
|
|
||||||
######################## RUNTIME IMAGE ########################
|
######################## RUNTIME IMAGE ########################
|
||||||
@@ -93,6 +109,7 @@ RUN [ "cross-build-end" ]
|
|||||||
|
|
||||||
VOLUME /data
|
VOLUME /data
|
||||||
EXPOSE 80
|
EXPOSE 80
|
||||||
|
EXPOSE 3012
|
||||||
|
|
||||||
# Copies the files from the context (Rocket.toml file and web-vault)
|
# Copies the files from the context (Rocket.toml file and web-vault)
|
||||||
# and the binary from the "build" stage to the current stage
|
# and the binary from the "build" stage to the current stage
|
||||||
|
17
docker/render_template
Executable file
17
docker/render_template
Executable file
@@ -0,0 +1,17 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
import os, argparse, json
|
||||||
|
|
||||||
|
import jinja2
|
||||||
|
|
||||||
|
args_parser = argparse.ArgumentParser()
|
||||||
|
args_parser.add_argument('template_file', help='Jinja2 template file to render.')
|
||||||
|
args_parser.add_argument('render_vars', help='JSON-encoded data to pass to the templating engine.')
|
||||||
|
cli_args = args_parser.parse_args()
|
||||||
|
|
||||||
|
render_vars = json.loads(cli_args.render_vars)
|
||||||
|
environment = jinja2.Environment(
|
||||||
|
loader=jinja2.FileSystemLoader(os.getcwd()),
|
||||||
|
trim_blocks=True,
|
||||||
|
)
|
||||||
|
print(environment.get_template(cli_args.template_file).render(render_vars))
|
@@ -1 +1 @@
|
|||||||
nightly-2019-12-19
|
nightly-2020-03-09
|
@@ -1,3 +1,4 @@
|
|||||||
|
use once_cell::sync::Lazy;
|
||||||
use serde_json::Value;
|
use serde_json::Value;
|
||||||
use std::process::Command;
|
use std::process::Command;
|
||||||
|
|
||||||
@@ -34,12 +35,12 @@ pub fn routes() -> Vec<Route> {
|
|||||||
post_config,
|
post_config,
|
||||||
delete_config,
|
delete_config,
|
||||||
backup_db,
|
backup_db,
|
||||||
|
test_smtp,
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
lazy_static! {
|
static CAN_BACKUP: Lazy<bool> =
|
||||||
static ref CAN_BACKUP: bool = cfg!(feature = "sqlite") && Command::new("sqlite3").arg("-version").status().is_ok();
|
Lazy::new(|| cfg!(feature = "sqlite") && Command::new("sqlite3").arg("-version").status().is_ok());
|
||||||
}
|
|
||||||
|
|
||||||
#[get("/")]
|
#[get("/")]
|
||||||
fn admin_disabled() -> &'static str {
|
fn admin_disabled() -> &'static str {
|
||||||
@@ -52,11 +53,15 @@ const ADMIN_PATH: &str = "/admin";
|
|||||||
const BASE_TEMPLATE: &str = "admin/base";
|
const BASE_TEMPLATE: &str = "admin/base";
|
||||||
const VERSION: Option<&str> = option_env!("GIT_VERSION");
|
const VERSION: Option<&str> = option_env!("GIT_VERSION");
|
||||||
|
|
||||||
|
fn admin_path() -> String {
|
||||||
|
format!("{}{}", CONFIG.domain_path(), ADMIN_PATH)
|
||||||
|
}
|
||||||
|
|
||||||
#[get("/", rank = 2)]
|
#[get("/", rank = 2)]
|
||||||
fn admin_login(flash: Option<FlashMessage>) -> ApiResult<Html<String>> {
|
fn admin_login(flash: Option<FlashMessage>) -> ApiResult<Html<String>> {
|
||||||
// If there is an error, show it
|
// If there is an error, show it
|
||||||
let msg = flash.map(|msg| format!("{}: {}", msg.name(), msg.msg()));
|
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
|
// Return the page
|
||||||
let text = CONFIG.render_template(BASE_TEMPLATE, &json)?;
|
let text = CONFIG.render_template(BASE_TEMPLATE, &json)?;
|
||||||
@@ -76,7 +81,7 @@ fn post_admin_login(data: Form<LoginForm>, mut cookies: Cookies, ip: ClientIp) -
|
|||||||
if !_validate_token(&data.token) {
|
if !_validate_token(&data.token) {
|
||||||
error!("Invalid admin token. IP: {}", ip.ip);
|
error!("Invalid admin token. IP: {}", ip.ip);
|
||||||
Err(Flash::error(
|
Err(Flash::error(
|
||||||
Redirect::to(ADMIN_PATH),
|
Redirect::to(admin_path()),
|
||||||
"Invalid admin token, please try again.",
|
"Invalid admin token, please try again.",
|
||||||
))
|
))
|
||||||
} else {
|
} else {
|
||||||
@@ -85,14 +90,14 @@ fn post_admin_login(data: Form<LoginForm>, mut cookies: Cookies, ip: ClientIp) -
|
|||||||
let jwt = encode_jwt(&claims);
|
let jwt = encode_jwt(&claims);
|
||||||
|
|
||||||
let cookie = Cookie::build(COOKIE_NAME, jwt)
|
let cookie = Cookie::build(COOKIE_NAME, jwt)
|
||||||
.path(ADMIN_PATH)
|
.path(admin_path())
|
||||||
.max_age(chrono::Duration::minutes(20))
|
.max_age(chrono::Duration::minutes(20))
|
||||||
.same_site(SameSite::Strict)
|
.same_site(SameSite::Strict)
|
||||||
.http_only(true)
|
.http_only(true)
|
||||||
.finish();
|
.finish();
|
||||||
|
|
||||||
cookies.add(cookie);
|
cookies.add(cookie);
|
||||||
Ok(Redirect::to(ADMIN_PATH))
|
Ok(Redirect::to(admin_path()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -111,6 +116,7 @@ struct AdminTemplateData {
|
|||||||
config: Value,
|
config: Value,
|
||||||
can_backup: bool,
|
can_backup: bool,
|
||||||
logged_in: bool,
|
logged_in: bool,
|
||||||
|
urlpath: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AdminTemplateData {
|
impl AdminTemplateData {
|
||||||
@@ -122,6 +128,7 @@ impl AdminTemplateData {
|
|||||||
config: CONFIG.prepare_json(),
|
config: CONFIG.prepare_json(),
|
||||||
can_backup: *CAN_BACKUP,
|
can_backup: *CAN_BACKUP,
|
||||||
logged_in: true,
|
logged_in: true,
|
||||||
|
urlpath: CONFIG.domain_path(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -153,26 +160,33 @@ fn invite_user(data: Json<InviteData>, _token: AdminToken, conn: DbConn) -> Empt
|
|||||||
err!("User already exists")
|
err!("User already exists")
|
||||||
}
|
}
|
||||||
|
|
||||||
if !CONFIG.invitations_allowed() {
|
|
||||||
err!("Invitations are not allowed")
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut user = User::new(email);
|
let mut user = User::new(email);
|
||||||
user.save(&conn)?;
|
user.save(&conn)?;
|
||||||
|
|
||||||
if CONFIG.mail_enabled() {
|
if CONFIG.mail_enabled() {
|
||||||
let org_name = "bitwarden_rs";
|
mail::send_invite(&user.email, &user.uuid, None, None, &CONFIG.invitation_org_name(), None)
|
||||||
mail::send_invite(&user.email, &user.uuid, None, None, &org_name, None)
|
|
||||||
} else {
|
} else {
|
||||||
let invitation = Invitation::new(data.email);
|
let invitation = Invitation::new(data.email);
|
||||||
invitation.save(&conn)
|
invitation.save(&conn)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[post("/test/smtp", data = "<data>")]
|
||||||
|
fn test_smtp(data: Json<InviteData>, _token: AdminToken) -> EmptyResult {
|
||||||
|
let data: InviteData = data.into_inner();
|
||||||
|
let email = data.email.clone();
|
||||||
|
|
||||||
|
if CONFIG.mail_enabled() {
|
||||||
|
mail::send_test(&email)
|
||||||
|
} else {
|
||||||
|
err!("Mail is not enabled")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[get("/logout")]
|
#[get("/logout")]
|
||||||
fn logout(mut cookies: Cookies) -> Result<Redirect, ()> {
|
fn logout(mut cookies: Cookies) -> Result<Redirect, ()> {
|
||||||
cookies.remove(Cookie::named(COOKIE_NAME));
|
cookies.remove(Cookie::named(COOKIE_NAME));
|
||||||
Ok(Redirect::to(ADMIN_PATH))
|
Ok(Redirect::to(admin_path()))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[get("/users")]
|
#[get("/users")]
|
||||||
|
@@ -642,20 +642,49 @@ fn post_attachment(
|
|||||||
) -> JsonResult {
|
) -> JsonResult {
|
||||||
let cipher = match Cipher::find_by_uuid(&uuid, &conn) {
|
let cipher = match Cipher::find_by_uuid(&uuid, &conn) {
|
||||||
Some(cipher) => cipher,
|
Some(cipher) => cipher,
|
||||||
None => err!("Cipher doesn't exist"),
|
None => err_discard!("Cipher doesn't exist", data),
|
||||||
};
|
};
|
||||||
|
|
||||||
if !cipher.is_write_accessible_to_user(&headers.user.uuid, &conn) {
|
if !cipher.is_write_accessible_to_user(&headers.user.uuid, &conn) {
|
||||||
err!("Cipher is not write accessible")
|
err_discard!("Cipher is not write accessible", data)
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut params = content_type.params();
|
let mut params = content_type.params();
|
||||||
let boundary_pair = params.next().expect("No boundary provided");
|
let boundary_pair = params.next().expect("No boundary provided");
|
||||||
let boundary = boundary_pair.1;
|
let boundary = boundary_pair.1;
|
||||||
|
|
||||||
|
let size_limit = if let Some(ref user_uuid) = cipher.user_uuid {
|
||||||
|
match CONFIG.user_attachment_limit() {
|
||||||
|
Some(0) => err_discard!("Attachments are disabled", data),
|
||||||
|
Some(limit) => {
|
||||||
|
let left = limit - Attachment::size_by_user(user_uuid, &conn);
|
||||||
|
if left <= 0 {
|
||||||
|
err_discard!("Attachment size limit reached! Delete some files to open space", data)
|
||||||
|
}
|
||||||
|
Some(left as u64)
|
||||||
|
}
|
||||||
|
None => None,
|
||||||
|
}
|
||||||
|
} else if let Some(ref org_uuid) = cipher.organization_uuid {
|
||||||
|
match CONFIG.org_attachment_limit() {
|
||||||
|
Some(0) => err_discard!("Attachments are disabled", data),
|
||||||
|
Some(limit) => {
|
||||||
|
let left = limit - Attachment::size_by_org(org_uuid, &conn);
|
||||||
|
if left <= 0 {
|
||||||
|
err_discard!("Attachment size limit reached! Delete some files to open space", data)
|
||||||
|
}
|
||||||
|
Some(left as u64)
|
||||||
|
}
|
||||||
|
None => None,
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
err_discard!("Cipher is neither owned by a user nor an organization", data);
|
||||||
|
};
|
||||||
|
|
||||||
let base_path = Path::new(&CONFIG.attachments_folder()).join(&cipher.uuid);
|
let base_path = Path::new(&CONFIG.attachments_folder()).join(&cipher.uuid);
|
||||||
|
|
||||||
let mut attachment_key = None;
|
let mut attachment_key = None;
|
||||||
|
let mut error = None;
|
||||||
|
|
||||||
Multipart::with_body(data.open(), boundary)
|
Multipart::with_body(data.open(), boundary)
|
||||||
.foreach_entry(|mut field| {
|
.foreach_entry(|mut field| {
|
||||||
@@ -674,18 +703,21 @@ fn post_attachment(
|
|||||||
let file_name = HEXLOWER.encode(&crypto::get_random(vec![0; 10]));
|
let file_name = HEXLOWER.encode(&crypto::get_random(vec![0; 10]));
|
||||||
let path = base_path.join(&file_name);
|
let path = base_path.join(&file_name);
|
||||||
|
|
||||||
let size = match field.data.save().memory_threshold(0).size_limit(None).with_path(path) {
|
let size = match field.data.save().memory_threshold(0).size_limit(size_limit).with_path(path.clone()) {
|
||||||
SaveResult::Full(SavedData::File(_, size)) => size as i32,
|
SaveResult::Full(SavedData::File(_, size)) => size as i32,
|
||||||
SaveResult::Full(other) => {
|
SaveResult::Full(other) => {
|
||||||
error!("Attachment is not a file: {:?}", other);
|
std::fs::remove_file(path).ok();
|
||||||
|
error = Some(format!("Attachment is not a file: {:?}", other));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
SaveResult::Partial(_, reason) => {
|
SaveResult::Partial(_, reason) => {
|
||||||
error!("Partial result: {:?}", reason);
|
std::fs::remove_file(path).ok();
|
||||||
|
error = Some(format!("Attachment size limit exceeded with this file: {:?}", reason));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
SaveResult::Error(e) => {
|
SaveResult::Error(e) => {
|
||||||
error!("Error: {:?}", e);
|
std::fs::remove_file(path).ok();
|
||||||
|
error = Some(format!("Error: {:?}", e));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -699,6 +731,10 @@ fn post_attachment(
|
|||||||
})
|
})
|
||||||
.expect("Error processing multipart data");
|
.expect("Error processing multipart data");
|
||||||
|
|
||||||
|
if let Some(ref e) = error {
|
||||||
|
err!(e);
|
||||||
|
}
|
||||||
|
|
||||||
nt.send_cipher_update(UpdateType::CipherUpdate, &cipher, &cipher.update_users_revision(&conn));
|
nt.send_cipher_update(UpdateType::CipherUpdate, &cipher, &cipher.update_users_revision(&conn));
|
||||||
|
|
||||||
Ok(Json(cipher.to_json(&headers.host, &headers.user.uuid, &conn)))
|
Ok(Json(cipher.to_json(&headers.host, &headers.user.uuid, &conn)))
|
||||||
|
@@ -172,7 +172,7 @@ fn hibp_breach(username: String) -> JsonResult {
|
|||||||
"BreachDate": "2019-08-18T00:00:00Z",
|
"BreachDate": "2019-08-18T00:00:00Z",
|
||||||
"AddedDate": "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),
|
"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,
|
"PwnCount": 0,
|
||||||
"DataClasses": [
|
"DataClasses": [
|
||||||
"Error - No API key set!"
|
"Error - No API key set!"
|
||||||
|
@@ -271,10 +271,10 @@ impl EmailTokenData {
|
|||||||
|
|
||||||
/// Takes an email address and obscures it by replacing it with asterisks except two characters.
|
/// Takes an email address and obscures it by replacing it with asterisks except two characters.
|
||||||
pub fn obscure_email(email: &str) -> String {
|
pub fn obscure_email(email: &str) -> String {
|
||||||
let split: Vec<&str> = email.split('@').collect();
|
let split: Vec<&str> = email.rsplitn(2, '@').collect();
|
||||||
|
|
||||||
let mut name = split[0].to_string();
|
let mut name = split[1].to_string();
|
||||||
let domain = &split[1];
|
let domain = &split[0];
|
||||||
|
|
||||||
let name_size = name.chars().count();
|
let name_size = name.chars().count();
|
||||||
|
|
||||||
|
@@ -1,3 +1,4 @@
|
|||||||
|
use once_cell::sync::Lazy;
|
||||||
use rocket::Route;
|
use rocket::Route;
|
||||||
use rocket_contrib::json::Json;
|
use rocket_contrib::json::Json;
|
||||||
use serde_json;
|
use serde_json;
|
||||||
@@ -18,10 +19,8 @@ use crate::CONFIG;
|
|||||||
|
|
||||||
const U2F_VERSION: &str = "U2F_V2";
|
const U2F_VERSION: &str = "U2F_V2";
|
||||||
|
|
||||||
lazy_static! {
|
static APP_ID: Lazy<String> = Lazy::new(|| format!("{}/app-id.json", &CONFIG.domain()));
|
||||||
static ref APP_ID: String = format!("{}/app-id.json", &CONFIG.domain());
|
static U2F: Lazy<U2f> = Lazy::new(|| U2f::new(APP_ID.clone()));
|
||||||
static ref U2F: U2f = U2f::new(APP_ID.clone());
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn routes() -> Vec<Route> {
|
pub fn routes() -> Vec<Route> {
|
||||||
routes![
|
routes![
|
||||||
@@ -92,6 +91,7 @@ struct RegistrationDef {
|
|||||||
key_handle: Vec<u8>,
|
key_handle: Vec<u8>,
|
||||||
pub_key: Vec<u8>,
|
pub_key: Vec<u8>,
|
||||||
attestation_cert: Option<Vec<u8>>,
|
attestation_cert: Option<Vec<u8>>,
|
||||||
|
device_name: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize)]
|
#[derive(Serialize, Deserialize)]
|
||||||
|
@@ -1,3 +1,4 @@
|
|||||||
|
use once_cell::sync::Lazy;
|
||||||
use std::fs::{create_dir_all, remove_file, symlink_metadata, File};
|
use std::fs::{create_dir_all, remove_file, symlink_metadata, File};
|
||||||
use std::io::prelude::*;
|
use std::io::prelude::*;
|
||||||
use std::net::ToSocketAddrs;
|
use std::net::ToSocketAddrs;
|
||||||
@@ -16,6 +17,7 @@ use soup::prelude::*;
|
|||||||
|
|
||||||
use crate::error::Error;
|
use crate::error::Error;
|
||||||
use crate::CONFIG;
|
use crate::CONFIG;
|
||||||
|
use crate::util::Cached;
|
||||||
|
|
||||||
pub fn routes() -> Vec<Route> {
|
pub fn routes() -> Vec<Route> {
|
||||||
routes![icon]
|
routes![icon]
|
||||||
@@ -25,16 +27,16 @@ const FALLBACK_ICON: &[u8; 344] = include_bytes!("../static/fallback-icon.png");
|
|||||||
|
|
||||||
const ALLOWED_CHARS: &str = "_-.";
|
const ALLOWED_CHARS: &str = "_-.";
|
||||||
|
|
||||||
lazy_static! {
|
static CLIENT: Lazy<Client> = Lazy::new(|| {
|
||||||
// Reuse the client between requests
|
// Reuse the client between requests
|
||||||
static ref CLIENT: Client = Client::builder()
|
Client::builder()
|
||||||
.use_sys_proxy()
|
.use_sys_proxy()
|
||||||
.gzip(true)
|
.gzip(true)
|
||||||
.timeout(Duration::from_secs(CONFIG.icon_download_timeout()))
|
.timeout(Duration::from_secs(CONFIG.icon_download_timeout()))
|
||||||
.default_headers(_header_map())
|
.default_headers(_header_map())
|
||||||
.build()
|
.build()
|
||||||
.unwrap();
|
.unwrap()
|
||||||
}
|
});
|
||||||
|
|
||||||
fn is_valid_domain(domain: &str) -> bool {
|
fn is_valid_domain(domain: &str) -> bool {
|
||||||
// Don't allow empty or too big domains or path traversal
|
// Don't allow empty or too big domains or path traversal
|
||||||
@@ -53,15 +55,15 @@ fn is_valid_domain(domain: &str) -> bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[get("/<domain>/icon.png")]
|
#[get("/<domain>/icon.png")]
|
||||||
fn icon(domain: String) -> Content<Vec<u8>> {
|
fn icon(domain: String) -> Cached<Content<Vec<u8>>> {
|
||||||
let icon_type = ContentType::new("image", "x-icon");
|
let icon_type = ContentType::new("image", "x-icon");
|
||||||
|
|
||||||
if !is_valid_domain(&domain) {
|
if !is_valid_domain(&domain) {
|
||||||
warn!("Invalid domain: {:#?}", domain);
|
warn!("Invalid domain: {:#?}", domain);
|
||||||
return Content(icon_type, FALLBACK_ICON.to_vec());
|
return Cached::long(Content(icon_type, FALLBACK_ICON.to_vec()));
|
||||||
}
|
}
|
||||||
|
|
||||||
Content(icon_type, get_icon(&domain))
|
Cached::long(Content(icon_type, get_icon(&domain)))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn check_icon_domain_is_blacklisted(domain: &str) -> bool {
|
fn check_icon_domain_is_blacklisted(domain: &str) -> bool {
|
||||||
|
@@ -37,7 +37,17 @@ fn app_id() -> Cached<Content<Json<Value>>> {
|
|||||||
{
|
{
|
||||||
"version": { "major": 1, "minor": 0 },
|
"version": { "major": 1, "minor": 0 },
|
||||||
"ids": [
|
"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",
|
"ios:bundle-id:com.8bit.bitwarden",
|
||||||
"android:apk-key-hash:dUGFzUzf3lmHSLBDBIv+WaFyZMI" ]
|
"android:apk-key-hash:dUGFzUzf3lmHSLBDBIv+WaFyZMI" ]
|
||||||
}]
|
}]
|
||||||
@@ -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"))),
|
"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"))),
|
"md5.js" => Ok(Content(ContentType::JavaScript, include_bytes!("../static/scripts/md5.js"))),
|
||||||
"identicon.js" => Ok(Content(ContentType::JavaScript, include_bytes!("../static/scripts/identicon.js"))),
|
"identicon.js" => Ok(Content(ContentType::JavaScript, include_bytes!("../static/scripts/identicon.js"))),
|
||||||
_ => err!("Image not found"),
|
_ => err!(format!("Static file not found: {}", filename)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
33
src/auth.rs
33
src/auth.rs
@@ -3,6 +3,7 @@
|
|||||||
//
|
//
|
||||||
use crate::util::read_file;
|
use crate::util::read_file;
|
||||||
use chrono::{Duration, Utc};
|
use chrono::{Duration, Utc};
|
||||||
|
use once_cell::sync::Lazy;
|
||||||
|
|
||||||
use jsonwebtoken::{self, Algorithm, Header};
|
use jsonwebtoken::{self, Algorithm, Header};
|
||||||
use serde::de::DeserializeOwned;
|
use serde::de::DeserializeOwned;
|
||||||
@@ -13,23 +14,21 @@ use crate::CONFIG;
|
|||||||
|
|
||||||
const JWT_ALGORITHM: Algorithm = Algorithm::RS256;
|
const JWT_ALGORITHM: Algorithm = Algorithm::RS256;
|
||||||
|
|
||||||
lazy_static! {
|
pub static DEFAULT_VALIDITY: Lazy<Duration> = Lazy::new(|| Duration::hours(2));
|
||||||
pub static ref DEFAULT_VALIDITY: Duration = Duration::hours(2);
|
static JWT_HEADER: Lazy<Header> = Lazy::new(|| Header::new(JWT_ALGORITHM));
|
||||||
static ref JWT_HEADER: Header = Header::new(JWT_ALGORITHM);
|
pub static JWT_LOGIN_ISSUER: Lazy<String> = Lazy::new(|| format!("{}|login", CONFIG.domain_origin()));
|
||||||
pub static ref JWT_LOGIN_ISSUER: String = format!("{}|login", CONFIG.domain());
|
static JWT_INVITE_ISSUER: Lazy<String> = Lazy::new(|| format!("{}|invite", CONFIG.domain_origin()));
|
||||||
pub static ref JWT_INVITE_ISSUER: String = format!("{}|invite", CONFIG.domain());
|
static JWT_DELETE_ISSUER: Lazy<String> = Lazy::new(|| format!("{}|delete", CONFIG.domain_origin()));
|
||||||
pub static ref JWT_DELETE_ISSUER: String = format!("{}|delete", CONFIG.domain());
|
static JWT_VERIFYEMAIL_ISSUER: Lazy<String> = Lazy::new(|| format!("{}|verifyemail", CONFIG.domain_origin()));
|
||||||
pub static ref JWT_VERIFYEMAIL_ISSUER: String = format!("{}|verifyemail", CONFIG.domain());
|
static JWT_ADMIN_ISSUER: Lazy<String> = Lazy::new(|| format!("{}|admin", CONFIG.domain_origin()));
|
||||||
pub static ref JWT_ADMIN_ISSUER: String = format!("{}|admin", CONFIG.domain());
|
static PRIVATE_RSA_KEY: Lazy<Vec<u8>> = Lazy::new(|| match read_file(&CONFIG.private_rsa_key()) {
|
||||||
static ref PRIVATE_RSA_KEY: Vec<u8> = match read_file(&CONFIG.private_rsa_key()) {
|
Ok(key) => key,
|
||||||
Ok(key) => key,
|
Err(e) => panic!("Error loading private RSA Key.\n Error: {}", e),
|
||||||
Err(e) => panic!("Error loading private RSA Key.\n Error: {}", e),
|
});
|
||||||
};
|
static PUBLIC_RSA_KEY: Lazy<Vec<u8>> = Lazy::new(|| match read_file(&CONFIG.public_rsa_key()) {
|
||||||
static ref PUBLIC_RSA_KEY: Vec<u8> = match read_file(&CONFIG.public_rsa_key()) {
|
Ok(key) => key,
|
||||||
Ok(key) => key,
|
Err(e) => panic!("Error loading public RSA Key.\n Error: {}", e),
|
||||||
Err(e) => panic!("Error loading public RSA Key.\n Error: {}", e),
|
});
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn encode_jwt<T: Serialize>(claims: &T) -> String {
|
pub fn encode_jwt<T: Serialize>(claims: &T) -> String {
|
||||||
match jsonwebtoken::encode(&JWT_HEADER, claims, &PRIVATE_RSA_KEY) {
|
match jsonwebtoken::encode(&JWT_HEADER, claims, &PRIVATE_RSA_KEY) {
|
||||||
|
182
src/config.rs
182
src/config.rs
@@ -1,19 +1,23 @@
|
|||||||
|
use once_cell::sync::Lazy;
|
||||||
use std::process::exit;
|
use std::process::exit;
|
||||||
use std::sync::RwLock;
|
use std::sync::RwLock;
|
||||||
|
|
||||||
use crate::error::Error;
|
use reqwest::Url;
|
||||||
use crate::util::get_env;
|
|
||||||
|
|
||||||
lazy_static! {
|
use crate::error::Error;
|
||||||
pub static ref CONFIG: Config = Config::load().unwrap_or_else(|e| {
|
use crate::util::{get_env, get_env_bool};
|
||||||
|
|
||||||
|
static CONFIG_FILE: Lazy<String> = Lazy::new(|| {
|
||||||
|
let data_folder = get_env("DATA_FOLDER").unwrap_or_else(|| String::from("data"));
|
||||||
|
get_env("CONFIG_FILE").unwrap_or_else(|| format!("{}/config.json", data_folder))
|
||||||
|
});
|
||||||
|
|
||||||
|
pub static CONFIG: Lazy<Config> = Lazy::new(|| {
|
||||||
|
Config::load().unwrap_or_else(|e| {
|
||||||
println!("Error loading config:\n\t{:?}\n", e);
|
println!("Error loading config:\n\t{:?}\n", e);
|
||||||
exit(12)
|
exit(12)
|
||||||
});
|
})
|
||||||
pub static ref CONFIG_FILE: String = {
|
});
|
||||||
let data_folder = get_env("DATA_FOLDER").unwrap_or_else(|| String::from("data"));
|
|
||||||
get_env("CONFIG_FILE").unwrap_or_else(|| format!("{}/config.json", data_folder))
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
pub type Pass = String;
|
pub type Pass = String;
|
||||||
|
|
||||||
@@ -23,13 +27,13 @@ macro_rules! make_config {
|
|||||||
$group:ident $(: $group_enabled:ident)? {
|
$group:ident $(: $group_enabled:ident)? {
|
||||||
$(
|
$(
|
||||||
$(#[doc = $doc:literal])+
|
$(#[doc = $doc:literal])+
|
||||||
$name:ident : $ty:ty, $editable:literal, $none_action:ident $(, $default:expr)?;
|
$name:ident : $ty:ident, $editable:literal, $none_action:ident $(, $default:expr)?;
|
||||||
)+},
|
)+},
|
||||||
)+) => {
|
)+) => {
|
||||||
pub struct Config { inner: RwLock<Inner> }
|
pub struct Config { inner: RwLock<Inner> }
|
||||||
|
|
||||||
struct Inner {
|
struct Inner {
|
||||||
templates: Handlebars,
|
templates: Handlebars<'static>,
|
||||||
config: ConfigItems,
|
config: ConfigItems,
|
||||||
|
|
||||||
_env: ConfigBuilder,
|
_env: ConfigBuilder,
|
||||||
@@ -50,7 +54,7 @@ macro_rules! make_config {
|
|||||||
|
|
||||||
let mut builder = ConfigBuilder::default();
|
let mut builder = ConfigBuilder::default();
|
||||||
$($(
|
$($(
|
||||||
builder.$name = get_env(&stringify!($name).to_uppercase());
|
builder.$name = make_config! { @getenv &stringify!($name).to_uppercase(), $ty };
|
||||||
)+)+
|
)+)+
|
||||||
|
|
||||||
builder
|
builder
|
||||||
@@ -189,6 +193,10 @@ macro_rules! make_config {
|
|||||||
let f: &dyn Fn(&ConfigItems) -> _ = &$default_fn;
|
let f: &dyn Fn(&ConfigItems) -> _ = &$default_fn;
|
||||||
f($config)
|
f($config)
|
||||||
}};
|
}};
|
||||||
|
|
||||||
|
( @getenv $name:expr, bool ) => { get_env_bool($name) };
|
||||||
|
( @getenv $name:expr, $ty:ident ) => { get_env($name) };
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//STRUCTURE:
|
//STRUCTURE:
|
||||||
@@ -236,12 +244,21 @@ make_config! {
|
|||||||
domain: String, true, def, "http://localhost".to_string();
|
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 |> Indicates if the domain is set by the admin. Otherwise the default will be used.
|
||||||
domain_set: bool, false, def, false;
|
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
|
/// Enable web vault
|
||||||
web_vault_enabled: bool, false, def, true;
|
web_vault_enabled: bool, false, def, true;
|
||||||
|
|
||||||
/// HIBP Api Key |> HaveIBeenPwned API Key, request it here: https://haveibeenpwned.com/API/Key
|
/// HIBP Api Key |> HaveIBeenPwned API Key, request it here: https://haveibeenpwned.com/API/Key
|
||||||
hibp_api_key: Pass, true, option;
|
hibp_api_key: Pass, true, option;
|
||||||
|
|
||||||
|
/// Per-user attachment limit (KB) |> Limit in kilobytes for a users attachments, once the limit is exceeded it won't be possible to upload more
|
||||||
|
user_attachment_limit: i64, true, option;
|
||||||
|
/// Per-organization attachment limit (KB) |> Limit in kilobytes for an organization attachments, once the limit is exceeded it won't be possible to upload more
|
||||||
|
org_attachment_limit: i64, true, option;
|
||||||
|
|
||||||
/// Disable icon downloads |> Set to true to disable icon downloading, this would still serve icons from
|
/// Disable icon downloads |> Set to true to disable icon downloading, this would still serve icons from
|
||||||
/// $ICON_CACHE_FOLDER, but it won't produce any external network request. Needs to set $ICON_CACHE_TTL to 0,
|
/// $ICON_CACHE_FOLDER, but it won't produce any external network request. Needs to set $ICON_CACHE_TTL to 0,
|
||||||
/// otherwise it will delete them and they won't be downloaded again.
|
/// otherwise it will delete them and they won't be downloaded again.
|
||||||
@@ -267,6 +284,9 @@ make_config! {
|
|||||||
|
|
||||||
/// Admin page token |> The token used to authenticate in this very same page. Changing it here won't deauthorize the current session
|
/// Admin page token |> The token used to authenticate in this very same page. Changing it here won't deauthorize the current session
|
||||||
admin_token: Pass, true, option;
|
admin_token: Pass, true, option;
|
||||||
|
|
||||||
|
/// Invitation organization name |> Name shown in the invitation emails that don't come from a specific organization
|
||||||
|
invitation_org_name: String, true, def, "Bitwarden_RS".to_string();
|
||||||
},
|
},
|
||||||
|
|
||||||
/// Advanced settings
|
/// Advanced settings
|
||||||
@@ -295,7 +315,7 @@ make_config! {
|
|||||||
|
|
||||||
/// Disable authenticator time drifted codes to be valid |> Enabling this only allows the current TOTP code to be valid
|
/// Disable authenticator time drifted codes to be valid |> Enabling this only allows the current TOTP code to be valid
|
||||||
/// TOTP codes of the previous and next 30 seconds will be invalid.
|
/// TOTP codes of the previous and next 30 seconds will be invalid.
|
||||||
authenticator_disable_time_drift: bool, true, def, false;
|
authenticator_disable_time_drift: bool, true, def, false;
|
||||||
|
|
||||||
/// Require new device emails |> When a user logs in an email is required to be sent.
|
/// Require new device emails |> When a user logs in an email is required to be sent.
|
||||||
/// If sending the email fails the login attempt will fail.
|
/// If sending the email fails the login attempt will fail.
|
||||||
@@ -319,6 +339,9 @@ make_config! {
|
|||||||
|
|
||||||
/// Bypass admin page security (Know the risks!) |> Disables the Admin Token for the admin page so you may use your own auth in-front
|
/// Bypass admin page security (Know the risks!) |> Disables the Admin Token for the admin page so you may use your own auth in-front
|
||||||
disable_admin_token: bool, true, def, false;
|
disable_admin_token: bool, true, def, false;
|
||||||
|
|
||||||
|
/// Allowed iframe ancestors (Know the risks!) |> Allows other domains to embed the web vault into an iframe, useful for embedding into secure intranets
|
||||||
|
allowed_iframe_ancestors: String, true, def, String::new();
|
||||||
},
|
},
|
||||||
|
|
||||||
/// Yubikey settings
|
/// Yubikey settings
|
||||||
@@ -400,8 +423,13 @@ fn validate_config(cfg: &ConfigItems) -> Result<(), Error> {
|
|||||||
err!("`DATABASE_URL` should start with postgresql: when using the PostgreSQL server")
|
err!("`DATABASE_URL` should start with postgresql: when using the PostgreSQL server")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let dom = cfg.domain.to_lowercase();
|
||||||
|
if !dom.starts_with("http://") && !dom.starts_with("https://") {
|
||||||
|
err!("DOMAIN variable needs to contain the protocol (http, https). Use 'http[s]://bw.example.com' instead of 'bw.example.com'");
|
||||||
|
}
|
||||||
|
|
||||||
if let Some(ref token) = cfg.admin_token {
|
if let Some(ref token) = cfg.admin_token {
|
||||||
if token.trim().is_empty() {
|
if token.trim().is_empty() && !cfg.disable_admin_token {
|
||||||
err!("`ADMIN_TOKEN` is enabled but has an empty value. To enable the admin page without token, use `DISABLE_ADMIN_TOKEN`")
|
err!("`ADMIN_TOKEN` is enabled but has an empty value. To enable the admin page without token, use `DISABLE_ADMIN_TOKEN`")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -442,6 +470,29 @@ fn validate_config(cfg: &ConfigItems) -> Result<(), Error> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Extracts an RFC 6454 web origin from a URL.
|
||||||
|
fn extract_url_origin(url: &str) -> String {
|
||||||
|
match Url::parse(url) {
|
||||||
|
Ok(u) => u.origin().ascii_serialization(),
|
||||||
|
Err(e) => {
|
||||||
|
println!("Error validating domain: {}", e);
|
||||||
|
String::new()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 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 {
|
||||||
|
match Url::parse(url) {
|
||||||
|
Ok(u) => u.path().trim_end_matches('/').to_string(),
|
||||||
|
Err(_) => {
|
||||||
|
// We already print it in the method above, no need to do it again
|
||||||
|
String::new()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Config {
|
impl Config {
|
||||||
pub fn load() -> Result<Self, Error> {
|
pub fn load() -> Result<Self, Error> {
|
||||||
// Loading from env and file
|
// Loading from env and file
|
||||||
@@ -506,7 +557,12 @@ impl Config {
|
|||||||
warn!("Failed to parse email address '{}'", email);
|
warn!("Failed to parse email address '{}'", email);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
self.signups_domains_whitelist().split(',').any(|d| d == e[0])
|
|
||||||
|
// Allow signups if the whitelist is empty/not configured
|
||||||
|
// (it doesn't contain any domains), or if it matches at least
|
||||||
|
// one domain.
|
||||||
|
let whitelist_str = self.signups_domains_whitelist();
|
||||||
|
( whitelist_str.is_empty() && CONFIG.signups_allowed() )|| whitelist_str.split(',').filter(|s| !s.is_empty()).any(|d| d == e[0])
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn delete_user_config(&self) -> Result<(), Error> {
|
pub fn delete_user_config(&self) -> Result<(), Error> {
|
||||||
@@ -568,7 +624,7 @@ impl Config {
|
|||||||
) -> Result<String, crate::error::Error> {
|
) -> Result<String, crate::error::Error> {
|
||||||
if CONFIG.reload_templates() {
|
if CONFIG.reload_templates() {
|
||||||
warn!("RELOADING TEMPLATES");
|
warn!("RELOADING TEMPLATES");
|
||||||
let hb = load_templates(CONFIG.templates_folder().as_ref());
|
let hb = load_templates(CONFIG.templates_folder());
|
||||||
hb.render(name, data).map_err(Into::into)
|
hb.render(name, data).map_err(Into::into)
|
||||||
} else {
|
} else {
|
||||||
let hb = &CONFIG.inner.read().unwrap().templates;
|
let hb = &CONFIG.inner.read().unwrap().templates;
|
||||||
@@ -577,17 +633,18 @@ impl Config {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
use handlebars::{
|
use handlebars::{Context, Handlebars, Helper, HelperResult, Output, RenderContext, RenderError, Renderable};
|
||||||
Context, Handlebars, Helper, HelperDef, HelperResult, Output, RenderContext, RenderError, Renderable,
|
|
||||||
};
|
|
||||||
|
|
||||||
fn load_templates(path: &str) -> Handlebars {
|
fn load_templates<P>(path: P) -> Handlebars<'static>
|
||||||
|
where
|
||||||
|
P: AsRef<std::path::Path>,
|
||||||
|
{
|
||||||
let mut hb = Handlebars::new();
|
let mut hb = Handlebars::new();
|
||||||
// Error on missing params
|
// Error on missing params
|
||||||
hb.set_strict_mode(true);
|
hb.set_strict_mode(true);
|
||||||
// Register helpers
|
// Register helpers
|
||||||
hb.register_helper("case", Box::new(CaseHelper));
|
hb.register_helper("case", Box::new(case_helper));
|
||||||
hb.register_helper("jsesc", Box::new(JsEscapeHelper));
|
hb.register_helper("jsesc", Box::new(js_escape_helper));
|
||||||
|
|
||||||
macro_rules! reg {
|
macro_rules! reg {
|
||||||
($name:expr) => {{
|
($name:expr) => {{
|
||||||
@@ -613,6 +670,7 @@ fn load_templates(path: &str) -> Handlebars {
|
|||||||
reg!("email/verify_email", ".html");
|
reg!("email/verify_email", ".html");
|
||||||
reg!("email/welcome", ".html");
|
reg!("email/welcome", ".html");
|
||||||
reg!("email/welcome_must_verify", ".html");
|
reg!("email/welcome_must_verify", ".html");
|
||||||
|
reg!("email/smtp_test", ".html");
|
||||||
|
|
||||||
reg!("admin/base");
|
reg!("admin/base");
|
||||||
reg!("admin/login");
|
reg!("admin/login");
|
||||||
@@ -626,48 +684,44 @@ fn load_templates(path: &str) -> Handlebars {
|
|||||||
hb
|
hb
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct CaseHelper;
|
fn case_helper<'reg, 'rc>(
|
||||||
|
h: &Helper<'reg, 'rc>,
|
||||||
|
r: &'reg Handlebars,
|
||||||
|
ctx: &'rc Context,
|
||||||
|
rc: &mut RenderContext<'reg, 'rc>,
|
||||||
|
out: &mut dyn Output,
|
||||||
|
) -> HelperResult {
|
||||||
|
let param = h
|
||||||
|
.param(0)
|
||||||
|
.ok_or_else(|| RenderError::new("Param not found for helper \"case\""))?;
|
||||||
|
let value = param.value().clone();
|
||||||
|
|
||||||
impl HelperDef for CaseHelper {
|
if h.params().iter().skip(1).any(|x| x.value() == &value) {
|
||||||
fn call<'reg: 'rc, 'rc>(
|
h.template().map(|t| t.render(r, ctx, rc, out)).unwrap_or(Ok(()))
|
||||||
&self,
|
} else {
|
||||||
h: &Helper<'reg, 'rc>,
|
|
||||||
r: &'reg Handlebars,
|
|
||||||
ctx: &Context,
|
|
||||||
rc: &mut RenderContext<'reg>,
|
|
||||||
out: &mut dyn Output,
|
|
||||||
) -> HelperResult {
|
|
||||||
let param = h.param(0).ok_or_else(|| RenderError::new("Param not found for helper \"case\""))?;
|
|
||||||
let value = param.value().clone();
|
|
||||||
|
|
||||||
if h.params().iter().skip(1).any(|x| x.value() == &value) {
|
|
||||||
h.template().map(|t| t.render(r, ctx, rc, out)).unwrap_or(Ok(()))
|
|
||||||
} else {
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct JsEscapeHelper;
|
|
||||||
|
|
||||||
impl HelperDef for JsEscapeHelper {
|
|
||||||
fn call<'reg: 'rc, 'rc>(
|
|
||||||
&self,
|
|
||||||
h: &Helper<'reg, 'rc>,
|
|
||||||
_: &'reg Handlebars,
|
|
||||||
_: &Context,
|
|
||||||
_: &mut RenderContext<'reg>,
|
|
||||||
out: &mut dyn Output,
|
|
||||||
) -> HelperResult {
|
|
||||||
let param = h.param(0).ok_or_else(|| RenderError::new("Param not found for helper \"js_escape\""))?;
|
|
||||||
|
|
||||||
let value =
|
|
||||||
param.value().as_str().ok_or_else(|| RenderError::new("Param for helper \"js_escape\" is not a String"))?;
|
|
||||||
|
|
||||||
let escaped_value = value.replace('\\', "").replace('\'', "\\x22").replace('\"', "\\x27");
|
|
||||||
let quoted_value = format!(""{}"", escaped_value);
|
|
||||||
|
|
||||||
out.write("ed_value)?;
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn js_escape_helper<'reg, 'rc>(
|
||||||
|
h: &Helper<'reg, 'rc>,
|
||||||
|
_r: &'reg Handlebars,
|
||||||
|
_ctx: &'rc Context,
|
||||||
|
_rc: &mut RenderContext<'reg, 'rc>,
|
||||||
|
out: &mut dyn Output,
|
||||||
|
) -> HelperResult {
|
||||||
|
let param = h
|
||||||
|
.param(0)
|
||||||
|
.ok_or_else(|| RenderError::new("Param not found for helper \"js_escape\""))?;
|
||||||
|
|
||||||
|
let value = param
|
||||||
|
.value()
|
||||||
|
.as_str()
|
||||||
|
.ok_or_else(|| RenderError::new("Param for helper \"js_escape\" is not a String"))?;
|
||||||
|
|
||||||
|
let escaped_value = value.replace('\\', "").replace('\'', "\\x22").replace('\"', "\\x27");
|
||||||
|
let quoted_value = format!(""{}"", escaped_value);
|
||||||
|
|
||||||
|
out.write("ed_value)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
@@ -49,7 +49,7 @@ impl Attachment {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
use crate::db::schema::attachments;
|
use crate::db::schema::{attachments, ciphers};
|
||||||
use crate::db::DbConn;
|
use crate::db::DbConn;
|
||||||
use diesel;
|
use diesel;
|
||||||
use diesel::prelude::*;
|
use diesel::prelude::*;
|
||||||
@@ -118,4 +118,26 @@ impl Attachment {
|
|||||||
.load::<Self>(&**conn)
|
.load::<Self>(&**conn)
|
||||||
.expect("Error loading attachments")
|
.expect("Error loading attachments")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn size_by_user(user_uuid: &str, conn: &DbConn) -> i64 {
|
||||||
|
let result: Option<i64> = attachments::table
|
||||||
|
.left_join(ciphers::table.on(ciphers::uuid.eq(attachments::cipher_uuid)))
|
||||||
|
.filter(ciphers::user_uuid.eq(user_uuid))
|
||||||
|
.select(diesel::dsl::sum(attachments::file_size))
|
||||||
|
.first(&**conn)
|
||||||
|
.expect("Error loading user attachment total size");
|
||||||
|
|
||||||
|
result.unwrap_or(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn size_by_org(org_uuid: &str, conn: &DbConn) -> i64 {
|
||||||
|
let result: Option<i64> = attachments::table
|
||||||
|
.left_join(ciphers::table.on(ciphers::uuid.eq(attachments::cipher_uuid)))
|
||||||
|
.filter(ciphers::organization_uuid.eq(org_uuid))
|
||||||
|
.select(diesel::dsl::sum(attachments::file_size))
|
||||||
|
.first(&**conn)
|
||||||
|
.expect("Error loading user attachment total size");
|
||||||
|
|
||||||
|
result.unwrap_or(0)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -73,6 +73,13 @@ impl TwoFactor {
|
|||||||
impl TwoFactor {
|
impl TwoFactor {
|
||||||
#[cfg(feature = "postgresql")]
|
#[cfg(feature = "postgresql")]
|
||||||
pub fn save(&self, conn: &DbConn) -> EmptyResult {
|
pub fn save(&self, conn: &DbConn) -> EmptyResult {
|
||||||
|
// We need to make sure we're not going to violate the unique constraint on user_uuid and atype.
|
||||||
|
// This happens automatically on other DBMS backends due to replace_into(). PostgreSQL does
|
||||||
|
// not support multiple constraints on ON CONFLICT clauses.
|
||||||
|
diesel::delete(twofactor::table.filter(twofactor::user_uuid.eq(&self.user_uuid)).filter(twofactor::atype.eq(&self.atype)))
|
||||||
|
.execute(&**conn)
|
||||||
|
.map_res("Error deleting twofactor for insert")?;
|
||||||
|
|
||||||
diesel::insert_into(twofactor::table)
|
diesel::insert_into(twofactor::table)
|
||||||
.values(self)
|
.values(self)
|
||||||
.on_conflict(twofactor::uuid)
|
.on_conflict(twofactor::uuid)
|
||||||
|
@@ -319,10 +319,9 @@ impl Invitation {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn take(mail: &str, conn: &DbConn) -> bool {
|
pub fn take(mail: &str, conn: &DbConn) -> bool {
|
||||||
CONFIG.invitations_allowed()
|
match Self::find_by_mail(mail, &conn) {
|
||||||
&& match Self::find_by_mail(mail, &conn) {
|
Some(invitation) => invitation.delete(&conn).is_ok(),
|
||||||
Some(invitation) => invitation.delete(&conn).is_ok(),
|
None => false,
|
||||||
None => false,
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
14
src/error.rs
14
src/error.rs
@@ -46,6 +46,7 @@ use std::option::NoneError as NoneErr;
|
|||||||
use std::time::SystemTimeError as TimeErr;
|
use std::time::SystemTimeError as TimeErr;
|
||||||
use u2f::u2ferror::U2fError as U2fErr;
|
use u2f::u2ferror::U2fError as U2fErr;
|
||||||
use yubico::yubicoerror::YubicoError as YubiErr;
|
use yubico::yubicoerror::YubicoError as YubiErr;
|
||||||
|
use lettre::smtp::error::Error as LettreErr;
|
||||||
|
|
||||||
#[derive(Display, Serialize)]
|
#[derive(Display, Serialize)]
|
||||||
pub struct Empty {}
|
pub struct Empty {}
|
||||||
@@ -73,6 +74,7 @@ make_error! {
|
|||||||
ReqError(ReqErr): _has_source, _api_error,
|
ReqError(ReqErr): _has_source, _api_error,
|
||||||
RegexError(RegexErr): _has_source, _api_error,
|
RegexError(RegexErr): _has_source, _api_error,
|
||||||
YubiError(YubiErr): _has_source, _api_error,
|
YubiError(YubiErr): _has_source, _api_error,
|
||||||
|
LetreErr(LettreErr): _has_source, _api_error,
|
||||||
}
|
}
|
||||||
|
|
||||||
// This is implemented by hand because NoneError doesn't implement neither Display nor Error
|
// This is implemented by hand because NoneError doesn't implement neither Display nor Error
|
||||||
@@ -209,6 +211,18 @@ macro_rules! err {
|
|||||||
}};
|
}};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! err_discard {
|
||||||
|
($msg:expr, $data:expr) => {{
|
||||||
|
std::io::copy(&mut $data.open(), &mut std::io::sink()).ok();
|
||||||
|
return Err(crate::error::Error::new($msg, $msg));
|
||||||
|
}};
|
||||||
|
($usr_msg:expr, $log_value:expr, $data:expr) => {{
|
||||||
|
std::io::copy(&mut $data.open(), &mut std::io::sink()).ok();
|
||||||
|
return Err(crate::error::Error::new($usr_msg, $log_value));
|
||||||
|
}};
|
||||||
|
}
|
||||||
|
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! err_json {
|
macro_rules! err_json {
|
||||||
($expr:expr, $log_value:expr) => {{
|
($expr:expr, $log_value:expr) => {{
|
||||||
|
38
src/mail.rs
38
src/mail.rs
@@ -1,8 +1,10 @@
|
|||||||
use lettre::smtp::authentication::Credentials;
|
use lettre::smtp::authentication::Credentials;
|
||||||
use lettre::smtp::authentication::Mechanism as SmtpAuthMechanism;
|
use lettre::smtp::authentication::Mechanism as SmtpAuthMechanism;
|
||||||
use lettre::smtp::ConnectionReuseParameters;
|
use lettre::smtp::ConnectionReuseParameters;
|
||||||
use lettre::{ClientSecurity, ClientTlsParameters, SmtpClient, SmtpTransport, Transport};
|
use lettre::{
|
||||||
use lettre_email::{EmailBuilder, MimeMultipartType, PartBuilder};
|
builder::{EmailBuilder, MimeMultipartType, PartBuilder},
|
||||||
|
ClientSecurity, ClientTlsParameters, SmtpClient, SmtpTransport, Transport,
|
||||||
|
};
|
||||||
use native_tls::{Protocol, TlsConnector};
|
use native_tls::{Protocol, TlsConnector};
|
||||||
use percent_encoding::{percent_encode, NON_ALPHANUMERIC};
|
use percent_encoding::{percent_encode, NON_ALPHANUMERIC};
|
||||||
use quoted_printable::encode_to_str;
|
use quoted_printable::encode_to_str;
|
||||||
@@ -256,7 +258,30 @@ pub fn send_change_email(address: &str, token: &str) -> EmptyResult {
|
|||||||
send_email(&address, &subject, &body_html, &body_text)
|
send_email(&address, &subject, &body_html, &body_text)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn send_test(address: &str) -> EmptyResult {
|
||||||
|
let (subject, body_html, body_text) = get_text(
|
||||||
|
"email/smtp_test",
|
||||||
|
json!({
|
||||||
|
"url": CONFIG.domain(),
|
||||||
|
}),
|
||||||
|
)?;
|
||||||
|
|
||||||
|
send_email(&address, &subject, &body_html, &body_text)
|
||||||
|
}
|
||||||
|
|
||||||
fn send_email(address: &str, subject: &str, body_html: &str, body_text: &str) -> EmptyResult {
|
fn send_email(address: &str, subject: &str, body_html: &str, body_text: &str) -> EmptyResult {
|
||||||
|
let address_split: Vec<&str> = address.rsplitn(2, '@').collect();
|
||||||
|
if address_split.len() != 2 {
|
||||||
|
err!("Invalid email address (no @)");
|
||||||
|
}
|
||||||
|
|
||||||
|
let domain_puny = match idna::domain_to_ascii_strict(address_split[0]) {
|
||||||
|
Ok(d) => d,
|
||||||
|
Err(_) => err!("Can't convert email domain to ASCII representation"),
|
||||||
|
};
|
||||||
|
|
||||||
|
let address = format!("{}@{}", address_split[1], domain_puny);
|
||||||
|
|
||||||
let html = PartBuilder::new()
|
let html = PartBuilder::new()
|
||||||
.body(encode_to_str(body_html))
|
.body(encode_to_str(body_html))
|
||||||
.header(("Content-Type", "text/html; charset=utf-8"))
|
.header(("Content-Type", "text/html; charset=utf-8"))
|
||||||
@@ -284,12 +309,11 @@ fn send_email(address: &str, subject: &str, body_html: &str, body_text: &str) ->
|
|||||||
|
|
||||||
let mut transport = mailer();
|
let mut transport = mailer();
|
||||||
|
|
||||||
let result = transport
|
let result = transport.send(email);
|
||||||
.send(email.into())
|
|
||||||
.map_err(|e| Error::new("Error sending email", e.to_string()))
|
|
||||||
.and(Ok(()));
|
|
||||||
|
|
||||||
// Explicitly close the connection, in case of error
|
// Explicitly close the connection, in case of error
|
||||||
transport.close();
|
transport.close();
|
||||||
result
|
|
||||||
|
result?;
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
47
src/main.rs
47
src/main.rs
@@ -16,8 +16,6 @@ extern crate diesel;
|
|||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate diesel_migrations;
|
extern crate diesel_migrations;
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate lazy_static;
|
|
||||||
#[macro_use]
|
|
||||||
extern crate derive_more;
|
extern crate derive_more;
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate num_derive;
|
extern crate num_derive;
|
||||||
@@ -42,7 +40,18 @@ mod util;
|
|||||||
pub use config::CONFIG;
|
pub use config::CONFIG;
|
||||||
pub use error::{Error, MapResult};
|
pub use error::{Error, MapResult};
|
||||||
|
|
||||||
|
use structopt::StructOpt;
|
||||||
|
|
||||||
|
#[derive(Debug, StructOpt)]
|
||||||
|
#[structopt(name = "bitwarden_rs", about = "A Bitwarden API server written in Rust")]
|
||||||
|
struct Opt {
|
||||||
|
/// Prints the app version
|
||||||
|
#[structopt(short, long)]
|
||||||
|
version: bool,
|
||||||
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
|
parse_args();
|
||||||
launch_info();
|
launch_info();
|
||||||
|
|
||||||
use log::LevelFilter as LF;
|
use log::LevelFilter as LF;
|
||||||
@@ -64,6 +73,18 @@ fn main() {
|
|||||||
launch_rocket(extra_debug);
|
launch_rocket(extra_debug);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn parse_args() {
|
||||||
|
let opt = Opt::from_args();
|
||||||
|
if opt.version {
|
||||||
|
if let Some(version) = option_env!("GIT_VERSION") {
|
||||||
|
println!("bitwarden_rs {}", version);
|
||||||
|
} else {
|
||||||
|
println!("bitwarden_rs (Version info from Git not present)");
|
||||||
|
}
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn launch_info() {
|
fn launch_info() {
|
||||||
println!("/--------------------------------------------------------------------\\");
|
println!("/--------------------------------------------------------------------\\");
|
||||||
println!("| Starting Bitwarden_RS |");
|
println!("| Starting Bitwarden_RS |");
|
||||||
@@ -177,7 +198,9 @@ fn check_rsa_keys() {
|
|||||||
info!("JWT keys don't exist, checking if OpenSSL is available...");
|
info!("JWT keys don't exist, checking if OpenSSL is available...");
|
||||||
|
|
||||||
Command::new("openssl").arg("version").status().unwrap_or_else(|_| {
|
Command::new("openssl").arg("version").status().unwrap_or_else(|_| {
|
||||||
info!("Can't create keys because OpenSSL is not available, make sure it's installed and available on the PATH");
|
info!(
|
||||||
|
"Can't create keys because OpenSSL is not available, make sure it's installed and available on the PATH"
|
||||||
|
);
|
||||||
exit(1);
|
exit(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -255,18 +278,20 @@ mod migrations {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn launch_rocket(extra_debug: bool) {
|
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();
|
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
|
// crate::utils::LOGGED_ROUTES to make sure they appear in the log
|
||||||
let rocket = rocket
|
let rocket = rocket
|
||||||
.mount("/", api::web_routes())
|
.mount(&[basepath, "/"].concat(), api::web_routes())
|
||||||
.mount("/api", api::core_routes())
|
.mount(&[basepath, "/api"].concat(), api::core_routes())
|
||||||
.mount("/admin", api::admin_routes())
|
.mount(&[basepath, "/admin"].concat(), api::admin_routes())
|
||||||
.mount("/identity", api::identity_routes())
|
.mount(&[basepath, "/identity"].concat(), api::identity_routes())
|
||||||
.mount("/icons", api::icons_routes())
|
.mount(&[basepath, "/icons"].concat(), api::icons_routes())
|
||||||
.mount("/notifications", api::notifications_routes())
|
.mount(&[basepath, "/notifications"].concat(), api::notifications_routes())
|
||||||
.manage(db::init_pool())
|
.manage(db::init_pool())
|
||||||
.manage(api::start_notification_server())
|
.manage(api::start_notification_server())
|
||||||
.attach(util::AppHeaders())
|
.attach(util::AppHeaders())
|
||||||
|
@@ -6,10 +6,10 @@
|
|||||||
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
||||||
<title>Bitwarden_rs Admin Panel</title>
|
<title>Bitwarden_rs Admin Panel</title>
|
||||||
|
|
||||||
<link rel="stylesheet" href="/bwrs_static/bootstrap.css" />
|
<link rel="stylesheet" href="{{urlpath}}/bwrs_static/bootstrap.css" />
|
||||||
<script src="/bwrs_static/bootstrap-native-v4.js"></script>
|
<script src="{{urlpath}}/bwrs_static/bootstrap-native-v4.js"></script>
|
||||||
<script src="/bwrs_static/md5.js"></script>
|
<script src="{{urlpath}}/bwrs_static/md5.js"></script>
|
||||||
<script src="/bwrs_static/identicon.js"></script>
|
<script src="{{urlpath}}/bwrs_static/identicon.js"></script>
|
||||||
<style>
|
<style>
|
||||||
body {
|
body {
|
||||||
padding-top: 70px;
|
padding-top: 70px;
|
||||||
@@ -38,10 +38,10 @@
|
|||||||
<div class="navbar-collapse">
|
<div class="navbar-collapse">
|
||||||
<ul class="navbar-nav">
|
<ul class="navbar-nav">
|
||||||
<li class="nav-item active">
|
<li class="nav-item active">
|
||||||
<a class="nav-link" href="/admin">Admin Panel</a>
|
<a class="nav-link" href="{{urlpath}}/admin">Admin Panel</a>
|
||||||
</li>
|
</li>
|
||||||
<li class="nav-item">
|
<li class="nav-item">
|
||||||
<a class="nav-link" href="/">Vault</a>
|
<a class="nav-link" href="{{urlpath}}/">Vault</a>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
@@ -55,7 +55,7 @@
|
|||||||
|
|
||||||
{{#if logged_in}}
|
{{#if logged_in}}
|
||||||
<li class="nav-item">
|
<li class="nav-item">
|
||||||
<a class="nav-link" href="/admin/logout">Log Out</a>
|
<a class="nav-link" href="{{urlpath}}/admin/logout">Log Out</a>
|
||||||
</li>
|
</li>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
</ul>
|
</ul>
|
||||||
|
@@ -19,7 +19,7 @@
|
|||||||
<span class="d-block">{{Email}}</span>
|
<span class="d-block">{{Email}}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="col">
|
<div class="col">
|
||||||
<strong> Organizations:</strong>
|
<strong> Organizations: </strong>
|
||||||
<span class="d-block">
|
<span class="d-block">
|
||||||
{{#each Organizations}}
|
{{#each Organizations}}
|
||||||
<span class="badge badge-primary" data-orgtype="{{Type}}">{{Name}}</span>
|
<span class="badge badge-primary" data-orgtype="{{Type}}">{{Name}}</span>
|
||||||
@@ -71,6 +71,7 @@
|
|||||||
them to avoid confusion. This does not apply to the read-only section, which can only be set through the
|
them to avoid confusion. This does not apply to the read-only section, which can only be set through the
|
||||||
environment.
|
environment.
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<form class="form accordion" id="config-form" onsubmit="saveConfig(); return false;">
|
<form class="form accordion" id="config-form" onsubmit="saveConfig(); return false;">
|
||||||
{{#each config}}
|
{{#each config}}
|
||||||
{{#if groupdoc}}
|
{{#if groupdoc}}
|
||||||
@@ -110,6 +111,17 @@
|
|||||||
</div>
|
</div>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
{{/each}}
|
{{/each}}
|
||||||
|
{{#case group "smtp"}}
|
||||||
|
<div class="form-group row pt-3 border-top" title="Send a test email to given email address">
|
||||||
|
<label for="smtp-test-email" class="col-sm-3 col-form-label">Test SMTP</label>
|
||||||
|
<div class="col-sm-8 input-group">
|
||||||
|
<input class="form-control" id="smtp-test-email" type="email" placeholder="Enter test email">
|
||||||
|
<div class="input-group-append">
|
||||||
|
<button type="button" class="btn btn-outline-primary" onclick="smtpTest(); return false;">Send test email</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{{/case}}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
@@ -191,7 +203,10 @@
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
function reload() { window.location.reload(); }
|
function reload() { window.location.reload(); }
|
||||||
function msg(text) { text && alert(text); reload(); }
|
function msg(text, reload_page = true) {
|
||||||
|
text && alert(text);
|
||||||
|
reload_page && reload();
|
||||||
|
}
|
||||||
function identicon(email) {
|
function identicon(email) {
|
||||||
const data = new Identicon(md5(email), { size: 48, format: 'svg' });
|
const data = new Identicon(md5(email), { size: 48, format: 'svg' });
|
||||||
return "data:image/svg+xml;base64," + data.toString();
|
return "data:image/svg+xml;base64," + data.toString();
|
||||||
@@ -206,26 +221,37 @@
|
|||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
function _post(url, successMsg, errMsg, body) {
|
function _post(url, successMsg, errMsg, body, reload_page = true) {
|
||||||
fetch(url, {
|
fetch(url, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
body: body,
|
body: body,
|
||||||
mode: "same-origin",
|
mode: "same-origin",
|
||||||
credentials: "same-origin",
|
credentials: "same-origin",
|
||||||
headers: { "Content-Type": "application/json" }
|
headers: { "Content-Type": "application/json" }
|
||||||
}).then(e => {
|
}).then( resp => {
|
||||||
if (e.ok) { return msg(successMsg); }
|
if (resp.ok) { msg(successMsg, reload_page); return Promise.reject({error: false}); }
|
||||||
e.json().then(json => {
|
respStatus = resp.status;
|
||||||
const msg = json ? json.ErrorModel.Message : "Unknown error";
|
respStatusText = resp.statusText;
|
||||||
msg(errMsg + ": " + msg);
|
return resp.text();
|
||||||
});
|
}).then( respText => {
|
||||||
}).catch(e => { msg(errMsg + ": Unknown error") });
|
try {
|
||||||
|
const respJson = JSON.parse(respText);
|
||||||
|
return respJson ? respJson.ErrorModel.Message : "Unknown error";
|
||||||
|
} catch (e) {
|
||||||
|
return Promise.reject({body:respStatus + ' - ' + respStatusText, error: true});
|
||||||
|
}
|
||||||
|
}).then( apiMsg => {
|
||||||
|
msg(errMsg + "\n" + apiMsg, reload_page);
|
||||||
|
}).catch( e => {
|
||||||
|
if (e.error === false) { return true; }
|
||||||
|
else { msg(errMsg + "\n" + e.body, reload_page); }
|
||||||
|
});
|
||||||
}
|
}
|
||||||
function deleteUser(id, mail) {
|
function deleteUser(id, mail) {
|
||||||
var input_mail = prompt("To delete user '" + mail + "', please type the email below")
|
var input_mail = prompt("To delete user '" + mail + "', please type the email below")
|
||||||
if (input_mail != null) {
|
if (input_mail != null) {
|
||||||
if (input_mail == mail) {
|
if (input_mail == mail) {
|
||||||
_post("/admin/users/" + id + "/delete",
|
_post("{{urlpath}}/admin/users/" + id + "/delete",
|
||||||
"User deleted correctly",
|
"User deleted correctly",
|
||||||
"Error deleting user");
|
"Error deleting user");
|
||||||
} else {
|
} else {
|
||||||
@@ -235,19 +261,19 @@
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
function remove2fa(id) {
|
function remove2fa(id) {
|
||||||
_post("/admin/users/" + id + "/remove-2fa",
|
_post("{{urlpath}}/admin/users/" + id + "/remove-2fa",
|
||||||
"2FA removed correctly",
|
"2FA removed correctly",
|
||||||
"Error removing 2FA");
|
"Error removing 2FA");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
function deauthUser(id) {
|
function deauthUser(id) {
|
||||||
_post("/admin/users/" + id + "/deauth",
|
_post("{{urlpath}}/admin/users/" + id + "/deauth",
|
||||||
"Sessions deauthorized correctly",
|
"Sessions deauthorized correctly",
|
||||||
"Error deauthorizing sessions");
|
"Error deauthorizing sessions");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
function updateRevisions() {
|
function updateRevisions() {
|
||||||
_post("/admin/users/update_revision",
|
_post("{{urlpath}}/admin/users/update_revision",
|
||||||
"Success, clients will sync next time they connect",
|
"Success, clients will sync next time they connect",
|
||||||
"Error forcing clients to sync");
|
"Error forcing clients to sync");
|
||||||
return false;
|
return false;
|
||||||
@@ -256,10 +282,18 @@
|
|||||||
inv = document.getElementById("email-invite");
|
inv = document.getElementById("email-invite");
|
||||||
data = JSON.stringify({ "email": inv.value });
|
data = JSON.stringify({ "email": inv.value });
|
||||||
inv.value = "";
|
inv.value = "";
|
||||||
_post("/admin/invite/", "User invited correctly",
|
_post("{{urlpath}}/admin/invite/", "User invited correctly",
|
||||||
"Error inviting user", data);
|
"Error inviting user", data);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
function smtpTest() {
|
||||||
|
test_email = document.getElementById("smtp-test-email");
|
||||||
|
data = JSON.stringify({ "email": test_email.value });
|
||||||
|
_post("{{urlpath}}/admin/test/smtp/",
|
||||||
|
"SMTP Test email sent correctly",
|
||||||
|
"Error sending SMTP test email", data, false);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
function getFormData() {
|
function getFormData() {
|
||||||
let data = {};
|
let data = {};
|
||||||
|
|
||||||
@@ -278,7 +312,7 @@
|
|||||||
}
|
}
|
||||||
function saveConfig() {
|
function saveConfig() {
|
||||||
data = JSON.stringify(getFormData());
|
data = JSON.stringify(getFormData());
|
||||||
_post("/admin/config/", "Config saved correctly",
|
_post("{{urlpath}}/admin/config/", "Config saved correctly",
|
||||||
"Error saving config", data);
|
"Error saving config", data);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -286,7 +320,7 @@
|
|||||||
var input = prompt("This will remove all user configurations, and restore the defaults and the " +
|
var input = prompt("This will remove all user configurations, and restore the defaults and the " +
|
||||||
"values set by the environment. This operation could be dangerous. Type 'DELETE' to proceed:");
|
"values set by the environment. This operation could be dangerous. Type 'DELETE' to proceed:");
|
||||||
if (input === "DELETE") {
|
if (input === "DELETE") {
|
||||||
_post("/admin/config/delete",
|
_post("{{urlpath}}/admin/config/delete",
|
||||||
"Config deleted correctly",
|
"Config deleted correctly",
|
||||||
"Error deleting config");
|
"Error deleting config");
|
||||||
} else {
|
} else {
|
||||||
@@ -296,9 +330,9 @@
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
function backupDatabase() {
|
function backupDatabase() {
|
||||||
_post("/admin/config/backup_db",
|
_post("{{urlpath}}/admin/config/backup_db",
|
||||||
"Backup created successfully",
|
"Backup created successfully",
|
||||||
"Error creating backup");
|
"Error creating backup", null, false);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
function masterCheck(check_id, inputs_query) {
|
function masterCheck(check_id, inputs_query) {
|
||||||
|
@@ -3,6 +3,6 @@ Invitation accepted
|
|||||||
<html>
|
<html>
|
||||||
<p>
|
<p>
|
||||||
Your invitation for <b>{{email}}</b> to join <b>{{org_name}}</b> was accepted.
|
Your invitation for <b>{{email}}</b> to join <b>{{org_name}}</b> was accepted.
|
||||||
Please <a href="{{url}}">log in</a> to the bitwarden_rs server and confirm them from the organization management page.
|
Please <a href="{{url}}/">log in</a> to the bitwarden_rs server and confirm them from the organization management page.
|
||||||
</p>
|
</p>
|
||||||
</html>
|
</html>
|
||||||
|
@@ -101,7 +101,7 @@ Invitation accepted
|
|||||||
</tr>
|
</tr>
|
||||||
<tr style="margin: 0; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 16px; color: #333; line-height: 25px; -webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none;">
|
<tr style="margin: 0; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 16px; color: #333; line-height: 25px; -webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none;">
|
||||||
<td class="content-block" style="font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 16px; color: #333; line-height: 25px; margin: 0; -webkit-font-smoothing: antialiased; padding: 0 0 10px; -webkit-text-size-adjust: none;" valign="top">
|
<td class="content-block" style="font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 16px; color: #333; line-height: 25px; margin: 0; -webkit-font-smoothing: antialiased; padding: 0 0 10px; -webkit-text-size-adjust: none;" valign="top">
|
||||||
Please <a href="{{url}}">log in</a> to the bitwarden_rs server and confirm them from the organization management page.
|
Please <a href="{{url}}/">log in</a> to the bitwarden_rs server and confirm them from the organization management page.
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr style="margin: 0; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 16px; color: #333; line-height: 25px; -webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none;">
|
<tr style="margin: 0; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 16px; color: #333; line-height: 25px; -webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none;">
|
||||||
|
@@ -3,6 +3,6 @@ Invitation to {{org_name}} confirmed
|
|||||||
<html>
|
<html>
|
||||||
<p>
|
<p>
|
||||||
Your invitation to join <b>{{org_name}}</b> was confirmed.
|
Your invitation to join <b>{{org_name}}</b> was confirmed.
|
||||||
It will now appear under the Organizations the next time you <a href="{{url}}">log in</a> to the web vault.
|
It will now appear under the Organizations the next time you <a href="{{url}}/">log in</a> to the web vault.
|
||||||
</p>
|
</p>
|
||||||
</html>
|
</html>
|
||||||
|
@@ -102,7 +102,7 @@ Invitation to {{org_name}} confirmed
|
|||||||
<tr style="margin: 0; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 16px; color: #333; line-height: 25px; -webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none;">
|
<tr style="margin: 0; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 16px; color: #333; line-height: 25px; -webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none;">
|
||||||
<td class="content-block last" style="font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 16px; color: #333; line-height: 25px; margin: 0; -webkit-font-smoothing: antialiased; padding: 0; -webkit-text-size-adjust: none;" valign="top">
|
<td class="content-block last" style="font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 16px; color: #333; line-height: 25px; margin: 0; -webkit-font-smoothing: antialiased; padding: 0; -webkit-text-size-adjust: none;" valign="top">
|
||||||
Any collections and logins being shared with you by this organization will now appear in your Bitwarden vault. <br>
|
Any collections and logins being shared with you by this organization will now appear in your Bitwarden vault. <br>
|
||||||
<a href="{{url}}">Log in</a>
|
<a href="{{url}}/">Log in</a>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
|
@@ -9,6 +9,6 @@ New Device Logged In From {{device}}
|
|||||||
Device Type: {{device}}
|
Device Type: {{device}}
|
||||||
|
|
||||||
You can deauthorize all devices that have access to your account from the
|
You can deauthorize all devices that have access to your account from the
|
||||||
<a href="{{url}}">web vault</a> under Settings > My Account > Deauthorize Sessions.
|
<a href="{{url}}/">web vault</a> under Settings > My Account > Deauthorize Sessions.
|
||||||
</p>
|
</p>
|
||||||
</html>
|
</html>
|
||||||
|
@@ -116,7 +116,7 @@ New Device Logged In From {{device}}
|
|||||||
</tr>
|
</tr>
|
||||||
<tr style="margin: 0; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 16px; color: #333; line-height: 25px; -webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none;">
|
<tr style="margin: 0; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 16px; color: #333; line-height: 25px; -webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none;">
|
||||||
<td class="content-block last" style="font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 16px; color: #333; line-height: 25px; margin: 0; -webkit-font-smoothing: antialiased; padding: 0; -webkit-text-size-adjust: none;" valign="top">
|
<td class="content-block last" style="font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 16px; color: #333; line-height: 25px; margin: 0; -webkit-font-smoothing: antialiased; padding: 0; -webkit-text-size-adjust: none;" valign="top">
|
||||||
You can deauthorize all devices that have access to your account from the <a href="{{url}}">web vault</a> under Settings > My Account > Deauthorize Sessions.
|
You can deauthorize all devices that have access to your account from the <a href="{{url}}/">web vault</a> under Settings > My Account > Deauthorize Sessions.
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
|
@@ -3,7 +3,7 @@ Your master password hint
|
|||||||
You (or someone) recently requested your master password hint.
|
You (or someone) recently requested your master password hint.
|
||||||
|
|
||||||
Your hint is: "{{hint}}"
|
Your hint is: "{{hint}}"
|
||||||
Log in: <a href="{{url}}">Web Vault</a>
|
Log in: <a href="{{url}}/">Web Vault</a>
|
||||||
|
|
||||||
If you cannot remember your master password, there is no way to recover your data. The only option to gain access to your account again is to <a href="{{url}}/#/recover-delete">delete the account</a> so that you can register again and start over. All data associated with your account will be deleted.
|
If you cannot remember your master password, there is no way to recover your data. The only option to gain access to your account again is to <a href="{{url}}/#/recover-delete">delete the account</a> so that you can register again and start over. All data associated with your account will be deleted.
|
||||||
|
|
||||||
|
@@ -102,7 +102,7 @@ Your master password hint
|
|||||||
<tr style="margin: 0; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 16px; color: #333; line-height: 25px; -webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none;">
|
<tr style="margin: 0; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 16px; color: #333; line-height: 25px; -webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none;">
|
||||||
<td class="content-block" style="font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 16px; color: #333; line-height: 25px; margin: 0; -webkit-font-smoothing: antialiased; padding: 0 0 10px; -webkit-text-size-adjust: none;" valign="top">
|
<td class="content-block" style="font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 16px; color: #333; line-height: 25px; margin: 0; -webkit-font-smoothing: antialiased; padding: 0 0 10px; -webkit-text-size-adjust: none;" valign="top">
|
||||||
Your hint is: "{{hint}}"<br style="margin: 0; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 16px; color: #333; line-height: 25px; -webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none;" />
|
Your hint is: "{{hint}}"<br style="margin: 0; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 16px; color: #333; line-height: 25px; -webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none;" />
|
||||||
Log in: <a href="{{url}}">Web Vault</a>
|
Log in: <a href="{{url}}/">Web Vault</a>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr style="margin: 0; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 16px; color: #333; line-height: 25px; -webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none;">
|
<tr style="margin: 0; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 16px; color: #333; line-height: 25px; -webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none;">
|
||||||
|
8
src/static/templates/email/smtp_test.hbs
Normal file
8
src/static/templates/email/smtp_test.hbs
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
Bitwarden_rs SMTP Test
|
||||||
|
<!---------------->
|
||||||
|
<html>
|
||||||
|
<p>
|
||||||
|
This is a test email to verify the SMTP configuration for <a href="{{url}}">{{url}}</a>.
|
||||||
|
</p>
|
||||||
|
<p>When you can read this email it is probably configured correctly.</p>
|
||||||
|
</html>
|
129
src/static/templates/email/smtp_test.html.hbs
Normal file
129
src/static/templates/email/smtp_test.html.hbs
Normal file
@@ -0,0 +1,129 @@
|
|||||||
|
Bitwarden_rs SMTP Test
|
||||||
|
<!---------------->
|
||||||
|
<html xmlns="http://www.w3.org/1999/xhtml" xmlns="http://www.w3.org/1999/xhtml" style="-webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none; box-sizing: border-box; color: #333; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 16px; line-height: 25px; margin: 0;">
|
||||||
|
<head>
|
||||||
|
<meta name="viewport" content="width=device-width" />
|
||||||
|
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
|
||||||
|
<title>Bitwarden_rs</title>
|
||||||
|
</head>
|
||||||
|
<body style="-webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none; height: 100%; line-height: 25px; width: 100% !important;" bgcolor="#f6f6f6">
|
||||||
|
<style type="text/css">
|
||||||
|
body {
|
||||||
|
margin: 0;
|
||||||
|
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
|
||||||
|
box-sizing: border-box;
|
||||||
|
font-size: 16px;
|
||||||
|
color: #333;
|
||||||
|
line-height: 25px;
|
||||||
|
-webkit-font-smoothing: antialiased;
|
||||||
|
-webkit-text-size-adjust: none;
|
||||||
|
}
|
||||||
|
body * {
|
||||||
|
margin: 0;
|
||||||
|
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
|
||||||
|
box-sizing: border-box;
|
||||||
|
font-size: 16px;
|
||||||
|
color: #333;
|
||||||
|
line-height: 25px;
|
||||||
|
-webkit-font-smoothing: antialiased;
|
||||||
|
-webkit-text-size-adjust: none;
|
||||||
|
}
|
||||||
|
img {
|
||||||
|
max-width: 100%;
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
body {
|
||||||
|
-webkit-font-smoothing: antialiased;
|
||||||
|
-webkit-text-size-adjust: none;
|
||||||
|
width: 100% !important;
|
||||||
|
height: 100%;
|
||||||
|
line-height: 25px;
|
||||||
|
}
|
||||||
|
body {
|
||||||
|
background-color: #f6f6f6;
|
||||||
|
}
|
||||||
|
@media only screen and (max-width: 600px) {
|
||||||
|
body {
|
||||||
|
padding: 0 !important;
|
||||||
|
}
|
||||||
|
.container {
|
||||||
|
padding: 0 !important;
|
||||||
|
width: 100% !important;
|
||||||
|
}
|
||||||
|
.container-table {
|
||||||
|
padding: 0 !important;
|
||||||
|
width: 100% !important;
|
||||||
|
}
|
||||||
|
.content {
|
||||||
|
padding: 0 0 10px 0 !important;
|
||||||
|
}
|
||||||
|
.content-wrap {
|
||||||
|
padding: 10px !important;
|
||||||
|
}
|
||||||
|
.invoice {
|
||||||
|
width: 100% !important;
|
||||||
|
}
|
||||||
|
.main {
|
||||||
|
border-right: none !important;
|
||||||
|
border-left: none !important;
|
||||||
|
border-radius: 0 !important;
|
||||||
|
}
|
||||||
|
.logo {
|
||||||
|
padding-top: 10px !important;
|
||||||
|
}
|
||||||
|
.footer {
|
||||||
|
margin-top: 10px !important;
|
||||||
|
}
|
||||||
|
.indented {
|
||||||
|
padding-left: 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<table class="body-wrap" cellpadding="0" cellspacing="0" style="-webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none; box-sizing: border-box; color: #333; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 16px; line-height: 25px; margin: 0; width: 100%;" bgcolor="#f6f6f6">
|
||||||
|
<tr style="-webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none; box-sizing: border-box; color: #333; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 16px; line-height: 25px; margin: 0;">
|
||||||
|
<td valign="middle" class="aligncenter middle logo" style="-webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none; box-sizing: border-box; color: #333; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 16px; line-height: 25px; margin: 0; padding: 20px 0 10px;" align="center">
|
||||||
|
<img src="{{url}}/bwrs_static/logo-gray.png" alt="" width="250" height="39" style="-webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none; border: none; box-sizing: border-box; color: #333; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 16px; line-height: 25px; margin: 0; max-width: 100%;" />
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr style="-webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none; box-sizing: border-box; color: #333; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 16px; line-height: 25px; margin: 0;">
|
||||||
|
<td class="container" align="center" style="-webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none; box-sizing: border-box; clear: both !important; color: #333; display: block !important; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 16px; line-height: 25px; margin: 0 auto; max-width: 600px !important; width: 600px;" valign="top">
|
||||||
|
<table cellpadding="0" cellspacing="0" class="container-table" style="-webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none; box-sizing: border-box; clear: both !important; color: #333; display: block !important; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 16px; line-height: 25px; margin: 0 auto; max-width: 600px !important; width: 600px;">
|
||||||
|
<tr style="-webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none; box-sizing: border-box; color: #333; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 16px; line-height: 25px; margin: 0;">
|
||||||
|
<td class="content" align="center" style="-webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none; box-sizing: border-box; color: #333; display: block; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 0; line-height: 0; margin: 0 auto; max-width: 600px; padding-bottom: 20px;" valign="top">
|
||||||
|
<table class="main" width="100%" cellpadding="0" cellspacing="0" style="font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 16px; color: #333; line-height: 25px; -webkit-font-smoothing: antialiased; margin: 0; -webkit-text-size-adjust: none; border: 1px solid #e9e9e9; border-radius: 3px;" bgcolor="white">
|
||||||
|
<tr style="margin: 0; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 16px; color: #333; line-height: 25px; -webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none;">
|
||||||
|
<td class="content-wrap" style="font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 16px; color: #333; line-height: 25px; margin: 0; -webkit-font-smoothing: antialiased; padding: 20px; -webkit-text-size-adjust: none;" valign="top">
|
||||||
|
<table width="100%" cellpadding="0" cellspacing="0" style="margin: 0; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 16px; color: #333; line-height: 25px; -webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none;">
|
||||||
|
<tr style="margin: 0; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 16px; color: #333; line-height: 25px; -webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none;">
|
||||||
|
<td class="content-block" style="font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 16px; color: #333; line-height: 25px; margin: 0; -webkit-font-smoothing: antialiased; padding: 0 0 10px; -webkit-text-size-adjust: none; text-align: center;" valign="top" align="center">
|
||||||
|
This is a test email to verify the SMTP configuration for <a href="{{url}}">{{url}}</a>.
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr style="margin: 0; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 16px; color: #333; line-height: 25px; -webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none;">
|
||||||
|
<td class="content-block last" style="font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 16px; color: #333; line-height: 25px; margin: 0; -webkit-font-smoothing: antialiased; padding: 0; -webkit-text-size-adjust: none; text-align: center;" valign="top" align="center">
|
||||||
|
When you can read this email it is probably configured correctly.
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
<table class="footer" cellpadding="0" cellspacing="0" width="100%" style="-webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none; box-sizing: border-box; clear: both; color: #999; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 12px; line-height: 20px; margin: 0; width: 100%;">
|
||||||
|
<tr style="-webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none; box-sizing: border-box; color: #333; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 16px; line-height: 25px; margin: 0;">
|
||||||
|
<td class="aligncenter social-icons" align="center" style="-webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none; box-sizing: border-box; color: #999; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 12px; line-height: 20px; margin: 0; padding: 15px 0 0 0;" valign="top">
|
||||||
|
<table cellpadding="0" cellspacing="0" style="-webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none; box-sizing: border-box; color: #333; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 16px; line-height: 25px; margin: 0 auto;">
|
||||||
|
<tr style="-webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none; box-sizing: border-box; color: #333; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 16px; line-height: 25px; margin: 0;">
|
||||||
|
<td style="-webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none; box-sizing: border-box; color: #999; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 12px; line-height: 20px; margin: 0; padding: 0 10px;" valign="top"><a href="https://github.com/dani-garcia/bitwarden_rs" target="_blank" style="-webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none; box-sizing: border-box; color: #999; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 12px; line-height: 20px; margin: 0; text-decoration: underline;"><img src="{{url}}/bwrs_static/mail-github.png" alt="GitHub" width="30" height="30" style="-webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none; border: none; box-sizing: border-box; color: #333; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 16px; line-height: 25px; margin: 0; max-width: 100%;" /></a></td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</body>
|
||||||
|
</html>
|
@@ -2,7 +2,7 @@ Welcome
|
|||||||
<!---------------->
|
<!---------------->
|
||||||
<html>
|
<html>
|
||||||
<p>
|
<p>
|
||||||
Thank you for creating an account at <a href="{{url}}">{{url}}</a>. You may now log in with your new account.
|
Thank you for creating an account at <a href="{{url}}/">{{url}}</a>. You may now log in with your new account.
|
||||||
</p>
|
</p>
|
||||||
<p>If you did not request to create an account, you can safely ignore this email.</p>
|
<p>If you did not request to create an account, you can safely ignore this email.</p>
|
||||||
</html>
|
</html>
|
||||||
|
@@ -96,7 +96,7 @@ Welcome
|
|||||||
<table width="100%" cellpadding="0" cellspacing="0" style="margin: 0; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 16px; color: #333; line-height: 25px; -webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none;">
|
<table width="100%" cellpadding="0" cellspacing="0" style="margin: 0; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 16px; color: #333; line-height: 25px; -webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none;">
|
||||||
<tr style="margin: 0; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 16px; color: #333; line-height: 25px; -webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none;">
|
<tr style="margin: 0; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 16px; color: #333; line-height: 25px; -webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none;">
|
||||||
<td class="content-block" style="font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 16px; color: #333; line-height: 25px; margin: 0; -webkit-font-smoothing: antialiased; padding: 0 0 10px; -webkit-text-size-adjust: none; text-align: center;" valign="top" align="center">
|
<td class="content-block" style="font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 16px; color: #333; line-height: 25px; margin: 0; -webkit-font-smoothing: antialiased; padding: 0 0 10px; -webkit-text-size-adjust: none; text-align: center;" valign="top" align="center">
|
||||||
Thank you for creating an account at <a href="{{url}}">{{url}}</a>. You may now log in with your new account.
|
Thank you for creating an account at <a href="{{url}}/">{{url}}</a>. You may now log in with your new account.
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr style="margin: 0; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 16px; color: #333; line-height: 25px; -webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none;">
|
<tr style="margin: 0; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 16px; color: #333; line-height: 25px; -webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none;">
|
||||||
|
@@ -2,7 +2,7 @@ Welcome
|
|||||||
<!---------------->
|
<!---------------->
|
||||||
<html>
|
<html>
|
||||||
<p>
|
<p>
|
||||||
Thank you for creating an account at <a href="{{url}}">{{url}}</a>. Before you can login with your new account, you must verify this email address by clicking the link below.
|
Thank you for creating an account at <a href="{{url}}/">{{url}}</a>. Before you can login with your new account, you must verify this email address by clicking the link below.
|
||||||
<br>
|
<br>
|
||||||
<br>
|
<br>
|
||||||
<a href="{{url}}/#/verify-email/?userId={{user_id}}&token={{token}}">
|
<a href="{{url}}/#/verify-email/?userId={{user_id}}&token={{token}}">
|
||||||
|
@@ -96,7 +96,7 @@ Welcome
|
|||||||
<table width="100%" cellpadding="0" cellspacing="0" style="margin: 0; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 16px; color: #333; line-height: 25px; -webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none;">
|
<table width="100%" cellpadding="0" cellspacing="0" style="margin: 0; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 16px; color: #333; line-height: 25px; -webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none;">
|
||||||
<tr style="margin: 0; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 16px; color: #333; line-height: 25px; -webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none;">
|
<tr style="margin: 0; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 16px; color: #333; line-height: 25px; -webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none;">
|
||||||
<td class="content-block" style="font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 16px; color: #333; line-height: 25px; margin: 0; -webkit-font-smoothing: antialiased; padding: 0 0 10px; -webkit-text-size-adjust: none; text-align: center;" valign="top" align="center">
|
<td class="content-block" style="font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 16px; color: #333; line-height: 25px; margin: 0; -webkit-font-smoothing: antialiased; padding: 0 0 10px; -webkit-text-size-adjust: none; text-align: center;" valign="top" align="center">
|
||||||
Thank you for creating an account at <a href="{{url}}">{{url}}</a>. Before you can login with your new account, you must verify this email address by clicking the link below.
|
Thank you for creating an account at <a href="{{url}}/">{{url}}</a>. Before you can login with your new account, you must verify this email address by clicking the link below.
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr style="margin: 0; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 16px; color: #333; line-height: 25px; -webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none;">
|
<tr style="margin: 0; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 16px; color: #333; line-height: 25px; -webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none;">
|
||||||
|
32
src/util.rs
32
src/util.rs
@@ -7,6 +7,8 @@ use rocket::response::{self, Responder};
|
|||||||
use rocket::{Data, Request, Response, Rocket};
|
use rocket::{Data, Request, Response, Rocket};
|
||||||
use std::io::Cursor;
|
use std::io::Cursor;
|
||||||
|
|
||||||
|
use crate::CONFIG;
|
||||||
|
|
||||||
pub struct AppHeaders();
|
pub struct AppHeaders();
|
||||||
|
|
||||||
impl Fairing for AppHeaders {
|
impl Fairing for AppHeaders {
|
||||||
@@ -23,7 +25,7 @@ impl Fairing for AppHeaders {
|
|||||||
res.set_raw_header("X-Frame-Options", "SAMEORIGIN");
|
res.set_raw_header("X-Frame-Options", "SAMEORIGIN");
|
||||||
res.set_raw_header("X-Content-Type-Options", "nosniff");
|
res.set_raw_header("X-Content-Type-Options", "nosniff");
|
||||||
res.set_raw_header("X-XSS-Protection", "1; mode=block");
|
res.set_raw_header("X-XSS-Protection", "1; mode=block");
|
||||||
let csp = "frame-ancestors 'self' chrome-extension://nngceckbapebfimnlniiiahkandclblb moz-extension://*;";
|
let csp = format!("frame-ancestors 'self' chrome-extension://nngceckbapebfimnlniiiahkandclblb moz-extension://* {};", CONFIG.allowed_iframe_ancestors());
|
||||||
res.set_raw_header("Content-Security-Policy", csp);
|
res.set_raw_header("Content-Security-Policy", csp);
|
||||||
|
|
||||||
// Disable cache unless otherwise specified
|
// Disable cache unless otherwise specified
|
||||||
@@ -107,7 +109,7 @@ impl<'r, R: Responder<'r>> Responder<'r> for Cached<R> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Log all the routes from the main base paths list, and the attachments endoint
|
// Log all the routes from the main paths list, and the attachments endpoint
|
||||||
// Effectively ignores, any static file route, and the alive endpoint
|
// Effectively ignores, any static file route, and the alive endpoint
|
||||||
const LOGGED_ROUTES: [&str; 6] = [
|
const LOGGED_ROUTES: [&str; 6] = [
|
||||||
"/api",
|
"/api",
|
||||||
@@ -131,7 +133,9 @@ impl Fairing for BetterLogging {
|
|||||||
fn on_launch(&self, rocket: &Rocket) {
|
fn on_launch(&self, rocket: &Rocket) {
|
||||||
if self.0 {
|
if self.0 {
|
||||||
info!(target: "routes", "Routes loaded:");
|
info!(target: "routes", "Routes loaded:");
|
||||||
for route in rocket.routes() {
|
let mut routes: Vec<_> = rocket.routes().collect();
|
||||||
|
routes.sort_by_key(|r| r.uri.path());
|
||||||
|
for route in routes {
|
||||||
if route.rank < 0 {
|
if route.rank < 0 {
|
||||||
info!(target: "routes", "{:<6} {}", route.method, route.uri);
|
info!(target: "routes", "{:<6} {}", route.method, route.uri);
|
||||||
} else {
|
} else {
|
||||||
@@ -153,7 +157,10 @@ impl Fairing for BetterLogging {
|
|||||||
}
|
}
|
||||||
let uri = request.uri();
|
let uri = request.uri();
|
||||||
let uri_path = uri.path();
|
let uri_path = uri.path();
|
||||||
if self.0 || LOGGED_ROUTES.iter().any(|r| uri_path.starts_with(r)) {
|
// FIXME: trim_start_matches() could result in over-trimming in pathological cases;
|
||||||
|
// strip_prefix() would be a better option once it's stable.
|
||||||
|
let uri_subpath = uri_path.trim_start_matches(&CONFIG.domain_path());
|
||||||
|
if self.0 || LOGGED_ROUTES.iter().any(|r| uri_subpath.starts_with(r)) {
|
||||||
match uri.query() {
|
match uri.query() {
|
||||||
Some(q) => info!(target: "request", "{} {}?{}", method, uri_path, &q[..q.len().min(30)]),
|
Some(q) => info!(target: "request", "{} {}?{}", method, uri_path, &q[..q.len().min(30)]),
|
||||||
None => info!(target: "request", "{} {}", method, uri_path),
|
None => info!(target: "request", "{} {}", method, uri_path),
|
||||||
@@ -165,8 +172,10 @@ impl Fairing for BetterLogging {
|
|||||||
if !self.0 && request.method() == Method::Options {
|
if !self.0 && request.method() == Method::Options {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let uri_path = request.uri().path();
|
// FIXME: trim_start_matches() could result in over-trimming in pathological cases;
|
||||||
if self.0 || LOGGED_ROUTES.iter().any(|r| uri_path.starts_with(r)) {
|
// strip_prefix() would be a better option once it's stable.
|
||||||
|
let uri_subpath = request.uri().path().trim_start_matches(&CONFIG.domain_path());
|
||||||
|
if self.0 || LOGGED_ROUTES.iter().any(|r| uri_subpath.starts_with(r)) {
|
||||||
let status = response.status();
|
let status = response.status();
|
||||||
if let Some(ref route) = request.route() {
|
if let Some(ref route) = request.route() {
|
||||||
info!(target: "response", "{} => {} {}", route, status.code, status.reason)
|
info!(target: "response", "{} => {} {}", route, status.code, status.reason)
|
||||||
@@ -309,6 +318,17 @@ where
|
|||||||
try_parse_string(env::var(key))
|
try_parse_string(env::var(key))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const TRUE_VALUES: &[&str] = &["true", "t", "yes", "y", "1"];
|
||||||
|
const FALSE_VALUES: &[&str] = &["false", "f", "no", "n", "0"];
|
||||||
|
|
||||||
|
pub fn get_env_bool(key: &str) -> Option<bool> {
|
||||||
|
match env::var(key) {
|
||||||
|
Ok(val) if TRUE_VALUES.contains(&val.to_lowercase().as_ref()) => Some(true),
|
||||||
|
Ok(val) if FALSE_VALUES.contains(&val.to_lowercase().as_ref()) => Some(false),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
// Date util methods
|
// Date util methods
|
||||||
//
|
//
|
||||||
|
Reference in New Issue
Block a user