groupware: make everything also work with the built-in LDAP and IDP

This commit is contained in:
Pascal Bleser
2025-10-08 19:20:31 +02:00
parent 6ab30db64c
commit 3f978d7870
5 changed files with 302 additions and 69 deletions

View File

@@ -3,7 +3,7 @@ authentication.fallback-admin.user = "mailadmin"
authentication.master.secret = "$6$4qPYDVhaUHkKcY7s$bB6qhcukb9oFNYRIvaDZgbwxrMa2RvF5dumCjkBFdX19lSNqrgKltf3aPrFMuQQKkZpK2YNuQ83hB1B3NiWzj."
authentication.master.user = "master"
directory.idmldap.attributes.class = "objectClass"
directory.idmldap.attributes.description = "description"
directory.idmldap.attributes.description = "displayName"
directory.idmldap.attributes.email = "mail"
directory.idmldap.attributes.groups = "memberOf"
directory.idmldap.attributes.name = "uid"
@@ -95,7 +95,7 @@ server.socket.reuse-addr = true
server.socket.reuse-port = true
storage.blob = "rocksdb"
storage.data = "rocksdb"
storage.directory = "ldap"
storage.directory = "%{env:STALWART_AUTH_DIRECTORY}%"
storage.fts = "rocksdb"
storage.lookup = "rocksdb"
store.rocksdb.compression = "lz4"

View File

@@ -24,6 +24,7 @@ services:
OC_LDAP_SERVER_WRITE_ENABLED: "false" # assuming the external ldap is not writable
# OC_RUN_SERVICES specifies to start all services except glauth, idm and accounts. These are replaced by external services
OC_EXCLUDE_RUN_SERVICES: idm
STALWART_AUTH_DIRECTORY: "ldap"
ldap-server:
image: bitnamilegacy/openldap:2.6

View File

@@ -58,6 +58,9 @@ services:
COMPANION_DOMAIN: ${COMPANION_DOMAIN:-companion.opencloud.test}
# enable to allow using the banned passwords list
OC_PASSWORD_POLICY_BANNED_PASSWORDS_LIST: banned-password-list.txt
IDM_REVASVC_PASSWORD: "admin"
OC_LDAP_BIND_PASSWORD: "admin"
IDM_LDAPS_ADDR: 0.0.0.0:9235
volumes:
- ./config/opencloud/app-registry.yaml:/etc/opencloud/app-registry.yaml
- ./config/opencloud/csp.yaml:/etc/opencloud/csp.yaml

View File

@@ -19,6 +19,8 @@ services:
- /etc/localtime:/etc/localtime:ro
- ./config/stalwart:/opt/stalwart/etc
- stalwart-data:/opt/stalwart/data
environment:
STALWART_AUTH_DIRECTORY: "${STALWART_AUTH_DIRECTORY:-idmldap}"
labels:
- "traefik.enable=true"
- "traefik.http.routers.stalwart.entrypoints=https"

View File

