Commit graph

205 commits

Author SHA1 Message Date
CTO Agent
1c740828ed Add Pangolin configuration file
Pangolin requires a config.toml file to start. Added ConfigMap with basic configuration including URL, database path, and OIDC settings. Updated deployment to mount the config file.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-07-26 12:17:14 +00:00
CTO Agent
3d0d5f3f3a Reduce Pangolin resource requests for initial deployment
Cluster nodes are at 93-99% memory capacity. Reduced requests from 512Mi to 256Mi and CPU from 200m to 100m to allow pod scheduling.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-07-26 12:14:42 +00:00
CTO Agent
2c8a7ca1e8 Add Pangolin Kubernetes manifests
Created complete Kubernetes deployment for Pangolin SSO gateway:

- PVC: 10Gi encrypted storage for data persistence
- Deployment: fosrl/pangolin:1.21.0 with OIDC configuration
  - Resources: 200m/512Mi requests, 1000m/2Gi limits
  - Recreate strategy for single-replica deployment
  - Health probes configured
- Service: ClusterIP on port 3001
- Ingress: pangolin.basicstack.de with TLS via cert-manager
- SealedSecret: Contains admin password and OIDC credentials
  - Client ID: 6cb8db32-410b-4df6-b2af-298af447b09a
  - Integrated with Pocket ID at auth.basicstack.de
  - Authorized group: pangolin-admins
- Argo CD Application: Automated sync with CreateNamespace

OIDC authentication configured for pangolin-admins group members:
- andreas.leinen@basicstack.de
- admin@basicstack.de

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-07-26 12:09:07 +00:00
CTO Agent
629882d29d Add Pangolin repository structure and DNS configuration
Created apps/pangolin/ directory with namespace.yaml defining the pangolin namespace.
Configured DNS A record for pangolin.basicstack.de → 178.105.17.239 (cluster ingress IP).
Updated DNS_REQUIREMENTS.md to document the new Pangolin service.

This completes Phase 1 of the Pangolin deployment (DEV-390):
- Repository structure created with namespace definition
- DNS record configured and verified in Hetzner zone
- Documentation updated

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-07-26 11:58:00 +00:00
CTO Agent
38f970b0e9 Fix Paperclip storage and permissions
- Change PVC storage class from local-path to hcloud-volumes-encrypted
- Extend initContainer to create full directory structure including instances/default/data/run-logs
- This fixes permission errors when agents try to create run logs

Resolves: DEV-388

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-07-26 11:10:21 +00:00
797ac22512 apps/paperclip/ingress.yaml aktualisiert
correction of the base url
2026-07-26 10:39:45 +00:00
49c6a2cd4a apps/paperclip/deployment.yaml aktualisiert
Correction of the base url
2026-07-26 10:39:10 +00:00
CTO Agent
ad28439642 Add namespace.yaml to prevent accidental deletion
Add explicit namespace definition to ensure ArgoCD doesn't delete the namespace during sync operations.

Resolves: DEV-387

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-07-26 10:09:40 +00:00
CTO Agent
c0dd78d172 Use exec probe for health checks instead of HTTP probe
The /api/health endpoint returns 403 when accessed externally but works from localhost. Changed readiness and liveness probes to use exec with wget from inside the container to work around this restriction.

Resolves: DEV-387

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-07-26 10:07:16 +00:00
CTO Agent
c0aecf125a Fix Paperclip health check endpoint path
Change health check from /health to /api/health to match the actual API endpoint. The server is healthy but the readiness/liveness probes were failing because they were checking the wrong path.

Resolves: DEV-387

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-07-26 10:05:50 +00:00
CTO Agent
c7c0ac2998 Revert "Use existing encrypted PVCs and postgres deployment"
This reverts commit 8cac765a64.
2026-07-26 10:04:28 +00:00
CTO Agent
8cac765a64 Use existing encrypted PVCs and postgres deployment
- Change paperclip deployment to use paperclip-data-encrypted PVC (preserves existing data)
- Remove pvc.yaml (use existing PVC instead of creating new one)
- Remove postgres StatefulSet and service (use existing postgres deployment)

This preserves the existing 43-day-old data in the encrypted volumes instead of creating a fresh deployment.

