diff --git a/.dockerignore b/.dockerignore
index 340b5ef..f2f90ab 100644
--- a/.dockerignore
+++ b/.dockerignore
@@ -22,3 +22,4 @@ server_key
users.db*
screenshot.png
recipe.json
+*.md
diff --git a/README.md b/README.md
index a678a6d..d744b08 100644
--- a/README.md
+++ b/README.md
@@ -1,25 +1,51 @@
-# lldap - Light LDAP implementation for authentication
+
lldap - Light LDAP implementation for authentication
-
-
-
+
+LDAP made easy.
+
-WARNING: This project is still in alpha, with the basic core functionality
-implemented but still very rough. For updates, follow
-[@nitnelave1](https://twitter.com/nitnelave1) or join our [Discord
-server](https://discord.gg/h5PEdRMNyP)!
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+## About
-This project is an lightweight authentication server that provides an
-opinionated, simplified LDAP interface for authentication: clients that can
-only speak LDAP protocol can talk to it and use it as an authentication server.
+This project is a lightweight authentication server that provides an
+opinionated, simplified LDAP interface for authentication. It integrates with
+many backends, from KeyCloak to Authelia to Nextcloud and more!
-
+
The goal is _not_ to provide a full LDAP server; if you're interested in that,
check out OpenLDAP. This server is a user management system that is:
-* simple to setup (no messing around with `slapd`)
-* simple to manage (friendly web UI)
+* simple to setup (no messing around with `slapd`),
+* simple to manage (friendly web UI),
+* low resources,
* opinionated with basic defaults so you don't have to understand the
subtleties of LDAP.
@@ -31,7 +57,7 @@ For more features (OAuth/OpenID support, reverse proxy, ...) you can install
other components (KeyCloak, Authelia, ...) using this server as the source of
truth for users, via LDAP.
-## Setup
+## Installation
### With Docker
@@ -87,6 +113,31 @@ To bring up the server, just run `cargo run`. The default config is in
`lldap_config.toml`, setting environment variables or passing arguments to
`cargo run`.
+### Cross-compilation
+
+No Docker image is provided for other architectures, due to the difficulty of
+setting up cross-compilation inside a Docker image.
+
+Some pre-compiled binaries are provided for each release, starting with 0.2.
+
+If you want to cross-compile, you can do so by installing
+[`cross`](https://github.com/rust-embedded/cross):
+
+```sh
+cargo install cross
+cross build --target=armv7-unknown-linux-musleabihf -p lldap --release
+./app/build.sh
+```
+
+(Replace `armv7-unknown-linux-musleabihf` with the correct Rust target for your
+device.)
+
+You can then get the compiled server binary in
+`target/armv7-unknown-linux-musleabihf/release/lldap` and the various needed files
+(`index.html`, `main.js`, `pkg` folder) in the `app` folder. Copy them to the
+Raspberry Pi (or other target), with the folder structure maintained (`app`
+files in an `app` folder next to the binary).
+
## Client configuration
To configure the services that will talk to LLDAP, here are the values:
@@ -108,10 +159,42 @@ admin rights in the Web UI.
### Sample client configurations
Some specific clients have been tested to work and come with sample
-configuration files, or guides. See the `example_configs` folder for help with:
- - Authelia
- - KeyCloak
- - Jisti Meet
+configuration files, or guides. See the [`example_configs`](example_configs)
+folder for help with:
+ - [Authelia](example_configs/authelia_config.yml)
+ - [KeyCloak](example_configs/keycloak.md)
+ - [Jisti Meet](example_configs/jitsi_meet.conf)
+
+## Comparisons with other services
+
+### vs OpenLDAP
+
+OpenLDAP is a monster of a service that implements all of LDAP and all of its
+extensions, plus some of its own. That said, if you need all that flexibility,
+it might be what you need! Note that installation can be a bit painful
+(figuring out how to use `slapd`) and people have mixed experiences following
+tutorials online. If you don't configure it properly, you might end up storing
+passwords in clear, so a breach of your server would reveal all the stored
+passwords!
+
+OpenLDAP doesn't come with a UI: if you want a web interface, you'll have to
+install one (not that many that look nice) and configure it.
+
+LLDAP is much simpler to setup, has a much smaller image (10x smaller, 20x if
+you add PhpLdapAdmin), and comes packed with its own purpose-built wed UI.
+
+### vs FreeIPA
+
+FreeIPA is the one-stop shop for identity management: LDAP, Kerberos, NTP, DNS, Samba, you name it, it has it. In addition to user
+management, it also does security policies, single sign-on, certificate
+management, linux account management and so on.
+
+If you need all of that, go for it! Keep in mind that a more complex system is
+more complex to maintain, though.
+
+LLDAP is much lighter to run (<100 MB RAM including the DB), easier to
+configure (no messing around with DNS or security policies) and simpler to
+use. It also comes conveniently packed in a docker container.
## I can't log in!
@@ -132,90 +215,6 @@ set isn't working, try the following:
- Make sure you restart the server.
- If it's still not working, join the [Discord server](https://discord.gg/h5PEdRMNyP) to ask for help.
-## Architecture
-
-The server is entirely written in Rust, using [actix](https://actix.rs) for the
-backend and [yew](https://yew.rs) for the frontend.
-
-Backend:
-* Listens on a port for LDAP protocol.
- * Only a small, read-only subset of the LDAP protocol is supported.
- * An extension to allow resetting the password through LDAP will be added.
-* Listens on another port for HTTP traffic.
- * The authentication API, based on JWTs, is under "/auth".
- * The user management API is a GraphQL API under "/api/graphql". The schema
- is defined in `schema.graphql`.
- * The static frontend files are served by this port too.
-
-Note that secure protocols (LDAPS, HTTPS) are currently not supported. This can
-be worked around by using a reverse proxy in front of the server (for the HTTP
-API) that wraps/unwraps the HTTPS messages, or only open the service to
-localhost or other trusted docker containers (for the LDAP API).
-
-Frontend:
-* User management UI.
-* Written in Rust compiled to WASM as an SPA with the Yew library.
-* Based on components, with a React-like organization.
-
-Data storage:
-* The data (users, groups, memberships, active JWTs, ...) is stored in SQL.
-* Currently only SQLite is supported (see
- https://github.com/launchbadge/sqlx/issues/1225 for what blocks us from
- supporting more SQL backends).
-
-### Code organization
-
-* `auth/`: Contains the shared structures needed for authentication, the
- interface between front and back-end. In particular, it contains the OPAQUE
- structures and the JWT format.
-* `app/`: The frontend.
- * `src/components`: The elements containing the business and display logic of
- the various pages and their components.
- * `src/infra`: Various tools and utilities.
-* `server/`: The backend.
- * `src/domain/`: Domain-specific logic: users, groups, checking passwords...
- * `src/infra/`: API, both GraphQL and LDAP
-
-## Authentication
-
-### Passwords
-
-Passwords are hashed using Argon2, the state of the art in terms of password
-storage. They are hashed using a secret provided in the configuration (which
-can be given as environment variable or command line argument as well): this
-should be kept secret and shouldn't change (it would invalidate all passwords).
-
-Authentication is done via the OPAQUE protocol, meaning that the passwords are
-never sent to the server, but instead the client proves that they know the
-correct password (zero-knowledge proof). This is likely overkill, especially
-considered that the LDAP interface requires sending the password to the server,
-but it's one less potential flaw (especially since the LDAP interface can be
-restricted to an internal docker-only network while the web app is exposed to
-the Internet).
-
-### JWTs and refresh tokens
-
-When logging in for the first time, users are provided with a refresh token
-that gets stored in an HTTP-only cookie, valid for 30 days. They can use this
-token to get a JWT to get access to various servers: the JWT lists the groups
-the user belongs to. To simplify the setup, there is a single JWT secret that
-should be shared between the authentication server and the application servers;
-and users don't get a different token per application server
-(this could be implemented, we just didn't have any use case yet).
-
-JWTs are only valid for one day: when they expire, a new JWT can be obtained
-from the authentication server using the refresh token. If the user stays
-logged in, they would only have to type their password once a month.
-
-#### Logout
-
-In order to handle logout correctly, we rely on a blacklist of JWTs. When a
-user logs out, their refresh token is removed from the backend, and all of
-their currently valid JWTs are added to a blacklist. Incoming requests are
-checked against this blacklist (in-memory, faster than calling the database).
-Applications that want to use these JWTs should subscribe to be notified of
-blacklisted JWTs (TODO: implement the PubSub service and API).
-
## Contributions
Contributions are welcome! Just fork and open a PR. Or just file a bug.
diff --git a/architecture.md b/architecture.md
new file mode 100644
index 0000000..df3002c
--- /dev/null
+++ b/architecture.md
@@ -0,0 +1,84 @@
+# Architecture
+
+The server is entirely written in Rust, using [actix](https://actix.rs) for the
+backend and [yew](https://yew.rs) for the frontend.
+
+Backend:
+* Listens on a port for LDAP protocol.
+ * Only a small, read-only subset of the LDAP protocol is supported.
+ * An extension to allow resetting the password through LDAP will be added.
+* Listens on another port for HTTP traffic.
+ * The authentication API, based on JWTs, is under "/auth".
+ * The user management API is a GraphQL API under "/api/graphql". The schema
+ is defined in `schema.graphql`.
+ * The static frontend files are served by this port too.
+
+Note that secure protocols (LDAPS, HTTPS) are currently not supported. This can
+be worked around by using a reverse proxy in front of the server (for the HTTP
+API) that wraps/unwraps the HTTPS messages, or only open the service to
+localhost or other trusted docker containers (for the LDAP API).
+
+Frontend:
+* User management UI.
+* Written in Rust compiled to WASM as an SPA with the Yew library.
+* Based on components, with a React-like organization.
+
+Data storage:
+* The data (users, groups, memberships, active JWTs, ...) is stored in SQL.
+* Currently only SQLite is supported (see
+ https://github.com/launchbadge/sqlx/issues/1225 for what blocks us from
+ supporting more SQL backends).
+
+### Code organization
+
+* `auth/`: Contains the shared structures needed for authentication, the
+ interface between front and back-end. In particular, it contains the OPAQUE
+ structures and the JWT format.
+* `app/`: The frontend.
+ * `src/components`: The elements containing the business and display logic of
+ the various pages and their components.
+ * `src/infra`: Various tools and utilities.
+* `server/`: The backend.
+ * `src/domain/`: Domain-specific logic: users, groups, checking passwords...
+ * `src/infra/`: API, both GraphQL and LDAP
+
+## Authentication
+
+### Passwords
+
+Passwords are hashed using Argon2, the state of the art in terms of password
+storage. They are hashed using a secret provided in the configuration (which
+can be given as environment variable or command line argument as well): this
+should be kept secret and shouldn't change (it would invalidate all passwords).
+
+Authentication is done via the OPAQUE protocol, meaning that the passwords are
+never sent to the server, but instead the client proves that they know the
+correct password (zero-knowledge proof). This is likely overkill, especially
+considered that the LDAP interface requires sending the password to the server,
+but it's one less potential flaw (especially since the LDAP interface can be
+restricted to an internal docker-only network while the web app is exposed to
+the Internet).
+
+### JWTs and refresh tokens
+
+When logging in for the first time, users are provided with a refresh token
+that gets stored in an HTTP-only cookie, valid for 30 days. They can use this
+token to get a JWT to get access to various servers: the JWT lists the groups
+the user belongs to. To simplify the setup, there is a single JWT secret that
+should be shared between the authentication server and the application servers;
+and users don't get a different token per application server
+(this could be implemented, we just didn't have any use case yet).
+
+JWTs are only valid for one day: when they expire, a new JWT can be obtained
+from the authentication server using the refresh token. If the user stays
+logged in, they would only have to type their password once a month.
+
+#### Logout
+
+In order to handle logout correctly, we rely on a blacklist of JWTs. When a
+user logs out, their refresh token is removed from the backend, and all of
+their currently valid JWTs are added to a blacklist. Incoming requests are
+checked against this blacklist (in-memory, faster than calling the database).
+Applications that want to use these JWTs should subscribe to be notified of
+blacklisted JWTs (TODO: implement the PubSub service and API).
+