Giter Site home page Giter Site logo

lldap / lldap Goto Github PK

View Code? Open in Web Editor NEW
3.5K 3.5K 166.0 3.04 MB

Light LDAP implementation

License: GNU General Public License v3.0

Rust 96.71% Shell 2.24% HTML 0.52% JavaScript 0.03% Dockerfile 0.38% CSS 0.12%
authentication ldap opaque rust security wasm web-assembly

lldap's People

Contributors

ajgon avatar amiga23 avatar arcoast avatar broeng avatar bunkerman avatar dependabot[bot] avatar fgardt avatar grehund avatar halyul avatar hobbabobba avatar jaidenw avatar jakob42 avatar kaysond avatar lkmhaqer avatar lordratner avatar lucat1 avatar lvillis avatar mackwic avatar martadinata666 avatar michsior14 avatar mickmorley avatar nitnelave avatar pixelrazor avatar povoq avatar roemer avatar sad-soul-eater avatar sblop avatar shroomify-it avatar strazto avatar wheinze avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

lldap's Issues

Support for MySQL/PostgreSQL

Hi, first of all, thanks for the project!

And I just want to clarify - this isn't an immediate feature request, but rather me asking would this be something you'd be okay putting on the roadmap?

Right now, LLDAP apparently stores everything in /data directory. But using SQL databases to back all of LLDAP's data could be so versatile:

  • If you want to stick with /data directory, you can! Just use SQLite as the backing store
  • You can directly use any number of MySQL/Postgres databases that users already have in order to store your, well, everything!

And I believe most of the SQL queries should be portable as well (with the only exception being DDLs). Thoughts?

Reset password by email support

Integration with SMTP to be able to send a link to a user so they can reset their password.

  • Integration with SMTP client (lettre 0.10)
    • Test email
    • Configuration variables
  • Short-lived password reset links
    • API endpoint (not graphql) to send a reset email.
    • SQL table with the reset tokens
    • API endpoint for password reset protected by the token (GET parameter)
  • "password forgotten" button and pages
    • "Forgot my password" -> enter your username/email -> message that email was sent
    • Receive an email with reset link: hostname/password-reset/step2?token=XXX
    • Page displays user ID, two password set fields. Token is invalidated on successful submission.
  • Admin button to force password reset (invalidate current password)

CSS files not eligible for integrity checks

Loading the files (not in docker) from the outside gives
"http://blabla.css" is not eligible for integrity checks since it’s neither CORS-enabled nor same-origin.

Maybe remove the integrity checks then?

Authorization endpoint for reverse proxies

The goal is to replace Authelia for basic uses.

  • API endpoint returning the HTTP code
  • Headers population (user, groups)
  • redirect to login form, handle redirection to custom URL

WikiJS LDAP failing authentication

Hi @nitnelave!

I'm using the standard WikiJS setup, and have gone to manually configure LDAP and run into issues.

image

The LDAP logs are:

lldap             | 2022-02-01T10:35:04.673732Z DEBUG lldap::infra::ldap_server: Received LDAP message: LdapMsg { msgid: 1, op: BindRequest(LdapBindRequest { dn: "cn=admin,ou=people,dc=example,dc=com", cred: Simple("********") }), ctrl: [] }    
lldap             | 2022-02-01T10:35:04.673760Z DEBUG lldap::infra::ldap_handler: Received bind request for "cn=admin,ou=people,dc=example,dc=com"    
lldap             | 2022-02-01T10:35:04.673790Z DEBUG lldap::infra::ldap_server: Replying with LDAP op: BindResponse(LdapBindResponse { res: LdapResult { code: Success, matcheddn: "", message: "", referral: [] }, saslcreds: None })    
lldap             | 2022-02-01T10:35:04.675053Z DEBUG lldap::infra::ldap_server: Received LDAP message: LdapMsg { msgid: 2, op: SearchRequest(LdapSearchRequest { base: "ou=people, dc=example, dc=com", scope: Subtree, aliases: Never, sizelimit: 0, timelimit: 10, typesonly: false, filter: And([Or([Equality("uid", "admin"), Equality("mail", "{{username}")]), Equality("objectClass", "person")]), attrs: [] }), ctrl: [] }    
lldap             | 2022-02-01T10:35:04.675082Z DEBUG lldap::infra::ldap_handler: Received search request: LdapSearchRequest { base: "ou=people, dc=example, dc=com", scope: Subtree, aliases: Never, sizelimit: 0, timelimit: 10, typesonly: false, filter: And([Or([Equality("uid", "admin"), Equality("mail", "{{username}")]), Equality("objectClass", "person")]), attrs: [] }    
lldap             | 2022-02-01T10:35:04.675098Z  WARN lldap::infra::ldap_handler: The specified search tree [("ou", "people"), (" dc", "example"), (" dc", "com")] is not under the common subtree [("dc", "example"), ("dc", "com")]    
lldap             | 2022-02-01T10:35:04.675111Z DEBUG lldap::infra::ldap_server: Replying with LDAP op: SearchResultDone(LdapResult { code: Success, matcheddn: "", message: "", referral: [] })    
lldap             | 2022-02-01T10:35:04.675507Z DEBUG lldap::infra::ldap_server: Received LDAP message: LdapMsg { msgid: 3, op: UnbindRequest, ctrl: [] }   