Resolves: DEV-387

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-07-26 10:03:33 +00:00
CTO Agent
1b613f06f5 Fix Paperclip deployment scheduling and ArgoCD degraded status
- Reduce resource requests from 500m/1Gi to 200m/512Mi to fit available cluster capacity
- Reduce resource limits from 2000m/4Gi to 1000m/2Gi for better resource sharing
- Add IgnoreExtraneous annotation to paperclip-secrets SealedSecret to fix ArgoCD degraded status

Resolves: DEV-387

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-07-26 10:00:04 +00:00
CTO Agent
4773547808 Add complete Paperclip deployment manifests to apps/paperclip
Add all necessary Kubernetes manifests for Paperclip deployment:
- deployment.yaml: Main Paperclip application deployment
- service.yaml: ClusterIP service for Paperclip (port 3100)
- ingress.yaml: Traefik ingress for paperclip.home.imicros.de
- pvc.yaml: 50Gi persistent volume for Paperclip data
- postgres-statefulset.yaml: PostgreSQL 16 StatefulSet
- postgres-service.yaml: PostgreSQL service

This completes the GitOps configuration for Paperclip in the
apps/paperclip directory. The ArgoCD application at apps/app-paperclip.yaml
is already configured to deploy from this path.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-07-26 09:47:55 +00:00
CTO Agent
325462b0f9 Consolidate Forgejo backup into forgejo namespace
Move backup resources from separate 'backup' namespace to 'forgejo':
- Change CronJob namespace from 'backup' to 'forgejo'
- Reuse existing forgejo-postgres-secret instead of duplicate backup secret
- Remove backup-namespace.yaml (no longer needed)
- Remove forgejo-backup-secret-sealed.yaml (using existing secret)

This simplifies the architecture by keeping all Forgejo-related
resources in a single namespace and eliminating credential duplication.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-07-26 09:35:31 +00:00
CTO Agent
311ec15b07 Consolidate Forgejo backup into main Forgejo app
Move backup configuration from separate app to forgejo folder:
- Move forgejo-backup-cronjob.yaml to apps/forgejo/
- Move forgejo-backup-secret-sealed.yaml to apps/forgejo/
- Add backup-namespace.yaml to ensure backup namespace is created
- Remove apps/backup/ folder
- Remove apps/app-backup.yaml Argo CD application

This consolidates the backup configuration into the main Forgejo
application, eliminating the need for a separate Argo CD app.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-07-26 09:24:51 +00:00
CTO Agent
a5a1190c3b Fix Passbolt ArgoCD degraded status
Add IgnoreExtraneous annotation to MariaDB sealed secret template metadata.
This prevents ArgoCD from seeing the unsealed secret (created by sealed-secrets controller) as an extraneous resource.

Same fix pattern as DEV-377, DEV-378, and DEV-379.

Resolves DEV-385

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-07-26 09:17:04 +00:00
CTO Agent
cfceb6d28c Remove obsolete passbolt-ingress.yaml file
This file was replaced by passbolt-ingress-fixed.yaml in the Passbolt
certificate fix (DEV-382). The new ingress uses cert-manager annotations
instead of Traefik-specific TLS configuration.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-07-25 17:44:11 +00:00
CTO Agent
487ac113d4 Fix Passbolt ingress class from nginx to traefik
The ingress was configured with ingressClassName: nginx, but the cluster
only has Traefik ingress controller installed. This caused the browser to
show Traefik's default certificate instead of the Let's Encrypt certificate.

Changed to ingressClassName: traefik to match all other ingresses in the
cluster (argocd, forgejo, directus, etc.)

Fixes: DEV-382

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-07-25 17:38:54 +00:00
CTO Agent
d6532697aa Move Passbolt deployment configuration to Git
- Add all Kubernetes manifest files for Passbolt deployment
- Add MariaDB deployment, service, PVC manifests
- Add Passbolt deployment, service, PVC, ingress manifests
- Add namespace manifest
- Update passbolt-secret to include SMTP authentication
- Add README with initial admin user setup instructions

The configuration was extracted from the running cluster and organized
into separate manifest files for better maintainability.

SMTP is configured to use Stalwart mail server with:
- Username: passbolt
- Email: passbolt@basicstack.de
- Host: stalwart-mail.stalwart.svc.cluster.local

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-07-25 16:57:35 +00:00
CTO Agent
ef5ef56784 Fix Forgejo deployment hanging in Argo CD sync
Change deployment strategy from RollingUpdate (default) to Recreate
to resolve volume attachment conflict. The forgejo-data PVC uses
ReadWriteOnce access mode, which only allows attachment to one pod
at a time. RollingUpdate tries to start new pod before terminating
old pod, causing multi-attach error.

