Compare commits
8 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f417427635 | ||
|
|
1f26262e13 | ||
|
|
42fccf4713 | ||
|
|
928faa4bcc | ||
|
|
3895a5050d | ||
|
|
f92035b6fd | ||
|
|
37a10c871f | ||
|
|
8397d536d9 |
50
.github/workflows/docker-build-static.yml
vendored
50
.github/workflows/docker-build-static.yml
vendored
@@ -216,6 +216,8 @@ jobs:
|
||||
LLDAP_database_url: postgres://lldapuser:lldappass@localhost/lldap
|
||||
LLDAP_ldap_port: 3890
|
||||
LLDAP_http_port: 17170
|
||||
LLDAP_JWT_SECRET: verysecret
|
||||
LLDAP_LDAP_USER_PASS: password
|
||||
|
||||
|
||||
- name: Run lldap with mariadb DB (MySQL Compatible) and healthcheck
|
||||
@@ -227,6 +229,8 @@ jobs:
|
||||
LLDAP_database_url: mysql://lldapuser:lldappass@localhost/lldap
|
||||
LLDAP_ldap_port: 3891
|
||||
LLDAP_http_port: 17171
|
||||
LLDAP_JWT_SECRET: verysecret
|
||||
LLDAP_LDAP_USER_PASS: password
|
||||
|
||||
|
||||
- name: Run lldap with sqlite DB and healthcheck
|
||||
@@ -238,6 +242,8 @@ jobs:
|
||||
LLDAP_database_url: sqlite://users.db?mode=rwc
|
||||
LLDAP_ldap_port: 3892
|
||||
LLDAP_http_port: 17172
|
||||
LLDAP_JWT_SECRET: verysecret
|
||||
LLDAP_LDAP_USER_PASS: password
|
||||
|
||||
- name: Check DB container logs
|
||||
run: |
|
||||
@@ -324,9 +330,9 @@ jobs:
|
||||
sleep 10s
|
||||
bin/lldap healthcheck
|
||||
env:
|
||||
LLDAP_database_url: sqlite://users.db?mode=rwc
|
||||
LLDAP_ldap_port: 3890
|
||||
LLDAP_http_port: 17170
|
||||
LLDAP_DATABASE_URL: sqlite://users.db?mode=rwc
|
||||
LLDAP_LDAP_PORT: 3890
|
||||
LLDAP_HTTP_PORT: 17170
|
||||
LLDAP_LDAP_USER_PASS: ldappass
|
||||
LLDAP_JWT_SECRET: somejwtsecret
|
||||
|
||||
@@ -350,8 +356,11 @@ jobs:
|
||||
sed -i -r -e "s/X'([[:xdigit:]]+'[^'])/'\\\x\\1/g" -e ":a; s/(INSERT INTO (user_attribute_schema|jwt_storage)\(.*\) VALUES\(.*),1([^']*\);)$/\1,true\3/; s/(INSERT INTO (user_attribute_schema|jwt_storage)\(.*\) VALUES\(.*),0([^']*\);)$/\1,false\3/; ta" -e '1s/^/BEGIN;\n/' -e '$aCOMMIT;' ./dump.sql
|
||||
|
||||
- name: Create schema on postgres
|
||||
env:
|
||||
LLDAP_DATABASE_URL: postgres://lldapuser:lldappass@localhost:5432/lldap
|
||||
LLDAP_JWT_SECRET: somejwtsecret
|
||||
run: |
|
||||
bin/lldap create_schema -d postgres://lldapuser:lldappass@localhost:5432/lldap
|
||||
bin/lldap create_schema
|
||||
|
||||
- name: Copy converted db to postgress and import
|
||||
run: |
|
||||
@@ -368,7 +377,10 @@ jobs:
|
||||
sed -i '1 i\SET FOREIGN_KEY_CHECKS = 0;' ./dump.sql
|
||||
|
||||
- name: Create schema on mariadb
|
||||
run: bin/lldap create_schema -d mysql://lldapuser:lldappass@localhost:3306/lldap
|
||||
env:
|
||||
LLDAP_DATABASE_URL: mysql://lldapuser:lldappass@localhost:3306/lldap
|
||||
LLDAP_JWT_SECRET: somejwtsecret
|
||||
run: bin/lldap create_schema
|
||||
|
||||
- name: Copy converted db to mariadb and import
|
||||
run: |
|
||||
@@ -384,7 +396,10 @@ jobs:
|
||||
sed -i '1 i\SET FOREIGN_KEY_CHECKS = 0;' ./dump.sql
|
||||
|
||||
- name: Create schema on mysql
|
||||
run: bin/lldap create_schema -d mysql://lldapuser:lldappass@localhost:3307/lldap
|
||||
env:
|
||||
LLDAP_DATABASE_URL: mysql://lldapuser:lldappass@localhost:3307/lldap
|
||||
LLDAP_JWT_SECRET: somejwtsecret
|
||||
run: bin/lldap create_schema
|
||||
|
||||
- name: Copy converted db to mysql and import
|
||||
run: |
|
||||
@@ -399,10 +414,9 @@ jobs:
|
||||
sleep 10s
|
||||
bin/lldap healthcheck
|
||||
env:
|
||||
LLDAP_database_url: postgres://lldapuser:lldappass@localhost:5432/lldap
|
||||
LLDAP_ldap_port: 3891
|
||||
LLDAP_http_port: 17171
|
||||
LLDAP_LDAP_USER_PASS: ldappass
|
||||
LLDAP_DATABASE_URL: postgres://lldapuser:lldappass@localhost:5432/lldap
|
||||
LLDAP_LDAP_PORT: 3891
|
||||
LLDAP_HTTP_PORT: 17171
|
||||
LLDAP_JWT_SECRET: somejwtsecret
|
||||
|
||||
- name: Run lldap with mariaDB and healthcheck again
|
||||
@@ -411,9 +425,9 @@ jobs:
|
||||
sleep 10s
|
||||
bin/lldap healthcheck
|
||||
env:
|
||||
LLDAP_database_url: mysql://lldapuser:lldappass@localhost:3306/lldap
|
||||
LLDAP_ldap_port: 3892
|
||||
LLDAP_http_port: 17172
|
||||
LLDAP_DATABASE_URL: mysql://lldapuser:lldappass@localhost:3306/lldap
|
||||
LLDAP_LDAP_PORT: 3892
|
||||
LLDAP_HTTP_PORT: 17172
|
||||
LLDAP_JWT_SECRET: somejwtsecret
|
||||
|
||||
- name: Run lldap with mysql and healthcheck again
|
||||
@@ -422,9 +436,9 @@ jobs:
|
||||
sleep 10s
|
||||
bin/lldap healthcheck
|
||||
env:
|
||||
LLDAP_database_url: mysql://lldapuser:lldappass@localhost:3307/lldap
|
||||
LLDAP_ldap_port: 3893
|
||||
LLDAP_http_port: 17173
|
||||
LLDAP_DATABASE_URL: mysql://lldapuser:lldappass@localhost:3307/lldap
|
||||
LLDAP_LDAP_PORT: 3893
|
||||
LLDAP_HTTP_PORT: 17173
|
||||
LLDAP_JWT_SECRET: somejwtsecret
|
||||
|
||||
- name: Test Dummy User Postgres
|
||||
@@ -738,5 +752,9 @@ jobs:
|
||||
artifacts: aarch64-lldap.tar.gz,
|
||||
amd64-lldap.tar.gz,
|
||||
armhf-lldap.tar.gz
|
||||
draft: true
|
||||
omitBodyDuringUpdate: true
|
||||
omitDraftDuringUpdate: true
|
||||
omitNameDuringUpdate: true
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ github.token }}
|
||||
|
||||
4
Cargo.lock
generated
4
Cargo.lock
generated
@@ -2499,7 +2499,7 @@ checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89"
|
||||
|
||||
[[package]]
|
||||
name = "lldap"
|
||||
version = "0.6.1"
|
||||
version = "0.6.2-alpha"
|
||||
dependencies = [
|
||||
"actix",
|
||||
"actix-files",
|
||||
@@ -2575,7 +2575,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "lldap_app"
|
||||
version = "0.6.1"
|
||||
version = "0.6.2-alpha"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"base64 0.13.1",
|
||||
|
||||
30
README.md
30
README.md
@@ -156,6 +156,7 @@ services:
|
||||
- LLDAP_JWT_SECRET=REPLACE_WITH_RANDOM
|
||||
- LLDAP_KEY_SEED=REPLACE_WITH_RANDOM
|
||||
- LLDAP_LDAP_BASE_DN=dc=example,dc=com
|
||||
- LLDAP_LDAP_USER_PASS=adminPas$word
|
||||
# If using LDAPS, set enabled true and configure cert and key path
|
||||
# - LLDAP_LDAPS_OPTIONS__ENABLED=true
|
||||
# - LLDAP_LDAPS_OPTIONS__CERT_FILE=/path/to/certfile.crt
|
||||
@@ -198,32 +199,36 @@ Each package offers a [systemd service](https://wiki.archlinux.org/title/systemd
|
||||
When using the distributed packages, the default login is `admin/password`. You can change that from the web UI after starting the service.
|
||||
|
||||
<details>
|
||||
<summary><b>Arch</b></summary>
|
||||
<summary><b>Arch Linux</b></summary>
|
||||
<br>
|
||||
Arch Linux offers unofficial support through the <a href="https://wiki.archlinux.org/title/Arch_User_Repository">Arch User Repository (AUR)</a>.<br>
|
||||
The package descriptions can be used <a href="https://wiki.archlinux.org/title/Arch_User_Repository#Getting_started">to create and install packages</a>.<br><br>
|
||||
Maintainer: <a href="https://github.com/Zepmann">@Zepmann</a><br>
|
||||
Support: <a href="https://github.com/lldap/lldap/discussions">Discussions</a><br>
|
||||
Package repository: <a href="https://aur.archlinux.org/packages">Arch user repository</a><br>
|
||||
Support: <a href="https://github.com/lldap/lldap/discussions/1044">Discussions</a><br>
|
||||
Package repository: <a href="https://aur.archlinux.org/packages">Arch User Repository</a><br><br>
|
||||
<table>
|
||||
<tr>
|
||||
<td>Available packages:</td>
|
||||
<td>Package name</td>
|
||||
<td>Maintainer</td>
|
||||
<td>Description</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a href="https://aur.archlinux.org/packages/lldap">lldap</a></td>
|
||||
<td><a href="https://github.com/Zepmann">@Zepmann</a></td>
|
||||
<td>Builds the latest stable version.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td></td>
|
||||
<td><a href="https://aur.archlinux.org/packages/lldap-bin">lldap-bin</a></td>
|
||||
<td>Uses the latest pre-compiled binaries from the <a href="https://aur.archlinux.org/packages/lldap-bin">releases in this repository</a>.<br>
|
||||
This package is recommended if you want to run lldap on a system with limited resources.</td>
|
||||
<td><a href="https://github.com/Zepmann">@Zepmann</a></td>
|
||||
<td>Uses the latest pre-compiled binaries from the <a href="https://github.com/lldap/lldap/releases">releases in this repository</a>.<br>
|
||||
This package is recommended if you want to run LLDAP on a system with limited resources.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td></td>
|
||||
<td><a href="https://aur.archlinux.org/packages/lldap-git">lldap-git</a></td>
|
||||
<td></td>
|
||||
<td>Builds the latest main branch code.</td>
|
||||
</tr>
|
||||
</table>
|
||||
LLDPA configuration file: /etc/lldap.toml<br>
|
||||
LLDAP configuration file: /etc/lldap.toml<br>
|
||||
</details>
|
||||
<details>
|
||||
<summary><b>Debian</b></summary>
|
||||
@@ -587,6 +592,7 @@ folder for help with:
|
||||
- [Grafana](example_configs/grafana_ldap_config.toml)
|
||||
- [Grocy](example_configs/grocy.md)
|
||||
- [Harbor](example_configs/harbor.md)
|
||||
- [HashiCorp Vault](example_configs/hashicorp-vault.md)
|
||||
- [Hedgedoc](example_configs/hedgedoc.md)
|
||||
- [Home Assistant](example_configs/home-assistant.md)
|
||||
- [Jellyfin](example_configs/jellyfin.md)
|
||||
@@ -594,6 +600,7 @@ folder for help with:
|
||||
- [Jitsi Meet](example_configs/jitsi_meet.conf)
|
||||
- [Kasm](example_configs/kasm.md)
|
||||
- [KeyCloak](example_configs/keycloak.md)
|
||||
- [Kimai](example_configs/kimai.yaml)
|
||||
- [LibreNMS](example_configs/librenms.md)
|
||||
- [Maddy](example_configs/maddy.md)
|
||||
- [Mastodon](example_configs/mastodon.env.example)
|
||||
@@ -707,6 +714,9 @@ modern identity protocols, check out Kanidm.
|
||||
If you just set up the server, can get to the login page but the password you
|
||||
set isn't working, try the following:
|
||||
|
||||
- If you have changed the admin password in the config after the first run, it
|
||||
won't be used (unless you force its use with `force_ldap_user_pass_reset`).
|
||||
The config password is only for the initial admin creation.
|
||||
- (For docker): Make sure that the `/data` folder is persistent, either to a
|
||||
docker volume or mounted from the host filesystem.
|
||||
- Check if there is a `lldap_config.toml` file (either in `/data` for docker
|
||||
|
||||
@@ -6,7 +6,7 @@ homepage = "https://github.com/lldap/lldap"
|
||||
license = "GPL-3.0-only"
|
||||
name = "lldap_app"
|
||||
repository = "https://github.com/lldap/lldap"
|
||||
version = "0.6.1"
|
||||
version = "0.6.2-alpha"
|
||||
include = ["src/**/*", "queries/**/*", "Cargo.toml", "../schema.graphql"]
|
||||
|
||||
[dependencies]
|
||||
|
||||
77
example_configs/hashicorp-vault.md
Normal file
77
example_configs/hashicorp-vault.md
Normal file
@@ -0,0 +1,77 @@
|
||||
# Configuration for HashiCorp Vault
|
||||
|
||||
Official LDAP configuration documentation is located [here](https://developer.hashicorp.com/vault/docs/auth/ldap).
|
||||
|
||||
**You'll need to authenticate using your root token or as a user who has permission to modify authentication methods!**
|
||||
|
||||
## User Interface
|
||||
|
||||
1. Navigate to `Access -> Authentication Methods`
|
||||
2. Click `Enable new method +` in the top right and choose `LDAP` under `Infra`
|
||||
3. Name the path whatever you want (preferably keep it default) and click `Enable method` at the bottom
|
||||
|
||||
* URL: `ldap://lldap.example.com:3890` or `ldaps://lldap.example.com:6360`
|
||||
* LDAP Options
|
||||
* If you're using LDAPS and your server does not have your LDAPS certificate installed check `Insecure TLS` otherwise leave this unchecked
|
||||
* User Attribute: `uid`
|
||||
* User Principal (UPN) Domain: **LEAVE THIS BLANK**
|
||||
* Customize User Search
|
||||
* Name of Object to bind (binddn): `cn=admin,ou=people,dc=example,dc=com`
|
||||
* User DN: `ou=people,dc=example,dc=com`
|
||||
* Bindpass: `ChangeMe!`
|
||||
* User Search Filter: `(&(uid={{.Username}})(objectClass=person))`
|
||||
* Customize Group Member Search
|
||||
* Group Filter: `(&(member={{.UserDN}})(objectclass=groupOfUniqueNames))`
|
||||
* Group Attribute: `cn`
|
||||
* Group DN: `ou=groups,dc=example,dc=com`
|
||||
|
||||
4. Click `Save` at the bottom
|
||||
5. Click into the auth menthod and then `Create group +` under the `Groups` tab
|
||||
6. Set the name as the group you want users to have to authenticate to HashiCorp Vault
|
||||
7. Set policy as `default` or whatever policy you want to tie to this group
|
||||
8. Click `Save` at the bottom
|
||||
|
||||
As long as your user is in the group you specified, you should now be able to select `LDAP` from the dropdown on the login page and use your credentials.
|
||||
|
||||
## CLI
|
||||
|
||||
**This requires the vault CLI to be installed on your machine**
|
||||
|
||||
1. Set VAULT_ADDR environment variable
|
||||
|
||||
```bash
|
||||
export VAULT_ADDR=https://vault.example.com
|
||||
```
|
||||
2. Login to vault and provide token when prompted
|
||||
|
||||
```bash
|
||||
vault login
|
||||
````
|
||||
3. Enable the LDAP authentication method
|
||||
|
||||
```bash
|
||||
vault auth enable ldap
|
||||
```
|
||||
4. Configure the LDAP authentication method
|
||||
|
||||
```bash
|
||||
vault write auth/ldap/config \
|
||||
url="ldaps://lldaps.example.com:6360" \
|
||||
binddn="cn=admin,ou=people,dc=example,dc=com" \
|
||||
bindpass="ChangeMe!" \
|
||||
userdn="ou=people,dc=example,dc=com" \
|
||||
userfilter="(&(uid={{.Username}})(objectClass=person))" \
|
||||
groupdn="ou=groups,dc=example,dc=com" \
|
||||
groupfilter="(&(member={{.UserDN}})(objectclass=groupOfUniqueNames))" \
|
||||
userattr="uid" \
|
||||
groupattr="cn" \
|
||||
discoverdn=false
|
||||
```
|
||||
If you are using plain LDAP, change the URL accordingly. If you're using LDAPS and your server does not have your LDAPS certificate installed append `insecure_tls=true` to the bottom of the command.
|
||||
5. Add your group to the LDAP configuration and set the policy
|
||||
|
||||
```bash
|
||||
vault write auth/ldap/groups/vault_users policies=default
|
||||
```
|
||||
|
||||
As long as your user is in the group you specified, you should now be able to select `LDAP` from the dropdown on the login page and use your credentials.
|
||||
36
example_configs/kimai.yaml
Normal file
36
example_configs/kimai.yaml
Normal file
@@ -0,0 +1,36 @@
|
||||
# See https://www.kimai.org/documentation/local-yaml.html
|
||||
# this file should be renamed local.yaml
|
||||
kimai:
|
||||
ldap:
|
||||
activate: true
|
||||
connection:
|
||||
host: lldap
|
||||
port: 3890 # 6360 for LDAPS
|
||||
useSsl: false # true for LDAPS
|
||||
useStartTls: false
|
||||
username: uid=admin,ou=people,dc=example,dc=com
|
||||
password: <PASSWORD_HERE>
|
||||
accountFilterFormat: (&(objectClass=person)(uid=%s))
|
||||
bindRequiresDn: true
|
||||
optReferrals: false
|
||||
|
||||
user:
|
||||
baseDn: ou=people, dc=example, dc=com
|
||||
usernameAttribute: uid
|
||||
filter: (&(objectClass=person)) # to filter by group, add (memberof=cn=kimai,ou=groups,dc=example,dc=com) [group 'kimai' must be created in web UI]
|
||||
attributes:
|
||||
- { ldap_attr: "uid", user_method: setUserIdentifier }
|
||||
- { ldap_attr: "mail", user_method: setEmail }
|
||||
- { ldap_attr: "cn", user_method: setAlias }
|
||||
|
||||
role:
|
||||
baseDn: ou=groups, dc=example, dc=com
|
||||
filter: (&(objectClass=groupOfUniqueNames))
|
||||
usernameAttribute: cn
|
||||
nameAttribute: cn
|
||||
userDnAttribute: member
|
||||
|
||||
# Convert LDAP group name (nameAttribute) to Kimai role. Available roles are listed here: https://www.kimai.org/documentation/permissions.html
|
||||
groups:
|
||||
- { ldap_value: lldap_admin, role: ROLE_SUPER_ADMIN }
|
||||
# add additional group mappings here
|
||||
@@ -1,18 +1,18 @@
|
||||
# Configuration for OneDev
|
||||
In Onedev, go to `Administration > Authentication Sources` and click `External Authentication`
|
||||
Select `Generic LDAP`
|
||||
In Onedev, go to `Administration > External Authentication Source > Authenticator` and Select `Generic LDAP`
|
||||
|
||||
* LDAP URL: ldap://lldap_ip_or_hostname:3890 or ldaps://lldap_ip_or_hostname:6360
|
||||
* Authentication Required: On
|
||||
* Manager DN: `uid=admin,ou=people,dc=example,dc=com`
|
||||
* Manager Password: Your bind user's password
|
||||
* User Search Base: `ou=people,dc=example,dc=com`
|
||||
* User Search Filter: `(&(uid={0})(objectclass=person))`
|
||||
* User Full Name Attribute: `displayName`
|
||||
* Email Attribute: mail
|
||||
* User SSH Key Attribute: (Leave Blank)
|
||||
* Group Retrieval: "Search Groups Using Filter"
|
||||
* Group Search Base: `ou=groups,dc=example,dc=com`
|
||||
* Group Search Filter" `(&(uniqueMember={0})(objectclass=groupOfUniqueNames))`
|
||||
* Group Search Filter: `(&(uniqueMember={0})(objectclass=groupOfUniqueNames))`
|
||||
* Group Name Attribute: cn
|
||||
* Create User As Guest: Off
|
||||
* Default Group: "No Default Group"
|
||||
|
||||
@@ -8,7 +8,7 @@ keywords = ["cli", "ldap", "graphql", "server", "authentication"]
|
||||
license = "GPL-3.0-only"
|
||||
name = "lldap"
|
||||
repository = "https://github.com/lldap/lldap"
|
||||
version = "0.6.1"
|
||||
version = "0.6.2-alpha"
|
||||
|
||||
[dependencies]
|
||||
actix = "0.13"
|
||||
|
||||
@@ -314,8 +314,8 @@ pub struct UserRestrictedListerBackendHandler<'a, Handler> {
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl<'a, Handler: ReadSchemaBackendHandler + Sync> ReadSchemaBackendHandler
|
||||
for UserRestrictedListerBackendHandler<'a, Handler>
|
||||
impl<Handler: ReadSchemaBackendHandler + Sync> ReadSchemaBackendHandler
|
||||
for UserRestrictedListerBackendHandler<'_, Handler>
|
||||
{
|
||||
async fn get_schema(&self) -> Result<Schema> {
|
||||
let mut schema = self.handler.get_schema().await?;
|
||||
@@ -331,8 +331,8 @@ impl<'a, Handler: ReadSchemaBackendHandler + Sync> ReadSchemaBackendHandler
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl<'a, Handler: UserListerBackendHandler + Sync> UserListerBackendHandler
|
||||
for UserRestrictedListerBackendHandler<'a, Handler>
|
||||
impl<Handler: UserListerBackendHandler + Sync> UserListerBackendHandler
|
||||
for UserRestrictedListerBackendHandler<'_, Handler>
|
||||
{
|
||||
async fn list_users(
|
||||
&self,
|
||||
@@ -354,8 +354,8 @@ impl<'a, Handler: UserListerBackendHandler + Sync> UserListerBackendHandler
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl<'a, Handler: GroupListerBackendHandler + Sync> GroupListerBackendHandler
|
||||
for UserRestrictedListerBackendHandler<'a, Handler>
|
||||
impl<Handler: GroupListerBackendHandler + Sync> GroupListerBackendHandler
|
||||
for UserRestrictedListerBackendHandler<'_, Handler>
|
||||
{
|
||||
async fn list_groups(&self, filters: Option<GroupRequestFilter>) -> Result<Vec<Group>> {
|
||||
let group_filter = self
|
||||
@@ -379,7 +379,7 @@ pub trait UserAndGroupListerBackendHandler:
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl<'a, Handler: GroupListerBackendHandler + UserListerBackendHandler + Sync>
|
||||
UserAndGroupListerBackendHandler for UserRestrictedListerBackendHandler<'a, Handler>
|
||||
impl<Handler: GroupListerBackendHandler + UserListerBackendHandler + Sync>
|
||||
UserAndGroupListerBackendHandler for UserRestrictedListerBackendHandler<'_, Handler>
|
||||
{
|
||||
}
|
||||
|
||||
@@ -37,7 +37,7 @@ impl<'de> Deserialize<'de> for TrueFalseAlways {
|
||||
|
||||
struct Visitor;
|
||||
|
||||
impl<'de> serde::de::Visitor<'de> for Visitor {
|
||||
impl serde::de::Visitor<'_> for Visitor {
|
||||
type Value = TrueFalseAlways;
|
||||
|
||||
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
|
||||
@@ -97,16 +97,16 @@ pub struct Configuration {
|
||||
pub http_host: String,
|
||||
#[builder(default = "17170")]
|
||||
pub http_port: u16,
|
||||
#[builder(default = r#"SecUtf8::from("secretjwtsecret")"#)]
|
||||
pub jwt_secret: SecUtf8,
|
||||
#[builder(default)]
|
||||
pub jwt_secret: Option<SecUtf8>,
|
||||
#[builder(default = r#"String::from("dc=example,dc=com")"#)]
|
||||
pub ldap_base_dn: String,
|
||||
#[builder(default = r#"UserId::new("admin")"#)]
|
||||
pub ldap_user_dn: UserId,
|
||||
#[builder(default)]
|
||||
pub ldap_user_email: String,
|
||||
#[builder(default = r#"SecUtf8::from("password")"#)]
|
||||
pub ldap_user_pass: SecUtf8,
|
||||
#[builder(default)]
|
||||
pub ldap_user_pass: Option<SecUtf8>,
|
||||
#[builder(default)]
|
||||
pub force_ldap_user_pass_reset: TrueFalseAlways,
|
||||
#[builder(default = "false")]
|
||||
@@ -607,11 +607,24 @@ where
|
||||
.unwrap_or_default(),
|
||||
figment_config,
|
||||
)?);
|
||||
if config.jwt_secret == SecUtf8::from("secretjwtsecret") {
|
||||
println!("WARNING: Default JWT secret used! This is highly unsafe and can allow attackers to log in as admin.");
|
||||
}
|
||||
if config.ldap_user_pass == SecUtf8::from("password") {
|
||||
println!("WARNING: Unsecure default admin password is used.");
|
||||
if config.jwt_secret.is_none() {
|
||||
use rand::{seq::SliceRandom, Rng};
|
||||
struct Symbols;
|
||||
|
||||
impl rand::prelude::Distribution<char> for Symbols {
|
||||
fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> char {
|
||||
*b"01234567890ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz+,-./:;<=>?_~!@#$%^&*()[]{}:;".choose(rng).unwrap() as char
|
||||
}
|
||||
}
|
||||
bail!("The JWT secret must be initialized to a random string, preferably at least 32 characters long. \
|
||||
Either set the `jwt_secret` config value or the `LLDAP_JWT_SECRET` environment variable. \
|
||||
You can generate the value by running\n\
|
||||
LC_ALL=C tr -dc 'A-Za-z0-9!#%&'\\''()*+,-./:;<=>?@[\\]^_{{|}}~' </dev/urandom | head -c 32; echo ''\n\
|
||||
or you can use this random value: {}",
|
||||
rand::thread_rng()
|
||||
.sample_iter(&Symbols)
|
||||
.take(32)
|
||||
.collect::<String>());
|
||||
}
|
||||
if config.smtp_options.tls_required.is_some() {
|
||||
println!("DEPRECATED: smtp_options.tls_required field is deprecated, it never did anything. You can replace it with smtp_options.smtp_encryption.");
|
||||
@@ -669,7 +682,9 @@ mod tests {
|
||||
fn figment_location_extraction_key_file() {
|
||||
Jail::expect_with(|jail| {
|
||||
jail.create_file("lldap_config.toml", r#"key_file = "test""#)?;
|
||||
jail.clear_env();
|
||||
jail.set_env("LLDAP_KEY_SEED", "a123");
|
||||
jail.set_env("LLDAP_JWT_SECRET", "secret");
|
||||
let ignore_keys = ["key_file", "cert_file"];
|
||||
let figment_config = Figment::from(Serialized::defaults(
|
||||
ConfigurationBuilder::default().private_build().unwrap(),
|
||||
@@ -696,7 +711,9 @@ mod tests {
|
||||
fn check_server_setup_key_extraction_seed_success_with_nonexistant_file() {
|
||||
Jail::expect_with(|jail| {
|
||||
jail.create_file("lldap_config.toml", r#"key_file = "test""#)?;
|
||||
jail.clear_env();
|
||||
jail.set_env("LLDAP_KEY_SEED", "a123");
|
||||
jail.set_env("LLDAP_JWT_SECRET", "secret");
|
||||
init(default_run_opts()).unwrap();
|
||||
Ok(())
|
||||
});
|
||||
@@ -706,7 +723,9 @@ mod tests {
|
||||
fn check_server_setup_key_extraction_seed_failure_with_existing_file() {
|
||||
Jail::expect_with(|jail| {
|
||||
jail.create_file("lldap_config.toml", r#"key_file = "test""#)?;
|
||||
jail.clear_env();
|
||||
jail.set_env("LLDAP_KEY_SEED", "a123");
|
||||
jail.set_env("LLDAP_JWT_SECRET", "secret");
|
||||
write_random_key(jail, "test");
|
||||
init(default_run_opts()).unwrap_err();
|
||||
Ok(())
|
||||
@@ -717,6 +736,8 @@ mod tests {
|
||||
fn check_server_setup_key_extraction_file_success_with_existing_file() {
|
||||
Jail::expect_with(|jail| {
|
||||
jail.create_file("lldap_config.toml", r#"key_file = "test""#)?;
|
||||
jail.clear_env();
|
||||
jail.set_env("LLDAP_JWT_SECRET", "secret");
|
||||
write_random_key(jail, "test");
|
||||
init(default_run_opts()).unwrap();
|
||||
Ok(())
|
||||
@@ -727,6 +748,8 @@ mod tests {
|
||||
fn check_server_setup_key_extraction_file_success_with_nonexistent_file() {
|
||||
Jail::expect_with(|jail| {
|
||||
jail.create_file("lldap_config.toml", r#"key_file = "test""#)?;
|
||||
jail.clear_env();
|
||||
jail.set_env("LLDAP_JWT_SECRET", "secret");
|
||||
init(default_run_opts()).unwrap();
|
||||
Ok(())
|
||||
});
|
||||
@@ -736,6 +759,8 @@ mod tests {
|
||||
fn check_server_setup_key_extraction_file_with_previous_different_file() {
|
||||
Jail::expect_with(|jail| {
|
||||
jail.create_file("lldap_config.toml", r#"key_file = "test""#)?;
|
||||
jail.clear_env();
|
||||
jail.set_env("LLDAP_JWT_SECRET", "secret");
|
||||
write_random_key(jail, "test");
|
||||
let config = init(default_run_opts()).unwrap();
|
||||
let info = config.get_private_key_info();
|
||||
@@ -766,6 +791,8 @@ mod tests {
|
||||
#[test]
|
||||
fn check_server_setup_key_extraction_file_to_seed() {
|
||||
Jail::expect_with(|jail| {
|
||||
jail.clear_env();
|
||||
jail.set_env("LLDAP_JWT_SECRET", "secret");
|
||||
jail.create_file("lldap_config.toml", "")?;
|
||||
write_random_key(jail, "server_key");
|
||||
init(default_run_opts()).unwrap();
|
||||
@@ -782,6 +809,8 @@ mod tests {
|
||||
#[test]
|
||||
fn check_server_setup_key_extraction_file_to_seed_removed_file() {
|
||||
Jail::expect_with(|jail| {
|
||||
jail.clear_env();
|
||||
jail.set_env("LLDAP_JWT_SECRET", "secret");
|
||||
jail.create_file("lldap_config.toml", "")?;
|
||||
write_random_key(jail, "server_key");
|
||||
let config = init(default_run_opts()).unwrap();
|
||||
|
||||
@@ -189,7 +189,7 @@ pub async fn build_tcp_server<Backend>(
|
||||
where
|
||||
Backend: TcpBackendHandler + BackendHandler + LoginHandler + OpaqueHandler + Clone + 'static,
|
||||
{
|
||||
let jwt_secret = config.jwt_secret.clone();
|
||||
let jwt_secret = config.jwt_secret.clone().unwrap();
|
||||
let jwt_blacklist = backend_handler
|
||||
.get_jwt_blacklist()
|
||||
.await
|
||||
|
||||
@@ -33,8 +33,17 @@ use tracing::{debug, error, info, instrument, span, warn, Instrument, Level};
|
||||
mod domain;
|
||||
mod infra;
|
||||
|
||||
const ADMIN_PASSWORD_MISSING_ERROR : &str = "The LDAP admin password must be initialized. \
|
||||
Either set the `ldap_user_pass` config value or the `LLDAP_LDAP_USER_PASS` environment variable. \
|
||||
A minimum of 8 characters is recommended.";
|
||||
|
||||
async fn create_admin_user(handler: &SqlBackendHandler, config: &Configuration) -> Result<()> {
|
||||
let pass_length = config.ldap_user_pass.unsecure().len();
|
||||
let pass_length = config
|
||||
.ldap_user_pass
|
||||
.as_ref()
|
||||
.expect(ADMIN_PASSWORD_MISSING_ERROR)
|
||||
.unsecure()
|
||||
.len();
|
||||
assert!(
|
||||
pass_length >= 8,
|
||||
"Minimum password length is 8 characters, got {} characters",
|
||||
@@ -48,7 +57,11 @@ async fn create_admin_user(handler: &SqlBackendHandler, config: &Configuration)
|
||||
..Default::default()
|
||||
})
|
||||
.and_then(|_| {
|
||||
register_password(handler, config.ldap_user_dn.clone(), &config.ldap_user_pass)
|
||||
register_password(
|
||||
handler,
|
||||
config.ldap_user_dn.clone(),
|
||||
config.ldap_user_pass.as_ref().unwrap(),
|
||||
)
|
||||
})
|
||||
.await
|
||||
.context("Error creating admin user")?;
|
||||
@@ -161,7 +174,10 @@ async fn set_up_server(config: Configuration) -> Result<ServerBuilder> {
|
||||
register_password(
|
||||
&backend_handler,
|
||||
config.ldap_user_dn.clone(),
|
||||
&config.ldap_user_pass,
|
||||
config
|
||||
.ldap_user_pass
|
||||
.as_ref()
|
||||
.expect(ADMIN_PASSWORD_MISSING_ERROR),
|
||||
)
|
||||
.instrument(span)
|
||||
.await
|
||||
|
||||
@@ -3,6 +3,8 @@ use std::env::var;
|
||||
|
||||
pub const DB_KEY: &str = "LLDAP_DATABASE_URL";
|
||||
pub const PRIVATE_KEY_SEED: &str = "LLDAP_KEY_SEED";
|
||||
pub const JWT_SECRET: &str = "LLDAP_JWT_SECRET";
|
||||
pub const LDAP_USER_PASSWORD: &str = "LLDAP_LDAP_USER_PASS";
|
||||
|
||||
pub fn database_url() -> String {
|
||||
let url = var(DB_KEY).ok();
|
||||
|
||||
@@ -43,14 +43,13 @@ const MAX_HEALTHCHECK_ATTEMPS: u8 = 10;
|
||||
|
||||
impl LLDAPFixture {
|
||||
pub fn new() -> Self {
|
||||
let mut cmd = create_lldap_command();
|
||||
cmd.arg("run");
|
||||
cmd.arg("--verbose");
|
||||
let child = cmd.spawn().expect("Unable to start server");
|
||||
let child = create_lldap_command("run")
|
||||
.arg("--verbose")
|
||||
.spawn()
|
||||
.expect("Unable to start server");
|
||||
let mut started = false;
|
||||
for _ in 0..MAX_HEALTHCHECK_ATTEMPS {
|
||||
let status = create_lldap_command()
|
||||
.arg("healthcheck")
|
||||
let status = create_lldap_command("healthcheck")
|
||||
.status()
|
||||
.expect("healthcheck fail");
|
||||
if status.success() {
|
||||
@@ -229,7 +228,7 @@ pub fn new_id(prefix: Option<&str>) -> String {
|
||||
}
|
||||
}
|
||||
|
||||
fn create_lldap_command() -> Command {
|
||||
fn create_lldap_command(subcommand: &str) -> Command {
|
||||
let mut cmd = Command::cargo_bin(env!("CARGO_PKG_NAME")).expect("cargo bin not found");
|
||||
// This gives us the absolute path of the repo base instead of running it in server/
|
||||
let path = canonicalize("..").expect("canonical path");
|
||||
@@ -237,5 +236,10 @@ fn create_lldap_command() -> Command {
|
||||
cmd.current_dir(path);
|
||||
cmd.env(env::DB_KEY, db_url);
|
||||
cmd.env(env::PRIVATE_KEY_SEED, "Random value");
|
||||
cmd.env(env::JWT_SECRET, "Random value");
|
||||
cmd.env(env::LDAP_USER_PASSWORD, "password");
|
||||
cmd.arg(subcommand);
|
||||
cmd.arg("--config-file=/dev/null");
|
||||
cmd.arg("--server-key-file=''");
|
||||
cmd
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user