As you pointed out in discord, the wiki seems to be adding a space which may be the issue. Are you able to help out?

Thank you!

Nextcloud tutorial

Hi,

First of all, thanks for this. It is great, and I look forward to more features, like being able to see logs in the webinterface etc.

I have configured Authelia and it works great. I do however have some problems with Nextcloud.
It connect to the server, and gets "green".
As with authelia I use same login
cn=admin,ou=people,dc=xxx-xxx,dc=xx
and base DN
ou=people,dc=xxx-xxx,dc=xx
It are able to detect 2 users. Great. However when ever I try to be specific and check a specific user I get:

`[PHP] Error: ldap_search(): Search: Server is unwilling to perform at /config/www/nextcloud/apps/user_ldap/lib/LDAP.php#336

POST /apps/user_ldap/ajax/wizard.php
from MY.IP.IP.IP by My.Name at 2021-12-06T09:52:01+00:00`

Full test from log: (Sorry about the crappy formatting...)

{"reqId":"te5Gyh7vo33lGkLnJNse","level":3,"time":"2021-12-06T09:52:01+00:00","remoteAddr":"MY.IP.IP.IP","user":"MY.NAME","app":"PHP","method":"POST","url":"/apps/user_ldap/ajax/wizard.php","message":"ldap_search(): Search: Server is unwilling to perform at /config/www/nextcloud/apps/user_ldap/lib/LDAP.php#336","userAgent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:94.0) Gecko/20100101 Firefox/94.0","version":"22.2.3.0","exception":{"Exception":"Error","Message":"ldap_search(): Search: Server is unwilling to perform at /config/www/nextcloud/apps/user_ldap/lib/LDAP.php#336","Code":0,"Trace":[{"file":"/config/www/nextcloud/apps/user_ldap/lib/LDAP.php","line":224,"function":"onError","class":"OC\\Log\\ErrorHandler","type":"::"},{"function":"OCA\\User_LDAP\\{closure}","class":"OCA\\User_LDAP\\LDAP","type":"->","args":["*** sensitive parameters replaced ***"]},{"function":"ldap_search"},{"file":"/config/www/nextcloud/apps/user_ldap/lib/LDAP.php","line":336,"function":"call_user_func_array"},{"file":"/config/www/nextcloud/apps/user_ldap/lib/LDAP.php","line":229,"function":"invokeLDAPMethod","class":"OCA\\User_LDAP\\LDAP","type":"->","args":["*** sensitive parameters replaced ***"]},{"function":"search","class":"OCA\\User_LDAP\\LDAP","type":"->"},{"file":"/config/www/nextcloud/apps/user_ldap/lib/Access.php","line":1113,"function":"call_user_func_array"},{"file":"/config/www/nextcloud/apps/user_ldap/lib/Access.php","line":1117,"function":"OCA\\User_LDAP\\{closure}","class":"OCA\\User_LDAP\\Access","type":"->","args":["*** sensitive parameters replaced ***"]},{"file":"/config/www/nextcloud/apps/user_ldap/lib/Access.php","line":1175,"function":"invokeLDAPMethod","class":"OCA\\User_LDAP\\Access","type":"->","args":["*** sensitive parameters replaced ***"]},{"file":"/config/www/nextcloud/apps/user_ldap/lib/Access.php","line":1280,"function":"executeSearch","class":"OCA\\User_LDAP\\Access","type":"->"},{"file":"/config/www/nextcloud/apps/user_ldap/lib/Access.php","line":1026,"function":"count","class":"OCA\\User_LDAP\\Access","type":"->"},{"file":"/config/www/nextcloud/apps/user_ldap/lib/Access.php","line":887,"function":"countUsers","class":"OCA\\User_LDAP\\Access","type":"->"},{"file":"/config/www/nextcloud/apps/user_ldap/lib/Wizard.php","line":657,"function":"countUsersByLoginName","class":"OCA\\User_LDAP\\Access","type":"->"},{"file":"/config/www/nextcloud/apps/user_ldap/ajax/wizard.php","line":112,"function":"testLoginName","class":"OCA\\User_LDAP\\Wizard","type":"->"},{"file":"/config/www/nextcloud/lib/private/Route/Route.php","line":155,"args":["/config/www/nextcloud/apps/user_ldap/ajax/wizard.php"],"function":"require_once"},{"function":"OC\\Route\\{closure}","class":"OC\\Route\\Route","type":"->","args":["*** sensitive parameters replaced ***"]},{"file":"/config/www/nextcloud/lib/private/Route/Router.php","line":310,"function":"call_user_func"},{"file":"/config/www/nextcloud/lib/base.php","line":1006,"function":"match","class":"OC\\Route\\Router","type":"->"},{"file":"/config/www/nextcloud/index.php","line":36,"function":"handleRequest","class":"OC","type":"::"}],"File":"/config/www/nextcloud/lib/private/Log/ErrorHandler.php","Line":92,"CustomMessage":"--"},"id":"61addd9ca0c7a"}