With Recreate strategy, old pod terminates first, volume detaches,
then new pod starts and successfully attaches the volume.

Resolves: DEV-380

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-07-25 15:25:23 +00:00
CTO Agent
8c2cc6fa27 Fix Stalwart ArgoCD degraded status by ignoring unsealed secrets
Add argocd.argoproj.io/compare-options: IgnoreExtraneous annotation to
both Stalwart sealed secret templates (stalwart-admin-credentials and
stalwart-s3-backup) to prevent ArgoCD from seeing the unsealed secrets
(created by sealed-secrets controller) as extraneous.

This is the same fix applied in DEV-377 (Directus) and DEV-378 (Harbor).

Resolves: DEV-379

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-07-25 15:15:42 +00:00
CTO Agent
2eaf4db8c4 Fix Harbor ArgoCD degraded status by ignoring unsealed secret
Add argocd.argoproj.io/compare-options: IgnoreExtraneous annotation to
harbor-secrets sealed secret template to prevent ArgoCD from seeing the
unsealed secret (created by sealed-secrets controller) as extraneous.

This is the same fix applied in DEV-377 for Directus and previously in
DEV-289, DEV-290 for other services.

Resolves: DEV-378

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-07-25 15:05:37 +00:00
CTO Agent
58ffdc9bf4 Add IgnoreExtraneous annotation to Directus sealed secrets
This tells Argo CD to ignore the unsealed secrets created by the sealed-secrets controller, preventing the health status from being degraded.

This is the same fix applied previously in DEV-289, DEV-290, and to Pocket ID for the same issue.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-07-25 14:57:27 +00:00
CTO Agent
1f81d61642 Fix Directus health check 403 errors (DEV-376)
Changed liveness probe from HTTP /server/health to TCP socket check
and readiness probe to use root path instead. The /server/health
endpoint in Directus v12+ requires authentication by default,
causing 403 responses that triggered pod restarts.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-07-25 14:30:14 +00:00
CTO Agent
38ed8316dd Add Directus license key as sealed secret
Adds the Directus license key (DP3WZ-8DT8F-79FME-5HJ1A-E4EMJ) as a sealed secret and configures the deployment to use it via the LICENSE_KEY environment variable.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-07-25 14:20:31 +00:00
CTO Agent
a309c6873c Add complete Directus configuration and update to v12.1.1
Moved Directus deployment configuration from k8s cluster to Git repository:
- Main Directus deployment updated to v12.1.1
- PostgreSQL deployment (postgres:16-alpine)
- Services for both Directus and PostgreSQL
- Ingress with TLS/cert-manager
- Backup CronJob (daily at 2 AM)
- PVCs for database and uploads

This enables GitOps management via Argo CD.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-07-25 13:45:47 +00:00
CTO Agent
8c66f6c955 Add metrics.k8s.io permissions to Dozzle ClusterRole
Dozzle pod was crashing with:
  pods.metrics.k8s.io is forbidden: User "system:serviceaccount:dozzle:dozzle"
  cannot list resource "pods" in API group "metrics.k8s.io" at the cluster scope

Added permission for the metrics.k8s.io API group to allow Dozzle to collect
pod metrics for its monitoring dashboard.

Fixes: DEV-372

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-07-25 11:34:26 +00:00
CTO Agent
b91cbc1e09 Fix Dozzle websocket timeouts by increasing oauth2-proxy upstream timeout
The websocket connection drops were caused by oauth2-proxy's upstream-timeout
being set to only 30s. Websocket connections are long-lived and need much
longer timeouts. Increased to 3600s (1 hour) per Traefik websocket best practices.

Fixes: DEV-371

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-07-25 11:14:50 +00:00
1068e74deb apps/dozzle/ingress.yaml aktualisiert 2026-07-25 11:06:17 +00:00
b3d252c375 apps/dozzle/ingress.yaml aktualisiert 2026-07-25 10:59:15 +00:00
CTO Agent
e51b191f4b Fix Dozzle websocket connection stability
Add Traefik annotations for websocket support:
- Enable sticky sessions to ensure websocket connections go to same pod
- Add custom sticky cookie name for better tracking
- Pass host header for proper routing

