Compare commits

...

90 Commits

Author SHA1 Message Date
DanCodes 0feac2d904
chore: use ubuntu image to reduce image size (#142)
* chore: use ubuntu image to reduce image size

* fix(lint): hadolint issues
2024-01-17 10:08:37 -08:00
ViViDboarder 5f63df3dee Make sure vaultwarden has an 'object' in the compose files 2024-01-17 09:45:38 -08:00
Ian e80dce8042
Merge pull request #129 from ViViDboarder/dependabot/cargo/serde_json-1.0.95
Bump serde_json from 1.0.91 to 1.0.95
2023-06-14 07:57:28 -07:00
Ian 0a0d9ed69f
Merge pull request #130 from ViViDboarder/dependabot/cargo/anyhow-1.0.70
Bump anyhow from 1.0.68 to 1.0.70
2023-06-14 07:56:50 -07:00
Ian 44179f886a
Merge pull request #123 from ViViDboarder/dependabot/cargo/ldap3-0.11.1
Bump ldap3 from 0.9.4 to 0.11.1
2023-06-14 07:56:06 -07:00
Ian 4b5c9f36c4
Merge pull request #131 from ViViDboarder/dependabot/cargo/thiserror-1.0.40
Bump thiserror from 1.0.38 to 1.0.40
2023-06-14 07:55:39 -07:00
Ian 8e8586bc90
Merge pull request #121 from ViViDboarder/dependabot/github_actions/docker/metadata-action-4
Bump docker/metadata-action from 3 to 4
2023-06-14 07:54:57 -07:00
Ian a96c8d91b8
Merge pull request #120 from ViViDboarder/dependabot/github_actions/docker/build-push-action-4
Bump docker/build-push-action from 3 to 4
2023-06-14 07:54:45 -07:00
Ian 3dda190c3a
Merge pull request #118 from ViViDboarder/dependabot/github_actions/hadolint/hadolint-action-3.1.0
Bump hadolint/hadolint-action from 1.6.0 to 3.1.0
2023-06-14 07:54:05 -07:00
Ian f366b20fef
Merge pull request #119 from ViViDboarder/dependabot/github_actions/pre-commit/action-3.0.0
Bump pre-commit/action from 2.0.3 to 3.0.0
2023-06-14 07:53:38 -07:00
dependabot[bot] 78fd8c4248
Bump thiserror from 1.0.38 to 1.0.40
Bumps [thiserror](https://github.com/dtolnay/thiserror) from 1.0.38 to 1.0.40.
- [Release notes](https://github.com/dtolnay/thiserror/releases)
- [Commits](https://github.com/dtolnay/thiserror/compare/1.0.38...1.0.40)

---
updated-dependencies:
- dependency-name: thiserror
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-04-01 04:58:55 +00:00
dependabot[bot] 10aeba1ce8
Bump anyhow from 1.0.68 to 1.0.70
Bumps [anyhow](https://github.com/dtolnay/anyhow) from 1.0.68 to 1.0.70.
- [Release notes](https://github.com/dtolnay/anyhow/releases)
- [Commits](https://github.com/dtolnay/anyhow/compare/1.0.68...1.0.70)

---
updated-dependencies:
- dependency-name: anyhow
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-04-01 04:58:48 +00:00
dependabot[bot] 6882de79af
Bump serde_json from 1.0.91 to 1.0.95
Bumps [serde_json](https://github.com/serde-rs/json) from 1.0.91 to 1.0.95.
- [Release notes](https://github.com/serde-rs/json/releases)
- [Commits](https://github.com/serde-rs/json/compare/v1.0.91...v1.0.95)

---
updated-dependencies:
- dependency-name: serde_json
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-04-01 04:58:41 +00:00
Ian 5209fb6c8d
Update README.md
Fixes #127
2023-03-30 09:33:47 -07:00
dependabot[bot] 02f6383444
Bump ldap3 from 0.9.4 to 0.11.1
Bumps [ldap3](https://github.com/inejge/ldap3) from 0.9.4 to 0.11.1.
- [Release notes](https://github.com/inejge/ldap3/releases)
- [Changelog](https://github.com/inejge/ldap3/blob/master/CHANGELOG.md)
- [Commits](https://github.com/inejge/ldap3/compare/v0.9.4...v0.11.1)

---
updated-dependencies:
- dependency-name: ldap3
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-02-01 04:01:33 +00:00
dependabot[bot] 8f897713fd
Bump docker/metadata-action from 3 to 4
Bumps [docker/metadata-action](https://github.com/docker/metadata-action) from 3 to 4.
- [Release notes](https://github.com/docker/metadata-action/releases)
- [Upgrade guide](https://github.com/docker/metadata-action/blob/master/UPGRADE.md)
- [Commits](https://github.com/docker/metadata-action/compare/v3...v4)

---
updated-dependencies:
- dependency-name: docker/metadata-action
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-02-01 04:01:10 +00:00
dependabot[bot] fcc02354ca
Bump docker/build-push-action from 3 to 4
Bumps [docker/build-push-action](https://github.com/docker/build-push-action) from 3 to 4.
- [Release notes](https://github.com/docker/build-push-action/releases)
- [Commits](https://github.com/docker/build-push-action/compare/v3...v4)

---
updated-dependencies:
- dependency-name: docker/build-push-action
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-02-01 04:01:05 +00:00
dependabot[bot] 50cc9cf2cb
Bump pre-commit/action from 2.0.3 to 3.0.0
Bumps [pre-commit/action](https://github.com/pre-commit/action) from 2.0.3 to 3.0.0.
- [Release notes](https://github.com/pre-commit/action/releases)
- [Commits](https://github.com/pre-commit/action/compare/v2.0.3...v3.0.0)

---
updated-dependencies:
- dependency-name: pre-commit/action
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-02-01 04:01:01 +00:00
dependabot[bot] e0005fbd38
Bump hadolint/hadolint-action from 1.6.0 to 3.1.0
Bumps [hadolint/hadolint-action](https://github.com/hadolint/hadolint-action) from 1.6.0 to 3.1.0.
- [Release notes](https://github.com/hadolint/hadolint-action/releases)
- [Changelog](https://github.com/hadolint/hadolint-action/blob/master/.releaserc)
- [Commits](https://github.com/hadolint/hadolint-action/compare/v1.6.0...v3.1.0)

---
updated-dependencies:
- dependency-name: hadolint/hadolint-action
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-01-26 00:47:01 +00:00
ViViDboarder 30974696be Update testing instructions and bump 1.0 2023-01-25 16:42:55 -08:00
Ian f7b7f28e77
Merge pull request #117 from ViViDboarder/bump-rust-versions
Upgrade dependencies to replace vulnerable versions
2023-01-25 16:40:54 -08:00
ViViDboarder 1111b2a3df Upgrade dependencies to replace vulnerable versions
This also requried a Rust upgrade.

Verified with `make itest`
2023-01-25 16:13:02 -08:00
Ian 9c6c339dc8
Merge pull request #116 from ViViDboarder/action-dependa-bump
Action dependa bump
2023-01-25 16:03:11 -08:00
ViViDboarder 74c5ec4e72 Remove pinned ca-certs version 2023-01-25 15:58:21 -08:00
ViViDboarder 3faf747817 Merge remote-tracking branch 'origin/dependabot/github_actions/actions/setup-python-4' into action-dependa-bump 2023-01-25 15:53:29 -08:00
ViViDboarder 98a276b644 Merge remote-tracking branch 'origin/dependabot/github_actions/docker/build-push-action-3' into action-dependa-bump 2023-01-25 15:53:01 -08:00
ViViDboarder b735760315 Merge remote-tracking branch 'origin/dependabot/github_actions/docker/login-action-2' into action-dependa-bump 2023-01-25 15:52:44 -08:00
ViViDboarder e4b9c19215 Merge remote-tracking branch 'origin/dependabot/github_actions/actions/checkout-3' into action-dependa-bump 2023-01-25 15:52:15 -08:00
Ian 66f35bff6d
Merge pull request #68 from ViViDboarder/dependabot/cargo/ldap3-0.9.4
Bump ldap3 from 0.9.3 to 0.9.4
2023-01-25 15:47:30 -08:00
dependabot[bot] 11ab2b92c2
Bump actions/setup-python from 2 to 4
Bumps [actions/setup-python](https://github.com/actions/setup-python) from 2 to 4.
- [Release notes](https://github.com/actions/setup-python/releases)
- [Commits](https://github.com/actions/setup-python/compare/v2...v4)

---
updated-dependencies:
- dependency-name: actions/setup-python
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-07-01 04:06:22 +00:00
dependabot[bot] 11a0c3902b
Bump ldap3 from 0.9.3 to 0.9.4
Bumps [ldap3](https://github.com/inejge/ldap3) from 0.9.3 to 0.9.4.
- [Release notes](https://github.com/inejge/ldap3/releases)
- [Changelog](https://github.com/inejge/ldap3/blob/v0.9.4/CHANGELOG.md)
- [Commits](https://github.com/inejge/ldap3/compare/v0.9.3...v0.9.4)

---
updated-dependencies:
- dependency-name: ldap3
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-06-09 04:53:37 +00:00
Ian ce6cb783d0
Merge pull request #82 from epsilon-0/master
add security features on OpenBSD
2022-06-08 21:51:43 -07:00
dependabot[bot] b2dd2d42bc
Bump docker/build-push-action from 2 to 3
Bumps [docker/build-push-action](https://github.com/docker/build-push-action) from 2 to 3.
- [Release notes](https://github.com/docker/build-push-action/releases)
- [Commits](https://github.com/docker/build-push-action/compare/v2...v3)

---
updated-dependencies:
- dependency-name: docker/build-push-action
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-06-01 06:12:22 +00:00
dependabot[bot] 274adaff9d
Bump docker/login-action from 1 to 2
Bumps [docker/login-action](https://github.com/docker/login-action) from 1 to 2.
- [Release notes](https://github.com/docker/login-action/releases)
- [Commits](https://github.com/docker/login-action/compare/v1...v2)

---
updated-dependencies:
- dependency-name: docker/login-action
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-06-01 06:12:17 +00:00
Aisha Tammy 97a64c7247 add security features on OpenBSD
Signed-off-by: Aisha Tammy <floss@bsd.ac>
2022-05-24 16:30:41 -04:00
dependabot[bot] eb204793d3
Bump actions/checkout from 2 to 3
Bumps [actions/checkout](https://github.com/actions/checkout) from 2 to 3.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/v2...v3)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-04-01 04:03:39 +00:00
Ian dd92cc509a
Merge pull request #54 from ViViDboarder/dependabot/cargo/serde-1.0.132
Bump serde from 1.0.131 to 1.0.132
2022-01-20 13:02:58 -08:00
Ian 8ab22b5788
Merge pull request #56 from ViViDboarder/dependabot/cargo/serde_json-1.0.73
Bump serde_json from 1.0.72 to 1.0.73
2022-01-20 13:02:42 -08:00
Ian 4d71304f91
Merge pull request #59 from ViViDboarder/dependabot/cargo/reqwest-0.11.9
Bump reqwest from 0.11.7 to 0.11.9
2022-01-20 13:02:17 -08:00
Ian 7514928afe
Merge pull request #55 from ViViDboarder/dependabot/cargo/anyhow-1.0.52
Bump anyhow from 1.0.51 to 1.0.52
2022-01-20 13:01:55 -08:00
dependabot[bot] ef52691c63
Bump reqwest from 0.11.7 to 0.11.9
Bumps [reqwest](https://github.com/seanmonstar/reqwest) from 0.11.7 to 0.11.9.
- [Release notes](https://github.com/seanmonstar/reqwest/releases)
- [Changelog](https://github.com/seanmonstar/reqwest/blob/master/CHANGELOG.md)
- [Commits](https://github.com/seanmonstar/reqwest/compare/v0.11.7...v0.11.9)

---
updated-dependencies:
- dependency-name: reqwest
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-01-20 20:59:16 +00:00
dependabot[bot] 8992de2e6b
Bump anyhow from 1.0.51 to 1.0.52
Bumps [anyhow](https://github.com/dtolnay/anyhow) from 1.0.51 to 1.0.52.
- [Release notes](https://github.com/dtolnay/anyhow/releases)
- [Commits](https://github.com/dtolnay/anyhow/compare/1.0.51...1.0.52)

---
updated-dependencies:
- dependency-name: anyhow
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-01-20 20:58:19 +00:00
ViViDboarder 5d41a1532f Update ca-certs for alpine 2022-01-20 12:52:59 -08:00
ViViDboarder 13760d7e6f Fix hadolint in github actions 2022-01-20 12:46:32 -08:00
ViViDboarder 71aa764522 Bump cargo versions in prep for release 2022-01-20 12:46:32 -08:00
ViViDboarder 653891102b Update pre-commit hooks 2022-01-20 12:32:10 -08:00
dependabot[bot] 2d5656bd22
Bump serde_json from 1.0.72 to 1.0.73
Bumps [serde_json](https://github.com/serde-rs/json) from 1.0.72 to 1.0.73.
- [Release notes](https://github.com/serde-rs/json/releases)
- [Commits](https://github.com/serde-rs/json/compare/v1.0.72...v1.0.73)

---
updated-dependencies:
- dependency-name: serde_json
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-01-01 04:09:35 +00:00
dependabot[bot] b134df4653
Bump serde from 1.0.131 to 1.0.132
Bumps [serde](https://github.com/serde-rs/serde) from 1.0.131 to 1.0.132.
- [Release notes](https://github.com/serde-rs/serde/releases)
- [Commits](https://github.com/serde-rs/serde/compare/v1.0.131...v1.0.132)

---
updated-dependencies:
- dependency-name: serde
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-01-01 04:09:20 +00:00
dependabot[bot] f1b229ebc3
Merge pull request #45 from ViViDboarder/dependabot/cargo/reqwest-0.11.4 2021-12-11 01:49:36 +00:00
ViViDboarder 829ae70a9b Update reqwest usage for new version
Had to rebuild lock
2021-12-10 16:32:54 -08:00
dependabot[bot] fce9d3064c
Bump reqwest from 0.9.24 to 0.11.4
Bumps [reqwest](https://github.com/seanmonstar/reqwest) from 0.9.24 to 0.11.4.
- [Release notes](https://github.com/seanmonstar/reqwest/releases)
- [Changelog](https://github.com/seanmonstar/reqwest/blob/master/CHANGELOG.md)
- [Commits](https://github.com/seanmonstar/reqwest/compare/v0.9.24...v0.11.4)

---
updated-dependencies:
- dependency-name: reqwest
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-12-10 18:38:37 +00:00
dependabot[bot] 741ece568a
Merge pull request #44 from ViViDboarder/dependabot/cargo/ldap3-0.9.3 2021-12-10 18:37:18 +00:00
dependabot[bot] 4c4112cd82
Merge pull request #49 from ViViDboarder/dependabot/cargo/anyhow-1.0.51 2021-12-10 18:32:48 +00:00
ViViDboarder 3a34f70385 Make ldap mutable to support new version 2021-12-10 10:25:12 -08:00
dependabot[bot] 1c69bd6936
Bump anyhow from 1.0.42 to 1.0.51
Bumps [anyhow](https://github.com/dtolnay/anyhow) from 1.0.42 to 1.0.51.
- [Release notes](https://github.com/dtolnay/anyhow/releases)
- [Commits](https://github.com/dtolnay/anyhow/compare/1.0.42...1.0.51)

---
updated-dependencies:
- dependency-name: anyhow
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-12-10 18:16:09 +00:00
Ian 9e7f4a7820
Merge pull request #48 from ViViDboarder/dependabot/cargo/serde_json-1.0.72
Bump serde_json from 1.0.64 to 1.0.72
2021-12-10 10:15:38 -08:00
Ian 3c83cde044
Merge pull request #46 from ViViDboarder/dependabot/cargo/serde-1.0.131
Bump serde from 1.0.126 to 1.0.131
2021-12-10 10:15:22 -08:00
Ian 07b05844b3
Merge pull request #47 from ViViDboarder/dependabot/cargo/thiserror-1.0.30
Bump thiserror from 1.0.26 to 1.0.30
2021-12-10 10:14:53 -08:00
dependabot[bot] 0c2c2eae0d
Bump serde_json from 1.0.64 to 1.0.72
Bumps [serde_json](https://github.com/serde-rs/json) from 1.0.64 to 1.0.72.
- [Release notes](https://github.com/serde-rs/json/releases)
- [Commits](https://github.com/serde-rs/json/compare/v1.0.64...v1.0.72)

---
updated-dependencies:
- dependency-name: serde_json
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-12-10 18:09:13 +00:00
Ian bc390527bd
Merge pull request #43 from ViViDboarder/dependabot/github_actions/pre-commit/action-2.0.3
Bump pre-commit/action from 2.0.2 to 2.0.3
2021-12-10 10:09:13 -08:00
dependabot[bot] d9468b0105
Bump thiserror from 1.0.26 to 1.0.30
Bumps [thiserror](https://github.com/dtolnay/thiserror) from 1.0.26 to 1.0.30.
- [Release notes](https://github.com/dtolnay/thiserror/releases)
- [Commits](https://github.com/dtolnay/thiserror/compare/1.0.26...1.0.30)

---
updated-dependencies:
- dependency-name: thiserror
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-12-10 18:09:09 +00:00
dependabot[bot] d29d7ffb07
Bump serde from 1.0.126 to 1.0.131
Bumps [serde](https://github.com/serde-rs/serde) from 1.0.126 to 1.0.131.
- [Release notes](https://github.com/serde-rs/serde/releases)
- [Commits](https://github.com/serde-rs/serde/compare/v1.0.126...v1.0.131)

---
updated-dependencies:
- dependency-name: serde
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-12-10 18:09:07 +00:00
dependabot[bot] ade0192e11
Bump ldap3 from 0.6.1 to 0.9.3
Bumps [ldap3](https://github.com/inejge/ldap3) from 0.6.1 to 0.9.3.
- [Release notes](https://github.com/inejge/ldap3/releases)
- [Changelog](https://github.com/inejge/ldap3/blob/master/CHANGELOG.md)
- [Commits](https://github.com/inejge/ldap3/compare/v0.6.1...v0.9.3)

---
updated-dependencies:
- dependency-name: ldap3
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-12-10 18:08:56 +00:00
dependabot[bot] 40113a71d4
Bump pre-commit/action from 2.0.2 to 2.0.3
Bumps [pre-commit/action](https://github.com/pre-commit/action) from 2.0.2 to 2.0.3.
- [Release notes](https://github.com/pre-commit/action/releases)
- [Commits](https://github.com/pre-commit/action/compare/v2.0.2...v2.0.3)

---
updated-dependencies:
- dependency-name: pre-commit/action
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-12-10 18:08:10 +00:00
Ian 55e3536c70
Merge pull request #42 from GoliathLabs/dependabot
Added: dependabot.yml
2021-12-10 10:07:51 -08:00
GoliathLabs d21c835411
Added: newline 2021-12-10 18:12:20 +01:00
GoliathLabs 0d49839189
Added: dependabot.yml 2021-12-10 18:10:47 +01:00
ViViDboarder 60699e6f5a Ignore consecutive run statement in hadolint 2021-11-29 10:47:33 -08:00
ViViDboarder 85511d8dc9 Update rust build to 1.56.1 and alpine rust version to 1.51.0 2021-11-29 10:22:52 -08:00
ViViDboarder db03a5299f Fix new linting error (thanks clippy!) 2021-09-20 17:01:52 -07:00
ViViDboarder 96d7483d31 More descriptive errors when failing to parse a config file 2021-09-20 17:01:27 -07:00
ViViDboarder 94d82a827f Bump version to v0.6.1 2021-07-23 15:53:23 -07:00
ViViDboarder 21a50a1ee0 Fix alpine build 2021-07-23 15:52:34 -07:00
ViViDboarder 5682eab8dc Update Readme to describe how to configure using env variables 2021-07-23 15:33:43 -07:00
ViViDboarder 556a8edbe7 Bump release v0.6.0 2021-07-23 15:29:49 -07:00
ViViDboarder 0ea5eadd71 Make running integration tests easier 2021-07-23 15:28:19 -07:00
ViViDboarder 5fd947f127 Clean up of env config PR 2021-07-23 15:27:45 -07:00
Tobias Florek f25278ea3c implement loading config from environment
This still has the bug, that envy conflicts with serde's
deny_unknown_fields option.

For the time being use e.g.
```
env -i VAULTWARDEN_URL=https://example.com vaultwarden_ldap
```
2021-07-23 15:09:30 -07:00
ViViDboarder 5238bc1b60 Slim final image
Using an intermediate image for building. Could possibly slim further by making the `RUN_TAG` a slim variant
2021-06-21 13:38:36 -07:00
ViViDboarder 08dcdd2bba Bump minor version 2021-06-13 20:22:30 -07:00
ViViDboarder a1a69617e3 Use correct image name 2021-06-13 20:00:15 -07:00
ViViDboarder 80a16664e8 Update Dockerfile.alpine to pass hadolint 2021-06-13 19:50:05 -07:00
ViViDboarder 13e0775d56 Add GitHub Actions
Using GHA for builds rather than Docker Hub
2021-06-13 19:07:02 -07:00
ViViDboarder 77919aa3d4 Improved error handling
Massive refactor of error handling to make messages easier to debug
2021-06-13 18:09:43 -07:00
Ian c090fb5a52
Merge pull request #32 from ibotty/log-uid
also log uid when email not found
2021-05-20 17:34:17 -07:00
ViViDboarder 231f9036fb Add --build flag to example compose 2021-05-20 17:33:30 -07:00
ViViDboarder adf460c05e Add some additional configs for easier testing 2021-05-20 17:29:45 -07:00
Tobias Florek 8be4057e3b also log uid when email not found 2021-05-20 09:09:50 +02:00
ViViDboarder 2f6ecc6860 Bump version for vaultwarden release 2021-05-07 12:58:48 -07:00
ViViDboarder a0bb9152ee Rebrand vaultwarden 2021-05-07 12:55:29 -07:00
19 changed files with 1414 additions and 1472 deletions

22
.github/dependabot.yml vendored Normal file
View File

@ -0,0 +1,22 @@
# 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]

35
.github/workflows/cargo.yml vendored Normal file
View File

@ -0,0 +1,35 @@
---
name: Tests
"on":
push:
branches: [master]
pull_request:
branches: [master]
env:
CARGO_TERM_COLOR: always
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Build
run: cargo build --verbose
- name: Run tests
run: cargo test --verbose
- uses: actions/setup-python@v4
- 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

56
.github/workflows/docker.yml vendored Normal file
View File

@ -0,0 +1,56 @@
---
name: Docker
"on":
push:
branches:
- master
tags:
- 'v*'
pull_request:
branches:
- master
jobs:
docker:
strategy:
matrix:
include:
- dockerfile: Dockerfile
latest: "auto"
suffix: ""
- dockerfile: Dockerfile.alpine
latest: "false"
suffix: "-alpine"
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Docker meta
id: meta
uses: docker/metadata-action@v4
with:
images: vividboarder/vaultwarden_ldap
flavor: |
latest=${{ matrix.latest }}
suffix=${{ matrix.suffix }}
tags: |
type=ref,event=branch
type=ref,event=pr
type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}}
- name: Login to DockerHub
if: github.event_name != 'pull_request'
uses: docker/login-action@v2
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Build and push
uses: docker/build-push-action@v4
with:
context: .
file: ${{ matrix.dockerfile }}
push: ${{ github.event_name != 'pull_request' }}
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}

View File

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

1910
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -1,12 +1,17 @@
[package]
name = "bitwarden_rs_ldap"
version = "0.3.1"
name = "vaultwarden_ldap"
version = "1.0.0"
authors = ["ViViDboarder <vividboarder@gmail.com>"]
edition = "2018"
[dependencies]
ldap3 = "0.6"
ldap3 = "0.11"
serde = { version = "1.0", features = ["derive"] }
toml = "0.5"
reqwest = "0.9"
reqwest = { version = "0.11", features = ["json", "blocking"] }
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,17 +1,26 @@
FROM rust:1.46
ARG BUILD_TAG=1.57.0
ARG RUN_TAG=$BUILD_TAG
FROM rust:$BUILD_TAG as builder
WORKDIR /usr/src/
RUN USER=root cargo new --bin bitwarden_rs_ldap
WORKDIR /usr/src/bitwarden_rs_ldap
RUN USER=root cargo new --bin vaultwarden_ldap
WORKDIR /usr/src/vaultwarden_ldap
# Compile dependencies
COPY Cargo.toml Cargo.lock ./
RUN cargo build --locked --release
# Remove bins to make sure we rebuild
RUN rm ./target/release/deps/bitwarden_rs_ldap*
# hadolint ignore=DL3059
RUN rm ./target/release/deps/vaultwarden_ldap*
# Copy source and install
COPY src ./src
RUN cargo install --path .
RUN cargo build --release
CMD ["bitwarden_rs_ldap"]
FROM ubuntu:focal
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,23 +1,23 @@
FROM ekidd/rust-musl-builder:1.46.0 AS builder
FROM ekidd/rust-musl-builder:1.57.0 AS builder
WORKDIR /home/rust/src
# Cache build deps
RUN USER=rust cargo init
COPY Cargo.toml Cargo.lock ./
RUN cargo build --locked --release
COPY --chown=rust:rust Cargo.toml Cargo.lock ./
RUN cargo build --locked --release && \
rm src/*.rs
# Remove temp src
RUN rm src/*.rs
# Remove bins to make sure we rebuild
# RUN rm ./target/release/deps/bitwarden_rs_ldap*
COPY --chown=rust:rust ./src ./src
RUN touch ./src/main.rs
RUN USER=rust touch ./src/main.rs
# hadolint ignore=DL3059
RUN cargo build --release
FROM alpine:3
RUN apk --no-cache add ca-certificates=20191127-r5
# hadolint ignore=DL3018
RUN apk --no-cache add ca-certificates
COPY --from=builder \
/home/rust/src/target/x86_64-unknown-linux-musl/release/bitwarden_rs_ldap \
/home/rust/src/target/x86_64-unknown-linux-musl/release/vaultwarden_ldap \
/usr/local/bin/
CMD ["/usr/local/bin/bitwarden_rs_ldap"]
CMD ["/usr/local/bin/vaultwarden_ldap"]

View File

@ -1,4 +1,4 @@
DOCKER_TAG ?= bitwarden_rs_ldap_${USER}
DOCKER_TAG ?= vaultwarden_ldap_${USER}
.PHONY: all
all: test check release
@ -7,29 +7,64 @@ all: test check release
.DEFAULT_GOAL = test
# Build debug version
target/debug/bitwarden_rs_ldap: src/
target/debug/vaultwarden_ldap: src/
cargo build
# Build release version
target/release/bitwarden_rs_ldap: src/
target/release/vaultwarden_ldap: src/
cargo build --locked --release
.PHONY: build
build: debug
.PHONY: debug
debug: target/debug/bitwarden_rs_ldap
debug: target/debug/vaultwarden_ldap
.PHONY: release
release: target/release/bitwarden_rs_ldap
release: target/release/vaultwarden_ldap
# Run debug version
.PHONY: run-debug
run-debug: target/debug/bitwarden_rs_ldap
target/debug/bitwarden_rs_ldap
run-debug: target/debug/vaultwarden_ldap
target/debug/vaultwarden_ldap
# Run all tests
.PHONY: test
test:
cargo test
# Run bootstrapped integration test
.PHONY: itest
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
# 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
.PHONY: clean-itest
clean-itest:
docker-compose down -v
# Installs pre-commit hooks
.PHONY: install-hooks
install-hooks:
@ -49,6 +84,9 @@ check-version:
clean:
rm -f ./target
.PHONY: docker-build-all
docker-build-all: docker-build docker-build-alpine
.PHONY: docker-build
docker-build:
docker build -f ./Dockerfile -t $(DOCKER_TAG) .

View File

@ -1,11 +1,11 @@
# bitwarden_rs_ldap
A simple LDAP connector for [bitwarden_rs](https://github.com/dani-garcia/bitwarden_rs)
# vaultwarden_ldap
LDAP user invites for [vaultwarden](https://github.com/dani-garcia/vaultwarden)
After configuring, run `bitwarden_rs_ldap` and it will invite any users it finds in LDAP to your `bitwarden_rs` instance.
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/).
## Deploying
This is easiest done using Docker. See the `docker-compose.yml` file in this repo for an example. If you would like to use Docker Hub rather than building, change `build: .` to `image: vividboarder/bitwarden_rs_ldap`.
This is easiest done using Docker. See the `docker-compose.yml` file in this repo for an example. If you would like to use Docker Hub rather than building, change `build: .` to `image: vividboarder/vaultwarden_ldap`.
Make sure to populate and mount your `config.toml`!
@ -17,9 +17,9 @@ Configuration values are as follows:
|Name|Type|Optional|Description|
|----|----|--------|-----------|
|`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_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`|
|`vaultwarden_url`|String||The root URL for accessing `vaultwarden`. Eg: `https://vw.example.com`|
|`vaultwarden_admin_token`|String||The value passed as `ADMIN_TOKEN` to `vaultwarden`|
|`vaultwarden_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_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 and if we should connect with `ldaps`. Defaults to `false`|
@ -34,6 +34,8 @@ 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`.
@ -42,10 +44,21 @@ For those less familiar with `cargo`, you can use the `make` targets that have b
## Testing
All testing is manual right now. First step is to set up Bitwarden and the LDAP server.
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.
```bash
docker-compose up -d bitwarden ldap ldap_admin
docker-compose up -d vaultwarden ldap ldap_admin
```
1. After that, open the admin portal on http://localhost:8001 and log in using the default account info:
@ -69,7 +82,6 @@ From there you can set up your test group and users.
docker-compose up ldap_sync
```
## Future
Alternately, you can bootstrap some of this by running:
* Any kind of proper logging
* Tests
docker-compose -f docker-compose.yml -f itest/docker-compose.itest.yml up --build

View File

@ -4,29 +4,30 @@ services:
ldap_sync:
build:
context: .
dockerfile: Dockerfile.alpine
dockerfile: Dockerfile
volumes:
- ./example.config.toml:/config.toml:ro
# ./root.cert:/usr/src/bitwarden_rs_ldap/root.cert:ro
# ./root.cert:/usr/src/vaultwarden_ldap/root.cert:ro
environment:
CONFIG_PATH: /config.toml
RUST_BACKTRACE: 1
depends_on:
- vaultwarden
- ldap
restart: always
bitwarden:
image: bitwardenrs/server
vaultwarden:
image: vaultwarden/server
ports:
- 8000:80
environment:
ADMIN_TOKEN: admin
SIGNUPS_ALLOWED: 'false'
INVITATIONS_ALLOWED: 'true'
I_REALLY_WANT_VOLATILE_STORAGE: 'true'
ldap:
image: osixia/openldap
ports:
- 389:389
- 636:636
volumes:
- /var/lib/ldap
- /etc/ldap/slapd.d
@ -42,3 +43,5 @@ services:
environment:
PHPLDAPADMIN_HTTPS: 'false'
PHPLDAPADMIN_LDAP_HOSTS: ldap
depends_on:
- ldap

View File

@ -1,5 +1,5 @@
bitwarden_url = "http://bitwarden:80"
bitwarden_admin_token = "admin"
vaultwarden_url = "http://vaultwarden:80"
vaultwarden_admin_token = "admin"
ldap_host = "ldap"
ldap_bind_dn = "cn=admin,dc=example,dc=org"
ldap_bind_password = "admin"

53
itest/50-seed-user.ldif Normal file
View File

@ -0,0 +1,53 @@
# LDIF Export for cn=Users,dc=example,dc=org
# Server: ldap (ldap)
# Search Scope: sub
# Search Filter: (objectClass=*)
# Total Entries: 2
#
# Generated by phpLDAPadmin (http://phpldapadmin.sourceforge.net) on May 4, 2021 6:06 pm
# Version: 1.2.5
version: 1
# Entry 1: cn=Users,dc=example,dc=org
dn: cn=Users,dc=example,dc=org
cn: Users
gidnumber: 500
objectclass: posixGroup
objectclass: top
# Entry 2: cn=Someone,cn=Users,dc=example,dc=org
dn: cn=Someone,cn=Users,dc=example,dc=org
cn: Someone
gidnumber: 500
homedirectory: /home/users/someone
mail: test@example.com
objectclass: inetOrgPerson
objectclass: posixAccount
objectclass: top
sn: Someone
uid: someone
uidnumber: 1000
# Entry 3: cn=SomeoneNoEmail,cn=Users,dc=example,dc=org
dn: cn=SomeoneNoEmail,cn=Users,dc=example,dc=org
cn: SomeoneNoEmail
gidnumber: 500
homedirectory: /home/users/someonenoemail
objectclass: inetOrgPerson
objectclass: posixAccount
objectclass: top
sn: SomeoneNoEmail
uid: someonenoemail
uidnumber: 1001
# Entry 4: cn=SomeoneNoEmailNoUid,cn=Users,dc=example,dc=org
# dn: cn=SomeoneNoEmailNoUid,cn=Users,dc=example,dc=org
# cn: SomeoneNoEmailNoUid
# gidnumber: 500
# homedirectory: /home/users/someonenoemailnoUid
# objectclass: inetOrgPerson
# objectclass: posixAccount
# objectclass: top
# sn: SomeoneNoEmail
# uidnumber: 1002

View File

@ -0,0 +1,21 @@
---
version: '3'
services:
ldap_sync:
environment:
CONFIG_PATH: ""
APP_VAULTWARDEN_URL: "http://vaultwarden:80"
APP_VAULTWARDEN_ADMIN_TOKEN: "admin"
APP_LDAP_HOST: "ldap"
APP_LDAP_BIND_DN: "cn=admin,dc=example,dc=org"
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"
vaultwarden: {}
ldap:
command: ["--copy-service"]
volumes:
- ./itest/50-seed-user.ldif:/container/service/slapd/assets/config/bootstrap/ldif/custom/50-seed-user.ldif

View File

@ -0,0 +1,13 @@
---
version: '3'
services:
ldap_sync:
volumes:
- ./itest/config.toml:/config.toml:ro
vaultwarden: {}
ldap:
command: ["--copy-service"]
volumes:
- ./itest/50-seed-user.ldif:/container/service/slapd/assets/config/bootstrap/ldif/custom/50-seed-user.ldif

View File

@ -1,182 +0,0 @@
extern crate reqwest;
extern crate serde;
use reqwest::Response;
use serde::Deserialize;
use std::collections::HashMap;
use std::error::Error;
use std::fs::File;
use std::io::Read;
use std::time::{Duration, Instant};
const COOKIE_LIFESPAN: Duration = Duration::from_secs(20 * 60);
#[derive(Debug, Deserialize)]
pub struct User {
#[serde(rename = "Email")]
email: String,
#[serde(rename = "_Status")]
status: i32,
}
impl User {
pub fn get_email(&self) -> String {
self.email.clone()
}
pub fn is_disabled(&self) -> bool {
// HACK: Magic number
self.status == 2
}
}
pub struct Client {
url: String,
admin_token: String,
root_cert_file: String,
cookie: Option<String>,
cookie_created: Option<Instant>,
}
impl Client {
/// Create new instance of client
pub fn new(url: String, admin_token: String, root_cert_file: String) -> Client {
Client {
url,
admin_token,
root_cert_file,
cookie: 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
fn auth(&mut self) -> Response {
let cookie_created = Instant::now();
let client = self.get_http_client();
let result = client
.post(format!("{}{}", &self.url, "/admin/").as_str())
.form(&[("token", &self.admin_token)])
.send()
.unwrap_or_else(|e| {
panic!("Could not authenticate with {}. {:?}", &self.url, e);
});
// TODO: Handle error statuses
if let Some(cookie) = result.headers().get(reqwest::header::SET_COOKIE) {
self.cookie = cookie.to_str().map(|s| String::from(s)).ok();
self.cookie_created = Some(cookie_created);
} else {
panic!("Could not authenticate.")
}
result
}
/// Ensure that the client has a current auth cookie
fn ensure_auth(&mut self) {
match &self.cookie {
Some(_) => {
if self
.cookie_created
.map_or(true, |created| (created.elapsed() >= COOKIE_LIFESPAN))
{
self.auth();
}
}
None => {
self.auth();
}
};
// TODO: handle errors
}
/// Make an authenticated GET to Bitwarden Admin
fn get(&mut self, path: &str) -> Response {
self.ensure_auth();
match &self.cookie {
None => {
panic!("We haven't authenticated. Must be an error");
}
Some(cookie) => {
let url = format!("{}/admin{}", &self.url, path);
let client = self.get_http_client();
let request = client
.get(url.as_str())
.header(reqwest::header::COOKIE, cookie.clone());
let response = request.send().unwrap_or_else(|e| {
panic!("Could not call with {}. {:?}", url, e);
});
// TODO: Handle error statuses
return response;
}
}
}
/// Make authenticated POST to Bitwarden Admin with JSON data
fn post(&mut self, path: &str, json: &HashMap<String, String>) -> Response {
self.ensure_auth();
match &self.cookie {
None => {
panic!("We haven't authenticated. Must be an error");
}
Some(cookie) => {
let url = format!("{}/admin{}", &self.url, path);
let client = self.get_http_client();
let request = client
.post(url.as_str())
.header("Cookie", cookie.clone())
.json(&json);
let response = request.send().unwrap_or_else(|e| {
panic!("Could not call with {}. {:?}", url, e);
});
// TODO: Handle error statuses
return response;
}
}
}
/// Invite user with provided email
pub fn invite(&mut self, email: &str) -> Response {
let mut json = HashMap::new();
json.insert("email".to_string(), email.to_string());
self.post("/invite", &json)
}
/// Get all existing users
pub fn users(&mut self) -> Result<Vec<User>, Box<dyn Error>> {
let all_users: Vec<User> = self.get("/users").json()?;
Ok(all_users)
}
}

View File

@ -17,18 +17,41 @@ pub fn get_config_path() -> String {
}
}
/// Reads configuration from file and panics if it can't
// Tries to read configuration from file, failing that from the environment,
// panics if it can't
pub fn read_config() -> Config {
match read_config_from_file() {
Ok(config) => config,
Err(err) => {
println!("{}", err);
match read_config_from_env() {
Ok(config) => config,
Err(err) => panic!("{}", err),
}
}
}
}
/// Tries to read configuration from file
pub fn read_config_from_file() -> Result<Config, String> {
let config_path = get_config_path();
let contents = fs::read_to_string(&config_path).unwrap_or_else(|_| {
panic!("Failed to open config file at {}", config_path);
});
let config: Config = toml::from_str(contents.as_str()).unwrap_or_else(|_| {
panic!("Failed to parse config file at {}", config_path);
});
let contents = fs::read_to_string(&config_path)
.map_err(|err| format!("Failed to open config file at {}: {}", config_path, err))?;
let config: Config = toml::from_str(contents.as_str())
.map_err(|err| format!("Failed to parse config file at {}: {}", config_path, err))?;
config
println!("Config read from file at {}", config_path);
Ok(config)
}
// Tries to read configuration from environment
pub fn read_config_from_env() -> Result<Config, String> {
let config = envy::prefixed("APP_")
.from_env()
.map_err(|err| format!("Error parsing config from env: {}", err))?;
println!("Config read from environment");
Ok(config)
}
#[derive(Deserialize)]
@ -36,9 +59,9 @@ pub fn read_config() -> Config {
/// Contains all config values for LDAP syncing
pub struct Config {
// Bitwarden connection config
bitwarden_url: String,
bitwarden_admin_token: String,
bitwarden_root_cert_file: Option<String>,
vaultwarden_url: String,
vaultwarden_admin_token: String,
vaultwarden_root_cert_file: Option<String>,
// LDAP Connection config
ldap_host: String,
ldap_scheme: Option<String>,
@ -66,17 +89,17 @@ impl Config {
read_config()
}
pub fn get_bitwarden_url(&self) -> String {
self.bitwarden_url.clone()
pub fn get_vaultwarden_url(&self) -> String {
self.vaultwarden_url.clone()
}
pub fn get_bitwarden_admin_token(&self) -> String {
self.bitwarden_admin_token.clone()
pub fn get_vaultwarden_admin_token(&self) -> String {
self.vaultwarden_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(),
pub fn get_vaultwarden_root_cert_file(&self) -> String {
match &self.vaultwarden_root_cert_file {
Some(vaultwarden_root_cert_file) => vaultwarden_root_cert_file.clone(),
None => String::new(),
}
}
@ -151,7 +174,7 @@ impl Config {
pub fn get_ldap_mail_field(&self) -> String {
match &self.ldap_mail_field {
Some(mail_field) => mail_field.clone(),
None => String::from("mail").clone(),
None => String::from("mail"),
}
}

View File

@ -1,46 +1,57 @@
extern crate anyhow;
extern crate ldap3;
extern crate pledge;
extern crate unveil;
use std::collections::HashSet;
use std::error::Error;
use std::thread::sleep;
use std::time::Duration;
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 bw_admin;
mod config;
mod vw_admin;
fn main() {
let config = config::Config::from_file();
let mut client = bw_admin::Client::new(
config.get_bitwarden_url().clone(),
config.get_bitwarden_admin_token().clone(),
config.get_bitwarden_root_cert_file().clone(),
let mut client = vw_admin::Client::new(
config.get_vaultwarden_url(),
config.get_vaultwarden_admin_token(),
config.get_vaultwarden_root_cert_file(),
);
if let Err(e) = invite_users(&config, &mut client, config.get_ldap_sync_loop()) {
panic!("{}", e);
}
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())
}
/// Invites new users to Bitwarden from LDAP
fn invite_users(
config: &config::Config,
client: &mut bw_admin::Client,
start_loop: bool,
) -> Result<(), Box<dyn Error>> {
fn invite_users(config: &config::Config, client: &mut vw_admin::Client, start_loop: bool) {
if start_loop {
start_sync_loop(config, client)?;
start_sync_loop(config, client).expect("Failed to start invite sync loop");
} else {
invite_from_ldap(config, client)?;
invite_from_ldap(config, client).expect("Failed to invite users");
}
Ok(())
}
/// Creates set of email addresses for users that already exist in Bitwarden
fn get_existing_users(client: &mut bw_admin::Client) -> Result<HashSet<String>, Box<dyn Error>> {
let all_users = client.users()?;
fn get_existing_users(client: &mut vw_admin::Client) -> Result<HashSet<String>, AnyError> {
let all_users = client
.users()
.context("Could not get list of existing users from server")?;
let mut user_emails = HashSet::with_capacity(all_users.len());
for user in all_users {
user_emails.insert(user.get_email().to_lowercase());
@ -67,45 +78,44 @@ fn ldap_client(
bind_pw: String,
no_tls_verify: bool,
starttls: bool,
) -> Result<LdapConn, Box<dyn Error>> {
) -> Result<LdapConn, AnyError> {
let settings = LdapConnSettings::new()
.set_starttls(starttls)
.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()) {
_ => {}
};
let mut 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")?;
Ok(ldap)
}
/// Retrieves search results from ldap
fn search_entries(config: &config::Config) -> Result<Vec<SearchEntry>, Box<dyn Error>> {
let ldap = ldap_client(
fn search_entries(config: &config::Config) -> Result<Vec<SearchEntry>, AnyError> {
let mut ldap = ldap_client(
config.get_ldap_url(),
config.get_ldap_bind_dn(),
config.get_ldap_bind_password(),
config.get_ldap_no_tls_verify(),
config.get_ldap_starttls(),
);
if ldap.is_err() {
println!("Error: Could not bind to ldap server");
}
)
.context("LDAP client initialization failed")?;
let mail_field = config.get_ldap_mail_field();
let fields = vec!["uid", "givenname", "sn", "cn", mail_field.as_str()];
// TODO: Something something error handling
let (results, _res) = ldap?
// 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,
)?
.success()?;
)
.context("LDAP search failure")?
.success()
.context("LDAP search usucessful")?;
// Build list of entries
let mut entries = Vec::new();
@ -119,53 +129,66 @@ fn search_entries(config: &config::Config) -> Result<Vec<SearchEntry>, Box<dyn E
/// Invite all LDAP users to Bitwarden
fn invite_from_ldap(
config: &config::Config,
client: &mut bw_admin::Client,
) -> Result<(), Box<dyn Error>> {
match get_existing_users(client) {
Ok(existing_users) => {
let mail_field = config.get_ldap_mail_field();
let mut num_users = 0;
for ldap_user in search_entries(config)? {
// Safely get first email from list of emails in field
if let Some(user_email) = ldap_user
.attrs
.get(mail_field.as_str())
.and_then(|l| (l.first()))
{
if existing_users.contains(&user_email.to_lowercase()) {
println!("User with email already exists: {}", user_email);
} else {
println!("Try to invite user: {}", user_email);
// TODO: Validate response
let _response = client.invite(user_email);
num_users = num_users + 1;
// println!("Invite response: {:?}", response);
}
} else {
println!("Warning: Email field, {:?}, not found on user", mail_field);
}
}
client: &mut vw_admin::Client,
) -> Result<(), AnyError> {
let existing_users =
get_existing_users(client).context("Failed to get existing users from server")?;
let mail_field = config.get_ldap_mail_field();
let mut num_users = 0;
// Maybe think about returning this value for some other use
println!("Sent invites to {} user(s).", num_users);
}
Err(e) => {
println!("Error: Failed to get existing users from Bitwarden");
return Err(e);
for ldap_user in search_entries(config)? {
//
// Safely get first email from list of emails in field
if let Some(user_email) = ldap_user
.attrs
.get(mail_field.as_str())
.and_then(|l| (l.first()))
{
if existing_users.contains(&user_email.to_lowercase()) {
println!("User with email already exists: {}", user_email);
} else {
println!("Try to invite user: {}", user_email);
client
.invite(user_email)
.context(format!("Failed to invite user {}", user_email))?;
num_users += 1;
}
} else {
match ldap_user.attrs.get("uid").and_then(|l| l.first()) {
Some(user_uid) => println!(
"Warning: Email field, {:?}, not found on user {}",
mail_field, user_uid
),
None => println!("Warning: Email field, {:?}, not found on user", mail_field),
}
}
}
// Maybe think about returning this value for some other use
println!("Sent invites to {} user(s).", num_users);
Ok(())
}
/// Begin sync loop to invite LDAP users to Bitwarden
fn start_sync_loop(
config: &config::Config,
client: &mut bw_admin::Client,
) -> Result<(), Box<dyn Error>> {
fn start_sync_loop(config: &config::Config, client: &mut vw_admin::Client) -> Result<(), AnyError> {
let interval = Duration::from_secs(config.get_ldap_sync_interval_seconds());
let mut fail_count = 0;
let fail_limit = 5;
loop {
invite_from_ldap(config, client)?;
if let Err(err) = invite_from_ldap(config, client) {
println!(
"Error inviting users from ldap. Count {}: {:?}",
fail_count, err
);
fail_count += 1;
if fail_count > fail_limit {
return Err(err);
}
} else {
fail_count = 0
}
sleep(interval);
}
}

186
src/vw_admin.rs Normal file
View File

@ -0,0 +1,186 @@
extern crate reqwest;
extern crate serde;
extern crate thiserror;
use reqwest::blocking::Response;
use serde::Deserialize;
use std::collections::HashMap;
use std::fs::File;
use std::io::Read;
use std::time::{Duration, Instant};
use thiserror::Error;
const COOKIE_LIFESPAN: Duration = Duration::from_secs(20 * 60);
#[derive(Error, Debug)]
pub enum ResponseError {
#[error("vaultwarden error {0}")]
ApiError(String),
#[error("http error making request {0:?}")]
HttpError(#[from] reqwest::Error),
}
#[derive(Debug, Deserialize)]
pub struct User {
#[serde(rename = "Email")]
email: String,
#[serde(rename = "_Status")]
status: i32,
}
impl User {
pub fn get_email(&self) -> String {
self.email.clone()
}
pub fn is_disabled(&self) -> bool {
// HACK: Magic number
self.status == 2
}
}
pub struct Client {
url: String,
admin_token: String,
root_cert_file: String,
cookie: Option<String>,
cookie_created: Option<Instant>,
}
impl Client {
/// Create new instance of client
pub fn new(url: String, admin_token: String, root_cert_file: String) -> Client {
Client {
url,
admin_token,
root_cert_file,
cookie: 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");
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());
if !&self.root_cert_file.is_empty() {
let cert = self.get_root_cert();
client = client.add_root_certificate(cert);
}
client.build().expect("Failed to build http client")
}
/// Authenticate client
fn auth(&mut self) -> Result<Response, ResponseError> {
let cookie_created = Instant::now();
let client = self.get_http_client();
let result = client
.post(format!("{}{}", &self.url, "/admin/").as_str())
.form(&[("token", &self.admin_token)])
.send()?
.error_for_status()?;
let cookie = result
.headers()
.get(reqwest::header::SET_COOKIE)
.ok_or_else(|| {
ResponseError::ApiError(String::from("Could not read authentication cookie"))
})?;
self.cookie = cookie.to_str().map(String::from).ok();
self.cookie_created = Some(cookie_created);
Ok(result)
}
fn cookie_expired(&self) -> bool {
match &self.cookie {
Some(_) => self
.cookie_created
.map_or(true, |created| (created.elapsed() >= COOKIE_LIFESPAN)),
None => true,
}
}
/// Ensure that the client has a current auth cookie
fn ensure_auth(&mut self) -> Result<(), ResponseError> {
if self.cookie_expired() {
match self.auth() {
Ok(_) => Ok(()),
Err(err) => Err(err),
}?
}
Ok(())
}
/// Make an authenticated GET to Bitwarden Admin
fn get(&mut self, path: &str) -> Result<Response, ResponseError> {
self.ensure_auth()?;
let url = format!("{}/admin{}", &self.url, path);
let client = self.get_http_client();
let request = client.get(url.as_str()).header(
reqwest::header::COOKIE,
self.cookie
.as_ref()
.expect("No cookie found to add to header")
.clone(),
);
let response = request.send()?.error_for_status()?;
Ok(response)
}
/// Make authenticated POST to Bitwarden Admin with JSON data
fn post(
&mut self,
path: &str,
json: &HashMap<String, String>,
) -> Result<Response, ResponseError> {
self.ensure_auth()?;
let url = format!("{}/admin{}", &self.url, path);
let client = self.get_http_client();
let request = client.post(url.as_str()).json(&json).header(
reqwest::header::COOKIE,
self.cookie
.as_ref()
.expect("No cookie found to add to header")
.clone(),
);
let response = request.send()?.error_for_status()?;
Ok(response)
}
/// Invite user with provided email
pub fn invite(&mut self, email: &str) -> Result<Response, ResponseError> {
let mut json = HashMap::new();
json.insert("email".to_string(), email.to_string());
self.post("/invite", &json)
}
/// Get all existing users
pub fn users(&mut self) -> Result<Vec<User>, ResponseError> {
let all_users: Vec<User> = self.get("/users")?.json()?;
Ok(all_users)
}
}