Skip to content

Commit

Permalink
Merge pull request #30 from vshn/document
Browse files Browse the repository at this point in the history
docs: Document Keycloak setup and some use cases
  • Loading branch information
davidgubler authored Jan 13, 2025
2 parents 842c987 + 59f6fda commit 5f6d021
Show file tree
Hide file tree
Showing 4 changed files with 256 additions and 0 deletions.
39 changes: 39 additions & 0 deletions DOVECOT.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# How to use Dovecot with ldaplogin

This document contains some examples of how you can configure Dovecot to work with ldaplogin.

## Global configuration

This needs to go to the global Dovecot configuration.

```
passdb {
driver = ldap
args = /etc/dovecot/dovecot-ldap.conf
}
userdb {
driver = ldap
args = /etc/dovecot/dovecot-ldap.conf
}
```

## LDAP configuration

This needs to go to the config file referenced by the global configuration, e.g. `/etc/dovecot/dovecot-ldap.conf`.

Note that this example assumes that you use Dovecot with SOGo, hence it uses the SOGo credentials to access LDAP. This is also important to ensure that Dovecot sees the same data as SOGo sees.

```
uris = ldaps://ldap.ldaplogin.example.com:10636
dn = uid=sogo,ou=Services,dc=example,dc=com
dnpass = _$PASSWORD}
ldap_version = 3
base = dc=example,dc=com
deref = never
scope = subtree
user_filter = (&(mail=%u)(objectclass=inetorgperson))
user_attrs = mailQuota=quota_rule=*:storage=%$
auth_bind = yes
pass_filter = (&(mail=%u)(objectclass=inetorgperson))
pass_attrs =
```
130 changes: 130 additions & 0 deletions KEYCLOAK.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
# How to set up Keycloak for ldaplogin

In order to use ldaplogin you need to connect it to your IDP. This page explains how to do this with Keycloak.

## Basics

ldaplogin is a mostly normal Keycloak client, but for all the features to work you should add some additional data to the ID tokens.

* A `groups` field: List of strings containing full group paths. This is pretty much required for basic ldaplogin functionality.
* A `emailQuota` field: For some use cases you may need a `mailQuota` field in LDAP. ldaplogin can do this if it gets a `emailQuota` field with the ID token. Note that we use `emailQuota` on the Keycloak side of things while calling it `mailQuota` in LDAP. This is in line with the fact that Keycloak calls it `email` while LDAP calls it `mail`. The field contains an integer, preferably the quota in KiB.
* A `groups_metadata` field: List of JSON objects containing keys `path`, `description` and `email`. This is needed if you want to have the `description` and `mail` fields of Groups available in LDAP

## `groups` field

You should probably configure a Keycloak-wide Client scope called `groups` which adds this field.

* Log in to your Keycloak instance as admin and go to your realm
* Go to 'Client scopes'
* Click 'Create client scope' and use name "groups"
* Disable 'Display on consent screen'
* Choose Type 'Default'
* Disable 'Display on consent screen'
* Save
* Go to 'Mappers'
* Click 'Configure a new mapper'
* Click 'Group Membership'
* Choose Name "groups" and Token Claim Name "groups" (the latter is important for ldaplogin to work properly!)
* Make sure the following are on: 'Full group path', 'Add to ID token', 'Add to access token', 'Add to userinfo'
* Save

Now that you have the global Client scope configured, you can edit your client.

* Go to 'Clients'
* Find your ldaplogin client
* Go to tab 'Client scopes'
* Click 'Add client scope'
* Find the "groups" scope and add it

Now test the result.

* Below 'Client scopes' there is a second level of tabs. Click 'Evaluate'.
* Select a user in the 'Users' input field. The user must be member of at least one group.
* Click 'Generated user info'
* The resulting JSON needs to have a field `groups` with a list of full group paths.

## `emailQuota` field

Depending on your use cases it may make sense to configure a Keycloak-wide Client scope called `emailQuota` which adds this field.

* Log in to your Keycloak instance as admin and go to your realm
* Go to 'Client scopes'
* Click 'Create client scope' and use name "emailQuota"
* Choose Type 'Default'
* Disable 'Display on consent screen'
* Save
* Go to 'Mappers'
* Click 'Configure a new mapper'
* Click 'User Attribute'
* Choose Name "emailQuota", User Attribute "emailQuota" and Token Claim Name "emailQuota"
* Choose Claim JSON Type "String"
* Make sure the following are on: 'Full group path', 'Add to ID token', 'Add to access token', 'Add to userinfo'
* Save

Now that you have the global Client scope configured, you can edit your client.

* Go to 'Clients'
* Find your ldaplogin client
* Go to tab 'Client scopes'
* Click 'Add client scope'
* Find the "emailQuota" scope and add it

Now test the result.

* Below 'Client scopes' there is a second level of tabs. Click 'Evaluate'.
* Select a user in the 'Users' input field. The user must have an "emailQuota" attribute.
* Click 'Generated user info'
* The resulting JSON needs to have a field `emailQuota`.

## `groups_metadata` field

### Prerequisites

Keycloak groups can have attributes like `email` or `description`. However by default ldaplogin does not have access to these attributes, because they don't show up in the ID token. This section explains a configuration to make these group attributes available to ldaplogin.

In order to make this work, every group needs to have a single `group\_metadata` attribute (note the singular!) containing a string of JSON with the following format:
```
{"path":"_$PATH_","description":"_$DESCRIPTION_","email":"_$EMAIL_"}
```