Resolves DEV-371

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-07-25 10:52:13 +00:00
CTO Agent
c0a1bcb673 Change Stalwart externalTrafficPolicy to Cluster for multi-node LB
Changed both stalwart-smtp and stalwart-imap services from
externalTrafficPolicy: Local to externalTrafficPolicy: Cluster.

This enables all 4 k3s nodes to appear healthy in Hetzner Load
Balancer health checks, instead of only k3s-worker-1 (where the
single Stalwart pod runs).

With Cluster policy, traffic arriving at any node will be
forwarded via kube-proxy to k3s-worker-1 where the pod is running.

Verification:
- All 4 nodes now show in service EXTERNAL-IP field
- Mail ports (25, 587, 993) accessible from all nodes
- Connectivity tested from k3s-worker-1 and k3s-worker-2

Fixes: DEV-368

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-07-25 09:35:16 +00:00
CTO Agent
c6b14ff0bc Fix Harbor PostgreSQL data directory permissions
Add securityContext configuration to the database StatefulSet to ensure
PostgreSQL can access its data directory with correct permissions.

The issue was that the PVC mount permissions did not match PostgreSQL's
requirements (u=rwx/0700 or u=rwx,g=rx/0750). This fix sets:
- runAsUser: 999 (postgres user)
- fsGroup: 999 (postgres group)
- fsGroupChangePolicy: OnRootMismatch (only change ownership when needed)

This resolves the crash loop where harbor-database-0 failed with:
'data directory has invalid permissions'

Fixes: DEV-364

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-07-25 08:46:33 +00:00
CTO Agent
38639f369e Document stable routing solution for Stalwart Mail
Add comprehensive analysis of current k3s ServiceLB issues and long-term
routing solutions to prevent CNI-HOSTPORT orphaned rules.

Recommended approach: Migrate to Hetzner Cloud Load Balancer (DEV-357)
- Eliminates CNI-HOSTPORT complexity
- True external load balancing
- ~€8.91/month cost

Alternative: Traefik TCP IngressRoute (interim solution)

Includes recovery procedures for orphaned iptables rules.

Related: DEV-359, DEV-357

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-07-22 18:07:38 +00:00
CTO Agent
a551ee5235 Remove insecure mail ports 143 (IMAP) and 465 (SMTPS)
Drop legacy insecure mail ports per DEV-359 approval:
- Remove port 465 (SMTPS) from SMTP service - enforce STARTTLS on 587
- Remove port 143 (IMAP) from IMAP service - enforce TLS on 993

This reduces attack surface and enforces secure mail protocols.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-07-22 18:04:05 +00:00
CTO Agent
d0dc5dc735 Fix Dozzle connection issues and restarts
Root cause: Dozzle was being rate-limited by the Kubernetes API when
fetching logs from many containers. The aggressive 1-second health check
timeouts caused the container to fail probes and restart repeatedly.

Changes:
- Increased all probe timeouts from 1s to 5s to handle API rate limiting
- Increased liveness probe period from 10s to 30s to reduce check frequency
- Increased readiness probe period from 5s to 10s
- Increased startup probe failure threshold from 15 to 20 (60s total startup time)
- Increased CPU limit from 500m to 1000m and request from 100m to 250m
- Increased memory limit from 512Mi to 1Gi and request from 128Mi to 256Mi
- Pinned image version to v10.6.10 instead of :latest

This prevents unnecessary restarts when Dozzle is waiting on rate-limited
Kubernetes API calls, fixing both the initial 503 errors and the connection
drops after prolonged use.

Resolves: DEV-351

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-07-19 16:37:53 +00:00
6de215aeca apps/dozzle/deployment.yaml aktualisiert 2026-07-19 16:17:17 +00:00
CTO Agent
5797b106a6 Add health probes to Dozzle container to fix startup race condition
Fixes DEV-350. During pod startup, Dozzle takes ~11 seconds to start
accepting connections, but oauth2-proxy can receive and proxy requests
immediately. This causes "connection refused" errors when users access
the UI right after a pod restart.

Solution:
- Add startupProbe with 30s timeout (15 failures × 2s) to give Dozzle
  time to start without failing readiness