I have been trying all weekend, and I expect this to be my mistake, as I am only just learning to get my head around LDAP...

Edit: Removed IP etc.

No such file or directory (os error 2)

Hi,

First, thank you a lot for this project.

When trying the release on armv7, and running from the release/
./lldap run -v

opening http://127.0.0.1:17170 with browser

I get the following error in plain page:
No such file or directory (os error 2)

There are the logs

Loading configuration from lldap_config.toml
Configuration: Configuration {
    ldap_port: 3890,
    ldaps_port: 6360,
    http_port: 17170,
    jwt_secret: ***SECRET***,
    ldap_base_dn: "dc=example,dc=com",
    ldap_user_dn: "admin",
    ldap_user_pass: ***SECRET***,
    database_url: "sqlite://users.db?mode=rwc",
    verbose: true,
    key_file: "server_key",
    smtp_options: MailOptions {
        enable_password_reset: false,
        from: None,
        reply_to: None,
        server: "localhost",
        port: 587,
        user: "admin",
        password: ***SECRET***,
        tls_required: true,
    },
    http_url: "http://localhost",
    server_setup: None,
}
WARNING: Default JWT secret used! This is highly unsafe and can allow attackers to log in as admin.
WARNING: Unsecure default admin password is used.
2022-02-20T08:28:22.831680Z  INFO lldap: Starting LLDAP....    
2022-02-20T08:28:22.840935Z  INFO sqlx::query: PRAGMA journal_mode = WAL; …; rows: 1, elapsed: 4.210ms

PRAGMA journal_mode = WAL;
PRAGMA foreign_keys = ON;
PRAGMA synchronous = FULL
  
2022-02-20T08:28:22.842141Z  INFO sqlx::query: PRAGMA foreign_keys = ON; rows: 0, elapsed: 530.458µs  
2022-02-20T08:28:22.845200Z  INFO sqlx::query: CREATE TABLE IF NOT …; rows: 0, elapsed: 624.750µs

CREATE TABLE IF NOT EXISTS `users` (
  `user_id` text(255) NOT NULL PRIMARY KEY,
  `email` text(255) NOT NULL,
  `display_name` text(255) NOT NULL,
  `first_name` text(255) NOT NULL,
  `last_name` text(255) NOT NULL,
  `avatar` binary,
  `creation_date` text NOT NULL,
  `password_hash` binary,
  `totp_secret` text(64),
  `mfa_type` text(64)
)
  
2022-02-20T08:28:22.847264Z  INFO sqlx::query: CREATE TABLE IF NOT …; rows: 0, elapsed: 538.583µs

CREATE TABLE IF NOT EXISTS `groups` (
  `group_id` integer NOT NULL PRIMARY KEY,
  `display_name` text(255) UNIQUE NOT NULL
)
  
2022-02-20T08:28:22.850903Z  INFO sqlx::query: CREATE TABLE IF NOT …; rows: 0, elapsed: 468.709µs

CREATE TABLE IF NOT EXISTS `memberships` (
  `user_id` text(255) NOT NULL,
  `group_id` integer NOT NULL,
  FOREIGN KEY (`user_id`) REFERENCES `users` (`user_id`) ON DELETE CASCADE ON
  UPDATE
    CASCADE,
    FOREIGN KEY (`group_id`) REFERENCES `groups` (`group_id`) ON DELETE CASCADE ON
  UPDATE
    CASCADE
)
  
2022-02-20T08:28:22.853613Z  INFO sqlx::query: SELECT `user_id`, `email`, `display_name`, …; rows: 1, elapsed: 1.055ms

