Skip to content

Update Keycloak backend #57

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 7 commits into from
Jan 15, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
215 changes: 117 additions & 98 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ The name is a slightly tortured acronym for: LD**A**P **pr**oxy for Open**I**D *
Start the `Apricot` server on port 1389 by running:

```bash
python run.py --client-id "<your client ID>" --client-secret "<your client secret>" --backend "<your backend>" --port 1389 --domain "<your domain name>" --redis-host "<your Redis server>"
python run.py --client-id "<your client ID>" --client-secret "<your client secret>" --backend "<your backend>" --port "<your desired port>" --domain "<your domain name>" --redis-host "<your Redis server>"
```

If you prefer to use Docker, you can edit `docker/docker-compose.yaml` and run:
Expand Down Expand Up @@ -41,22 +41,22 @@ To enable it you need to configure the tls port ex. `--tls-port=1636`, and provi
This will create an LDAP tree that looks like this:

```ldif
dn: DC=<your domain>
dn: DC=example,DC=com
objectClass: dcObject

dn: OU=users,DC=<your domain>
dn: OU=users,DC=example,DC=com
objectClass: organizationalUnit
ou: users

dn: OU=groups,DC=<your domain>
dn: OU=groups,DC=example,DC=com
objectClass: organizationalUnit
ou: groups
```

Each user will have an entry like

```ldif
dn: CN=<user name>,OU=users,DC=<your domain>
dn: CN=<user name>,OU=users,DC=example,DC=com
objectClass: inetOrgPerson
objectClass: organizationalPerson
objectClass: person
Expand All @@ -69,73 +69,166 @@ memberOf: <DN for each group that this user belongs to>
Each group will have an entry like

```ldif
dn: CN=<group name>,OU=groups,DC=<your domain>
dn: CN=<group name>,OU=groups,DC=example,DC=com
objectClass: groupOfNames
objectClass: posixGroup
objectClass: top
<group data fields here>
member: <DN for each user belonging to this group>
```

## Primary groups
## Querying the server

:exclamation: You can disable the creation of mirrored groups with the `--disable-primary-groups` command line option :exclamation:
Anonymous queries are enabled by default.

```bash
ldapsearch -H ldap://<your server location>:<your desired port> -x -b "DC=example,DC=com"
```

If you want to query on behalf of a particular user you will need to use the full distinguished name.

```bash
ldapsearch -H ldap://<your server location>:<your desired port> -x -b "DC=example,DC=com" -D "CN=<user name>,OU=users,DC=example,DC=com"
```

## OpenID Connect

Instructions for specific OpenID Connect backends below.

### Microsoft Entra

You will need to use the following command line arguments:

```bash
--backend MicrosoftEntra \
--entra-tenant-id "<your tenant ID>"
```

You will need to register an application to interact with `Microsoft Entra`.
Do this as follows:

- Create a new `App Registration` in your `Microsoft Entra`.
- Set the name to whatever you choose (e.g. `apricot`)
- Set access to `Accounts in this organizational directory only`.
- Set `Redirect URI` to `Public client/native (mobile & desktop)` with a value of `urn:ietf:wg:oauth:2.0:oob`
- Under `Certificates & secrets` add a `New client secret`
- Set the description to whatever you choose (e.g. `Apricot Authentication Secret`)
- Set the expiry time to whatever is relevant for your use-case
- You **must** record the value of this secret at **creation time**, as it will not be visible later.
- Under `API permissions`:
- Enable the following permissions:
- `Microsoft Graph` > `User.Read.All` (application)
- `Microsoft Graph` > `GroupMember.Read.All` (application)
- `Microsoft Graph` > `User.Read.All` (delegated)
- Select this and click the `Grant admin consent` button (otherwise each user will need to manually consent)

### Keycloak

You will need to use the following command line arguments:

```bash
--backend Keycloak \
--keycloak-base-url "<your hostname>/<path to keycloak>" \
--keycloak-domain-attribute "<the attribute used as your domain>" \
--keycloak-realm "<your realm>"
```

#### User domain attribute

You will need to add a custom attribute to each user you want Apricot to use.
The name of this attribute should be used as the value of the `--keycloak-domain-attribute` argument above.
The value of this attribute should be the same as the `--domain` argument to Apricot.

Any users with this attribute missing or set to something else will be ignored by Apricot.
This allows you to attach multiple Apricot servers to the same Keycloak instance, each with their own set of users.

:exclamation: You can disable user domain verification with the `--disable-user-domain-verification` command line option :exclamation:

#### Client application

You will need to register an application to interact with `Keycloak`.
Do this as follows:

- Create a new `Client` in your `Keycloak` instance.
- Under `General Settings`:
- Client type: `OpenID Connect`
- Client name: `apricot`
- Under `Capability config`
- Enable `Client authentication`
- Enable the following authentication flows and disable the rest:
- `Direct access grants`
- `Service accounts roles`
- Save the client
- For the client you have just created
- Under `Credentials` copy `client secret`
- Under `Service accounts roles`:
- Click on `Assign role` then `Filter by clients`
- Assign the following roles:
- `realm-management` > `view-users`
- `realm-management` > `manage-users`
- `realm-management` > `query-groups`
- `realm-management` > `query-users`

## Disabling Apricot groups

### Primary groups

Apricot creates an associated group for each user, which acts as its POSIX user primary group.

For example:

```ldif
dn: CN=sherlock.holmes,OU=users,DC=<your domain>
dn: CN=sherlock.holmes,OU=users,DC=example,DC=com
objectClass: inetOrgPerson
objectClass: organizationalPerson
objectClass: person
objectClass: posixAccount
objectClass: top
...
memberOf: CN=sherlock.holmes,OU=groups,DC=<your domain>
memberOf: CN=sherlock.holmes,OU=groups,DC=example,DC=com
...
```

will have an associated group

```ldif
dn: CN=sherlock.holmes,OU=groups,DC=<your domain>
dn: CN=sherlock.holmes,OU=groups,DC=example,DC=com
objectClass: groupOfNames
objectClass: posixGroup
objectClass: top
...
member: CN=sherlock.holmes,OU=users,DC=<your domain>
member: CN=sherlock.holmes,OU=users,DC=example,DC=com
...
```

## Mirrored groups
:exclamation: You can disable the creation of these groups with the `--disable-primary-groups` command line option :exclamation:

:exclamation: You can disable the creation of mirrored groups with the `--disable-mirrored-groups` command line option :exclamation:
## Mirrored groups

Each group of users will have an associated group-of-groups where each user in the group will have its user primary group in the group-of-groups.
Apricot creates a group-of-groups for each group of users.
This simply contains the primary group for each user in the original group.
Note that these groups-of-groups are **not** `posixGroup`s as POSIX does not allow nested groups.

For example:

```ldif
dn:CN=Detectives,OU=groups,DC=<your domain>
dn:CN=Detectives,OU=groups,DC=example,DC=com
objectClass: groupOfNames
objectClass: posixGroup
objectClass: top
...
member: CN=sherlock.holmes,OU=users,DC=<your domain>
member: CN=sherlock.holmes,OU=users,DC=example,DC=com
...
```

will have an associated group-of-groups

```ldif
dn: CN=Primary user groups for Detectives,OU=groups,DC=<your domain>
dn: CN=Primary user groups for Detectives,OU=groups,DC=example,DC=com
objectClass: groupOfNames
objectClass: top
...
member: CN=sherlock.holmes,OU=groups,DC=<your domain>
member: CN=sherlock.holmes,OU=groups,DC=example,DC=com
...
```

Expand All @@ -148,95 +241,21 @@ This allows a user to make a request for "all primary user groups needed by memb
which will return:

```ldif
dn:CN=Detectives,OU=groups,DC=<your domain>
dn:CN=Detectives,OU=groups,DC=example,DC=com
objectClass: groupOfNames
objectClass: posixGroup
objectClass: top
...
member: CN=sherlock.holmes,OU=users,DC=<your domain>
member: CN=sherlock.holmes,OU=users,DC=example,DC=com
...