- Add readinessProbe to prevent traffic routing until Dozzle is ready
- Add livenessProbe to restart container if Dozzle becomes unhealthy

All probes use the /healthcheck endpoint on port 8080.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-07-19 14:59:35 +00:00
CTO Agent
7c11256739 fix(dozzle): Remove invalid --proxy-buffer-size flag from oauth2-proxy
The oauth2-proxy container was failing with "unknown flag: --proxy-buffer-size".
This flag is not valid for oauth2-proxy and has been removed.

Fixes DEV-349

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-07-19 14:48:47 +00:00
CTO Agent
69b1668854 Fix Dozzle WebSocket/SSE streaming issues
Added oauth2-proxy flags to properly handle Server-Sent Events (SSE):
- --flush-interval=1s: Flush buffered data regularly for SSE
- --upstream-timeout=30s: Allow longer streaming connections
- --proxy-buffer-size=8k: Reduce buffering latency

Simplified ingress configuration to standard Traefik setup.

OAuth2-proxy was buffering responses by default, which broke the
WebSocket/SSE endpoints used for real-time log streaming. The
flush-interval flag ensures data is streamed properly.

Related: DEV-349

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-07-19 14:41:00 +00:00
CTO Agent
d7a81a2f7d Fix Dozzle WebSocket connectivity by forcing IPv4 upstream
oauth2-proxy was resolving localhost to IPv6 [::1]:8080 instead of
IPv4 127.0.0.1:8080, causing connection refused errors on the streaming
endpoints (/api/events/stream and /api/hosts/.../logs/stream).

Changed upstream URL from http://localhost:8080 to http://127.0.0.1:8080
to force IPv4 resolution.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-07-19 14:31:50 +00:00
CTO Agent
9ae3973d9b Fix Dozzle WebSocket connectivity for API access
Added WebSocket support configuration to both oauth2-proxy and Traefik ingress:
- Enable explicit WebSocket proxying in oauth2-proxy (--proxy-websockets=true)
- Pass Host header through oauth2-proxy (--pass-host-header=true)
- Add WebSocket upgrade headers to Traefik ingress annotations

This fixes the "UI cannot connect to API" error by ensuring real-time
log streaming WebSocket connections work through the authentication proxy.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-07-19 14:25:07 +00:00
CTO Agent
3b80ed550c Fix Dozzle UI-to-API connection by adding WebSocket support
Add Traefik WebSocket annotations to ingress to enable real-time log
streaming. Without these, the UI cannot establish WebSocket connections
to the API backend.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-07-19 14:19:04 +00:00
CTO Agent
b88e178ec3 fix(dozzle): add nodes permission to ClusterRole for k8s mode
Dozzle running in k8s mode requires permission to list nodes at cluster
scope. Without this permission, the pod fails with error:
"nodes is forbidden: User \"system:serviceaccount:dozzle:dozzle\" cannot
list resource \"nodes\" in API group \"\" at the cluster scope"

This change adds the nodes resource with get, list, and watch verbs to
the ClusterRole, allowing Dozzle to discover all nodes in the cluster
when running in k8s/swarm mode.

Resolves: DEV-349

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-07-19 13:51:06 +00:00
fa19370e49 apps/dozzle/deployment.yaml aktualisiert
Added env DOZZLE_MODE "k8s"
2026-07-19 13:45:39 +00:00
CTO Agent
088f3728ba Remove command args to test Dozzle Kubernetes auto-detection
Trying to resolve "Could not connect to any Docker Engine" error by letting Dozzle auto-detect its environment without explicit flags.

Current blocker: Dozzle v10.6.10 is not auto-detecting Kubernetes mode despite having proper RBAC and service account configured.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-07-19 13:39:33 +00:00
CTO Agent
dc77249ea6 Fix YAML syntax error in deployment
Add missing 'containers:' line that was accidentally removed in previous edit.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-07-19 13:37:36 +00:00
CTO Agent
694529b0af Fix Dozzle image version and add --agent flag
Revert to :latest image (v8.9.4 doesn't exist) and add --agent flag to help Dozzle detect and work in Kubernetes mode.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-07-19 13:35:38 +00:00
CTO Agent
d1357f316e Use specific Dozzle version and add no-analytics flag
Switch from :latest to v8.9.4 for stability and add --no-analytics flag to improve startup behavior in Kubernetes.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-07-19 13:33:59 +00:00