Commit graph

39 commits

Author SHA1 Message Date
20a9581b40 apps/stalwart/stalwart-fresh-deployment.yaml aktualisiert 2026-07-26 13:31:38 +00:00
CTO Agent
f5fc083a5a Restore Stalwart bootstrap ConfigMap (non-destructive recovery)
CONTEXT: DEV-403 - Stalwart pod not starting after ConfigMap revert

ROOT CAUSE:
- The stalwart-bootstrap-config ConfigMap was manually created on July 5
- It was added to GitOps at commit 191614f (12:27 today)
- It was reverted at commit fcc5042 (12:56 today)
- ArgoCD pruned the ConfigMap from the cluster after the revert
- Stalwart deployment requires this ConfigMap to start

RECOVERY:
This commit restores the ConfigMap to Git so ArgoCD can recreate it.
The ConfigMap content is identical to the working version from before.
This is a non-destructive recovery - no data or accounts are affected.

The actual Stalwart data on PVC stalwart-data remains intact.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-07-26 13:09:20 +00:00
CTO Agent
fcc504208e Revert "Add Stalwart bootstrap ConfigMap to GitOps tracking"
This reverts commit 191614f895.
2026-07-26 12:56:25 +00:00
CTO Agent
191614f895 Add Stalwart bootstrap ConfigMap to GitOps tracking
The stalwart-bootstrap-config ConfigMap was previously created manually
via kubectl and not tracked in Git. This adds it to the repository for
proper GitOps management via Argo CD.

Related to DEV-403 - Stalwart sync hanging issue.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-07-26 12:27:00 +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
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
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
0e12e443b7 Update Stalwart stability check window
Start new 7-day verification window: 2026-07-13 21:07 UTC → 2026-07-20 21:07 UTC
- CEO confirmed Web-UI SMTP and IMAP working
- Restarting verification per Option A (clean 7-day window)
- Baseline status: all services verified working

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-07-13 21:08:11 +00:00
CTO Agent
3cc450d08e docs(stalwart): Update stability check status with investigation results
- Verified all infrastructure working correctly (SMTP/IMAP/LoadBalancer)
- Identified user SMTP issue as client configuration (port 143 vs 587)
- Documented proposed verification window restart (2026-07-13 to 2026-07-20)
- Original window had 2+ days with broken LoadBalancer (10.42.1.1 vs 178.105.17.239)

Related: DEV-300

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-07-13 20:36:22 +00:00
CTO Agent
6541502682 docs(stalwart): Clarify LoadBalancer was working all along via hostPort
Investigation revealed that k3s ServiceLB uses hostPort bindings on nodes'
public IPs, not the misleading EXTERNAL-IP VIP (10.42.1.1) shown in kubectl.

External connectivity was working correctly the entire time. The confusion
was caused by misinterpreting the flannel pod network IP in the EXTERNAL-IP
field as meaning external traffic couldn't reach the service.

Verified all SMTP/IMAP ports responding correctly with proper Stalwart banners.

Related: DEV-235

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-07-13 20:12:11 +00:00
CTO Agent
ae3f164987 fix(stalwart): Add externalTrafficPolicy: Local to LoadBalancer services
Root cause: ArgoCD continuously reverts the LoadBalancer services to use
externalTrafficPolicy: Cluster (the k8s default), causing k3s to assign
internal flannel VXLAN IPs (10.42.1.x) instead of the node public IPs.

With externalTrafficPolicy: Cluster, traffic can be routed to any node,
and k3s's service controller assigns the flannel overlay IPs. This breaks
external connectivity because those IPs are not routable from outside.

With externalTrafficPolicy: Local, traffic is only routed to pods on the
same node, and k3s assigns the node's actual public IP to the LoadBalancer.

This was the missing piece from the reliability hardening in commit b0f2acf.
Without this in git, any manual kubectl patch is reverted by ArgoCD sync.

Evidence: stalwart-smtp and stalwart-imap both showing LoadBalancer IPs:
10.42.1.1, 10.42.1.2, 10.42.1.3, 10.42.1.5 (internal flannel IPs)