dn: CN=sherlock.holmes,OU=groups,DC=<your domain>
dn: CN=sherlock.holmes,OU=groups,DC=example,DC=com
objectClass: groupOfNames
objectClass: posixGroup
objectClass: top
...
member: CN=sherlock.holmes,OU=users,DC=<your domain>
member: CN=sherlock.holmes,OU=users,DC=example,DC=com
...
```

## OpenID Connect

Instructions for specific OpenID Connect backends below.

### Microsoft Entra

You will need to use the following command line arguments:

```bash
--backend MicrosoftEntra \
--entra-tenant-id "<your tenant ID>"
```

You will need to register an application to interact with `Microsoft Entra`.
Do this as follows:

- Create a new `App Registration` in your `Microsoft Entra`.
- Set the name to whatever you choose (e.g. `apricot`)
- Set access to `Accounts in this organizational directory only`.
- Set `Redirect URI` to `Public client/native (mobile & desktop)` with a value of `urn:ietf:wg:oauth:2.0:oob`
- Under `Certificates & secrets` add a `New client secret`
- Set the description to whatever you choose (e.g. `Apricot Authentication Secret`)
- Set the expiry time to whatever is relevant for your use-case
- You **must** record the value of this secret at **creation time**, as it will not be visible later.
- Under `API permissions`:
- Enable the following permissions:
- `Microsoft Graph` > `User.Read.All` (application)
- `Microsoft Graph` > `GroupMember.Read.All` (application)
- `Microsoft Graph` > `User.Read.All` (delegated)
- Select this and click the `Grant admin consent` button (otherwise each user will need to manually consent)

### Keycloak

You will need to use the following command line arguments:

```bash
--backend Keycloak \
--keycloak-base-url "<your hostname>/<path to keycloak>" \
--keycloak-domain-attribute "<the attribute used as your domain>" \
--keycloak-realm "<your realm>"
```

You will need to register an application to interact with `Keycloak`.
Do this as follows:

- Under the realm option `Client scopes` create a new scope, e.g. `domainScope` with:
- Type: `Default`
- Include in token scope: `true`
- Save
- In the created scope click `Mappers` > `Configure new mapper` and now create either
- `Hardcoded claim`
- => Every user gets the same domain
- name: `domain`
- token claim name: `domain`
- claim value: `<your domain>`
- `User attribute`
- => Every user has an attribute for the domain
- name: `domain`
- user attribute: `<the attribute used as your domain>`
- token claim name: `domain`
- Create a new `Client` in your `Keycloak` instance.
- Set the name to whatever you choose (e.g. `apricot`)
- Enable `Client authentication`
- Enable the following authentication flows and disable the rest:
- Direct access grants
- Service account roles
- Under `Credentials` copy `client secret`
- Under `Service account roles`:
- Click on `Assign role` then `Filter by clients`
- Assign the following roles:
- `realm-management` > `view-users`
- `realm-management` > `manage-users`
- `realm-management` > `query-groups`
- `realm-management` > `query-users`
- Under `Client scopes` click `Add client scope` > `domainScope`. Make sure to select type `Default`
:exclamation: You can disable the creation of mirrored groups with the `--disable-mirrored-groups` command line option :exclamation:
Loading
Loading