SELECT
  `user_id`,
  `email`,
  `display_name`,
  `first_name`,
  `last_name`,
  `avatar`,
  `creation_date`
FROM
  `users`
WHERE
  `user_id` = 'admin'
  
2022-02-20T08:28:22.858539Z  INFO sqlx::query: CREATE TABLE IF NOT …; rows: 0, elapsed: 535.458µs

CREATE TABLE IF NOT EXISTS `jwt_refresh_storage` (
  `refresh_token_hash` integer NOT NULL PRIMARY KEY,
  `user_id` text(255) NOT NULL,
  `expiry_date` text NOT NULL,
  FOREIGN KEY (`user_id`) REFERENCES `users` (`user_id`) ON DELETE CASCADE ON
  UPDATE
    CASCADE
)
  
2022-02-20T08:28:22.861456Z  INFO sqlx::query: CREATE TABLE IF NOT …; rows: 0, elapsed: 687.250µs

CREATE TABLE IF NOT EXISTS `jwt_storage` (
  `jwt_hash` integer NOT NULL PRIMARY KEY,
  `user_id` text(255) NOT NULL,
  `expiry_date` text NOT NULL,
  `blacklisted` integer DEFAULT FALSE NOT NULL,
  FOREIGN KEY (`user_id`) REFERENCES `users` (`user_id`) ON DELETE CASCADE ON
  UPDATE
    CASCADE
)
  
2022-02-20T08:28:22.864162Z  INFO sqlx::query: CREATE TABLE IF NOT …; rows: 0, elapsed: 658.375µs

CREATE TABLE IF NOT EXISTS `password_reset_tokens` (
  `token` text(255) NOT NULL PRIMARY KEY,
  `user_id` text(255) NOT NULL,
  `expiry_date` text NOT NULL,
  FOREIGN KEY (`user_id`) REFERENCES `users` (`user_id`) ON DELETE CASCADE ON
  UPDATE
    CASCADE
)
  
2022-02-20T08:28:22.865411Z  INFO sqlx::query: SELECT `jwt_hash` FROM `jwt_storage`; rows: 0, elapsed: 726.500µs  
2022-02-20T08:28:22.867881Z  INFO lldap::infra::db_cleaner: DB Cleanup Cron started  

Note that it is the same error that in issue #41 but I'm not using docker.
I also compiled for aarch64 (my initial target), and the error was the same.

"Invalid username or password"

No matter what I try I'm unable to log in. Currently using the docker image, but I had the same issue when I set it up without docker before there was an image for it. It's running behind a reverse proxy, swag to be specific. Web page loads up no issue. I put in the username/password I set up but it says invalid. I've tried all sorts of combinations of things. I set the password/username in the toml file and in env variables, and tried not changing the username from "admin". No luck. Tried setting the password to something simple just in case it didn't like one of my characters, no change. I tried both binding the data directory to a folder and letting docker create a volume for it. Any help and/or guidance would be greatly appreciated, thanks!

Improve errors and logging

Right now, errors and login are mostly an artifact of error handling in the code. No thought has been put towards what the actual log or errors will look like as a whole. That can lead to useless log messages, missing log messages, and cryptic or overly long/detailed errors.

We should run through various error scenarios and see what gets logged and the errors that we get.

Authelia - Current user is not allowed to query LDAP

Hello there!

I wanted to try your Light LDAP and it's working pretty fine, especially the web ui! :)

But when i try to connect my Authelia to LLDAP i'm always getting the following error:

LDAP Result Code 1 \"Operations Error\": Could not parse base DN

i checked the base multiple times, and in the authelia config and in the LLDAP config it's exactly the same, so there should not be a problem at this point.

Does anyone has an idea, why this issue occurs?

Greetings

Feature Request: set secrets from file

A common feature in many docker containers (mariadb, all of lsio containers, authelia) is the ability to set secret configuration parameters, or configuration parameters in general, via a file. Users supply an env var such as LLDAP_LDAP_USER_PASS_FILE which contains the location of a file whose contents set the parameter.

In particular, this is useful because it enables you to employ docker secrets, which in turn allows you to store secrets in a completely encrypted manner, and doesn't depend on plaintext storage.

See:

Improve accessibility

Have a look at aria labels and other relevant concerns: autofocus, tabindex, mobile keyboard for email and so on.

Serve libraries locally

Right now all of the below libraries are pointing to cdns. For people like me who run a reverse proxy with some security headers, this can get blocked.

https://github.com/nitnelave/lldap/blob/5b5395103ae56ebbea841be76c000b8b243895dc/app/index.html#L8-L24