Related: DEV-230, DEV-231, DEV-233, DEV-235

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-07-13 20:03:49 +00:00
CTO Agent
c755bc1a45 Document SMTP/IMAP external access issue and solution options
Root cause: k3s ServiceLB assigns internal VIPs (10.42.1.x) that are not
publicly routable. External traffic to mail.basicstack.de cannot reach the
SMTP/IMAP services.

Investigation shows:
- Web-UI works (goes through Traefik IngressRoute)
- SMTP/IMAP ports have correct firewall rules
- iptables DNAT rules exist but don't help external traffic
- Internal connectivity works correctly
- LoadBalancer 'external IPs' are actually pod network IPs

Three solution options documented:
A. NodePort + iptables REDIRECT (recommended)
B. Deploy MetalLB for true LoadBalancer IPs
C. Hetzner Cloud LoadBalancers (not recommended, cost)

Awaiting approval on approach before implementing fix.

Related: DEV-235

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-07-13 20:00:19 +00:00
CTO Agent
c84411d99f Add comprehensive Stalwart stability check procedure
This document establishes operational rules and diagnostic playbooks for
Stalwart stability monitoring and incident response.

Key sections:
- Critical operational rules (repository as single point of truth)
- Requirement to read git history before any investigation
- Current 7-day stability check procedures (DEV-235)
- Known issues and their fixes (k3s networking, commit b0f2acf)
- Diagnostic playbook for SMTP/IMAP/Web-UI issues
- Issue documentation template
- Evidence requirements for verification completion

This ensures all future investigations follow documented procedures and
avoid repeated debugging cycles.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-07-13 19:56:35 +00:00
CTO Agent
7b6a0064b9 Move Application manifests to apps root for proper self-management
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>
2026-07-12 09:27:28 +00:00
CTO Agent
d8cd3638fa Fix Stalwart backup CronJob to handle ReadWriteOnce PVC
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>
2026-07-11 11:21:39 +00:00
CTO Agent
b0f2acf5f3 Stalwart reliability hardening: fix k3s service networking issues
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>
2026-07-11 11:06:36 +00:00
e836b71eac apps/stalwart/README.md aktualisiert 2026-07-04 17:19:55 +00:00
CTO Agent
8054ef4018 Implement automatic TLS certificate renewal for Stalwart
Added Stakater Reloader to automatically restart Stalwart pods when
TLS certificates are renewed by cert-manager. This ensures seamless
certificate rotation without manual intervention.

Changes:
- Deploy Stakater Reloader in infrastructure/networking/
- Add Reloader annotation to Stalwart StatefulSet to watch stalwart-tls secret
- Document certificate renewal process and troubleshooting

The certificate is managed by cert-manager with Let's Encrypt and will
automatically renew 30 days before expiration (renewal date: 2026-08-20).
Reloader detects secret updates and triggers a rolling restart of the
Stalwart StatefulSet to load the new certificate.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-07-04 16:48:48 +00:00
a24420ac3e apps/stalwart/manual_config_steps.md aktualisiert 2026-07-04 16:39:11 +00:00
453fca6486 apps/stalwart/manual_config_steps.md aktualisiert 2026-07-04 16:37:22 +00:00
975a932049 apps/stalwart/manual_config_steps.md aktualisiert 2026-07-04 16:36:59 +00:00
d010d9a63f apps/stalwart/manual_config_steps.md aktualisiert 2026-07-04 16:36:36 +00:00
712c11fd38 apps/stalwart/README.md aktualisiert 2026-07-04 16:31:09 +00:00
72c7a78cc8 apps/stalwart/manual_config_steps.md aktualisiert 2026-07-04 16:30:29 +00:00
d7367feead apps/stalwart/manual_config_steps.md aktualisiert 2026-07-04 16:30:01 +00:00
fbe131821e apps/stalwart/manual_config_steps.md aktualisiert 2026-07-04 16:27:09 +00:00
71eacdb9be apps/stalwart/manual_config_steps.md aktualisiert 2026-07-04 16:26:47 +00:00
ddf55d5050 apps/stalwart/manual_config_steps.md hinzugefügt 2026-07-04 16:26:20 +00:00
3b97089908 apps/stalwart/README.md aktualisiert 2026-07-04 16:11:57 +00:00
c4efccea91 apps/stalwart/README.md aktualisiert 2026-07-04 13:46:20 +00:00
cea2c2346e apps/stalwart/README.md aktualisiert 2026-07-04 13:41:06 +00:00
CTO Agent
afe05cc772 Clean up Stalwart deployment - remove all old configs and OIDC attempts
Removed all experimental files, patches, OIDC configs, and Helm values.
Keeping only the clean v0.16.11 deployment with username/password auth.