@@ -1,18 +1,21 @@
# Introduction
# Groupware Developer Guide
The Groupware component of OpenCloud
<!-- markdownlint-disable MD033 -->
* is implemented as yet another microservice within the OpenCloud framework (see `./services/groupware/`)
* is essentially providing a REST API to the OpenCloud UI clients (web, mobile) that is high-level and adapted to the needs of the UIs
* the implementation of that REST API turns those high-level APIs into lower-level [JMAP](https://jmap.io/) API calls to [Stalwart, the JMAP mail server](https://stalw.art/), using our own JMAP client library in `./pkg/jmap/` with a couple of additional RFCs used by JMAP in `./pkg/jscalendar` and `./pkg/jscontact`
## Introduction
# Repository
The Groupware component of OpenCloud is implemented as a (micro)service within the OpenCloud framework (see `./services/groupware/`).
The code lives in the same tree as the other OpenCloud backend services, albeit in the `groupware` branch, that gets rebased on `main` on a regular basis (at least once per week.)
It is essentially providing a REST API to the OpenCloud UI clients (web, mobile) that is high-level and adapted to the needs of the UIs.
The implementation of that REST API turns those high-level APIs into lower-level [JMAP](https://jmap.io/) API calls to [Stalwart, the JMAP mail server](https://stalw.art/), using our own JMAP client library in `./pkg/jmap/` with a couple of additional RFCs used by JMAP in `./pkg/jscalendar` and `./pkg/jscontact`.
## Repository
The code lives in the same tree as the other OpenCloud backend services, albeit currently in the `groupware` branch, that gets rebased on `main` on a regular basis (at least once per week.)
Use [the `groupware` branch](https://github.com/opencloud-eu/opencloud/tree/groupware)
```bash
cd ~/src/opencloud/
OCDIR="$PWD"
@@ -21,6 +24,8 @@ git clone --branch groupware git@github.com:opencloud-eu/opencloud.git
Note that setting the variable `OCDIR` is merely going to help us with keeping the instructions below as generic as possible, it is not an environment variable that is used by OpenCloud.
### Tools Repository
Also, you might want to check out these [helper scripts in opencloud-tools](https://github.com/pbleser-oc/opencloud-tools) somewhere and put that directory into your `PATH`, as it contains scripts to test and build the OpenCloud Groupware:
```bash
@@ -29,24 +34,27 @@ git clone git@github.com:pbleser-oc/opencloud-tools.git ./bin
echo "export PATH=\"\$PATH:$OCDIR/bin\"" >> ~/.bashrc
```
#### Tools Prerequisites
Those scripts have the following prerequisites:
* the [`jq`](https://github.com/jqlang/jq) JSON query command-line tool to extract access tokens,
* either the [httpie](https://httpie.io/cli) (`pipx install httpie`) or [`xh`](https://github.com/ducaale/xh) (`cargo install xh --locked`) command-line HTTP clients, just out of convenience as their output is much nicer than curl's
* `curl` as well, to retrieve the access tokens from Keycloak (no need for nice output there)
# Running
Since we require having a Stalwart container running at the very least, the preferred way of running OpenCloud and its adjacent services for developing the Groupware component is by using the `opencloud_full` Docker Compose setup in `$OCDIR/devtools/deployments/opencloud_full`.
## Configuration
The default hostname domain for the containers is `.opencloud.test`
Since we require having a Stalwart container running at the very least, the preferred way of running OpenCloud and its adjacent services for developing the Groupware component is by using the `opencloud_full` Docker Compose setup in `$OCDIR/opencloud/devtools/deployments/opencloud_full/`.
This section will explain how to configure that Docker Compose setup for the needs of the Groupware backend.
### Hosts
The default hostname domain for the containers is `.opencloud.test`
Make sure to have the following entries in your `/etc/hosts`:
```text
```ruby
127.0.0.1 cloud.opencloud.test
127.0.0.1 keycloak.opencloud.test
127.0.0.1 wopiserver.opencloud.test
@@ -71,9 +79,21 @@ done \
### Compose
It first needs to be tuned a little, and for that, edit `$OCDIR/opencloud/devtools/deployments/opencloud_full/.env`, making the following changes (make sure to check out [the shell command-line that automates all of that, below](#automate-env-setup)):
There are two options, either
1. running the Groupware backend with OpenLDAP and Keycloak containers, more akin to a production setup;
2. running the Groupware backend using the built-in LDAP and OIDC services, for a minimalistic setup that uses less resources and is more likely to be found in a home lab setup.
In either case, the Docker Compose configuration in `$OCDIR/opencloud/devtools/deployments/opencloud_full/` needs to be modified.
#### Production Setup
<a name="prod-setup"></a>
Edit `$OCDIR/opencloud/devtools/deployments/opencloud_full/.env`, making the following changes (make sure to check out [the shell command-line that automates all of that, below](#automate-env-setup-prod)):
* change the container image to `opencloudeu/opencloud:dev`:
```diff
-OC_DOCKER_IMAGE=opencloudeu/opencloud-rolling
+OC_DOCKER_IMAGE=opencloudeu/opencloud
@@ -82,36 +102,42 @@ It first needs to be tuned a little, and for that, edit `$OCDIR/opencloud/devtoo
```
* add the `groupware` service to `START_ADDITIONAL_SERVICES`:
```diff
-START_ADDITIONAL_SERVICES="notifications"
+START_ADDITIONAL_SERVICES="notifications,groupware"
```
* enable the OpenLDAP container:
```diff
-#LDAP=:ldap.yml
+LDAP=:ldap.yml
```
* enable the Keycloak container:
```diff
-#KEYCLOAK=:keycloak.yml
+KEYCLOAK=:keycloak.yml
```
* enable the Stalwart container:
```diff
-#STALWART=:stalwart.yml
+STALWART=:stalwart.yml
```
* optionally disable the Collabora container
```diff
-COLLABORA=:collabora.yml
+#COLLABORA=:collabora.yml
```
* optionally disable UI containers
```diff
-UNZIP=:web_extensions/unzip.yml
-DRAWIO=:web_extensions/drawio.yml
@@ -125,7 +151,7 @@ It first needs to be tuned a little, and for that, edit `$OCDIR/opencloud/devtoo
+#EXTERNALSITES=:web_extensions/externalsites.yml
```
<a name="automate-env-setup"></a>
<a name="automate-env-setup-prod"></a>
All those changes above can be automated with the following script:
```bash
@@ -133,14 +159,122 @@ cd "$OCDIR/opencloud/devtools/deployments/opencloud_full/"
perl -pi -e '
s|^(OC_DOCKER_IMAGE)=.*$|$1=opencloudeu/opencloud|;
s|^(OC_DOCKER_TAG)=.*$|$1=dev|;
s|^(START_ADDITIONAL_SERVICES=".*)"|$1,groupware"|;
s|^([A-Z]+=:web_extensions/.*yml)$|#$1|;
s,^(COLLABORA)=(.+)$,#$1=$2,;
s|^(START_ADDITIONAL_SERVICES=".*(?<!groupware))"|$1,groupware"|;
s,^#(LDAP|KEYCLOAK|STALWART)=(.+)$,$1=$2,;
' .env
```
## Running
To disable Web UI services in case you are only interested in the backend service(s):
```bash
cd "$OCDIR/opencloud/devtools/deployments/opencloud_full/"
perl -pi -e '
s|^([A-Z]+=:web_extensions/.*yml)$|#$1|;
s,^(COLLABORA)=(.+)$,#$1=$2,;
' .env
```
#### Homelab Setup
<a name="homelab-setup"></a>
Edit `$OCDIR/opencloud/devtools/deployments/opencloud_full/.env`, making the following changes (make sure to check out [the shell command-line that automates all of that, below](#automate-env-setup-homelab)):
* change the container image to `opencloudeu/opencloud:dev`:
```diff
-OC_DOCKER_IMAGE=opencloudeu/opencloud-rolling
+OC_DOCKER_IMAGE=opencloudeu/opencloud
-OC_DOCKER_TAG=
+OC_DOCKER_TAG=dev
```
* enable the creation of demo users:
```diff
-DEMO_USERS=
+DEMO_USERS=true
```
* add the `groupware` service to `START_ADDITIONAL_SERVICES`:
```diff
-START_ADDITIONAL_SERVICES="notifications"
+START_ADDITIONAL_SERVICES="notifications,groupware"
```
* enable the Stalwart container:
```diff
-#STALWART=:stalwart.yml
+STALWART=:stalwart.yml
```
* while not required, it is recommended to enable basic authentication support which, while less secure, allows for easier tooling when developing and testing HTTP APIs, by adding `PROXY_ENABLE_BASIC_AUTH=true` somewhere before the last line of the `.env` file:
```diff
# Domain of Stalwart
# Defaults to "stalwart.opencloud.test"
STALWART_DOMAIN=
+# Enable basic authentication to facilitate HTTP API testing
+# Do not do this in production.
+PROXY_ENABLE_BASIC_AUTH=true
+
## IMPORTANT ##
```
* optionally disable the Collabora container
```diff
-COLLABORA=:collabora.yml
+#COLLABORA=:collabora.yml
```
* optionally disable UI containers
```diff
-UNZIP=:web_extensions/unzip.yml
-DRAWIO=:web_extensions/drawio.yml
-JSONVIEWER=:web_extensions/jsonviewer.yml
-PROGRESSBARS=:web_extensions/progressbars.yml
-EXTERNALSITES=:web_extensions/externalsites.yml
+#UNZIP=:web_extensions/unzip.yml
+#DRAWIO=:web_extensions/drawio.yml
+#JSONVIEWER=:web_extensions/jsonviewer.yml
+#PROGRESSBARS=:web_extensions/progressbars.yml
+#EXTERNALSITES=:web_extensions/externalsites.yml
```
<a name="automate-env-setup-homelab"></a>
All those changes above can be automated with the following script:
```bash
cd "$OCDIR/opencloud/devtools/deployments/opencloud_full/"
perl -pi -e '
BEGIN{$basic_auth=0}
s|^(OC_DOCKER_IMAGE)=.*$|$1=opencloudeu/opencloud|;
s|^(OC_DOCKER_TAG)=.*$|$1=dev|;
s|^(START_ADDITIONAL_SERVICES=".*(?<!groupware))"|$1,groupware"|;
s,^(DEMO_USERS)=.+,$1=true,;
s,^#(STALWART)=(.+)$,$1=$2,;
s,^#(PROXY_ENABLE_BASIC_AUTH)=(.*)$,$1=true,;
$basic_auth=1 if /^PROXY_ENABLE_BASIC_AUTH=/;
print "\n# Enable basic authentication to facilitate HTTP API testing\n# Do not do this in production.\nPROXY_ENABLE_BASIC_AUTH=true\n\n" if /^## IMPORTANT ##/ && !$basic_auth;
' .env
```
To disable Web UI services in case you are only interested in the backend service(s):
```bash
cd "$OCDIR/opencloud/devtools/deployments/opencloud_full/"
perl -pi -e '
s|^([A-Z]+=:web_extensions/.*yml)$|#$1|;
s,^(COLLABORA)=(.+)$,#$1=$2,;
' .env
```
## Building
Build the `opencloudeu/opencloud:dev` image first:
@@ -156,6 +290,8 @@ make -C ./opencloud/services/idp/ generate
make -C ./opencloud/ clean build dev-docker
```
## Running
And then either run everything from the Docker Compose `opencloud_full` setup:
```bash
@@ -166,7 +302,8 @@ docker compose up -d
or, if you plan to make changes to the backend code base, it might be more convenient to do so from within VSCode, in which case you should run all the services from the Docker Compose setup as above, but stop the `opencloud` service container (as that one will be running from within your IDE instead):
```bash
docker stop opencloud_full-opencloud-1
cd "$OCDIR/opencloud/devtools/deployments/opencloud_full/"
docker compose stop opencloud
```
and then use the Launcher `OpenCloud server with external services` in VSCode.
@@ -177,13 +314,31 @@ To check whether the various services are running correctly:
### LDAP
#### Production Setup LDAP
When using the &ldquo;production&rdquo; setup (as depicted in section [Production Setup](#prod-setup) above), queries can be performed directly against the \
OpenLDAP container (`opencloud_full-openldap-1`) since its LDAP ports are mapped onto the host (to `:389` and `:636` for LDAP and LDAPS, respectively).
When using the OpenLDAP container, the necessary LDAP parameters are as follows:
* <u>Bind DN:</u> `cn=admin,dc=opencloud,dc=eu`
* <u>Bind Password:</u> `admin`
* <u>Base DN:</u> `ou=users,dc=opencloud,dc=eu`
* <u>Host:</u> `localhost`
* <u>LDAP Port:</u> `389`
* <u>LDAPS Port:</u> `636`
Run the following command on your host (requires the `ldap-tools` package with the `ldapsearch` CLI tool), which should output a list of DNs of demo users:
```bash
ldapsearch -h localhost -D 'cn=admin,dc=opencloud,dc=eu' -x -w 'admin' -b 'ou=users,dc=opencloud,dc=eu' -LLL '(objectClass=person)' dn
ldapsearch -h localhost -D 'cn=admin,dc=opencloud,dc=eu' \
-x -w 'admin' -b 'ou=users,dc=opencloud,dc=eu' -LLL \
'(objectClass=person)' dn
```
Sample output:
```text
```ldif
dn: uid=alan,ou=users,dc=opencloud,dc=eu
dn: uid=lynn,ou=users,dc=opencloud,dc=eu
@@ -198,20 +353,82 @@ dn: uid=margaret,ou=users,dc=opencloud,dc=eu
```
### Keycloak
#### Homelab Setup LDAP
Instead, when using the &ldquo;homelab&rdquo; setup (as depicted in section [Homelab Setup](#homelab-setup) above), queries cannot be performed directly from the host \
but, instead, require spinning up another container in the same Docker network and do so from there.
The necessary LDAP parameters are as follows:
* <u>Bind DN:</u> `uid=libregraph,ou=sysusers,o=libregraph-idm`
* <u>Bind Password:</u> `admin` (or whichever password is set in the `IDM_REVASVC_PASSWORD` environment variable in `opencloud.yml`)
* <u>Base DN:</u> `o=libregraph-idm` or ``
* <u>Host:</u> `localhost`
* <u>LDAP Port:</u> none, only supports LDAPS
* <u>LDAPS Port:</u> `9235`
To access the LDAP tree, spawn a new container in the same network, e.g. like this for a Debian 12 container:
To check whether it works correctly:
```bash
curl -ks -D- -X POST "https://keycloak.opencloud.test/realms/openCloud/protocol/openid-connect/token" -d username=alan -d password=demo -d grant_type=password -d client_id=groupware -d scope=openid
docker run --network 'opencloud_full_opencloud-net' --rm \
--name "debian-${RANDOM}" -ti 'debian:12'
```
should provide you with a JSON response that contains an `access_token`.
In that container, install the necessary packages to have the LDAP command-line tools:
```bash
apt-get update -y && apt-get install -y ca-certificates ldap-utils
```
Run the following command in that container, which should output a list of DNs of demo users:
```bash
LDAPTLS_REQCERT=never ldapsearch -H ldaps://opencloud:9235 \
-D 'uid=reva,ou=sysusers,o=libregraph-idm' -x -w 'admin' \
-b 'o=libregraph-idm' -LLL \
'(objectClass=person)' dn
```
Sample output:
```ldif
dn: uid=admin,ou=users,o=libregraph-idm
dn: uid=alan,ou=users,o=libregraph-idm
dn: uid=lynn,ou=users,o=libregraph-idm
dn: uid=mary,ou=users,o=libregraph-idm
dn: uid=margaret,ou=users,o=libregraph-idm
dn: uid=dennis,ou=users,o=libregraph-idm
```
### Testing Keycloak
> [!NOTE]
> Only available in the [&ldquo;production&rdquo; setup](#prod-setup)
To check whether it works correctly, the following `curl` command:
```bash
curl -ks -D- -X POST \
"https://keycloak.opencloud.test/realms/openCloud/protocol/openid-connect/token" \
-d username=alan -d password=demo -d grant_type=password \
-d client_id=groupware -d scope=openid
```
should provide you with a JSON response that contains an `access_token` property.
If it is not set up correctly, it should give you this instead:
```json
{"error":"invalid_client","error_description":"Invalid client or Invalid client credentials"}
```
### Stalwart
### Testing Stalwart
To then test the IMAP authentication with Stalwart, run the following command on your host (requires the `openssl` CLI tool):
@@ -220,17 +437,20 @@ openssl s_client -crlf -connect localhost:993
```
When then greeted with the following prompt:
```text
```java
* OK [CAPABILITY ...] Stalwart IMAP4rev2 at your service.
```
enter the following command:
```text
```bash
A LOGIN alan demo
```
to which one should receive the following response:
```text
```java
A OK [CAPABILITY IMAP4rev2 ...] Authentication successful
```
@@ -241,17 +461,25 @@ Once a [Stalwart](https://stalw.art/) container is running (using the Docker Com
```bash
cd "$OCDIR/"
git clone git@github.com:opencloud-eu/imap-filler.git
cd ./imap-filler
go run . --empty=true --username=alan --password=demo \
--url=localhost:993 --folder=Inbox --senders=3 --count=20
cd ./imap-filler/
go run . --username=alan --password=demo \
--url=localhost:993 \
--empty=true \
--folder=Inbox \
--senders=6 \
--count=50
```
> [!NOTE]
> Note that this operation does not use the Groupware APIs or any other OpenCloud backend services either,
> as it directly communicates with Stalwart via IMAPS on port `993` which is mapped on the host.
For more details on the usage of that little helper tool, consult its [`README.md`](https://github.com/opencloud-eu/imap-filler/blob/main/README.md), although it is quite self-explanatory.
> [!NOTE]
> This only needs to be done once, since the emails are stored in a volume used by the Stalwart container.
# Building
## Building after Changes
If you run the `opencloud` service as a container, use the following script to update the container image and restart it:
@@ -270,7 +498,7 @@ docker compose up -d opencloud
If you run it from your IDE, there is obviously no need to do that.
# API Docs
## API Docs
The REST API documentation is extracted from the source code structure and documentation using [`go-swagger`](https://goswagger.io/go-swagger/), which needs to be installed locally as a prerequisite:
@@ -293,7 +521,7 @@ firefox ./api.html
Note that `redocly-cli` does not need to be installed, it will be pulled locally by the `Makefile`, provided that you have [pnpm](https://pnpm.io/) installed as a pre-requisite, which is already necessary for other OpenCloud components.
# Testing
## Testing
This section assumes that you are using the [helper scripts in opencloud-tools](https://github.com/pbleser-oc/opencloud-tools) as instructed above.
@@ -304,12 +532,17 @@ export baseurl=https://localhost:9200
```
The scripts default to using the user `alan` (with the password `demo`), which can be changed by setting the following environment variables:
* `username`
* `password`
Your main swiss army knife tool will be `oc-gw` (mnemonic for "OpenCloud Groupware"), which
* always retrieves an access token from Keycloak, using the credentials defined in the environment variables `username` and `password` (defaulting to `adam`/`demo`), using the "Direct Access Grant" OIDC or "Resource Owner Password Credentials Grant" OAuth2 flow
* and then use that JWT for Bearer authentication against the OpenCloud Groupware REST API
* checks whether a container named `opencloud_full-opencloud-1` is running locally
* if so, whether it has basic auth enabled or not
* if yes, uses basic auth directly to authenticate against the OpenCloud Proxy service that ingresses for the OpenCloud Groupware backend, using the credentials defined in the environment variables `username` and `password` (defaulting to `alan`/`demo`)
* if not, always retrieves a fresh access token from Keycloak, using the credentials defined in the environment variables `username` and `password` (defaulting to `alan`/`demo`), using the "Direct Access Grant" OIDC API of Keycloak and then use that JWT for Bearer authentication against the OpenCloud Groupware REST API
* if no such container is running locally, it assumes that the `opencloud` process is running from within an IDE, with its OpenCloud Proxy service listening on `https://localhost:9200`
It will also save you some typing as whenever you use `//` for the URL, it will replace that by the Groupware REST API base URL, e.g.
@@ -333,10 +566,14 @@ Obviously, you may use whichever HTTP client you are most comfortable with.
Here is how to do it without the `oc-gw` script, using [`curl`](https://curl.se/):
First, make sure to retrieve a JWT for authentication from Keycloak:
When using the &ldquo;production&rdquo; setup, first make sure to retrieve a JWT for authentication from Keycloak:
```bash
token=$(curl --silent --insecure --fail -X POST "https://keycloak.opencloud.test/realms/openCloud/protocol/openid-connect/token" -d username="alan" -d password="demo" -d grant_type=password -d client_id="groupware" -d scope=openid | jq -r '.access_token')
token=$(curl --silent --insecure --fail -X POST \
"https://keycloak.opencloud.test/realms/openCloud/protocol/openid-connect/token" \
-d username="alan" -d password="demo" \
-d grant_type=password -d client_id="groupware" -d scope=openid \
| jq -r '.access_token')
```
Then use that token to authenticate the Groupware API request:
@@ -345,61 +582,51 @@ Then use that token to authenticate the Groupware API request:
curl --insecure -s -H "Authorization: Bearer ${token}" "https://cloud.opencloud.test/groupware/"
```
When using the &ldquo;homelab&rdquo; setup, authenticate directly using basic auth:
```bash
curl --insecure -s -u "alan:demo" "https://cloud.opencloud.test/groupware/"
```
> [!TIP]
> Until everything is documented, the complete list of URI routes can be found in `$OCDIR/opencloud/services/groupware/pkg/groupware/groupware_route.go`
> Until everything is documented, the complete list of URI routes can be found in \
[`$OCDIR/opencloud/services/groupware/pkg/groupware/groupware_route.go`](./pkg/groupware/groupware_route.go)
# Services
## Services
## Stalwart
### Stalwart
### Web UI
#### Web UI
To access the Stalwart admin UI, open <https://stalwart.opencloud.test/> and use the following credentials to log in:
* username: `mailadmin`
* password: `admin`
The usual admin username `admin` had to be changed into `mailadmin` because there is already an `admin` user that ships with the default users in OpenCloud, and Stalwart always checks the LDAP directory before its internal usernames.
Those credentials are configured in `devtools/deployments/opencloud_full/config/stalwart/config.toml`:
```ruby
authentication.fallback-admin.secret = "$6$4qPYDVhaUHkKcY7s$bB6qhcukb9oFNYRIvaDZgbwxrMa2RvF5dumCjkBFdX19lSNqrgKltf3aPrFMuQQKkZpK2YNuQ83hB1B3NiWzj."
authentication.fallback-admin.user = "mailadmin"
```
### Restart from Scratch
#### Restart from Scratch
To start with a Stalwart container from scratch, removing all the data (including emails):
```bash
cd "$OCDIR/opencloud/devtools/deployments/opencloud_full"
docker compose stop stalwart
docker compose rm stalwart
docker compose rm stalwart --stop
docker volume rm opencloud_full_stalwart-data
docker compose up -d stalwart
```
### Diagnostics
#### Diagnostics
If anything goes wrong, the first thing to check is Stalwart's logs, that are configured on the most verbose level (trace) and should thus provide a lot of insight:
```bash
docker logs -f opencloud_full-stalwart-1
```
## OpenLDAP
The `opencloud_full-ldap-server-1` container exports the ports 389 (LDAP) and 636 (LDAPS) on the host.
To access the LDAP tree:
* Host: `localhost`
* Port: `389`
* Bind DN: `cn=admin,dc=opencloud,dc=eu`
* Password: `admin`
* Base DN: `dc=opencloud,dc=eu`
As an example, to list all the users, using the `ldap-tools` on your host:
```bash
ldapsearch -h localhost -D 'cn=admin,dc=opencloud,dc=eu' -x -w 'admin' -b 'ou=users,dc=opencloud,dc=eu' -LLL '(objectClass=person)'
```