You need to manually maintain this information somehow! There are no mechanisms in Keycloak to do this for you. Also note that even if your group already has an `email` or `description` attribute, you will have to maintain the `group\_metadata` attribute as well, even though it seems redundant.

With this 'group\_metadata' JSON available Keycloak can compile a 'groups\_metadata' (note the plural!) field in the ID token which contains detail about all of the user's groups. This section explains how to set this up.

### Setup

You should probably configure a Keycloak-wide Client scope called 'groups\_metadata' which adds this field.

* Log in to your Keycloak instance as admin and go to your realm
* Go to 'Client scopes'
* Click 'Create client scope' and use name "groups\_metadata"
* Choose Type 'Default'
* Disable 'Display on consent screen'
* Save
* Go to 'Mappers'
* Click 'Configure a new mapper'
* Click 'User Attribute'
* Choose Name "groups\_metadata"
* Choose User Attribute "group\_metadata" (note the singular!)
* Choose Token Claim Name "groups\_metadata" (note the plural!)
* Choose Claim JSON Type "JSON"
* Make sure the following are on: "Full group path", "Add to ID token", "Add to access token", "Add to userinfo"
* Enable "Multivalued" and "Aggregate attribute values"
* Save

Now that you have the global Client scope configured, you can edit your client.

* Go to 'Clients'
* Find your ldaplogin client
* Go to tab 'Client scopes'
* Click 'Add client scope'
* Find the 'groups\_metadata' scope and add it

Now test the result.

* Below 'Client scopes' there is a second level of tabs. Click 'Evaluate'.
* Select a user in the 'Users' input field. The user must be member of at least one group.
* Click 'Generated user info'
* The resulting JSON needs to have a field `groups_metadata` with a list of JSON objects describing the user's groups.

If it doesn't work check the Mapper configuration of your client scope and verify that the user is member of a group, and the group has a `group_metadata` attribute with valid JSON in it.
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,12 @@ LDAPLogin operates as follows:
* User logs in to the service using the regular user name and the temporary password
* Service connects to LDAPLogin's LDAP port in order to validate the temporary password and get all required user information

This document describes the configuration of ldaplogin itself. Other relevant documentation:

* [How to set up Keycloak for ldaplogin](KEYCLOAK.md)
* [How to use SOGo with ldaplogin](SOGO.md)
* [How to use Dovecot with ldaplogin](DOVECOT.md)

## Known issues

* Performance isn't optimized at all, especially handling of groups. You'll probably start noticing issues if you get into the 1000s of users.
Expand Down
81 changes: 81 additions & 0 deletions SOGO.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
# How to use SOGo with ldaplogin

This document contains some examples of how you can configure SOGo to work with ldaplogin.

## ldaplogin service configuration

This ldaplogin configuration is needed to provide LDAP for SOGo.

```
SERVICES=sogo
SERVICE_SOGO_PASSWORD=_$PASSWORD_
SERVICE_SOGO_NAME=SOGo
SERVICE_SOGO_URL=https://sogo.example.com
SERVICE_SOGO_GROUP=/mygroups/staff
SERVICE_SOGO_STATIC_PASSWORDS=true
SERVICE_SOGO_DYNAMIC_PASSWORDS_EXPIRE=604800 # 7 days, needs to be slightly longer than the maximum SOGo web session lifetime
```

## SOGoUserSources

This configuration adds:
* A user directory for authentication and as an address book
* A groups directory as an address book
* A resources directory for booking resources

```
/* LDAP authentication */
SOGoUserSources = (
{
type = ldap;
CNFieldName = cn;
UIDFieldName = uid;
IDFieldName = uid;
MailFieldNames = (mail, alias);
IMAPLoginFieldName = mail;
bindFields = (uid, mail);
baseDN = "ou=sogo,ou=People,dc=example,dc=com";
bindDN = "uid=sogo,ou=Services,dc=example,dc=com";
bindPassword = "_$PASSWORD_";
canAuthenticate = YES;
displayName = "Users";
hostname = "ldaps://ldap.ldaplogin.example.com:10636";
id = public;
isAddressBook = YES;
listRequiresDot = NO;
},
{
type = ldap;
CNFieldName = cn;
UIDFieldName = cn;
IDFieldName = cn;
baseDN = "ou=sogo,ou=Groups,dc=example,dc=com";
bindDN = "uid=sogo,ou=Services,dc=example,dc=com";
bindPassword = "_$PASSWORD_";
displayName = "Groups";
hostname = "ldaps://ldap.ldaplogin.example.com:10636";
id = example_com_groups;
isAddressBook = YES;
listRequiresDot = NO;
},
{
type = ldap;
CNFieldName = cn;
UIDFieldName = cn;
IDFieldName = cn;
MultipleBookingsFieldName = Multiplebookings;
KindFieldName = Kind;
baseDN = "ou=Resources,dc=example,dc=com";
bindDN = "uid=sogo,ou=Services,dc=example,dc=com";
bindPassword = "_$PASSWORD_";
/* sogo needs this to track permissions to the resource users */
canAuthenticate = YES;
displayName = "Resources";
hostname = "ldaps://ldap.ldaplogin.example.com:10636";
id = example_com_resources;
isAddressBook = YES;
listRequiresDot = NO;
}
);
```

0 comments on commit 5f6d021

Please sign in to comment.