Compare commits

..

No commits in common. "master" and "v0.6.0" have entirely different histories.

16 changed files with 1258 additions and 701 deletions

View File

@ -1,22 +0,0 @@
# Docs: <https://docs.github.com/en/free-pro-team@latest/github/administering-a-repository/customizing-dependency-updates>
version: 2
updates:
- package-ecosystem: github-actions
directory: /
schedule: {interval: monthly}
reviewers: [ViViDboarder]
assignees: [ViViDboarder]
- package-ecosystem: docker
directory: /
schedule: {interval: monthly}
reviewers: [ViViDboarder]
assignees: [ViViDboarder]
- package-ecosystem: cargo
directory: /
schedule: {interval: monthly}
reviewers: [ViViDboarder]
assignees: [ViViDboarder]

View File

@ -16,20 +16,13 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v2
- name: Build
run: cargo build --verbose
- name: Run tests
run: cargo test --verbose
- uses: actions/setup-python@v4
- uses: actions/setup-python@v2
- name: Run pre-commit hooks
uses: pre-commit/action@v3.0.0
env:
SKIP: hadolint
- name: Run hadolint
uses: hadolint/hadolint-action@v3.1.0
uses: pre-commit/action@v2.0.2

View File

@ -25,10 +25,10 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
uses: actions/checkout@v2
- name: Docker meta
id: meta
uses: docker/metadata-action@v4
uses: docker/metadata-action@v3
with:
images: vividboarder/vaultwarden_ldap
flavor: |
@ -42,12 +42,12 @@ jobs:
- name: Login to DockerHub
if: github.event_name != 'pull_request'
uses: docker/login-action@v2
uses: docker/login-action@v1
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Build and push
uses: docker/build-push-action@v4
uses: docker/build-push-action@v2
with:
context: .
file: ${{ matrix.dockerfile }}

View File

@ -1,7 +1,7 @@
---
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.1.0
rev: v3.4.0
hooks:
- id: check-added-large-files
- id: check-yaml
@ -16,10 +16,7 @@ repos:
- id: cargo-check
- id: clippy
- repo: https://github.com/IamTheFij/docker-pre-commit
rev: v2.0.1
rev: v2.0.0
hooks:
- id: docker-compose-check
- repo: https://github.com/hadolint/hadolint
rev: v2.8.0
hooks:
- id: hadolint

1797
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -1,17 +1,15 @@
[package]
name = "vaultwarden_ldap"
version = "1.0.0"
version = "0.6.0"
authors = ["ViViDboarder <vividboarder@gmail.com>"]
edition = "2018"
[dependencies]
ldap3 = "0.11"
ldap3 = "0.6"
serde = { version = "1.0", features = ["derive"] }
toml = "0.5"
reqwest = { version = "0.11", features = ["json", "blocking"] }
reqwest = "0.9"
serde_json = "1.0"
thiserror = "1.0"
anyhow = "1.0"
envy = "0.4.1"
pledge = "0.4.2"
unveil = "0.3.2"

View File

