https://github.com/alan-turing-institute/apricot
An OpenID Connect LDAP proxy
https://github.com/alan-turing-institute/apricot
data-safe-haven hacktoberfest hut23 hut23-1304
Last synced: 5 months ago
JSON representation
An OpenID Connect LDAP proxy
- Host: GitHub
- URL: https://github.com/alan-turing-institute/apricot
- Owner: alan-turing-institute
- License: bsd-3-clause
- Created: 2023-09-25T13:58:33.000Z (over 1 year ago)
- Default Branch: main
- Last Pushed: 2024-10-25T10:09:37.000Z (6 months ago)
- Last Synced: 2024-10-25T17:02:40.713Z (6 months ago)
- Topics: data-safe-haven, hacktoberfest, hut23, hut23-1304
- Language: Python
- Homepage:
- Size: 196 KB
- Stars: 14
- Watchers: 6
- Forks: 3
- Open Issues: 3
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# Apricot
`Apricot` is a proxy for delegating LDAP requests to an OpenID Connect backend.
The name is a slightly tortured acronym for: LD**A**P **pr**oxy for Open**I**D **Co**nnec**t**.## Usage
Start the `Apricot` server on port 1389 by running:
```bash
python run.py --client-id "" --client-secret "" --backend "" --port 1389 --domain "" --redis-host ""
```If you prefer to use Docker, you can edit `docker/docker-compose.yaml` and run:
```bash
docker compose up
```from the `docker` directory.
### Using Redis [Optional]
You can use a Redis server to store generated `uidNumber` and `gidNumber` values in a more persistent way.
To do this, you will need to provide the `--redis-host` and `--redis-port` arguments to `run.py`.### Configure background refresh [Optional]
By default Apricot will refresh the LDAP tree whenever it is accessed and it contains data older than 60 seconds.
If it takes a long time to fetch all users and groups, or you want to ensure that each request gets a prompt response, you may want to configure background refresh to have it periodically be refreshed in the background.This is enabled with the `--background-refresh` flag, which uses the `--refresh-interval` parameter as the interval to refresh the ldap database.
### Using TLS [Optional]
You can set up a TLS listener to communicate with encryption enabled over the configured port.
To enable it you need to configure the tls port ex. `--tls-port=1636`, and provide a path to the pem files for the certificate `--tls-certificate=` and the private key `--tls-private-key=`.## Outputs
This will create an LDAP tree that looks like this:
```ldif
dn: DC=
objectClass: dcObjectdn: OU=users,DC=
objectClass: organizationalUnit
ou: usersdn: OU=groups,DC=
objectClass: organizationalUnit
ou: groups
```Each user will have an entry like
```ldif
dn: CN=,OU=users,DC=
objectClass: inetOrgPerson
objectClass: organizationalPerson
objectClass: person
objectClass: posixAccount
objectClass: topmemberOf:
```Each group will have an entry like
```ldif
dn: CN=,OU=groups,DC=
objectClass: groupOfNames
objectClass: posixGroup
objectClass: topmember:
```## Primary groups
:exclamation: You can disable the creation of mirrored groups with the `--disable-primary-groups` command line option :exclamation:
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=
objectClass: inetOrgPerson
objectClass: organizationalPerson
objectClass: person
objectClass: posixAccount
objectClass: top
...
memberOf: CN=sherlock.holmes,OU=groups,DC=
...
```will have an associated group
```ldif
dn: CN=sherlock.holmes,OU=groups,DC=
objectClass: groupOfNames
objectClass: posixGroup
objectClass: top
...
member: CN=sherlock.holmes,OU=users,DC=
...
```## Mirrored groups
:exclamation: You can disable the creation of mirrored groups with the `--disable-mirrored-groups` command line option :exclamation:
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.
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=
objectClass: groupOfNames
objectClass: posixGroup
objectClass: top
...
member: CN=sherlock.holmes,OU=users,DC=
...
```will have an associated group-of-groups
```ldif
dn: CN=Primary user groups for Detectives,OU=groups,DC=
objectClass: groupOfNames
objectClass: top
...
member: CN=sherlock.holmes,OU=groups,DC=
...
```This allows a user to make a request for "all primary user groups needed by members of group X" without getting a large number of primary user groups for unrelated users. To do this, you will need an LDAP request that looks like:
```ldif
(&(objectClass=posixGroup)(|(CN=Detectives)(memberOf=Primary user groups for Detectives)))
```which will return:
```ldif
dn:CN=Detectives,OU=groups,DC=
objectClass: groupOfNames
objectClass: posixGroup
objectClass: top
...
member: CN=sherlock.holmes,OU=users,DC=
...dn: CN=sherlock.holmes,OU=groups,DC=
objectClass: groupOfNames
objectClass: posixGroup
objectClass: top
...
member: CN=sherlock.holmes,OU=users,DC=
...
```## 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 ""
```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 "/" \
--keycloak-domain-attribute "" \
--keycloak-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: ``
- `User attribute`
- => Every user has an attribute for the domain
- name: `domain`
- user attribute: ``
- 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`