Specifically, I set Content-Security-Policy: default-src 'self' 'unsafe-inline', which prevents backend services from loading resources from external sites as a security measure.

Would you be open to a PR that adds these libs to the repo and serves them locally? I'm thinking we should add a app/static directory and serve all static files from there (we can also then add a COPY /app/app/static to the dockerfile so nothing gets forgotten)

Mealie LDAP Failing Authentication

Hi @nitnelave!

I'm running the standard mealie setup and struggling to get it to authenticate with LLDAP.

The extra environment variables I am using are:

LDAP_AUTH_ENABLED: "True"
LDAP_SERVER_URL: ldap://lldap:3890
LDAP_BIND_TEMPLATE: cn={},ou=people,dc=example,dc=com
LDAP_ADMIN_FILTER(member=cn=admins,ou=groups,dc=example,dc=com)

I'm not certain this one is hitting lldap at all, but struggling to work out what is going wrong.

Thanks in advance!

Investigate a potential SQL injection

Creating a user with a quote inside the name doesn't work. Variable substitution should be delegated to sqlx rather than handled in sea-query, maybe?

frontend docker error

Hi thanks for making this. I was looking to something lightweight for my 10 user home-server. Unfortunately i am not able to make it work using docker compose. Here is my compose

services:
  lldap:
    image: nitnelave/lldap
    container_name: lldap
    user: "1000:1000"
    ports:
      # For LDAP
      - 3890:3890
      # For the web front-end
      - 17170:17170
    volumes:
      - /home/test123/volumes/lldap/data:/data
    environment:
      - JWT_SECRET=test123
      - LDAP_USER_PASS=test123
      - LDAP_BASE_DN=dc= test123,dc=com

i cannot access web app as i get error: No such file or directory (os error 2)
logs show internal server error
i have also created lldap_config.toml in data dir

Feature Request: Arbitrary and Duplicate Text Fields

Overview

As an LLDAP admin, I would like to be able to define arbitrary text fields that may be repeated to extend the user object schema.

Details

I would like to see the following:

  • the ability to define arbitrary fields with string data
  • the ability to restrict the ability for users to change these fields
  • the ability to have a list or repeated values for the field

Context

I am interested in using LLDAP as a user DB for my mail server and to replace my OpenLDAP instance with something much more manageable. However, my users may have more than one email address assigned to them. Currently, I have that implemented with a repeated mailalias attribute. For example, my user looks something like the following:

dn: uid=user,ou=people,dc=domain,dc=tld
objectClass: mailAccount
objectClass: person
objectClass: uidObject
objectClass: top
uid: user
mail: [email protected]
mailenable: TRUE
sn: SN
cn: CN
mailalias: [email protected]
mailalias: [email protected]
mailalias: [email protected]

Restricting the ability for users to change the mailalias attributes is also crucial to prevent users from impersonating other users and sending mail from arbitrary addresses.

Other use cases include:

  • SSH Public Keys
  • Home directories

Task breakdown:

  • Prepare DB and migration
  • Create Schema data structure + default schema
  • Migrate first and last name to attributes
  • Implement read in the domain
  • Implement read in graphql
  • Implement read in LDAP
  • Implement write in domain
  • Implement write in graphql
  • Implement write in LDAP
  • Implement front-end read/write
  • Implement schema manipulation features
    • domain
    • graphql
    • frontend
  • Implement aliases
    • Query from LDAP
    • create/delete/query domain
    • create/delete/query graphql
    • create/delete/display frontend
  • Custom LDAP object classes
    • backend
    • frontend

Internationalization

Provide functionality to translate strings, and actual translations.

  • Provide the infrastructure
  • Move all strings to templates
  • Create translations for main languages
    • French
    • Spanish
    • German
    • Chinese
    • More if contributed.

Changing password should require a current password check

Currently that is achieved through the UI logic only, but anyone hitting the API can change their own password without verifying that they know the current one. It's probably okay for now, but it enables the following attach scenario:

User is logged in on a public/unattended computer OR an attacker gets hold of a currently valid auth token.
Attacker can use a purpose-built client to change the password of the victim.

To solve this, we can disallow the "register" opaque methods for any non-admin, and add a new endpoint to change the password that does both steps in parallel: establishing a new password while checking the old one. Both OPAQUE messages are sent for every step.

How do I login?

I really want to use this, but how do I login to the interface? All I see is username and email

Need help with docker compose

Hi, I'm trying to work out what I'm doing wrong. I'm trying to run under docker using a slightly modified version of the example docker-compose.yml file from the project readme. The only real change is that I'm trying to map data to a sub-directory of the working directory that holds my docker-compose file so that I can define my own environment variable settings. But when I run docker-compose up I get this traceback;

