mirror of
https://github.com/ViViDboarder/bitwarden_rs_ldap.git
synced 2024-11-21 18:56:27 +00:00
Merge pull request #13 from jerhat/master
Added optional bitwarden_root_cert config to trust an additional root certificate...
This commit is contained in:
commit
7b3a814ce7
@ -19,9 +19,11 @@ Configuration values are as follows:
|
|||||||
|----|----|--------|-----------|
|
|----|----|--------|-----------|
|
||||||
|`bitwarden_url`|String||The root URL for accessing `bitwarden_rs`. Eg: `https://bw.example.com`|
|
|`bitwarden_url`|String||The root URL for accessing `bitwarden_rs`. Eg: `https://bw.example.com`|
|
||||||
|`bitwarden_admin_token`|String||The value passed as `ADMIN_TOKEN` to `bitwarden_rs`|
|
|`bitwarden_admin_token`|String||The value passed as `ADMIN_TOKEN` to `bitwarden_rs`|
|
||||||
|
|`bitwarden_root_cert_file`|String|Optional|Path to an additional der-encoded root certificate to trust. Eg. `root.cert`. If using Docker see `docker-compose.yml` for how to expose it. Defaults to `empty`|
|
||||||
|`ldap_host`|String||The hostname or IP address for your ldap server|
|
|`ldap_host`|String||The hostname or IP address for your ldap server|
|
||||||
|`ldap_scheme`|String|Optional|The that should be used to connect. `ldap` or `ldaps`. This is set by default based on SSL settings|
|
|`ldap_scheme`|String|Optional|The that should be used to connect. `ldap` or `ldaps`. This is set by default based on SSL settings|
|
||||||
|`ldap_ssl`|Boolean|Optional|Indicates if SSL should be used. Defaults to `false`|
|
|`ldap_ssl`|Boolean|Optional|Indicates if SSL should be used. Defaults to `false`|
|
||||||
|
|`ldap_no_tls_verify`|Boolean|Optional|Indicates if certificate should be verified when using SSL. Defaults to `true`|
|
||||||
|`ldap_port`|Integer|Optional|Port used to connect to the LDAP server. This will default to 389 or 636, depending on your SSL settings|
|
|`ldap_port`|Integer|Optional|Port used to connect to the LDAP server. This will default to 389 or 636, depending on your SSL settings|
|
||||||
|`ldap_bind_dn`|String||The dn for the bind user that will connect to LDAP. Eg. `cn=admin,dc=example,dc=org`|
|
|`ldap_bind_dn`|String||The dn for the bind user that will connect to LDAP. Eg. `cn=admin,dc=example,dc=org`|
|
||||||
|`ldap_bind_password`|String||The password for the provided bind user.|
|
|`ldap_bind_password`|String||The password for the provided bind user.|
|
||||||
|
@ -7,6 +7,7 @@ services:
|
|||||||
# dockerfile: Dockerfile.alpine
|
# dockerfile: Dockerfile.alpine
|
||||||
volumes:
|
volumes:
|
||||||
- ./example.config.toml:/usr/src/bitwarden_rs_ldap/config.toml:ro
|
- ./example.config.toml:/usr/src/bitwarden_rs_ldap/config.toml:ro
|
||||||
|
# ./root.cert:/usr/src/bitwarden_rs_ldap/root.cert:ro
|
||||||
environment:
|
environment:
|
||||||
RUST_BACKTRACE: 1
|
RUST_BACKTRACE: 1
|
||||||
restart: always
|
restart: always
|
||||||
|
@ -5,6 +5,8 @@ use reqwest::Response;
|
|||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::error::Error;
|
use std::error::Error;
|
||||||
|
use std::fs::File;
|
||||||
|
use std::io::Read;
|
||||||
use std::time::{Duration, Instant};
|
use std::time::{Duration, Instant};
|
||||||
|
|
||||||
const COOKIE_LIFESPAN: Duration = Duration::from_secs(20 * 60);
|
const COOKIE_LIFESPAN: Duration = Duration::from_secs(20 * 60);
|
||||||
@ -31,29 +33,50 @@ impl User {
|
|||||||
pub struct Client {
|
pub struct Client {
|
||||||
url: String,
|
url: String,
|
||||||
admin_token: String,
|
admin_token: String,
|
||||||
|
root_cert_file: String,
|
||||||
cookie: Option<String>,
|
cookie: Option<String>,
|
||||||
cookie_created: Option<Instant>,
|
cookie_created: Option<Instant>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Client {
|
impl Client {
|
||||||
/// Create new instance of client
|
/// Create new instance of client
|
||||||
pub fn new(url: String, admin_token: String) -> Client {
|
pub fn new(url: String, admin_token: String, root_cert_file: String) -> Client {
|
||||||
Client {
|
Client {
|
||||||
url,
|
url,
|
||||||
admin_token,
|
admin_token,
|
||||||
|
root_cert_file,
|
||||||
cookie: None,
|
cookie: None,
|
||||||
cookie_created: None,
|
cookie_created: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_root_cert(&self) -> reqwest::Certificate {
|
||||||
|
let mut buf = Vec::new();
|
||||||
|
|
||||||
|
// read a local binary DER encoded certificate
|
||||||
|
File::open(&self.root_cert_file)
|
||||||
|
.expect("Could not open root cert file")
|
||||||
|
.read_to_end(&mut buf)
|
||||||
|
.expect("Could not read root cert file");
|
||||||
|
|
||||||
|
return reqwest::Certificate::from_der(&buf).expect("Could not load der root cert file");
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_http_client(&self) -> reqwest::Client {
|
||||||
|
let mut client = reqwest::Client::builder().redirect(reqwest::RedirectPolicy::none());
|
||||||
|
|
||||||
|
if !&self.root_cert_file.is_empty() {
|
||||||
|
let cert = self.get_root_cert();
|
||||||
|
client = client.add_root_certificate(cert);
|
||||||
|
}
|
||||||
|
|
||||||
|
return client.build().unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
/// Authenticate client
|
/// Authenticate client
|
||||||
fn auth(&mut self) -> Response {
|
fn auth(&mut self) -> Response {
|
||||||
let cookie_created = Instant::now();
|
let cookie_created = Instant::now();
|
||||||
let client = reqwest::Client::builder()
|
let client = self.get_http_client();
|
||||||
// Avoid redirects because server will redirect to admin page after auth
|
|
||||||
.redirect(reqwest::RedirectPolicy::none())
|
|
||||||
.build()
|
|
||||||
.unwrap();
|
|
||||||
let result = client
|
let result = client
|
||||||
.post(format!("{}{}", &self.url, "/admin/").as_str())
|
.post(format!("{}{}", &self.url, "/admin/").as_str())
|
||||||
.form(&[("token", &self.admin_token)])
|
.form(&[("token", &self.admin_token)])
|
||||||
@ -102,7 +125,8 @@ impl Client {
|
|||||||
}
|
}
|
||||||
Some(cookie) => {
|
Some(cookie) => {
|
||||||
let url = format!("{}/admin{}", &self.url, path);
|
let url = format!("{}/admin{}", &self.url, path);
|
||||||
let request = reqwest::Client::new()
|
let client = self.get_http_client();
|
||||||
|
let request = client
|
||||||
.get(url.as_str())
|
.get(url.as_str())
|
||||||
.header(reqwest::header::COOKIE, cookie.clone());
|
.header(reqwest::header::COOKIE, cookie.clone());
|
||||||
let response = request.send().unwrap_or_else(|e| {
|
let response = request.send().unwrap_or_else(|e| {
|
||||||
@ -126,7 +150,8 @@ impl Client {
|
|||||||
}
|
}
|
||||||
Some(cookie) => {
|
Some(cookie) => {
|
||||||
let url = format!("{}/admin{}", &self.url, path);
|
let url = format!("{}/admin{}", &self.url, path);
|
||||||
let request = reqwest::Client::new()
|
let client = self.get_http_client();
|
||||||
|
let request = client
|
||||||
.post(url.as_str())
|
.post(url.as_str())
|
||||||
.header("Cookie", cookie.clone())
|
.header("Cookie", cookie.clone())
|
||||||
.json(&json);
|
.json(&json);
|
||||||
|
@ -38,11 +38,13 @@ pub struct Config {
|
|||||||
// Bitwarden connection config
|
// Bitwarden connection config
|
||||||
bitwarden_url: String,
|
bitwarden_url: String,
|
||||||
bitwarden_admin_token: String,
|
bitwarden_admin_token: String,
|
||||||
|
bitwarden_root_cert_file: Option<String>,
|
||||||
// LDAP Connection config
|
// LDAP Connection config
|
||||||
ldap_host: String,
|
ldap_host: String,
|
||||||
ldap_scheme: Option<String>,
|
ldap_scheme: Option<String>,
|
||||||
ldap_ssl: Option<bool>,
|
ldap_ssl: Option<bool>,
|
||||||
ldap_port: Option<u16>,
|
ldap_port: Option<u16>,
|
||||||
|
ldap_no_tls_verify: Option<bool>,
|
||||||
// LDAP auth config
|
// LDAP auth config
|
||||||
ldap_bind_dn: String,
|
ldap_bind_dn: String,
|
||||||
ldap_bind_password: Pass,
|
ldap_bind_password: Pass,
|
||||||
@ -71,6 +73,13 @@ impl Config {
|
|||||||
self.bitwarden_admin_token.clone()
|
self.bitwarden_admin_token.clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn get_bitwarden_root_cert_file(&self) -> String {
|
||||||
|
match &self.bitwarden_root_cert_file {
|
||||||
|
Some(bitwarden_root_cert_file) => bitwarden_root_cert_file.clone(),
|
||||||
|
None => String::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn get_ldap_url(&self) -> String {
|
pub fn get_ldap_url(&self) -> String {
|
||||||
format!(
|
format!(
|
||||||
"{}://{}:{}",
|
"{}://{}:{}",
|
||||||
@ -101,6 +110,10 @@ impl Config {
|
|||||||
self.ldap_ssl.unwrap_or(false)
|
self.ldap_ssl.unwrap_or(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn get_ldap_no_tls_verify(&self) -> bool {
|
||||||
|
self.ldap_no_tls_verify.unwrap_or(false)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn get_ldap_port(&self) -> u16 {
|
pub fn get_ldap_port(&self) -> u16 {
|
||||||
match self.ldap_port {
|
match self.ldap_port {
|
||||||
Some(ldap_port) => ldap_port,
|
Some(ldap_port) => ldap_port,
|
||||||
|
@ -5,7 +5,7 @@ use std::error::Error;
|
|||||||
use std::thread::sleep;
|
use std::thread::sleep;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
use ldap3::{DerefAliases, LdapConn, Scope, SearchEntry, SearchOptions};
|
use ldap3::{DerefAliases, LdapConn, LdapConnSettings, Scope, SearchEntry, SearchOptions};
|
||||||
|
|
||||||
mod bw_admin;
|
mod bw_admin;
|
||||||
mod config;
|
mod config;
|
||||||
@ -15,6 +15,7 @@ fn main() {
|
|||||||
let mut client = bw_admin::Client::new(
|
let mut client = bw_admin::Client::new(
|
||||||
config.get_bitwarden_url().clone(),
|
config.get_bitwarden_url().clone(),
|
||||||
config.get_bitwarden_admin_token().clone(),
|
config.get_bitwarden_admin_token().clone(),
|
||||||
|
config.get_bitwarden_root_cert_file().clone(),
|
||||||
);
|
);
|
||||||
|
|
||||||
if let Err(e) = invite_users(&config, &mut client, config.get_ldap_sync_loop()) {
|
if let Err(e) = invite_users(&config, &mut client, config.get_ldap_sync_loop()) {
|
||||||
@ -64,8 +65,10 @@ fn ldap_client(
|
|||||||
ldap_url: String,
|
ldap_url: String,
|
||||||
bind_dn: String,
|
bind_dn: String,
|
||||||
bind_pw: String,
|
bind_pw: String,
|
||||||
|
no_tls_verify: bool,
|
||||||
) -> Result<LdapConn, Box<dyn Error>> {
|
) -> Result<LdapConn, Box<dyn Error>> {
|
||||||
let ldap = LdapConn::new(ldap_url.as_str())?;
|
let settings = LdapConnSettings::new().set_no_tls_verify(no_tls_verify);
|
||||||
|
let ldap = LdapConn::with_settings(settings, ldap_url.as_str())?;
|
||||||
match ldap.simple_bind(bind_dn.as_str(), bind_pw.as_str()) {
|
match ldap.simple_bind(bind_dn.as_str(), bind_pw.as_str()) {
|
||||||
_ => {}
|
_ => {}
|
||||||
};
|
};
|
||||||
@ -79,6 +82,7 @@ fn search_entries(config: &config::Config) -> Result<Vec<SearchEntry>, Box<dyn E
|
|||||||
config.get_ldap_url(),
|
config.get_ldap_url(),
|
||||||
config.get_ldap_bind_dn(),
|
config.get_ldap_bind_dn(),
|
||||||
config.get_ldap_bind_password(),
|
config.get_ldap_bind_password(),
|
||||||
|
config.get_ldap_no_tls_verify(),
|
||||||
);
|
);
|
||||||
|
|
||||||
if ldap.is_err() {
|
if ldap.is_err() {
|
||||||
|
Loading…
Reference in New Issue
Block a user