mirror of
https://github.com/opencloud-eu/opencloud.git
synced 2026-07-13 09:02:09 -04:00
groupware: port opencloud_full to Stalwart 0.16 (only the built-in IDM LDAP option for now)
This commit is contained in:
@@ -0,0 +1 @@
|
||||
{"@type":"RocksDb","path":"/var/lib/stalwart/","blobSize":16834,"bufferSize":134217728,"poolWorkers":null}
|
||||
File diff suppressed because one or more lines are too long
@@ -1,113 +0,0 @@
|
||||
authentication.fallback-admin.secret = "$6$4qPYDVhaUHkKcY7s$bB6qhcukb9oFNYRIvaDZgbwxrMa2RvF5dumCjkBFdX19lSNqrgKltf3aPrFMuQQKkZpK2YNuQ83hB1B3NiWzj."
|
||||
authentication.fallback-admin.user = "mailadmin"
|
||||
authentication.master.secret = "$6$4qPYDVhaUHkKcY7s$bB6qhcukb9oFNYRIvaDZgbwxrMa2RvF5dumCjkBFdX19lSNqrgKltf3aPrFMuQQKkZpK2YNuQ83hB1B3NiWzj."
|
||||
authentication.master.user = "master"
|
||||
directory.idmldap.attributes.class = "objectClass"
|
||||
directory.idmldap.attributes.description = "displayName"
|
||||
directory.idmldap.attributes.email = "mail"
|
||||
directory.idmldap.attributes.groups = "memberOf"
|
||||
directory.idmldap.attributes.name = "cn"
|
||||
directory.idmldap.attributes.secret = "userPassword"
|
||||
directory.idmldap.base-dn = "o=libregraph-idm"
|
||||
directory.idmldap.bind.auth.method = "default"
|
||||
directory.idmldap.bind.dn = "uid=reva,ou=sysusers,o=libregraph-idm"
|
||||
directory.idmldap.bind.secret = "admin"
|
||||
directory.idmldap.cache.size = 1048576
|
||||
directory.idmldap.cache.ttl.negative = "10m"
|
||||
directory.idmldap.cache.ttl.positive = "1h"
|
||||
directory.idmldap.filter.email = "(&(|(objectClass=person)(objectClass=groupOfNames))(mail=?))"
|
||||
directory.idmldap.filter.name = "(&(|(objectClass=person)(objectClass=groupOfNames))(cn=?))"
|
||||
directory.idmldap.timeout = "15s"
|
||||
directory.idmldap.tls.allow-invalid-certs = true
|
||||
directory.idmldap.tls.enable = true
|
||||
directory.idmldap.type = "ldap"
|
||||
directory.idmldap.url = "ldaps://opencloud:9235"
|
||||
directory.keycloak.auth.method = "user-token"
|
||||
directory.keycloak.cache.size = 1048576
|
||||
directory.keycloak.cache.ttl.negative = "10m"
|
||||
directory.keycloak.cache.ttl.positive = "1h"
|
||||
directory.keycloak.endpoint.method = "introspect"
|
||||
directory.keycloak.endpoint.url = "http://keycloak:8080/realms/openCloud/protocol/openid-connect/userinfo"
|
||||
directory.keycloak.fields.email = "email"
|
||||
directory.keycloak.fields.full-name = "name"
|
||||
directory.keycloak.fields.username = "preferred_username"
|
||||
directory.keycloak.timeout = "15s"
|
||||
directory.keycloak.type = "oidc"
|
||||
directory.ldap.attributes.class = "objectClass"
|
||||
directory.ldap.attributes.description = "displayName"
|
||||
directory.ldap.attributes.email = "mail"
|
||||
directory.ldap.attributes.email-alias = "mailAlias"
|
||||
directory.ldap.attributes.groups = "memberOf"
|
||||
directory.ldap.attributes.name = "uid"
|
||||
directory.ldap.attributes.secret = "userPassword"
|
||||
directory.ldap.attributes.secret-changed = "pwdChangedTime"
|
||||
directory.ldap.base-dn = "dc=opencloud,dc=eu"
|
||||
directory.ldap.bind.auth.dn = "cn=?,ou=users,dc=opencloud,dc=eu"
|
||||
directory.ldap.bind.auth.enable = true
|
||||
directory.ldap.bind.auth.search = true
|
||||
directory.ldap.bind.dn = "cn=admin,dc=opencloud,dc=eu"
|
||||
directory.ldap.bind.secret = "admin"
|
||||
directory.ldap.cache.ttl.negative = "10m"
|
||||
directory.ldap.cache.ttl.positive = "1h"
|
||||
directory.ldap.filter.email = "(&(|(objectClass=person)(objectClass=groupOfNames))(|(uid=?)(mail=?)(mailAlias=?)(cn=?)))"
|
||||
directory.ldap.filter.name = "(&(|(objectClass=person)(objectClass=groupOfNames))(|(uid=?)(cn=?)))"
|
||||
directory.ldap.timeout = "5s"
|
||||
directory.ldap.tls.allow-invalid-certs = true
|
||||
directory.ldap.tls.enable = true
|
||||
directory.ldap.type = "ldap"
|
||||
directory.ldap.url = "ldap://ldap-server:1389"
|
||||
email.encryption.enable = true
|
||||
email.encryption.append = false
|
||||
http.allowed-endpoint = 200
|
||||
http.hsts = true
|
||||
http.permissive-cors = false
|
||||
http.url = "'https://' + config_get('server.hostname')"
|
||||
http.use-x-forwarded = true
|
||||
metrics.prometheus.auth.secret = "secret"
|
||||
metrics.prometheus.auth.username = "metrics"
|
||||
metrics.prometheus.enable = true
|
||||
server.listener.http.bind = "0.0.0.0:8080"
|
||||
server.listener.http.protocol = "http"
|
||||
server.listener.https.bind = "0.0.0.0:443"
|
||||
server.listener.https.protocol = "http"
|
||||
server.listener.https.tls.implicit = true
|
||||
server.listener.imap.bind = "0.0.0.0:143"
|
||||
server.listener.imap.protocol = "imap"
|
||||
server.listener.imaptls.bind = "0.0.0.0:993"
|
||||
server.listener.imaptls.protocol = "imap"
|
||||
server.listener.imaptls.tls.implicit = true
|
||||
server.listener.pop3.bind = "0.0.0.0:110"
|
||||
server.listener.pop3.protocol = "pop3"
|
||||
server.listener.pop3s.bind = "0.0.0.0:995"
|
||||
server.listener.pop3s.protocol = "pop3"
|
||||
server.listener.pop3s.tls.implicit = true
|
||||
server.listener.sieve.bind = "0.0.0.0:4190"
|
||||
server.listener.sieve.protocol = "managesieve"
|
||||
server.listener.smtp.bind = "0.0.0.0:25"
|
||||
server.listener.smtp.protocol = "smtp"
|
||||
server.listener.submission.bind = "0.0.0.0:587"
|
||||
server.listener.submission.protocol = "smtp"
|
||||
server.listener.submissions.bind = "0.0.0.0:465"
|
||||
server.listener.submissions.protocol = "smtp"
|
||||
server.listener.submissions.tls.implicit = true
|
||||
server.max-connections = 8192
|
||||
server.socket.backlog = 1024
|
||||
server.socket.nodelay = true
|
||||
server.socket.reuse-addr = true
|
||||
server.socket.reuse-port = true
|
||||
storage.blob = "rocksdb"
|
||||
storage.data = "rocksdb"
|
||||
storage.directory = "idmldap"
|
||||
storage.fts = "rocksdb"
|
||||
storage.lookup = "rocksdb"
|
||||
store.rocksdb.compression = "lz4"
|
||||
store.rocksdb.path = "/opt/stalwart/data"
|
||||
store.rocksdb.type = "rocksdb"
|
||||
tracer.console.ansi = true
|
||||
tracer.console.buffered = true
|
||||
tracer.console.enable = true
|
||||
tracer.console.level = "trace"
|
||||
tracer.console.lossy = false
|
||||
tracer.console.multiline = false
|
||||
tracer.console.type = "stdout"
|
||||
sharing.allow-directory-query = false
|
||||
27
devtools/deployments/opencloud_full/stalwart-snapshot
Executable file
27
devtools/deployments/opencloud_full/stalwart-snapshot
Executable file
@@ -0,0 +1,27 @@
|
||||
#!/bin/bash
|
||||
# Snapshots the current configuration of a Stalwart >= 0.16 server.
|
||||
# Requires having stalwart-cli installed.
|
||||
set -euo pipefail
|
||||
exec stalwart-cli -k --url 'https://stalwart.opencloud.test' --user admin --password secret snapshot \
|
||||
--include-secrets \
|
||||
Tenant \
|
||||
Domain \
|
||||
Directory \
|
||||
Authentication \
|
||||
DkimSignature \
|
||||
AcmeProvider \
|
||||
Certificate \
|
||||
DnsServer \
|
||||
Role \
|
||||
Authentication \
|
||||
Account \
|
||||
NetworkListener \
|
||||
Tracer \
|
||||
Sharing \
|
||||
SystemSettings \
|
||||
DataRetention \
|
||||
BlobStore \
|
||||
InMemoryStore \
|
||||
SearchStore \
|
||||
--allow-unresolved PublicKey \
|
||||
"$@"
|
||||
66
devtools/deployments/opencloud_full/stalwart-snapshot-passwords
Executable file
66
devtools/deployments/opencloud_full/stalwart-snapshot-passwords
Executable file
@@ -0,0 +1,66 @@
|
||||
#!/usr/bin/env node
|
||||
// Processes a Stalwart snapshot JSON file to perform the following:
|
||||
// - replace masked passwords with their correct value
|
||||
// - replace the defaultHostname system setting
|
||||
|
||||
const fs = require('fs');
|
||||
const readline = require('readline');
|
||||
|
||||
const args = process.argv.slice(2);
|
||||
const inputs = args.length > 0
|
||||
? args.map(file => fs.createReadStream(file))
|
||||
: [process.stdin];
|
||||
|
||||
async function processLines() {
|
||||
for (const stream of inputs) {
|
||||
const rl = readline.createInterface({
|
||||
input: stream,
|
||||
crlfDelay: Infinity
|
||||
});
|
||||
|
||||
for await (const line of rl) {
|
||||
const trimmed = line.trim();
|
||||
if (!trimmed) continue;
|
||||
|
||||
const item = JSON.parse(trimmed);
|
||||
const t = item['@type'];
|
||||
const o = item['object'];
|
||||
|
||||
if (t === 'create' && o === 'Account') {
|
||||
const value = item['value'] || {};
|
||||
for (const [id, account] of Object.entries(value)) {
|
||||
const name = account['name'];
|
||||
if (name === 'master' || name === 'admin') {
|
||||
const credentials = account['credentials'] || {};
|
||||
for (const [cid, creds] of Object.entries(credentials)) {
|
||||
if (creds['@type'] === 'Password') {
|
||||
creds['secret'] = 'supersecret1234';
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (t === 'create' && o === 'Directory') {
|
||||
const value = item['value'] || {};
|
||||
for (const [id, dir] of Object.entries(value)) {
|
||||
if (dir['@type'] === 'Ldap') {
|
||||
if (dir['bindSecret']) {
|
||||
dir['bindSecret']['secret'] = 'admin';
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (t === 'update' && o === 'SystemSettings') {
|
||||
if (item['value']) {
|
||||
item['value']['defaultHostname'] = 'stalwart.opencloud.test';
|
||||
}
|
||||
}
|
||||
|
||||
console.log(JSON.stringify(item));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
processLines();
|
||||
@@ -7,20 +7,22 @@ services:
|
||||
- ${STALWART_DOMAIN:-stalwart.opencloud.test}
|
||||
|
||||
stalwart:
|
||||
image: ghcr.io/stalwartlabs/stalwart:v0.15.5-alpine
|
||||
image: ghcr.io/stalwartlabs/stalwart:v0.16.7-alpine
|
||||
hostname: ${STALWART_DOMAIN:-stalwart.opencloud.test}
|
||||
networks:
|
||||
- opencloud-net
|
||||
ports:
|
||||
- "127.0.0.1:8443:443"
|
||||
- "127.0.0.1:8080:8080"
|
||||
- "127.0.0.1:143:143"
|
||||
- "127.0.0.1:993:993"
|
||||
- "127.0.0.1:1465:465"
|
||||
volumes:
|
||||
- /etc/localtime:/etc/localtime:ro
|
||||
- "./config/stalwart/${STALWART_AUTH_DIRECTORY:-idmldap}.toml:/opt/stalwart/etc/config.toml"
|
||||
- stalwart-data:/opt/stalwart/data
|
||||
- ./config/stalwart/config.json:/etc/stalwart/config.json
|
||||
- stalwart-data:/var/lib/stalwart
|
||||
environment:
|
||||
STALWART_AUTH_DIRECTORY: "${STALWART_AUTH_DIRECTORY:-idmldap}"
|
||||
STALWART_RECOVERY_ADMIN: "admin:secret"
|
||||
STALWART_PUBLIC_URL: "https://${STALWART_DOMAIN:-stalwart.opencloud.test}"
|
||||
labels:
|
||||
- "traefik.enable=true"
|
||||
- "traefik.http.routers.stalwart.entrypoints=https"
|
||||
@@ -30,7 +32,83 @@ services:
|
||||
- "traefik.http.services.stalwart.loadbalancer.server.port=8080"
|
||||
logging:
|
||||
driver: ${LOG_DRIVER:-local}
|
||||
restart: always
|
||||
restart: unless-stopped
|
||||
entrypoint: ["/bin/sh", "-c"]
|
||||
command:
|
||||
- |
|
||||
if [ ! -f /var/lib/stalwart/.initialized ]; then
|
||||
echo "Not initialized, starting in recovery mode"
|
||||
STALWART_RECOVERY_MODE=1 /usr/local/bin/stalwart --config /etc/stalwart/config.json &
|
||||
pid=$$!
|
||||
while [ ! -f /var/lib/stalwart/.initialized ]; do sleep 1; done
|
||||
echo "Initialized, stopping recovery mode"
|
||||
kill -TERM $$pid
|
||||
wait $$pid
|
||||
fi
|
||||
echo "Starting Stalwart in production mode"
|
||||
exec /usr/local/bin/stalwart --config /etc/stalwart/config.json
|
||||
|
||||
stalwart-import:
|
||||
image: stalwart-cli:latest
|
||||
build:
|
||||
context: ./config/stalwart/
|
||||
args:
|
||||
VERSION: "1.0.8"
|
||||
dockerfile_inline: |
|
||||
FROM alpine:latest AS downloader
|
||||
ARG VERSION
|
||||
RUN apk add --no-cache curl tar xz
|
||||
RUN arch=$(apk --print-arch) && \
|
||||
mkdir /cli && \
|
||||
curl --proto '=https' --tlsv1.2 -LsSf \
|
||||
https://github.com/stalwartlabs/cli/releases/download/v$${VERSION}/stalwart-cli-$${arch}-unknown-linux-musl.tar.xz \
|
||||
| tar xJf - --strip-components=1 -C /cli
|
||||
|
||||
FROM scratch
|
||||
COPY --from=downloader /cli/stalwart-cli /stalwart-cli
|
||||
CMD ["/stalwart-cli"]
|
||||
|
||||
networks:
|
||||
- opencloud-net
|
||||
volumes:
|
||||
- ./config/stalwart/${STALWART_AUTH_DIRECTORY:-idmldap}.json:/snapshot.json:ro
|
||||
- stalwart-data:/var/lib/stalwart
|
||||
environment:
|
||||
STALWART_URL: "http://stalwart:8080"
|
||||
STALWART_USER: "admin"
|
||||
STALWART_PASSWORD: "secret"
|
||||
entrypoint: ["/bin/sh", "-c"]
|
||||
command:
|
||||
- |
|
||||
if [ -f /var/lib/stalwart/.initialized ]; then
|
||||
echo "Already initialized, skipping stalwart-cli."
|
||||
exit 0
|
||||
fi
|
||||
echo "Waiting for the Stalwart recovery API to become live"
|
||||
until wget -qO- $$STALWART_URL/healthz/live >/dev/null 2>&1; do sleep 1; done
|
||||
echo "Applying configuration"
|
||||
/stalwart-cli --debug apply --file /snapshot.json
|
||||
touch /var/lib/stalwart/.initialized
|
||||
echo "Successfully initialized"
|
||||
|
||||
stdin_open: true
|
||||
tty: true
|
||||
depends_on:
|
||||
- stalwart
|
||||
|
||||
stalwart-reset:
|
||||
image: alpine:latest
|
||||
profiles:
|
||||
- maintenance
|
||||
volumes:
|
||||
- stalwart-data:/var/lib/stalwart
|
||||
entrypoint: ["/bin/sh", "-c"]
|
||||
command:
|
||||
- |
|
||||
if [ -f /var/lib/stalwart/.initialized ]; then
|
||||
rm -f /var/lib/stalwart/.initialized
|
||||
echo "Success: Lockfile removed. Next time 'stalwart' boots, it will run in recovery mode."
|
||||
fi
|
||||
|
||||
volumes:
|
||||
stalwart-data:
|
||||
|
||||
Reference in New Issue
Block a user