Starting lldap_lldap_1 ... done
Attaching to lldap_lldap_1
lldap_1  | Error: Could not write the generated server setup to file `/data/private_key`
lldap_1  |
lldap_1  | Caused by:
lldap_1  |     Permission denied (os error 13)
lldap_lldap_1 exited with code 1

So it looks like the container can't write to my lldata directory but I can't figure out why. Can anyone help out with suggestions?

My docker-compose.yml file is as follows. I've just starred out the secret and password valuesservices:

lldap:
  image: nitnelave/lldap
  ports:
    # For LDAP
    - "3890:3890"
    # For the web front-end
    - "17170:17170"
  volumes:
    - "./lldata:/data"
  environment:
    - JWT_SECRET=*******
    - LDAP_USER_PASS=********
    - LDAP_BASE_DN=dc=home,dc=arpa

Improve documentation

  • Update/trim configuration options, create command-line flags where relevant and add merging code.
  • Create a default config.
  • Add screenshots (once #12 is at least partially done)
  • Write a "Getting started" guide.

Keycloak unable to bind to directory

Probably doing something wrong, however when I attempt to set up Keycloak to pull users in, I wind up with the following error message when attempting to test authentication with the admin user. I can successfully get Keycloak to connect, however.

ERROR [org.keycloak.services] (default task-28) KC-SERVICES0055: Error when authenticating to LDAP: [LDAP: error code 64 - Not a subtree of the base tree]: javax.naming.InvalidNameException: [LDAP: error code 64 - Not a subtree of the base tree]

Dockerfile not configured correctly

When running the docker container, I get the following error:

2021-09-11T19:16:21.433988728Z error: Found argument '--config-file' which wasn't expected, or isn't valid in this context
2021-09-11T19:16:21.434022061Z 
2021-09-11T19:16:21.434030777Z 	Did you mean to put '--config-file' after the subcommand 'run'?
2021-09-11T19:16:21.434040535Z 
2021-09-11T19:16:21.434048680Z If you tried to supply `--config-file` as a PATTERN use `-- --config-file`
2021-09-11T19:16:21.434055764Z 
2021-09-11T19:16:21.434062126Z USAGE:
2021-09-11T19:16:21.434074178Z     lldap <SUBCOMMAND>
2021-09-11T19:16:21.434081312Z 
2021-09-11T19:16:21.434087533Z For more information try --help

It appears that there is no subcommand passed as specified in the usage instructions above.

Refresh token is not used

When the page loads, if the login token is outdated it should try to refresh the auth and if that fails, redirect to login page.

MultiArch ARM64 Support

Would be great if the container images support MultiArch/ARM64 for running on arm64 embedded hardware like raspberry pi or arm64 based cloud instances.

Create Docker image

This shouldn't be too hard, but let's try to make it small if we can:

  • Start with an alpine linux image (other option is to start from a scratch image, but that can be annoying to debug).
  • Use a two-step image build, where one image installs Rust, npm and so on, builds the project, and the second images copies the resulting binary and compiled JS.

Import tool from LDAP

We could add a tool (maybe external? A python script?) that reads an existing LDAP server and imports the existing users (and passwords? Maybe too hard)

Administration interface

All about user management.

  • Users
    • List users
    • User details page
      • Editable (?)
      • View group memberships
    • Create user
    • Delete user
  • Groups
    • Create group
    • List groups
    • Add user to groups
    • Add groups to user
    • Delete group
    • List group's users
    • Rename group

If people want to help with this, it's fairly easy to add methods in the relevant handlers by copying similar ones. The places to add code:

  • Request/response: add new types in model/src/lib.rs for the request and response. Example: CreateUserRequest.
  • Server logic implementation: in src/domain/handler.rs and the corresponding sql_handler.rs. Example: list_users
  • Routing layer: add a route in src/infra/tcp_api.rs and a handler, calling the method above. Example: user_list_handler
  • Client-side: add a method in app/src/api.rs to call the server, and a component to send the request and display the result. Example: app/src/user_table.rs

(Note: specific files listed here are likely to move around a bit)

Login Page Should Not Flag Password as Bad when less than 8 characters

Date = Tue 19 Oct 2021 02:07:48 PM UTC
nitnelave/lldap latest 2058071a9e8b 6 hours ago 27.4MB

When logging in, the page should not give a hint as to the minimum password size:

image

As soon as there is 8 characters in the password field, the warning is removed.

7 Characters:
image

8 characters
image

Possible issue in Dockerfile

When I try to start the newest docker image, I'm receiving the following error:

error: Found argument '--config_file' which wasn't expected, or isn't valid in this context

It appears that /app/lldap is expecting config [DASH] file and it is being passed config [UNDERSCORE] file. Should the CMD line in the Dockerfile be config [dash] file?

Error: This error results from an error during password verification

If its not obvious by now I've been slowly trying to get this up and running all night XD

I'm now stuck on the following error: Error: This error results from an error during password verification

Couldn't find that error text in the repo so I guess its a dependency? I'm wondering if it's a permissions problem because the image is built depending on being run as uid 10001

My compose file is:

---
version: '3.8'

services:
  lldap:
    hostname: "{{ .Service.Name }}.{{ .Task.Slot }}"
    image: nitnelave/lldap:latest@sha256:0af94a81b3e5d55967e5012298d81bb5eceaa3c9670fbc39d2ba63bada7ffcf1
    volumes:
      - /var/lib/homelab/lldap/config:/data
    networks:
      - public
    healthcheck:
      interval: 30s
      retries: 3
      start_period: 60s
      test: exit 0
      timeout: 5s
    deploy:
      mode: replicated
      replicas: 1
      restart_policy:
        condition: on-failure
        delay: 15s
        max_attempts: 3
        window: 60s
      update_config:
        delay: 15s
        failure_action: rollback
        max_failure_ratio: 0
        monitor: 30s
        order: start-first
        parallelism: 1
      rollback_config:
        delay: 15s
        failure_action: pause
        max_failure_ratio: 0
        monitor: 30s
        order: start-first
        parallelism: 1
      labels:
        traefik.enable: 'true'
        traefik.http.routers.lldap.entrypoints: websecure
        traefik.http.routers.lldap.middlewares: local-only@file
        traefik.http.services.lldap.loadbalancer.server.port: 17170
        traefik.subdomain: lldap
      resources:
        limits:
          memory: 1G

networks:
  public:
    attachable: false
    ipam:
      config:
      - subnet: 172.21.6.0/24

and my config is

## Default configuration for Docker.
## All the values can be overridden through environment variables, prefixed
## with "LLDAP_". For instance, "ldap_port" can be overridden with the
## "LLDAP_LDAP_PORT" variable.

## The port on which to have the LDAP server.
ldap_port = 3890

## The port on which to have the HTTP server, for user login and
## administration.
http_port = 17170

## The public URL of the server, for password reset links.
http_url = "https://lldap.homelab.lan"

## Random secret for JWT signature.
## This secret should be random, and should be shared with application
## servers that need to consume the JWTs.
## Changing this secret will invalidate all user sessions and require
## them to re-login.
## You should probably set it through the LLDAP_JWT_SECRET environment
## variable from a secret ".env" file.
## You can generate it with (on linux):
## LC_ALL=C tr -dc 'A-Za-z0-9!"#%&'\''()*+,-./:;<=>?@[\]^_{|}~' </dev/urandom | head -c 32; echo ''
jwt_secret = "sEa5%:8auaXV!JbQ%|Uwh2hKtNYg)E:J"

## Base DN for LDAP.
## This is usually your domain name, and is used as a
## namespace for your users. The choice is arbitrary, but will be needed
## to configure the LDAP integration with other services.
## The sample value is for "example.com", but you can extend it with as
## many "dc" as you want, and you don't actually need to own the domain
## name.
ldap_base_dn = "DC=homelab,DC=lan"

## Admin username.
## For the LDAP interface, a value of "admin" here will create the LDAP
## user "cn=admin,ou=people,dc=example,dc=com" (with the base DN above).
## For the administration interface, this is the username.
ldap_user_dn = "admin"

## Admin password.
## Password for the admin account, both for the LDAP bind and for the
## administration interface. It is only used when initially creating
## the admin user.
## It should be minimum 8 characters long.
## You can set it with the LLDAP_LDAP_USER_PASS environment variable.
## Note: you can create another admin user for user administration, this
## is just the default one.
ldap_user_pass = "C3lPplboew17lmqN9fYdAwgJhUZivme0wst+VfaM"

## Database URL.
## This encodes the type of database (SQlite, Mysql and so
## on), the path, the user, password, and sometimes the mode (when
## relevant).
## Note: Currently, only SQlite is supported. SQlite should come with
## "?mode=rwc" to create the DB if not present.
## Example URLs:
##  - "postgres://postgres-user:password@postgres-server/my-database"
##  - "mysql://mysql-user:password@mysql-server/my-database"
##
## This can be overridden with the DATABASE_URL env variable.
database_url = "sqlite:///data/users.db?mode=rwc"

## Private key file.
## Contains the secret private key used to store the passwords safely.
## Note that even with a database dump and the private key, an attacker
## would still have to perform an (expensive) brute force attack to find
## each password.
## Randomly generated on first run if it doesn't exist.
key_file = "/data/private_key"

Feature Request: versioned docker hub images

For reproducible deployments, its necessary to have different image tags for each version. Without this, rollbacks in docker swarm or kubernetes aren't possible.

I realize its a bit early on in this project for versions, but I'm excited about its potential so I figured I'd put it here anyways! :)