Files kept:
- stalwart-fresh-deployment.yaml (main manifest)
- stalwart-admin-credentials-sealed.yaml (admin password)
- stalwart-s3-backup-sealed.yaml (backup credentials)
- README.md (updated documentation)

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-07-04 13:22:36 +00:00
CTO Agent
3acfa1f5e1 Remove config.toml ConfigMap and add TLS certificate mount
- Removed stalwart-config ConfigMap (config.toml not used in v0.16.11)
- All configuration is done via Stalwart API and stored in RocksDB
- Added TLS certificate mount from stalwart-tls secret
- Using stalwartlabs/stalwart:v0.16.11

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-07-04 12:13:18 +00:00
CTO Agent
0d4d9b9adf fix(stalwart): Add config.json to load web UI configuration
- Mount /etc/stalwart/config.json pointing to RocksDB
- Allows Stalwart to load configuration from web UI setup
- Resolves bootstrap mode loop

Refs: DEV-206

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-07-04 10:34:28 +00:00
CTO Agent
843affde05 fix(stalwart): Correct permissions to 2000:2000
- Fixed init container to set ownership to 2000:2000
- Stalwart container runs as user 2000 by default
- Resolves permission denied error during web UI setup

Refs: DEV-206

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-07-04 10:23:15 +00:00
CTO Agent
509bbebe10 feat(stalwart): Fresh deployment with basic auth
- Deployed Stalwart v0.16.11 in stalwart namespace
- Configured encrypted hcloud storage (20Gi PVC)
- Set up LoadBalancer services for SMTP/IMAP
- Configured Ingress with TLS for mail.basicstack.de
- Daily backup CronJob with S3/restic
- Bootstrap mode requires web UI setup completion

Refs: DEV-206

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-07-04 10:17:39 +00:00
CTO Agent
d5e8c28a6a Convert all secrets to SealedSecrets for enhanced security
This commit converts all application secrets to SealedSecrets, preventing
plaintext secrets from being stored in git.

Changes:
- Added .gitignore to prevent future plaintext secret commits
- Created 20 SealedSecret manifests across 8 applications:
  * Stalwart (4 secrets): admin credentials, OAuth proxy, OIDC, S3 backup
  * Directus (5 secrets): admin, agent token, app secrets, DB, OIDC
  * Paperclip (4 secrets): main secrets, auth, OIDC, session
  * Forgejo (2 secrets): postgres, backup
  * BookStack (2 secrets): OIDC, MySQL
  * Passbolt (2 secrets): MariaDB, app secrets
  * Pocket ID (1 secret)
- Removed hardcoded secrets from 6 stalwart deployment files
- Replaced plaintext credentials with references to sealed secrets

All sealed secrets have been applied to the cluster and services verified
to be running correctly.

Related: DEV-203

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-07-01 18:38:27 +00:00
CTO Agent
c64f9ab0d0 Initialize CD/CI repository structure with Stalwart example
Set up the repository structure following GitOps principles:
- apps/ for application deployments (Stalwart as example)
- infrastructure/ for cluster-wide configs (networking, monitoring)
- docs/ for general documentation

Migrated complete Stalwart deployment configuration including:
- Multiple deployment variants (basic, OIDC-enabled)
- Helm values files
- Monitoring and dashboard configurations
- Operational documentation (backup/restore, bootstrap)
- Configuration patches and fixes

Added comprehensive README files at each level to guide future use.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-07-01 18:08:31 +00:00