@ -1,4 +1,4 @@
ARG BUILD_TAG=1.57.0
ARG BUILD_TAG=1.46
ARG RUN_TAG=$BUILD_TAG
FROM rust:$BUILD_TAG as builder
@ -12,15 +12,13 @@ COPY Cargo.toml Cargo.lock ./
RUN cargo build --locked --release
# Remove bins to make sure we rebuild
# hadolint ignore=DL3059
RUN rm ./target/release/deps/vaultwarden_ldap*
# Copy source and install
COPY src ./src
RUN cargo build --release
FROM ubuntu:focal
FROM rust:$RUN_TAG
WORKDIR /app
RUN apt-get update -y && apt-get install -y libssl-dev=1.1.1f-1ubuntu2.20 --no-install-recommends && rm -rf /var/lib/apt/lists/*
COPY --from=builder /usr/src/vaultwarden_ldap/target/release/vaultwarden_ldap /usr/local/bin/
CMD ["/usr/local/bin/vaultwarden_ldap"]

View File

@ -1,21 +1,19 @@
FROM ekidd/rust-musl-builder:1.57.0 AS builder
FROM ekidd/rust-musl-builder:1.46.0 AS builder
WORKDIR /home/rust/src
# Cache build deps
RUN USER=rust cargo init
COPY --chown=rust:rust Cargo.toml Cargo.lock ./
COPY Cargo.toml Cargo.lock ./
RUN cargo build --locked --release && \
rm src/*.rs
COPY --chown=rust:rust ./src ./src
RUN USER=rust touch ./src/main.rs
# hadolint ignore=DL3059
RUN cargo build --release
RUN touch ./src/main.rs && \
cargo build --release
FROM alpine:3
# hadolint ignore=DL3018
RUN apk --no-cache add ca-certificates
RUN apk --no-cache add ca-certificates=20191127-r5
COPY --from=builder \
/home/rust/src/target/x86_64-unknown-linux-musl/release/vaultwarden_ldap \
/usr/local/bin/

View File

@ -38,28 +38,14 @@ test:
itest:
docker-compose -f docker-compose.yml \
-f itest/docker-compose.itest.yml \
build
docker-compose -f docker-compose.yml \
-f itest/docker-compose.itest.yml \
up -d vaultwarden ldap
docker-compose -f docker-compose.yml \
-f itest/docker-compose.itest.yml \
run ldap_sync
docker-compose stop
up --build
# Run bootstrapped integration test using env for config
.PHONY: itest-env
itest-env:
docker-compose -f docker-compose.yml \
-f itest/docker-compose.itest-env.yml \
build
docker-compose -f docker-compose.yml \
-f itest/docker-compose.itest-env.yml \
up -d vaultwarden ldap
docker-compose -f docker-compose.yml \
-f itest/docker-compose.itest-env.yml \
run ldap_sync
docker-compose stop
up --build
.PHONY: clean-itest
clean-itest:

View File

@ -1,7 +1,7 @@
# vaultwarden_ldap
LDAP user invites for [vaultwarden](https://github.com/dani-garcia/vaultwarden)
An LDAP connector for [vaultwarden](https://github.com/dani-garcia/vaultwarden)
After configuring, run `vaultwarden_ldap` and it will invite any users it finds in LDAP to your `vaultwarden` instance. This is NOT a sync tool like the [Bitwarden Directory Connector](https://bitwarden.com/help/directory-sync/).
After configuring, run `vaultwarden_ldap` and it will invite any users it finds in LDAP to your `vaultwarden` instance.
## Deploying
@ -34,8 +34,6 @@ Configuration values are as follows:
|`ldap_sync_interval_seconds`|Integer|Optional|Number of seconds to wait between each LDAP request. Defaults to `60`|
|`ldap_sync_loop`|Boolean|Optional|Indicates whether or not syncing should be polled in a loop or done once. Defaults to `true`|
Alternatively, instead of using `config.toml`, all values can be provided using enviroment variables prefixed with `APP_`. For example: `APP_VAULTWARDEN_URL=https://vault.example.com`
## Development
This repo has a predefined set of [pre-commit](https://pre-commit.com) rules. You can install pre-commit via any means you'd like. Once your system has `pre-commit` installed, you can run `make install-hooks` to ensure the hooks will run with every commit. You can also force running all hooks with `make check`.
@ -44,18 +42,7 @@ For those less familiar with `cargo`, you can use the `make` targets that have b
## Testing
There are no unit tests, but there are integration tests that require manual verification.
### Integration tests
Running `make itest` will spin up an ldap server with a test user, a Vaultwarden server, and then run the sync. If successful the log should show an invitation sent to the test user. If you run `make itest` again, it should show no invites sent because the user already has been invited. If you'd like to reset the testing, `make clean-itest` will clear out the Vaultwarden database and start fresh.
It's also possible to test passing configs via enviornment variables by running `make itest-env`. The validation steps are the same.
### Steps for manual testing
The first step is to set up Bitwarden and the LDAP server.
All testing is manual right now. First step is to set up Bitwarden and the LDAP server.
```bash
docker-compose up -d vaultwarden ldap ldap_admin
@ -85,3 +72,8 @@ docker-compose up ldap_sync
Alternately, you can bootstrap some of this by running:
docker-compose -f docker-compose.yml -f itest/docker-compose.itest.yml up --build
## Future
* Any kind of proper logging
* Tests

View File

@ -24,7 +24,6 @@ services:
ADMIN_TOKEN: admin
SIGNUPS_ALLOWED: 'false'
INVITATIONS_ALLOWED: 'true'
I_REALLY_WANT_VOLATILE_STORAGE: 'true'
ldap:
image: osixia/openldap

View File

@ -11,9 +11,9 @@ services:
APP_LDAP_BIND_PASSWORD: "admin"
APP_LDAP_SEARCH_BASE_DN: "dc=example,dc=org"
APP_LDAP_SEARCH_FILTER: "(&(objectClass=*)(uid=*))"
APP_LDAP_SYNC_LOOP: "false"
APP_LDAP_SYNC_INTERVAL_SECONDS: 10
vaultwarden: {}
vaultwarden:
ldap:
command: ["--copy-service"]

View File

@ -2,10 +2,8 @@
version: '3'
services:
ldap_sync:
volumes:
- ./itest/config.toml:/config.toml:ro
vaultwarden: {}
vaultwarden:
ldap:
command: ["--copy-service"]

View File

@ -37,9 +37,9 @@ pub fn read_config_from_file() -> Result<Config, String> {
let config_path = get_config_path();
let contents = fs::read_to_string(&config_path)
.map_err(|err| format!("Failed to open config file at {}: {}", config_path, err))?;
.map_err(|_| format!("Failed to open config file at {}", config_path))?;
let config: Config = toml::from_str(contents.as_str())
.map_err(|err| format!("Failed to parse config file at {}: {}", config_path, err))?;
.map_err(|_| format!("Failed to parse config file at {}", config_path))?;
println!("Config read from file at {}", config_path);
Ok(config)

View File

@ -1,7 +1,5 @@
extern crate anyhow;
extern crate ldap3;
extern crate pledge;
extern crate unveil;
use std::collections::HashSet;
use std::thread::sleep;
@ -11,8 +9,6 @@ use anyhow::Context as _;
use anyhow::Error as AnyError;
use anyhow::Result;
use ldap3::{DerefAliases, LdapConn, LdapConnSettings, Scope, SearchEntry, SearchOptions};
use pledge::pledge;
use unveil::unveil;
mod config;
mod vw_admin;
@ -25,16 +21,6 @@ fn main() {
config.get_vaultwarden_root_cert_file(),
);
unveil(config::get_config_path(), "r")
.or_else(unveil::Error::ignore_platform)
.expect("Could not unveil config file");
unveil("", "")
.or_else(unveil::Error::ignore_platform)
.expect("Could not disable further unveils");
pledge("dns flock inet rpath stdio tty", "")
.or_else(pledge::Error::ignore_platform)
.expect("Could not pledge permissions");
invite_users(&config, &mut client, config.get_ldap_sync_loop())
}
@ -82,7 +68,7 @@ fn ldap_client(
let settings = LdapConnSettings::new()
.set_starttls(starttls)
.set_no_tls_verify(no_tls_verify);
let mut ldap = LdapConn::with_settings(settings, ldap_url.as_str())
let ldap = LdapConn::with_settings(settings, ldap_url.as_str())
.context("Failed to connect to LDAP server")?;
ldap.simple_bind(bind_dn.as_str(), bind_pw.as_str())
.context("Could not bind to LDAP server")?;
@ -92,7 +78,7 @@ fn ldap_client(
/// Retrieves search results from ldap
fn search_entries(config: &config::Config) -> Result<Vec<SearchEntry>, AnyError> {
let mut ldap = ldap_client(
let ldap = ldap_client(
config.get_ldap_url(),
config.get_ldap_bind_dn(),
config.get_ldap_bind_password(),
@ -104,13 +90,13 @@ fn search_entries(config: &config::Config) -> Result<Vec<SearchEntry>, AnyError>
let mail_field = config.get_ldap_mail_field();
let fields = vec!["uid", "givenname", "sn", "cn", mail_field.as_str()];
// Something something error handling
// TODO: Something something error handling
let (results, _res) = ldap
.with_search_options(SearchOptions::new().deref(DerefAliases::Always))
.search(
config.get_ldap_search_base_dn().as_str(),
&config.get_ldap_search_base_dn().as_str(),
Scope::Subtree,
config.get_ldap_search_filter().as_str(),
&config.get_ldap_search_filter().as_str(),
fields,
)
.context("LDAP search failure")?

View File

@ -2,7 +2,7 @@ extern crate reqwest;
extern crate serde;
extern crate thiserror;
use reqwest::blocking::Response;
use reqwest::Response;
use serde::Deserialize;
use std::collections::HashMap;
use std::fs::File;
@ -72,9 +72,8 @@ impl Client {
reqwest::Certificate::from_der(&buf).expect("Could not load DER root cert file")
}
fn get_http_client(&self) -> reqwest::blocking::Client {
let mut client =
reqwest::blocking::Client::builder().redirect(reqwest::redirect::Policy::none());
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();