Add external OpenLDAP deployment for OpenCloud

- Add OpenLDAP deployment with persistent storage
- Configure OpenCloud to use external LDAP for user/group storage
- Exclude internal IDM service (using external LDAP instead)
- Keep Pocket ID OIDC for authentication
- Add LDAP directory initialization structure
- Add comprehensive deployment guide

Architecture:
- External OpenLDAP (ldap://openldap.opencloud.svc:389)
- Pocket ID OIDC (https://auth.basicstack.de)
- Auto-provision users on first OIDC login to LDAP
- Users: ou=users,dc=basicstack,dc=de
- Groups: ou=groups,dc=basicstack,dc=de

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
CTO Agent 2026-07-05 10:21:11 +00:00
parent c149a278dc
commit 41ca29024e
6 changed files with 514 additions and 14 deletions

View file

@ -0,0 +1,257 @@
# OpenCloud with External OpenLDAP Deployment Guide
## Architecture Overview
This deployment uses:
- **External OpenLDAP** for user/group storage
- **Pocket ID** (auth.basicstack.de) for authentication via OIDC
- **OpenCloud** as the file storage platform
### Authentication Flow
1. User accesses `https://opencloud.basicstack.de`
2. OpenCloud redirects to Pocket ID for authentication
3. User logs in at `https://auth.basicstack.de`
4. Pocket ID returns OIDC token to OpenCloud
5. OpenCloud auto-provisions user in external OpenLDAP
6. User is granted access based on OIDC group claims
## Deployment Steps
### 1. Create OpenLDAP Admin Secret
First, generate strong passwords and create the sealed secret:
```bash
# Generate passwords
ADMIN_PASSWORD=$(openssl rand -base64 32)
CONFIG_PASSWORD=$(openssl rand -base64 32)
# Save them securely (e.g., password manager)
echo "Admin Password: $ADMIN_PASSWORD"
echo "Config Password: $CONFIG_PASSWORD"
# Create sealed secret
kubectl create secret generic openldap-admin-secret \
--namespace opencloud \
--from-literal=admin-password="$ADMIN_PASSWORD" \
--from-literal=config-password="$CONFIG_PASSWORD" \
--dry-run=client -o yaml | \
kubeseal --format yaml > openldap-admin-sealed.yaml
# Apply sealed secret
kubectl apply -f openldap-admin-sealed.yaml
```
### 2. Deploy OpenLDAP
```bash
# Deploy OpenLDAP
kubectl apply -f openldap-deployment.yaml
# Wait for OpenLDAP to be ready
kubectl wait --for=condition=ready pod -l app=openldap -n opencloud --timeout=120s
# Check OpenLDAP pod status
kubectl get pods -n opencloud -l app=openldap
kubectl logs -n opencloud -l app=openldap -f
```
### 3. Initialize LDAP Directory Structure
Once OpenLDAP is running, initialize the directory with required OUs:
```bash
# Get the OpenLDAP pod name
LDAP_POD=$(kubectl get pods -n opencloud -l app=openldap -o jsonpath='{.items[0].metadata.name}')
# Copy the LDIF file to the pod
kubectl cp ldap-init-structure.ldif opencloud/$LDAP_POD:/tmp/init-structure.ldif
# Apply the LDIF (replace <admin-password> with your actual password)
kubectl exec -n opencloud $LDAP_POD -- \
ldapadd -x -D "cn=admin,dc=basicstack,dc=de" -w "<admin-password>" -f /tmp/init-structure.ldif
# Verify structure was created
kubectl exec -n opencloud $LDAP_POD -- \
ldapsearch -x -D "cn=admin,dc=basicstack,dc=de" -w "<admin-password>" -b "dc=basicstack,dc=de" -LLL
```
### 4. Deploy OpenCloud
**Important:** Make sure the old OpenCloud PVC is deleted first to ensure fresh initialization with new LDAP configuration.
```bash
# Scale down existing OpenCloud deployment (if running)
kubectl scale deployment -n opencloud opencloud --replicas=0
# Delete old PVC
kubectl delete pvc -n opencloud opencloud-data
# Apply updated deployment
kubectl apply -f opencloud-deployment.yaml
kubectl apply -f opencloud-configmap.yaml
# Scale up
kubectl scale deployment -n opencloud opencloud --replicas=1
# Watch initialization
kubectl logs -n opencloud -l app=opencloud -f
```
### 5. Verify Configuration
```bash
# Check all pods are running
kubectl get pods -n opencloud
# Verify OpenCloud can connect to LDAP
kubectl exec -n opencloud deployment/opencloud -- \
ldapsearch -x -H ldap://openldap.opencloud.svc.cluster.local:389 \
-D "cn=admin,dc=basicstack,dc=de" -w "<admin-password>" \
-b "dc=basicstack,dc=de" -LLL
# Check OpenCloud logs for LDAP connection
kubectl logs -n opencloud -l app=opencloud | grep -i ldap
```
### 6. Test OIDC Authentication
1. Open browser to `https://opencloud.basicstack.de`
2. Should redirect to `https://auth.basicstack.de`
3. Login with Pocket ID credentials
4. Should redirect back to OpenCloud
5. User auto-provisioned in OpenLDAP
## LDAP Directory Structure
```
dc=basicstack,dc=de
├── cn=admin (admin user)
├── ou=users
│ └── (auto-provisioned users from OIDC)
└── ou=groups
├── cn=opencloudUsers (default users group)
└── cn=opencloudAdmins (administrators group)
```
## Configuration Details
### OpenLDAP Connection Details
- **Service:** `openldap.opencloud.svc.cluster.local`
- **Port:** 389 (LDAP), 636 (LDAPS - disabled for internal use)
- **Base DN:** `dc=basicstack,dc=de`
- **Admin DN:** `cn=admin,dc=basicstack,dc=de`
- **User Base:** `ou=users,dc=basicstack,dc=de`
- **Group Base:** `ou=groups,dc=basicstack,dc=de`
### OpenCloud Services Excluded
- **search** - Broken in v7.2.0
- **idp** - Using external Pocket ID for authentication
- **idm** - Using external OpenLDAP for user storage
### OIDC Configuration
- **Issuer:** `https://auth.basicstack.de`
- **Client ID:** (from `opencloud-oidc-secret`)
- **Scopes:** `openid profile email groups offline_access`
- **Auto-provisioning:** Enabled
- **User claim:** `email`
- **Role claim:** `groups`
### Role Mapping
OIDC groups → OpenCloud roles:
- `opencloudAdmin` → admin
- `opencloudSpaceAdmin` → spaceadmin
- `opencloudUser` → user
- `opencloudGuest` → guest
## Troubleshooting
### OpenLDAP not starting
```bash
# Check logs
kubectl logs -n opencloud -l app=openldap
# Check PVCs
kubectl get pvc -n opencloud
# Check events
kubectl get events -n opencloud --sort-by='.lastTimestamp' | tail -20
```
### OpenCloud can't connect to LDAP
```bash
# Test LDAP connectivity from OpenCloud pod
kubectl exec -n opencloud deployment/opencloud -- \
nc -zv openldap.opencloud.svc.cluster.local 389
# Check LDAP service
kubectl get svc -n opencloud openldap
# Check LDAP endpoints
kubectl get endpoints -n opencloud openldap
```
### Users not auto-provisioning
```bash
# Check OpenCloud proxy logs
kubectl logs -n opencloud -l app=opencloud | grep -i provision
# Check OIDC flow
kubectl logs -n opencloud -l app=opencloud | grep -i oidc
# Verify LDAP directory structure
kubectl exec -n opencloud deployment/openldap -- \
ldapsearch -x -D "cn=admin,dc=basicstack,dc=de" -w "<password>" \
-b "ou=users,dc=basicstack,dc=de" -LLL
```
### View auto-provisioned users
```bash
# List all users in LDAP
kubectl exec -n opencloud deployment/openldap -- \
ldapsearch -x -D "cn=admin,dc=basicstack,dc=de" -w "<password>" \
-b "ou=users,dc=basicstack,dc=de" -LLL "(objectClass=inetOrgPerson)"
```
## Backup and Maintenance
### Backup LDAP Data
```bash
# Backup entire LDAP directory
kubectl exec -n opencloud deployment/openldap -- \
slapcat -l /tmp/backup.ldif
kubectl cp opencloud/$LDAP_POD:/tmp/backup.ldif ./ldap-backup-$(date +%Y%m%d).ldif
```
### Monitor LDAP Performance
```bash
# Check LDAP stats
kubectl exec -n opencloud deployment/openldap -- \
ldapsearch -x -H ldapi:/// -Y EXTERNAL -b "cn=Monitor" -LLL
```
## Files
- `openldap-deployment.yaml` - OpenLDAP Kubernetes deployment
- `openldap-admin-sealed.yaml` - Admin credentials (sealed secret)
- `ldap-init-structure.ldif` - Initial directory structure
- `opencloud-deployment.yaml` - OpenCloud deployment (updated for external LDAP)
- `opencloud-configmap.yaml` - OpenCloud configuration (updated for external LDAP)
## References
- OpenLDAP: https://www.openldap.org/
- OpenCloud LDAP Configuration: https://docs.opencloud.eu/
- Pocket ID: https://github.com/stonith404/pocket-id

View file

@ -0,0 +1,37 @@
# OpenLDAP Directory Structure Initialization
# This LDIF creates the organizational units needed by OpenCloud
#
# Apply with:
# kubectl exec -n opencloud deployment/openldap -- \
# ldapadd -x -D "cn=admin,dc=basicstack,dc=de" -w <admin-password> -f /tmp/init-structure.ldif
#
# Or copy to pod and apply:
# kubectl cp ldap-init-structure.ldif opencloud/openldap-<pod-id>:/tmp/init-structure.ldif
# kubectl exec -n opencloud openldap-<pod-id> -- \
# ldapadd -x -D "cn=admin,dc=basicstack,dc=de" -w <admin-password> -f /tmp/init-structure.ldif
# Create Users Organizational Unit
dn: ou=users,dc=basicstack,dc=de
objectClass: organizationalUnit
ou: users
description: OpenCloud Users
# Create Groups Organizational Unit
dn: ou=groups,dc=basicstack,dc=de
objectClass: organizationalUnit
ou: groups
description: OpenCloud Groups
# Example: Create default user group
dn: cn=opencloudUsers,ou=groups,dc=basicstack,dc=de
objectClass: groupOfNames
cn: opencloudUsers
description: Default OpenCloud Users Group
member: cn=admin,dc=basicstack,dc=de
# Example: Create admin group
dn: cn=opencloudAdmins,ou=groups,dc=basicstack,dc=de
objectClass: groupOfNames
cn: opencloudAdmins
description: OpenCloud Administrators
member: cn=admin,dc=basicstack,dc=de

View file

@ -20,7 +20,7 @@ data:
system_user_id: ${OC_SYSTEM_USER_ID}
admin_user_id: ${OC_ADMIN_USER_ID}
# Graph service (using internal IDM LDAP for user storage)
# Graph service (using external OpenLDAP for user storage)
graph:
application:
id: ${OC_GRAPH_APPLICATION_ID:-025a50d1-5f8d-4309-a201-dd938e7b0b2f}
@ -30,7 +30,13 @@ data:
insecure: true
identity:
ldap:
bind_password: ${OC_GRAPH_LDAP_BIND_PASSWORD}
uri: ldap://openldap.opencloud.svc.cluster.local:389
base_dn: dc=basicstack,dc=de
bind_dn: cn=admin,dc=basicstack,dc=de
bind_password: ${OPENLDAP_ADMIN_PASSWORD}
user_base_dn: ou=users,dc=basicstack,dc=de
group_base_dn: ou=groups,dc=basicstack,dc=de
insecure: true
service_account:
service_account_id: ${OC_SERVICE_ACCOUNT_ID}
service_account_secret: ${OC_SERVICE_ACCOUNT_SECRET}
@ -40,13 +46,13 @@ data:
# ldap:
# bind_password: ${OC_IDP_LDAP_BIND_PASSWORD}
# IDM service (needed for user storage even with external OIDC)
idm:
service_user_passwords:
admin_password: ${OC_IDM_ADMIN_PASSWORD}
idm_password: ${OC_IDM_IDM_PASSWORD}
reva_password: ${OC_IDM_REVA_PASSWORD}
idp_password: ${OC_IDM_IDP_PASSWORD}
# IDM service excluded (using external OpenLDAP for user storage)
# idm:
# service_user_passwords:
# admin_password: ${OC_IDM_ADMIN_PASSWORD}
# idm_password: ${OC_IDM_IDM_PASSWORD}
# reva_password: ${OC_IDM_REVA_PASSWORD}
# idp_password: ${OC_IDM_IDP_PASSWORD}
# Collaboration services
collaboration:
@ -99,16 +105,30 @@ data:
oidc:
insecure: false
# User/Group services (using LDAP driver connected to internal IDM)
# User/Group services (using external OpenLDAP)
users:
drivers:
ldap:
bind_password: ${OC_USERS_LDAP_BIND_PASSWORD}
uri: ldap://openldap.opencloud.svc.cluster.local:389
base_dn: dc=basicstack,dc=de
bind_dn: cn=admin,dc=basicstack,dc=de
bind_password: ${OPENLDAP_ADMIN_PASSWORD}
user_base_dn: ou=users,dc=basicstack,dc=de
user_filter: (objectClass=inetOrgPerson)
user_object_class: inetOrgPerson
insecure: true
groups:
drivers:
ldap:
bind_password: ${OC_GROUPS_LDAP_BIND_PASSWORD}
uri: ldap://openldap.opencloud.svc.cluster.local:389
base_dn: dc=basicstack,dc=de
bind_dn: cn=admin,dc=basicstack,dc=de
bind_password: ${OPENLDAP_ADMIN_PASSWORD}
group_base_dn: ou=groups,dc=basicstack,dc=de
group_filter: (objectClass=groupOfNames)
group_object_class: groupOfNames
insecure: true
# OCM (Open Cloud Mesh)
ocm:

View file

@ -91,9 +91,9 @@ spec:
- name: PROXY_TLS
value: "false"
# Exclude broken search service and internal IDP only (IDM needed for user storage, using external OIDC via Pocket ID for auth)
# Exclude broken search service, internal IDP and IDM (using external OpenLDAP for user storage and Pocket ID for auth)
- name: OC_EXCLUDE_RUN_SERVICES
value: "search,idp"
value: "search,idp,idm"
# Data paths
- name: OPENCLOUD_BASE_DATA_PATH
@ -264,6 +264,13 @@ spec:
name: opencloud-config-secrets
key: ldap-bind-password
# External OpenLDAP admin password (for user/group storage)
- name: OPENLDAP_ADMIN_PASSWORD
valueFrom:
secretKeyRef:
name: openldap-admin-secret
key: admin-password
# IDM service user passwords
- name: OC_IDM_ADMIN_PASSWORD
valueFrom:

View file

@ -0,0 +1,27 @@
---
# OpenLDAP Admin Credentials
# This is a PLACEHOLDER - you need to create the actual SealedSecret
#
# To create the sealed secret, run:
#
# kubectl create secret generic openldap-admin-secret \
# --namespace opencloud \
# --from-literal=admin-password='YOUR_SECURE_ADMIN_PASSWORD' \
# --from-literal=config-password='YOUR_SECURE_CONFIG_PASSWORD' \
# --dry-run=client -o yaml | \
# kubeseal --format yaml > openldap-admin-sealed.yaml
#
# Then replace this file with the output.
#
# LDAP Admin DN: cn=admin,dc=basicstack,dc=de
# LDAP Base DN: dc=basicstack,dc=de
apiVersion: v1
kind: Secret
metadata:
name: openldap-admin-secret
namespace: opencloud
type: Opaque
stringData:
# REPLACE THESE WITH ACTUAL SEALED SECRET
admin-password: "CHANGE_ME"
config-password: "CHANGE_ME"

View file

@ -0,0 +1,152 @@
---
# OpenLDAP Deployment for OpenCloud
# Provides external LDAP directory for user and group storage
apiVersion: v1
kind: Namespace
metadata:
name: opencloud
---
# OpenLDAP Data Storage
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: openldap-data
namespace: opencloud
spec:
accessModes:
- ReadWriteOnce
storageClassName: hcloud-volumes-encrypted
resources:
requests:
storage: 10Gi
---
# OpenLDAP Config Storage
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: openldap-config
namespace: opencloud
spec:
accessModes:
- ReadWriteOnce
storageClassName: hcloud-volumes-encrypted
resources:
requests:
storage: 1Gi
---
# OpenLDAP Service
apiVersion: v1
kind: Service
metadata:
name: openldap
namespace: opencloud
spec:
type: ClusterIP
selector:
app: openldap
ports:
- name: ldap
port: 389
targetPort: 389
protocol: TCP
- name: ldaps
port: 636
targetPort: 636
protocol: TCP
---
# OpenLDAP Deployment
apiVersion: apps/v1
kind: Deployment
metadata:
name: openldap
namespace: opencloud
spec:
replicas: 1
selector:
matchLabels:
app: openldap
template:
metadata:
labels:
app: openldap
spec:
containers:
- name: openldap
image: osixia/openldap:1.5.0
env:
# Organization and domain
- name: LDAP_ORGANISATION
value: "BasicStack"
- name: LDAP_DOMAIN
value: "basicstack.de"
# Admin credentials
- name: LDAP_ADMIN_PASSWORD
valueFrom:
secretKeyRef:
name: openldap-admin-secret
key: admin-password
# Config admin password
- name: LDAP_CONFIG_PASSWORD
valueFrom:
secretKeyRef:
name: openldap-admin-secret
key: config-password
# Base DN will be: dc=basicstack,dc=de
# Admin DN will be: cn=admin,dc=basicstack,dc=de
# Logging
- name: LDAP_LOG_LEVEL
value: "256"
# TLS disabled for internal cluster communication
- name: LDAP_TLS
value: "false"
# Remove default database and start fresh
- name: LDAP_REMOVE_CONFIG_AFTER_SETUP
value: "false"
ports:
- containerPort: 389
name: ldap
- containerPort: 636
name: ldaps
volumeMounts:
- name: openldap-data
mountPath: /var/lib/ldap
- name: openldap-config
mountPath: /etc/ldap/slapd.d
resources:
requests:
memory: "256Mi"
cpu: "100m"
limits:
memory: "512Mi"
cpu: "500m"
livenessProbe:
tcpSocket:
port: 389
initialDelaySeconds: 30
periodSeconds: 10
timeoutSeconds: 5
readinessProbe:
tcpSocket:
port: 389
initialDelaySeconds: 20
periodSeconds: 5
timeoutSeconds: 3
volumes:
- name: openldap-data
persistentVolumeClaim:
claimName: openldap-data
- name: openldap-config
persistentVolumeClaim:
claimName: openldap-config