From 41ca29024ea709fff52a516a1b007d0418ab26a1 Mon Sep 17 00:00:00 2001 From: CTO Agent Date: Sun, 5 Jul 2026 10:21:11 +0000 Subject: [PATCH] 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 --- apps/opencloud/OPENLDAP_DEPLOYMENT.md | 257 ++++++++++++++++++++++ apps/opencloud/ldap-init-structure.ldif | 37 ++++ apps/opencloud/opencloud-configmap.yaml | 44 +++- apps/opencloud/opencloud-deployment.yaml | 11 +- apps/opencloud/openldap-admin-sealed.yaml | 27 +++ apps/opencloud/openldap-deployment.yaml | 152 +++++++++++++ 6 files changed, 514 insertions(+), 14 deletions(-) create mode 100644 apps/opencloud/OPENLDAP_DEPLOYMENT.md create mode 100644 apps/opencloud/ldap-init-structure.ldif create mode 100644 apps/opencloud/openldap-admin-sealed.yaml create mode 100644 apps/opencloud/openldap-deployment.yaml diff --git a/apps/opencloud/OPENLDAP_DEPLOYMENT.md b/apps/opencloud/OPENLDAP_DEPLOYMENT.md new file mode 100644 index 0000000..3365068 --- /dev/null +++ b/apps/opencloud/OPENLDAP_DEPLOYMENT.md @@ -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 with your actual password) +kubectl exec -n opencloud $LDAP_POD -- \ + ldapadd -x -D "cn=admin,dc=basicstack,dc=de" -w "" -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 "" -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 "" \ + -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 "" \ + -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 "" \ + -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 diff --git a/apps/opencloud/ldap-init-structure.ldif b/apps/opencloud/ldap-init-structure.ldif new file mode 100644 index 0000000..fcd05b7 --- /dev/null +++ b/apps/opencloud/ldap-init-structure.ldif @@ -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 -f /tmp/init-structure.ldif +# +# Or copy to pod and apply: +# kubectl cp ldap-init-structure.ldif opencloud/openldap-:/tmp/init-structure.ldif +# kubectl exec -n opencloud openldap- -- \ +# ldapadd -x -D "cn=admin,dc=basicstack,dc=de" -w -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 diff --git a/apps/opencloud/opencloud-configmap.yaml b/apps/opencloud/opencloud-configmap.yaml index 3b8888f..7f69762 100644 --- a/apps/opencloud/opencloud-configmap.yaml +++ b/apps/opencloud/opencloud-configmap.yaml @@ -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: diff --git a/apps/opencloud/opencloud-deployment.yaml b/apps/opencloud/opencloud-deployment.yaml index fcf89ed..2718208 100644 --- a/apps/opencloud/opencloud-deployment.yaml +++ b/apps/opencloud/opencloud-deployment.yaml @@ -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: diff --git a/apps/opencloud/openldap-admin-sealed.yaml b/apps/opencloud/openldap-admin-sealed.yaml new file mode 100644 index 0000000..bdc56e6 --- /dev/null +++ b/apps/opencloud/openldap-admin-sealed.yaml @@ -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" diff --git a/apps/opencloud/openldap-deployment.yaml b/apps/opencloud/openldap-deployment.yaml new file mode 100644 index 0000000..89f9122 --- /dev/null +++ b/apps/opencloud/openldap-deployment.yaml @@ -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