This adds the sealedsecrets.bitnami.com/managed: "true" label to the
mysql sealed secret to fix Health Degraded status in Argo CD.
Co-Authored-By: Paperclip <noreply@paperclip.ing>
The deployment was using client ID "bookstack" which does not exist in
Pocket ID. Updated to use the actual client ID from Pocket ID.
This fixes the "Record not found" error when attempting to login via
Pocket ID.
Co-Authored-By: Paperclip <noreply@paperclip.ing>
- Fix Ingress rule host from bookstack.basicstack.de to books.basicstack.de
- Update OIDC issuer from pocket-id.basicstack.de to auth.basicstack.de
- Enable AUTH_AUTO_INITIATE for seamless SSO
- Update OIDC_NAME from "Pocket-ID" to "Pocket ID"
Co-Authored-By: Paperclip <noreply@paperclip.ing>
Updated APP_URL environment variable and Ingress configuration to use the correct hostname books.basicstack.de.
Co-Authored-By: Paperclip <noreply@paperclip.ing>
- Change MySQL from StatefulSet to Deployment to match existing setup
- Use existing PVC mysql-data-encrypted (contains live data) instead of creating new mysql-data
- Add PersistentVolumeClaim for bookstack-config to persist APP_KEY and configuration
- Remove duplicate MySQL resource definitions
- Align with current working deployment while making Forgejo the source of truth
Co-Authored-By: Paperclip <noreply@paperclip.ing>
Added comprehensive Kubernetes deployment for Bookstack including:
- MySQL StatefulSet with PersistentVolumeClaim (10Gi encrypted storage)
- MySQL Service (ClusterIP)
- Bookstack Deployment with OIDC/Pocket-ID integration
- Bookstack Service (ClusterIP)
- Ingress with TLS certificate (bookstack.basicstack.de)
The deployment uses existing sealed secrets for MySQL credentials and
Bookstack OIDC client secret. Bookstack is configured with proper
health checks and resource limits.
Co-Authored-By: Paperclip <noreply@paperclip.ing>
The old recursive directory sync has been replaced with individual
Application manifests. The Application resource was deleted from the
cluster and this file is no longer needed.
Related: DEV-287
Co-Authored-By: Paperclip <noreply@paperclip.ing>
Migrated from single recursive directory sync to individual Application
manifests for better control and visibility.
Changes:
- Created app-*.yaml for each application (10 apps)
- Disabled auto-sync on all apps for safe migration
- Disabled old app-stack-basicstack-de.yaml complex sync
- Updated app-argocd.yaml to disable auto-sync during migration
- Added ARGOCD-MIGRATION.md with migration plan and next steps
- Updated apps/README.md to document new structure
Each app now needs:
1. Manual sync verification
2. Health check
3. Auto-sync enablement (tracked in separate tasks)
Related: DEV-287
Co-Authored-By: Paperclip <noreply@paperclip.ing>
Re-sealed the three Argo CD secrets with namespace: argocd instead of
namespace: paperclip to match the kustomization.yaml deployment target.
The sealed-secrets controller was failing to unseal these because they
were encrypted for the 'paperclip' namespace but Kustomize was deploying
them to the 'argocd' namespace. Sealed secrets use strict scope by
default, so namespace/name must match exactly.
Fixed files:
- apps/argocd/argocd-oidc-secret-sealed.yaml
- apps/argocd/repo-basicstack-org-secret-sealed.yaml
- apps/argocd/repo-stack-basicstack-de-secret-sealed.yaml
Root cause: DEV-284 investigation revealed controller error logs showing
"no key could decrypt secret" for all three Argo CD sealed secrets.
Co-Authored-By: Paperclip <noreply@paperclip.ing>
Created detailed documentation covering:
- Authentication and SSO login flow
- Adding new applications (Git and UI methods)
- Repository credential rotation procedure
- Emergency recovery procedures (with critical safety warnings)
- Self-management architecture
- Monitoring, maintenance, and troubleshooting
- Backup and disaster recovery
Updated README.md with Argo CD section and links to docs.
Updated docs/README.md to index the new Argo CD documentation.
Co-Authored-By: Paperclip <noreply@paperclip.ing>
When Argo CD manages its own install manifest, it overwrites the live
argocd-ssh-known-hosts-cm with the upstream defaults (GitHub/GitLab/etc),
losing the Forgejo internal service key. Add the forgejo.forgejo.svc.cluster.local
host key so self-management doesn't break repo access after each sync.
Co-Authored-By: Paperclip <noreply@paperclip.ing>
The Argo CD CRDs exceed the 262144-byte annotation limit that client-side
apply imposes via last-applied-configuration. Server-side apply bypasses
this limit and is required for Argo CD to manage its own large CRDs.
Co-Authored-By: Paperclip <noreply@paperclip.ing>
The argocd-cm document had two separate data: sections — one at the top
with resource customizations and one at the bottom with url/oidc.config.
This is invalid YAML and caused kustomize build to fail with
"mapping key data already defined". Merged url and oidc.config into the
single data: block to fix the argocd Application sync error.
Co-Authored-By: Paperclip <noreply@paperclip.ing>
Removing the exclusion caused the root app to apply argocd raw manifests
directly (bypassing kustomize), breaking namespace resolution. The correct
self-management design is: app-argocd.yaml Application is picked up by the
root app, which then delegates to the argocd Application managing apps/argocd/
via kustomize. The exclusion of argocd/** prevents double-applying resources.
Co-Authored-By: Paperclip <noreply@paperclip.ing>
Remove the 'argocd/**' exclusion from the root stack Application so that
Argo CD manages its own configuration via the app-of-apps pattern.
The apps/argocd/ directory already contains all manifests (install, ingress,
sealed secrets) and app-argocd.yaml defines the dedicated argocd Application.
Co-Authored-By: Paperclip <noreply@paperclip.ing>
Move metadata.name and app.kubernetes.io/part-of label from data section
to proper metadata section. This was causing kustomize build failures.
Co-Authored-By: Paperclip <noreply@paperclip.ing>
Change from multiline exclude with **/ pattern to simple single-line
'argocd/**' pattern relative to apps/ path.
Co-Authored-By: Paperclip <noreply@paperclip.ing>
Move all Application manifests (app-*.yaml) out of apps/argocd/ to apps/
to avoid chicken-and-egg issue where Applications couldn't update themselves.
Architecture:
- apps/app-stack-basicstack-de.yaml: manages apps/** excluding argocd/**
- apps/app-argocd.yaml: manages apps/argocd/** via kustomize
- apps/app-basicstack-org.yaml: manages basicstack.org repo
This enables full self-management: all Applications can sync their own
configurations from git.
Co-Authored-By: Paperclip <noreply@paperclip.ing>
Fully exclude argocd directory from stack Application to prevent
namespace errors. The argocd Application (apps/app-argocd.yaml) handles
all argocd resources via kustomize.
Co-Authored-By: Paperclip <noreply@paperclip.ing>
Add test annotation to argocd-cm ConfigMap to verify that Argo CD
automatically syncs changes to its own configuration.
Co-Authored-By: Paperclip <noreply@paperclip.ing>
This allows the stack Application to manage Argo CD's own configuration
via GitOps, implementing the app-of-apps pattern.
Co-Authored-By: Paperclip <noreply@paperclip.ing>
Creates the Argo CD Application that manages all cluster infrastructure
applications from the stack.basicstack.de repository. Configuration:
- Points to cluster-internal Forgejo (forgejo.forgejo.svc.cluster.local)
- Excludes argocd directory to prevent self-management
- Enables selfHeal for automatic sync of changes
- Disables prune for manual deletion approval
- Deployed to the cluster and verified working
All existing services (Stalwart, Directus, Forgejo, BookStack, Pocket ID,
OpenCloud, Passbolt) remain operational. Some pods restarted due to applying
pending git changes (expected GitOps behavior).
Note: Warning about duplicate opencloud namespace definition in
openldap-deployment.yaml and opencloud-deployment.yaml - does not affect
functionality, can be addressed in follow-up cleanup.
Co-Authored-By: Paperclip <noreply@paperclip.ing>
- Downloaded upstream Argo CD install.yaml from stable branch
- Customized for basicstack.de cluster:
- Disabled dex deployment (using Pocket ID OIDC instead)
- Added --insecure flag to argocd-server (TLS at Traefik ingress)
- Configured OIDC integration with auth.basicstack.de
- Added RBAC policy mapping argo_admins group to admin role
- Mounted OIDC client credentials from argocd-oidc-secret
- Created Traefik ingress for argo.basicstack.de with cert-manager TLS
Sealed secrets for OIDC and repository credentials already in place.
Ready for deployment per DEV-249 plan.
Co-Authored-By: Paperclip <noreply@paperclip.ing>
Created three SealedSecrets for Argo CD integration:
- argocd-oidc-secret: Pocket ID OIDC client credentials
- repo-stack-basicstack-de: SSH deploy key for stack repository
- repo-basicstack-org: SSH deploy key for basicstack.org repository
Repository secrets include argocd.argoproj.io/secret-type label
for automatic Argo CD discovery.
Co-Authored-By: Paperclip <noreply@paperclip.ing>
- Use existing basicstack-backup bucket with opencloud/ prefix
- Configure endpoint: https://hel1.your-objectstorage.com
- Add pod affinity to run on same node as OpenCloud (RWO volume requirement)
- Schedule: daily at 2:00 AM UTC
- Retention: last 7 days
- First backup verified successful: 110.611 MiB transferred
Co-Authored-By: Paperclip <noreply@paperclip.ing>
Document all four OIDC clients for OpenCloud:
- Web application (UUID-based client ID)
- Desktop client (OpenCloudDesktop)
- Android mobile (OpenCloudAndroid)
- iOS mobile (OpenCloudIOS)
Includes configuration details, security notes, and troubleshooting.
Co-Authored-By: Paperclip <noreply@paperclip.ing>
Implement scale-down/backup/scale-up pattern to work around PVC access mode limitation.
Changes:
- Add RBAC (ServiceAccount, Role, RoleBinding) with statefulsets/scale and pods permissions
- Switch to alpine:3.19 base image with kubectl and restic
- Scale down StatefulSet to 0 replicas before backup
- Run restic backup while pod is stopped
- Scale back up to 1 replica with error handling
- Add cleanup trap to ensure scale-up even on failure
- Set 10-minute timeout and backoff limit
Tested successfully: backup completes in ~32 seconds with minimal downtime.
Resolves DEV-236.
Co-Authored-By: Paperclip <noreply@paperclip.ing>
Root cause: k3s service ClusterIP routing instability causing intermittent
failures despite healthy pods. This is the 5th incident - prior fixes treated
symptoms, not the systemic networking fragility.
Changes:
- Add startup probe (60s delay, prevents premature service registration)
- Fix backup job env var substitution (use shell ${VAR}, not K8s $(VAR))
- Add comprehensive monitoring (ServiceMonitor, PrometheusRule, blackbox probes)
- Add alerting for service failures, high latency, pod restarts, backup failures
Evidence:
- Pod healthy (4d15h uptime, 0 restarts) but service ClusterIP routing broken
- Direct pod IP worked, service ClusterIP failed with "Connection reset by peer"
- Iptables rules correct, endpoints correct, but packets not flowing
- Required pod restart + Traefik restart to restore service
Monitoring now tests full service path from outside cluster, not just pod health.
Will alert immediately on failures instead of relying on reactive discovery.
Related: DEV-213, DEV-221, DEV-223, DEV-224, DEV-230, DEV-231
Co-Authored-By: Paperclip <noreply@paperclip.ing>
Created comprehensive network documentation for BasicStack k3s cluster:
- NETWORK_ARCHITECTURE.md: Complete network architecture with diagrams,
node configuration, CNI (Flannel) details, ingress/LoadBalancer setup,
DNS configuration, TLS certificates, network policies, traffic flows,
and troubleshooting procedures
- DNS_REQUIREMENTS.md: Complete DNS record requirements for all services
including A records, MX records, SPF, DKIM, DMARC, and PTR records
- NETWORK_VERIFICATION.md: Verification report documenting current state
of all network components with findings and recommendations
Updated infrastructure README with links to new network documentation.
Key findings:
- All worker nodes correctly configured with --node-ip set to private IPs
- Flannel VXLAN properly configured with public IP annotations
- Traefik ingress controller operational
- 16/17 TLS certificates valid (registry-tls needs investigation)
- 3 LoadBalancer services properly configured
- Network policies securing database services
Addresses DEV-225: Verify and document k3s cluster network configuration
Related: DEV-224 (node-ip configuration), DEV-223 (DNS issues)
Co-Authored-By: Paperclip <noreply@paperclip.ing>
- Add CLUSTER_ACCESS.md with comprehensive cluster access guide
- Fix Service CIDR in K3S_OPERATIONS.md (10.43.0.0/16, not 10.96.0.0/12)
- Document API server instability fix (cluster-cidr configuration)
- Add troubleshooting section for CIDR mismatch issues
- Update change history with cluster update details
Co-Authored-By: Paperclip <noreply@paperclip.ing>
OpenCloud's IDM service provides an internal LDAP server on localhost:9125.
Updated configuration to connect to IDM's LDAP instead of external OpenLDAP:
- Base DN: o=libregraph-idm (IDM's base DN)
- Graph binds as uid=libregraph,ou=sysusers,o=libregraph-idm
- Users/Groups bind as uid=reva,ou=sysusers,o=libregraph-idm
- Uses IDM service user passwords from secrets
Co-Authored-By: Paperclip <noreply@paperclip.ing>
When using OpenCloud's built-in IDM service, the graph, users, and groups
services should not use external LDAP. Commented out all LDAP-specific
environment variables that were causing startup failures.
Co-Authored-By: Paperclip <noreply@paperclip.ing>
- Enable IDM service by removing it from OC_EXCLUDE_RUN_SERVICES
- Remove external LDAP configuration from configmap (graph, users, groups)
- Built-in IDM will handle user/group storage internally
- OIDC auto-provisioning via Pocket ID remains unchanged
This allows OpenCloud to save new users using its internal IDM service.
Co-Authored-By: Paperclip <noreply@paperclip.ing>
The postStart lifecycle hook caused osixia/openldap to crash on startup:
its init script does chown -R on /container/service/slapd/assets/, and
the ConfigMap subPath mount there is read-only, killing the container.
Remove the postStart hook and the schema volume mount from the OpenLDAP
deployment. Add a standalone Kubernetes Job (opencloud-ldap-schema-job.yaml)
that connects via network LDAP as cn=admin,cn=config and loads the schema
after OpenLDAP is confirmed ready. The Job is idempotent (skips if the
schema already exists) and retries up to 10 times on failure.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
User creation failed with "openCloudUUID: attribute type undefined" because
OpenLDAP was missing the OpenCloud schema (OIDs under 1.3.6.1.4.1.63016).
Changes:
- Add opencloud-ldap-schema.yaml ConfigMap with the official OpenCloud LDAP
schema defining openCloudUUID, openCloudUser, openCloudExternalIdentity,
openCloudUserEnabled, openCloudUserType, openCloudLastSignInTimestamp
- Mount the ConfigMap into the OpenLDAP pod
- Add lifecycle postStart hook to load schema via ldapadd -Y EXTERNAL -H ldapi:///
(idempotent: skips if already loaded)
- Re-exclude IDM in OpenCloud deployment (external LDAP handles user storage)
Co-Authored-By: Paperclip <noreply@paperclip.ing>
Graph service was routing auto-provisioning to external OpenLDAP which lacks
the OpenCloud schema (openCloudUUID: attribute type undefined). The explicit
graph.identity.ldap, users.drivers.ldap, and groups.drivers.ldap sections in
opencloud.yaml overrode the default IDM LDAP, causing user creation to fail.
Remove all external LDAP sections so Graph/Users/Groups default to IDM's
internal LDAP, which has the full OpenCloud schema. IDM is already running.
Co-Authored-By: Paperclip <noreply@paperclip.ing>
The /etc/opencloud mount is read-only (ConfigMap), so opencloud init
cannot write the generated IDM password config. IDM fails to start with
"password of service user IDM has not been set properly".
Add the idm.service_user_passwords section to opencloud.yaml, referencing
the OC_IDM_* env vars that are already populated from the sealed secret.
Co-Authored-By: Paperclip <noreply@paperclip.ing>
After Pocket ID OIDC flow completed, users got "Nicht angemeldet" because:
1. PROXY_ROLE_ASSIGNMENT_DRIVER=oidc required an 'opencloud_role' OIDC claim
that Pocket ID wasn't sending → users got no role → login rejected
2. IDM was excluded, removing the internal user store that auto-provisioning
needs to create user accounts when they first log in
Fixes:
- Switch to PROXY_ROLE_ASSIGNMENT_DRIVER=default so all OIDC-authenticated
users automatically receive the standard user role
- Re-enable IDM service so auto-provisioned accounts have a working user store
Co-Authored-By: Paperclip <noreply@paperclip.ing>
Browser was blocking fetch of https://auth.basicstack.de/.well-known/openid-configuration
due to missing connect-src directive in Content-Security-Policy.
Adds csp.yaml to the ConfigMap (mounted at /etc/opencloud/csp.yaml) with
extended connect-src that includes auth.basicstack.de and WebSocket origins.
Sets PROXY_CSP_CONFIG_FILE_LOCATION env var so the proxy service picks it up.
Co-Authored-By: Paperclip <noreply@paperclip.ing>