You could even just start with a 0.0.1

Update Opaque-ke dependency

As part of their new API, the serializer doesn't write binary as base64 in JSON anymore. We should switch the json serializer to a json+binary-to-base64 one.

Create integration tests

That can be done at several levels:

  • HTTP request level, testing how the server responds to various valid and invalid requests.
  • client-only tests mocking the server
  • full browser-based integration tests

Improve looks

All about making it pretty!

  • Logo
  • Proper HTML structure
  • CSS classes
  • Images, banners, ...

Build Script Fails

Expected Behavior

Build script completes without errors.

Actual Behavior

Build fails. I suspect this is because i do not have npm in my path or something.

$ ./app/build.sh 
[INFO]: Checking for the Wasm target...
[INFO]: Compiling to Wasm...
    Finished release [optimized] target(s) in 0.05s
:-) [WARN]: origin crate has no README
[INFO]: Installing wasm-bindgen...
[INFO]: Optimizing wasm binaries with `wasm-opt`...
[INFO]: Optional fields missing from Cargo.toml: 'description', 'repository', and 'license'. These are not necessary, but recommended
[INFO]: :-) Done in 9.53s
[INFO]: :-) Your wasm pkg is ready to publish at /home/jermz/projects/lldap-original/app/pkg.
./app/build.sh: 5: rollup: not found

Steps to Reproduce the Problem

  1. Start up a new linux VM
  2. Clone Repo
  3. cd into directory
  4. Install Rust
  5. Follow instructions on README.md

Specifications

  • Version: N/A
  • Platform: Linux ##(amd64)
  • Subsystem: Debian Sid

User interface

From the PoV of the user.

  • Login form
  • View details
  • Logout
  • Change password
  • Edit fields (name & stuff)

PKG files not being loaded?

Hey, I'm really sorry about filing all these issues but it's just what I've been encountering as I've been trying to set it up myself...

After I've finally set up the server (after dealing with the permissions issues), I load up the server at 17170 and... nothing. Just a blank screen.

On further inspection, it seems that GET call to /pkg/bundle.js was returning 404. Fair enough.

I checked the /app directory, and there it is - /app/app/bundle.js.

I was checking the code for why it might 404, and I think this is the issue: https://github.com/nitnelave/lldap/blob/edf9e538ceee77d00fc1994f156d97ba52622421/server/src/infra/tcp_server.rs#L78

Specifically, when you bundle all the stuff together with rollup, you place it within /app/app/pkg/bundle.js, and within the Dockerfile, you copy it onto the /app/app/bundle.js: https://github.com/nitnelave/lldap/blob/main/Dockerfile#L60

I'm guessing when you last used/tested the frontend, it was pre-packaging the files during Docker build steps (i.e. the endpoint was serving ./pkg/bundle.js just fine because the pkg directory was there), but because the server looks for the hardcoded ./pkg/* directory, it cannot find the bundle (and everything else that was packaged) when you run the Docker container.

I highly suspect that this is the case. Could you put out a fix if it is? It's preventing anyone from actually using the frontend. Thanks

Proper password salting

The password hashing code uses a static salt. This defeats the point of having a salt - two users with the same password will have the same hash. The comment that it "doesn't affect the security" is incorrect and demonstrates a big misunderstanding about the purpose of the salt.

Please randomly generate the salt using a secure random generator, and use the hash_encoded and verify_encoded to store the salt along with the hash.

Group inheritance and Indirect group membership

More of a nice-to-have, I don't expect many users to have a very complex group membership structure that requires that.

We can make a group inherit from another one, or be a subgroup of another one: if group A has subgroup B, then users in B are indirectly part of group A.

To avoid making too many requests for reading (common case), we can keep a "resloved" membership table that contains both direct and indirect memberships. This can be updated when adding a user to a group, and can be reset when deleting a user or a group.

Another way to do it is to have just the resolved group inheritance, not the full user one; then a user is part of group A if:

  • They are explicitly a member of A.
  • They are explicitly a member of a group that resolves to inherit from A.
    That's achievable with a single query, with a join.
    This would only require updates/rebuilding when adding/deleting groups/group inheritance.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.