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>
This commit is contained in:
parent
3acfa1f5e1
commit
afe05cc772
28 changed files with 53 additions and 3328 deletions
|
|
@ -1,54 +1,58 @@
|
||||||
# Stalwart Mail Server Deployment
|
# Stalwart Mail Server v0.16.11
|
||||||
|
|
||||||
This directory contains the complete deployment configuration for the Stalwart mail server on the basicstack.de cluster.
|
Clean deployment of Stalwart mail server with username/password authentication only.
|
||||||
|
|
||||||
## Files Overview
|
## Architecture
|
||||||
|
|
||||||
### Deployment Manifests
|
- **Version**: v0.16.11
|
||||||
- `stalwart-deployment.yaml` - Basic deployment configuration
|
- **Authentication**: Username/password only (NO OAuth/OIDC)
|
||||||
- `stalwart-deployment-new.yaml` - Updated deployment variant
|
- **Configuration**: API-based (stored in RocksDB)
|
||||||
- `stalwart-deployment-with-oidc.yaml` - Deployment with OIDC integration
|
- **Storage**: Encrypted hcloud-volumes (20Gi)
|
||||||
- `stalwart-deployment-oidc-only.yaml` - OIDC-only authentication deployment
|
- **Backup**: Daily restic backup to S3 at 3 AM
|
||||||
|
- **Web UI**: https://mail.basicstack.de
|
||||||
|
|
||||||
### Helm Configuration
|
## Files
|
||||||
- `stalwart-values.yaml` - Main Helm values file
|
|
||||||
- `stalwart-values-fixed-ports.yaml` - Values with corrected port configurations
|
|
||||||
- `stalwart-values-correct.yaml` - Verified correct values
|
|
||||||
- `stalwart-helm-values-oidc.yaml` - Helm values for OIDC setup
|
|
||||||
- `stalwart-helm-fix.yaml` - Helm chart fixes
|
|
||||||
|
|
||||||
### Configuration
|
- `stalwart-fresh-deployment.yaml` - Main deployment manifest
|
||||||
- `stalwart-oidc-config.yaml` - OIDC provider configuration
|
- `stalwart-admin-credentials-sealed.yaml` - Sealed secret for admin password
|
||||||
- `stalwart-config-fix.yaml` - Configuration corrections
|
- `stalwart-s3-backup-sealed.yaml` - Sealed secret for S3 backup credentials
|
||||||
- `stalwart-config-fix-v2.yaml` - Updated configuration fix
|
|
||||||
|
|
||||||
### Monitoring
|
## Deployment
|
||||||
- `stalwart-monitoring.yaml` - Prometheus ServiceMonitor and metrics
|
|
||||||
- `stalwart-dashboard-configmap.yaml` - Grafana dashboard configuration
|
|
||||||
|
|
||||||
### Maintenance
|
|
||||||
- `stalwart-console-pod.yaml` - Debug/console pod for troubleshooting
|
|
||||||
- `stalwart-service-patch.yaml` - Service configuration patch
|
|
||||||
- `stalwart-statefulset-patch.yaml` - StatefulSet patches (v1, v2, v3)
|
|
||||||
|
|
||||||
### Documentation
|
|
||||||
- `stalwart-backup-restore.md` - Backup and restore procedures
|
|
||||||
- `stalwart-bootstrap-completion-guide.md` - Initial setup guide
|
|
||||||
|
|
||||||
## Deployment Notes
|
|
||||||
|
|
||||||
This is a reference implementation showing the evolution of a production deployment. Multiple variants are preserved to show:
|
|
||||||
- Different authentication strategies (local vs. OIDC)
|
|
||||||
- Configuration iterations and fixes
|
|
||||||
- Monitoring integration
|
|
||||||
- Operational procedures
|
|
||||||
|
|
||||||
When deploying a new service, you don't need this many files - choose the appropriate manifest for your use case and adapt it.
|
|
||||||
|
|
||||||
## Current Production Deployment
|
|
||||||
|
|
||||||
Review the files to determine which manifest represents the current production state, or check the cluster directly with:
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
kubectl get deployment,statefulset -n stalwart
|
# Apply sealed secrets first
|
||||||
|
kubectl apply -f stalwart-admin-credentials-sealed.yaml
|
||||||
|
kubectl apply -f stalwart-s3-backup-sealed.yaml
|
||||||
|
|
||||||
|
# Create bootstrap config
|
||||||
|
kubectl create configmap stalwart-bootstrap-config \
|
||||||
|
--from-literal=config.json='{"@type":"RocksDb","path":"/var/lib/stalwart"}' \
|
||||||
|
-n stalwart
|
||||||
|
|
||||||
|
# Deploy Stalwart
|
||||||
|
kubectl apply -f stalwart-fresh-deployment.yaml
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Initial Admin Login
|
||||||
|
|
||||||
|
After deployment, log in at https://mail.basicstack.de with:
|
||||||
|
- Username: `admin`
|
||||||
|
- Password: (from stalwart-admin-credentials secret)
|
||||||
|
|
||||||
|
## Configuration
|
||||||
|
|
||||||
|
All configuration is done via the web UI or API. The bootstrap config only points to the RocksDB database location. NO config.toml files are used.
|
||||||
|
|
||||||
|
## Ports
|
||||||
|
|
||||||
|
- SMTP: 25, 587, 465
|
||||||
|
- IMAP: 143, 993
|
||||||
|
- HTTP: 8080 (web UI)
|
||||||
|
|
||||||
|
## Storage
|
||||||
|
|
||||||
|
Data is stored in `/var/lib/stalwart` using the RocksDB database format. This includes:
|
||||||
|
- Email messages
|
||||||
|
- User accounts
|
||||||
|
- Server configuration
|
||||||
|
- TLS certificates configuration
|
||||||
|
|
|
||||||
|
|
@ -1,230 +0,0 @@
|
||||||
# Stalwart Mail Server — Backup & Restore Procedures
|
|
||||||
|
|
||||||
## Overview
|
|
||||||
|
|
||||||
Stalwart stores all data in a RocksDB database mounted at `/opt/stalwart-mail` on a 20Gi encrypted hcloud volume (`hcloud-volumes-encrypted` storage class).
|
|
||||||
|
|
||||||
## Backup Strategy
|
|
||||||
|
|
||||||
### Option A: Hetzner Volume Snapshots (Recommended)
|
|
||||||
|
|
||||||
Hetzner Cloud provides volume snapshots that capture the full encrypted volume state.
|
|
||||||
|
|
||||||
#### Manual Snapshot via hcloud CLI
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Get the volume ID
|
|
||||||
bin/hcloud volume list | grep stalwart
|
|
||||||
|
|
||||||
# Create a snapshot (works even while volume is mounted — RocksDB is crash-safe)
|
|
||||||
bin/hcloud volume snapshot create <volume-id> --description "stalwart-backup-$(date +%Y%m%d)"
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Scheduled Snapshots (Daily)
|
|
||||||
|
|
||||||
Create a CronJob in Kubernetes to automate snapshots:
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
# Requires hcloud CLI and API token in a secret
|
|
||||||
apiVersion: batch/v1
|
|
||||||
kind: CronJob
|
|
||||||
metadata:
|
|
||||||
name: stalwart-snapshot
|
|
||||||
namespace: mail
|
|
||||||
spec:
|
|
||||||
schedule: "0 3 * * *" # 3 AM daily
|
|
||||||
jobTemplate:
|
|
||||||
spec:
|
|
||||||
template:
|
|
||||||
spec:
|
|
||||||
restartPolicy: OnFailure
|
|
||||||
containers:
|
|
||||||
- name: hcloud-snapshot
|
|
||||||
image: alpine:latest
|
|
||||||
command:
|
|
||||||
- /bin/sh
|
|
||||||
- -c
|
|
||||||
- |
|
|
||||||
apk add --no-cache curl jq
|
|
||||||
VOLUME_ID=$(curl -s -H "Authorization: Bearer $HCLOUD_TOKEN" \
|
|
||||||
https://api.hetzner.cloud/v1/volumes | \
|
|
||||||
jq -r '.volumes[] | select(.name | contains("stalwart")) | .id')
|
|
||||||
curl -X POST -H "Authorization: Bearer $HCLOUD_TOKEN" \
|
|
||||||
-H "Content-Type: application/json" \
|
|
||||||
-d "{\"description\": \"stalwart-auto-$(date +%Y%m%d)\"}" \
|
|
||||||
https://api.hetzner.cloud/v1/volumes/$VOLUME_ID/actions/create_snapshot
|
|
||||||
env:
|
|
||||||
- name: HCLOUD_TOKEN
|
|
||||||
valueFrom:
|
|
||||||
secretKeyRef:
|
|
||||||
name: hcloud-credentials
|
|
||||||
key: token
|
|
||||||
```
|
|
||||||
|
|
||||||
### Option B: Filesystem-Level Backup
|
|
||||||
|
|
||||||
Back up the data directory while Stalwart is paused or using a consistent snapshot.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Scale down Stalwart (brief downtime)
|
|
||||||
kubectl scale deployment stalwart -n mail --replicas=0
|
|
||||||
|
|
||||||
# Exec into a temporary pod with the same PVC
|
|
||||||
kubectl run backup-helper --image=alpine --restart=Never \
|
|
||||||
-n mail \
|
|
||||||
--overrides='{"spec":{"volumes":[{"name":"data","persistentVolumeClaim":{"claimName":"stalwart-data"}}],"containers":[{"name":"backup-helper","image":"alpine","command":["sleep","3600"],"volumeMounts":[{"name":"data","mountPath":"/opt/stalwart-mail"}]}]}}'
|
|
||||||
|
|
||||||
# Copy data out
|
|
||||||
kubectl cp mail/backup-helper:/opt/stalwart-mail ./stalwart-backup-$(date +%Y%m%d)
|
|
||||||
|
|
||||||
# Clean up helper pod
|
|
||||||
kubectl delete pod backup-helper -n mail
|
|
||||||
|
|
||||||
# Restore Stalwart
|
|
||||||
kubectl scale deployment stalwart -n mail --replicas=1
|
|
||||||
```
|
|
||||||
|
|
||||||
### Option C: Stalwart Admin API Backup (Config Only)
|
|
||||||
|
|
||||||
Backup the configuration without downtime:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Backup config via Stalwart admin API
|
|
||||||
curl -u admin:PASSWORD https://mail.paperclip.cloud/api/store/backup \
|
|
||||||
-o stalwart-config-backup-$(date +%Y%m%d).zip
|
|
||||||
```
|
|
||||||
|
|
||||||
## Restore Procedures
|
|
||||||
|
|
||||||
### Restore from Volume Snapshot
|
|
||||||
|
|
||||||
1. **Create a new volume from the snapshot**:
|
|
||||||
```bash
|
|
||||||
bin/hcloud volume create --name stalwart-restore --size 20 \
|
|
||||||
--snapshot <snapshot-id> --location fsn1
|
|
||||||
```
|
|
||||||
|
|
||||||
2. **Scale down Stalwart**:
|
|
||||||
```bash
|
|
||||||
kubectl scale deployment stalwart -n mail --replicas=0
|
|
||||||
```
|
|
||||||
|
|
||||||
3. **Delete old PVC** (after backing up the PV name):
|
|
||||||
```bash
|
|
||||||
PV_NAME=$(kubectl get pvc stalwart-data -n mail -o jsonpath='{.spec.volumeName}')
|
|
||||||
kubectl delete pvc stalwart-data -n mail
|
|
||||||
```
|
|
||||||
|
|
||||||
4. **Create PV pointing to restored volume**:
|
|
||||||
```bash
|
|
||||||
RESTORED_VOLUME_ID=<new-volume-id>
|
|
||||||
kubectl apply -f - <<EOF
|
|
||||||
apiVersion: v1
|
|
||||||
kind: PersistentVolume
|
|
||||||
metadata:
|
|
||||||
name: stalwart-restored
|
|
||||||
spec:
|
|
||||||
capacity:
|
|
||||||
storage: 20Gi
|
|
||||||
accessModes:
|
|
||||||
- ReadWriteOnce
|
|
||||||
persistentVolumeReclaimPolicy: Retain
|
|
||||||
storageClassName: hcloud-volumes-encrypted
|
|
||||||
csi:
|
|
||||||
driver: csi.hetzner.cloud
|
|
||||||
volumeHandle: "$RESTORED_VOLUME_ID"
|
|
||||||
EOF
|
|
||||||
```
|
|
||||||
|
|
||||||
5. **Create PVC bound to restored PV**:
|
|
||||||
```bash
|
|
||||||
kubectl apply -f - <<EOF
|
|
||||||
apiVersion: v1
|
|
||||||
kind: PersistentVolumeClaim
|
|
||||||
metadata:
|
|
||||||
name: stalwart-data
|
|
||||||
namespace: mail
|
|
||||||
spec:
|
|
||||||
accessModes:
|
|
||||||
- ReadWriteOnce
|
|
||||||
storageClassName: hcloud-volumes-encrypted
|
|
||||||
resources:
|
|
||||||
requests:
|
|
||||||
storage: 20Gi
|
|
||||||
volumeName: stalwart-restored
|
|
||||||
EOF
|
|
||||||
```
|
|
||||||
|
|
||||||
6. **Scale Stalwart back up**:
|
|
||||||
```bash
|
|
||||||
kubectl scale deployment stalwart -n mail --replicas=1
|
|
||||||
kubectl rollout status deployment stalwart -n mail
|
|
||||||
```
|
|
||||||
|
|
||||||
7. **Verify restoration**:
|
|
||||||
```bash
|
|
||||||
kubectl logs -n mail deployment/stalwart --tail=20
|
|
||||||
curl -k https://mail.paperclip.cloud/api/principal -u admin:PASSWORD
|
|
||||||
```
|
|
||||||
|
|
||||||
### Restore from Filesystem Backup
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Scale down Stalwart
|
|
||||||
kubectl scale deployment stalwart -n mail --replicas=0
|
|
||||||
|
|
||||||
# Create restore helper pod
|
|
||||||
kubectl run restore-helper --image=alpine --restart=Never \
|
|
||||||
-n mail \
|
|
||||||
--overrides='{"spec":{"volumes":[{"name":"data","persistentVolumeClaim":{"claimName":"stalwart-data"}}],"containers":[{"name":"restore-helper","image":"alpine","command":["sleep","3600"],"volumeMounts":[{"name":"data","mountPath":"/opt/stalwart-mail"}]}]}}'
|
|
||||||
|
|
||||||
# Wait for pod
|
|
||||||
kubectl wait pod restore-helper -n mail --for=condition=Ready
|
|
||||||
|
|
||||||
# Clear existing data and restore
|
|
||||||
kubectl exec -n mail restore-helper -- rm -rf /opt/stalwart-mail/*
|
|
||||||
kubectl cp ./stalwart-backup-YYYYMMDD/. mail/restore-helper:/opt/stalwart-mail/
|
|
||||||
|
|
||||||
# Clean up helper
|
|
||||||
kubectl delete pod restore-helper -n mail
|
|
||||||
|
|
||||||
# Scale back up
|
|
||||||
kubectl scale deployment stalwart -n mail --replicas=1
|
|
||||||
```
|
|
||||||
|
|
||||||
## Snapshot Retention Policy
|
|
||||||
|
|
||||||
Recommended retention:
|
|
||||||
- **Daily snapshots**: Keep 7 days
|
|
||||||
- **Weekly snapshots**: Keep 4 weeks
|
|
||||||
- **Monthly snapshots**: Keep 6 months
|
|
||||||
|
|
||||||
Hetzner snapshots are billed at €0.01/GB/month, so a 20GB volume costs €0.20/month per snapshot.
|
|
||||||
|
|
||||||
## Testing Backup/Restore
|
|
||||||
|
|
||||||
Test the restore procedure quarterly:
|
|
||||||
|
|
||||||
1. Create a snapshot
|
|
||||||
2. Create a new volume from the snapshot in a test namespace
|
|
||||||
3. Deploy a test Stalwart instance pointing to the restored volume
|
|
||||||
4. Verify mail data is accessible via admin API
|
|
||||||
5. Delete test resources
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Verification test command
|
|
||||||
kubectl run test-restore --image=curlimages/curl --restart=Never \
|
|
||||||
-n mail -- curl -k -u admin:PASSWORD \
|
|
||||||
https://mail.paperclip.cloud/api/principal
|
|
||||||
kubectl logs test-restore -n mail
|
|
||||||
kubectl delete pod test-restore -n mail
|
|
||||||
```
|
|
||||||
|
|
||||||
## Recovery Time Objectives
|
|
||||||
|
|
||||||
| Scenario | RTO | RPO |
|
|
||||||
|----------|-----|-----|
|
|
||||||
| Pod crash | ~30 seconds | 0 (persistent volume) |
|
|
||||||
| Node failure | ~2 minutes | 0 (PVC reattaches) |
|
|
||||||
| Volume corruption | 30-60 minutes | <24 hours (last snapshot) |
|
|
||||||
| Data center failure | 1-2 hours | <24 hours (manual restore to new region) |
|
|
||||||
|
|
@ -1,162 +0,0 @@
|
||||||
# Stalwart 0.16 Bootstrap Wizard Completion Guide
|
|
||||||
|
|
||||||
## Current Status
|
|
||||||
|
|
||||||
- ✅ Pod running: `stalwart-0` in namespace `stalwart`
|
|
||||||
- ✅ JMAP API accessible (tested)
|
|
||||||
- ✅ OIDC credentials prepared in Kubernetes secret
|
|
||||||
- ⏳ Bootstrap wizard awaiting completion
|
|
||||||
|
|
||||||
## Bootstrap Access Credentials
|
|
||||||
|
|
||||||
**URL**: https://mail.basicstack.de/admin
|
|
||||||
**Username**: `admin`
|
|
||||||
**Password**: `YFMySjQYfMB3tYZa`
|
|
||||||
|
|
||||||
> **Note**: These credentials are valid only until the bootstrap wizard is completed, then they will be automatically disabled.
|
|
||||||
|
|
||||||
## OIDC Configuration Details
|
|
||||||
|
|
||||||
Retrieved from Kubernetes secret `stalwart-oidc` in namespace `stalwart`:
|
|
||||||
|
|
||||||
```
|
|
||||||
Client ID: 0f37a0e3-8d3b-4413-a394-36226f42a980
|
|
||||||
Client Secret: LPo8VejJXznisTQ87TGAs4Ad0Typ1MJw
|
|
||||||
Issuer URL: https://auth.basicstack.de
|
|
||||||
Redirect URI: https://mail.basicstack.de/admin/oauth/callback
|
|
||||||
Scopes: openid profile email
|
|
||||||
```
|
|
||||||
|
|
||||||
## Step-by-Step Bootstrap Wizard Completion
|
|
||||||
|
|
||||||
### Step 1: Access Bootstrap Interface
|
|
||||||
|
|
||||||
1. Open browser to: https://mail.basicstack.de/admin
|
|
||||||
2. Login with bootstrap credentials:
|
|
||||||
- Username: `admin`
|
|
||||||
- Password: `YFMySjQYfMB3tYZa`
|
|
||||||
|
|
||||||
### Step 2: Configure Data Store
|
|
||||||
|
|
||||||
The wizard should show the data store configuration. This is likely auto-configured:
|
|
||||||
|
|
||||||
- **Type**: RocksDB
|
|
||||||
- **Path**: `/opt/stalwart-mail/data`
|
|
||||||
|
|
||||||
Verify the settings and proceed.
|
|
||||||
|
|
||||||
### Step 3: Set Up OIDC Authentication
|
|
||||||
|
|
||||||
Configure OAuth/OIDC provider with these exact settings:
|
|
||||||
|
|
||||||
| Field | Value |
|
|
||||||
|-------|-------|
|
|
||||||
| Provider Name | Pocket ID |
|
|
||||||
| Issuer URL | `https://auth.basicstack.de` |
|
|
||||||
| Client ID | `0f37a0e3-8d3b-4413-a394-36226f42a980` |
|
|
||||||
| Client Secret | `LPo8VejJXznisTQ87TGAs4Ad0Typ1MJw` |
|
|
||||||
| Redirect URI | `https://mail.basicstack.de/admin/oauth/callback` |
|
|
||||||
| Scopes | `openid profile email` |
|
|
||||||
|
|
||||||
**Authorization Endpoint** (auto-discovered): `https://auth.basicstack.de/api/oidc/authorize`
|
|
||||||
**Token Endpoint** (auto-discovered): `https://auth.basicstack.de/api/oidc/token`
|
|
||||||
**UserInfo Endpoint** (auto-discovered): `https://auth.basicstack.de/api/oidc/userinfo`
|
|
||||||
|
|
||||||
### Step 4: Create/Link Administrator Account
|
|
||||||
|
|
||||||
Choose **Option A** (recommended): Link to Pocket ID user
|
|
||||||
|
|
||||||
- When prompted, link the admin account to a Pocket ID user
|
|
||||||
- The Pocket ID OIDC client is already configured with group restrictions
|
|
||||||
- Only users in the `Stalwart-admin` group can access
|
|
||||||
|
|
||||||
If the wizard requires creating an internal admin first:
|
|
||||||
- Create a temporary internal admin
|
|
||||||
- Link it to OIDC
|
|
||||||
- The bootstrap password login will be automatically disabled after setup
|
|
||||||
|
|
||||||
### Step 5: Disable Password Authentication (if prompted)
|
|
||||||
|
|
||||||
- **Web UI Authentication**: OIDC only
|
|
||||||
- **Mail Client Authentication**: Uses internal directory (separate from web UI auth)
|
|
||||||
|
|
||||||
This ensures the web admin interface only accepts OIDC login while mail clients continue to work normally.
|
|
||||||
|
|
||||||
### Step 6: Complete Setup
|
|
||||||
|
|
||||||
1. Review all configuration
|
|
||||||
2. Click "Complete Setup" or equivalent final button
|
|
||||||
3. The system will:
|
|
||||||
- Save configuration to RocksDB
|
|
||||||
- Exit bootstrap mode
|
|
||||||
- Disable the bootstrap credentials
|
|
||||||
- Enable OIDC authentication
|
|
||||||
|
|
||||||
### Step 7: Verify OIDC Login
|
|
||||||
|
|
||||||
1. Log out from the bootstrap session
|
|
||||||
2. Access https://mail.basicstack.de/admin again
|
|
||||||
3. Click "Login with Pocket ID" or the OAuth login button
|
|
||||||
4. Should redirect to https://auth.basicstack.de for authentication
|
|
||||||
5. After successful Pocket ID login, should return to Stalwart admin interface
|
|
||||||
|
|
||||||
## Post-Bootstrap Verification
|
|
||||||
|
|
||||||
Run these commands to verify the configuration:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Check pod is still running
|
|
||||||
export KUBECONFIG=/paperclip/instances/default/workspaces/b4536334-39f2-4e05-b2f1-bb0e4670fba8/k3s.kubeconfig
|
|
||||||
/paperclip/instances/default/workspaces/b4536334-39f2-4e05-b2f1-bb0e4670fba8/bin/kubectl get pods -n stalwart
|
|
||||||
|
|
||||||
# Test JMAP API (should still work with OIDC credentials now)
|
|
||||||
curl -s -X POST https://mail.basicstack.de/jmap \
|
|
||||||
-H "Content-Type: application/json" \
|
|
||||||
-d '{"using":["urn:ietf:params:jmap:core"],"methodCalls":[["Core/echo",{"test":"post-bootstrap"},"0"]]}'
|
|
||||||
|
|
||||||
# Verify management API is now accessible
|
|
||||||
curl -s https://mail.basicstack.de/api/session
|
|
||||||
```
|
|
||||||
|
|
||||||
## Troubleshooting
|
|
||||||
|
|
||||||
### Issue: Cannot access bootstrap UI
|
|
||||||
- Verify pod is running: `kubectl get pods -n stalwart`
|
|
||||||
- Check pod logs: `kubectl logs stalwart-0 -n stalwart`
|
|
||||||
- Verify ingress: `kubectl get ingress -n stalwart`
|
|
||||||
|
|
||||||
### Issue: OIDC login not working after setup
|
|
||||||
- Check Stalwart logs for OAuth errors
|
|
||||||
- Verify redirect URI matches exactly
|
|
||||||
- Confirm user is in `Stalwart-admin` group in Pocket ID
|
|
||||||
- Test Pocket ID OIDC client directly
|
|
||||||
|
|
||||||
### Issue: Bootstrap credentials don't work
|
|
||||||
- Pod may have been restarted and new credentials generated
|
|
||||||
- Check parent issue DEV-155 for any updates
|
|
||||||
- May need to restart the pod or check the deployment
|
|
||||||
|
|
||||||
## Security Notes
|
|
||||||
|
|
||||||
- Bootstrap credentials automatically expire after setup completion
|
|
||||||
- OIDC client secret is stored in Kubernetes secret `stalwart-oidc`
|
|
||||||
- Access restricted to `Stalwart-admin` group members only
|
|
||||||
- All communication over HTTPS/TLS
|
|
||||||
|
|
||||||
## References
|
|
||||||
|
|
||||||
- Parent Issue: DEV-155
|
|
||||||
- OIDC Client configured in Pocket ID (client ID: 0f37a0e3-8d3b-4413-a394-36226f42a980)
|
|
||||||
- User Group: `Stalwart-admin` (group ID: 99ffc3ae-0112-4fa0-bec9-90da19bdaddd)
|
|
||||||
- Pocket ID: https://auth.basicstack.de
|
|
||||||
- Stalwart: https://mail.basicstack.de
|
|
||||||
|
|
||||||
## Acceptance Criteria Checklist
|
|
||||||
|
|
||||||
- [ ] Bootstrap setup wizard completed
|
|
||||||
- [ ] OIDC authentication configured with Pocket ID
|
|
||||||
- [ ] Can login to web UI via Pocket ID
|
|
||||||
- [ ] Password login disabled for web UI
|
|
||||||
- [ ] Bootstrap mode exited
|
|
||||||
- [ ] Configuration saved to RocksDB
|
|
||||||
- [ ] Management API responding (not "no available server")
|
|
||||||
|
|
@ -1,78 +0,0 @@
|
||||||
{
|
|
||||||
"store": {
|
|
||||||
"data": {
|
|
||||||
"type": "rocksdb",
|
|
||||||
"path": "/var/lib/stalwart/data"
|
|
||||||
},
|
|
||||||
"blob": {
|
|
||||||
"type": "rocksdb",
|
|
||||||
"path": "/var/lib/stalwart/blobs"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"directory": {
|
|
||||||
"internal": {
|
|
||||||
"type": "internal",
|
|
||||||
"store": "data"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"server": {
|
|
||||||
"hostname": "mail.basicstack.de",
|
|
||||||
"listener": {
|
|
||||||
"management": {
|
|
||||||
"bind": ["[::]:8080"],
|
|
||||||
"protocol": "http"
|
|
||||||
},
|
|
||||||
"smtp": {
|
|
||||||
"bind": ["[::]:25"],
|
|
||||||
"protocol": "smtp"
|
|
||||||
},
|
|
||||||
"submission": {
|
|
||||||
"bind": ["[::]:587"],
|
|
||||||
"protocol": "smtp"
|
|
||||||
},
|
|
||||||
"submissions": {
|
|
||||||
"bind": ["[::]:465"],
|
|
||||||
"protocol": "smtp",
|
|
||||||
"tls": {
|
|
||||||
"implicit": true
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"imap": {
|
|
||||||
"bind": ["[::]:143"],
|
|
||||||
"protocol": "imap"
|
|
||||||
},
|
|
||||||
"imaps": {
|
|
||||||
"bind": ["[::]:993"],
|
|
||||||
"protocol": "imap",
|
|
||||||
"tls": {
|
|
||||||
"implicit": true
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"sieve": {
|
|
||||||
"bind": ["[::]:4190"],
|
|
||||||
"protocol": "managesieve"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"session": {
|
|
||||||
"auth": {
|
|
||||||
"mechanisms": ["plain", "login"],
|
|
||||||
"directory": "internal"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"queue": {
|
|
||||||
"path": "/var/lib/stalwart/queue"
|
|
||||||
},
|
|
||||||
"report": {
|
|
||||||
"path": "/var/lib/stalwart/reports"
|
|
||||||
},
|
|
||||||
"resolver": {
|
|
||||||
"type": "system"
|
|
||||||
},
|
|
||||||
"certificate": {
|
|
||||||
"default": {
|
|
||||||
"cert": "/etc/stalwart/tls/tls.crt",
|
|
||||||
"private-key": "/etc/stalwart/tls/tls.key"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,15 +0,0 @@
|
||||||
apiVersion: v1
|
|
||||||
kind: ConfigMap
|
|
||||||
metadata:
|
|
||||||
name: stalwart-stalwart-config
|
|
||||||
namespace: mail
|
|
||||||
labels:
|
|
||||||
app.kubernetes.io/instance: stalwart
|
|
||||||
app.kubernetes.io/name: stalwart
|
|
||||||
data:
|
|
||||||
config.json: |
|
|
||||||
{
|
|
||||||
"@type": "RocksDb",
|
|
||||||
"path": "/var/lib/stalwart",
|
|
||||||
"compression": "lz4"
|
|
||||||
}
|
|
||||||
|
|
@ -1,66 +0,0 @@
|
||||||
apiVersion: v1
|
|
||||||
kind: ConfigMap
|
|
||||||
metadata:
|
|
||||||
name: stalwart-stalwart-config
|
|
||||||
namespace: mail
|
|
||||||
labels:
|
|
||||||
app.kubernetes.io/instance: stalwart
|
|
||||||
app.kubernetes.io/name: stalwart
|
|
||||||
data:
|
|
||||||
config.json: |
|
|
||||||
{
|
|
||||||
"store": {
|
|
||||||
"db": {
|
|
||||||
"type": "rocksdb",
|
|
||||||
"path": "/var/lib/stalwart/data"
|
|
||||||
},
|
|
||||||
"blob": {
|
|
||||||
"type": "rocksdb",
|
|
||||||
"path": "/var/lib/stalwart/blobs"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"directory": {
|
|
||||||
"internal": {
|
|
||||||
"type": "internal",
|
|
||||||
"store": "db"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"authentication": {
|
|
||||||
"fallback-admin": {
|
|
||||||
"user": "admin",
|
|
||||||
"secret": "REPLACED_BY_SECRET"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"server": {
|
|
||||||
"hostname": "mail.basicstack.de",
|
|
||||||
"http": {
|
|
||||||
"bind": ["[::]:8080"],
|
|
||||||
"protocol": "http"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"session": {
|
|
||||||
"ehlo": {
|
|
||||||
"require": true
|
|
||||||
},
|
|
||||||
"auth": {
|
|
||||||
"directory": "internal"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"queue": {
|
|
||||||
"path": "/var/lib/stalwart/queue",
|
|
||||||
"hash": 64
|
|
||||||
}
|
|
||||||
}
|
|
||||||
---
|
|
||||||
apiVersion: v1
|
|
||||||
kind: Secret
|
|
||||||
metadata:
|
|
||||||
name: stalwart-stalwart-env
|
|
||||||
namespace: mail
|
|
||||||
labels:
|
|
||||||
app.kubernetes.io/instance: stalwart
|
|
||||||
app.kubernetes.io/name: stalwart
|
|
||||||
type: Opaque
|
|
||||||
stringData:
|
|
||||||
STALWART_LOG_LEVEL: "info"
|
|
||||||
STALWART_LOG_FORMAT: "json"
|
|
||||||
|
|
@ -1,5 +0,0 @@
|
||||||
{
|
|
||||||
"@type": "RocksDb",
|
|
||||||
"path": "/var/lib/stalwart",
|
|
||||||
"compression": "lz4"
|
|
||||||
}
|
|
||||||
|
|
@ -1,21 +0,0 @@
|
||||||
apiVersion: v1
|
|
||||||
kind: Pod
|
|
||||||
metadata:
|
|
||||||
name: stalwart-console
|
|
||||||
namespace: mail
|
|
||||||
spec:
|
|
||||||
containers:
|
|
||||||
- name: stalwart
|
|
||||||
image: stalwartlabs/stalwart:v0.16.9
|
|
||||||
command: ["/bin/sh", "-c", "sleep 3600"]
|
|
||||||
volumeMounts:
|
|
||||||
- name: data
|
|
||||||
mountPath: /var/lib/stalwart
|
|
||||||
securityContext:
|
|
||||||
runAsUser: 2000
|
|
||||||
runAsGroup: 2000
|
|
||||||
volumes:
|
|
||||||
- name: data
|
|
||||||
persistentVolumeClaim:
|
|
||||||
claimName: data-stalwart-stalwart-0
|
|
||||||
restartPolicy: Never
|
|
||||||
|
|
@ -1,593 +0,0 @@
|
||||||
apiVersion: v1
|
|
||||||
data:
|
|
||||||
stalwart-mail.json: |
|
|
||||||
{
|
|
||||||
"annotations": {
|
|
||||||
"list": [
|
|
||||||
{
|
|
||||||
"builtIn": 1,
|
|
||||||
"datasource": "-- Grafana --",
|
|
||||||
"enable": true,
|
|
||||||
"hide": true,
|
|
||||||
"iconColor": "rgba(0, 211, 255, 1)",
|
|
||||||
"name": "Annotations & Alerts",
|
|
||||||
"type": "dashboard"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"editable": true,
|
|
||||||
"gnetId": null,
|
|
||||||
"graphTooltip": 0,
|
|
||||||
"id": null,
|
|
||||||
"links": [],
|
|
||||||
"panels": [
|
|
||||||
{
|
|
||||||
"datasource": "Prometheus",
|
|
||||||
"fieldConfig": {
|
|
||||||
"defaults": {
|
|
||||||
"color": {
|
|
||||||
"mode": "thresholds"
|
|
||||||
},
|
|
||||||
"mappings": [
|
|
||||||
{
|
|
||||||
"options": {
|
|
||||||
"0": {
|
|
||||||
"color": "red",
|
|
||||||
"index": 0,
|
|
||||||
"text": "Down"
|
|
||||||
},
|
|
||||||
"1": {
|
|
||||||
"color": "green",
|
|
||||||
"index": 1,
|
|
||||||
"text": "Running"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"type": "value"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"thresholds": {
|
|
||||||
"mode": "absolute",
|
|
||||||
"steps": [
|
|
||||||
{
|
|
||||||
"color": "red",
|
|
||||||
"value": null
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"color": "green",
|
|
||||||
"value": 1
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"unit": "none"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"gridPos": {
|
|
||||||
"h": 4,
|
|
||||||
"w": 6,
|
|
||||||
"x": 0,
|
|
||||||
"y": 0
|
|
||||||
},
|
|
||||||
"id": 1,
|
|
||||||
"options": {
|
|
||||||
"colorMode": "background",
|
|
||||||
"graphMode": "none",
|
|
||||||
"justifyMode": "auto",
|
|
||||||
"orientation": "auto",
|
|
||||||
"reduceOptions": {
|
|
||||||
"calcs": [
|
|
||||||
"lastNotNull"
|
|
||||||
],
|
|
||||||
"fields": "",
|
|
||||||
"values": false
|
|
||||||
},
|
|
||||||
"textMode": "auto"
|
|
||||||
},
|
|
||||||
"pluginVersion": "8.0.0",
|
|
||||||
"targets": [
|
|
||||||
{
|
|
||||||
"expr": "kube_pod_status_phase{namespace=\"mail\", pod=~\"stalwart-.*\", phase=\"Running\"}",
|
|
||||||
"refId": "A"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"title": "Pod Status",
|
|
||||||
"type": "stat"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"datasource": "Prometheus",
|
|
||||||
"fieldConfig": {
|
|
||||||
"defaults": {
|
|
||||||
"color": {
|
|
||||||
"mode": "thresholds"
|
|
||||||
},
|
|
||||||
"mappings": [],
|
|
||||||
"thresholds": {
|
|
||||||
"mode": "absolute",
|
|
||||||
"steps": [
|
|
||||||
{
|
|
||||||
"color": "green",
|
|
||||||
"value": null
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"color": "yellow",
|
|
||||||
"value": 1
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"color": "red",
|
|
||||||
"value": 5
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"unit": "none"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"gridPos": {
|
|
||||||
"h": 4,
|
|
||||||
"w": 6,
|
|
||||||
"x": 6,
|
|
||||||
"y": 0
|
|
||||||
},
|
|
||||||
"id": 2,
|
|
||||||
"options": {
|
|
||||||
"colorMode": "background",
|
|
||||||
"graphMode": "area",
|
|
||||||
"justifyMode": "auto",
|
|
||||||
"orientation": "auto",
|
|
||||||
"reduceOptions": {
|
|
||||||
"calcs": [
|
|
||||||
"lastNotNull"
|
|
||||||
],
|
|
||||||
"fields": "",
|
|
||||||
"values": false
|
|
||||||
},
|
|
||||||
"textMode": "auto"
|
|
||||||
},
|
|
||||||
"pluginVersion": "8.0.0",
|
|
||||||
"targets": [
|
|
||||||
{
|
|
||||||
"expr": "kube_pod_container_status_restarts_total{namespace=\"mail\", pod=~\"stalwart-.*\"}",
|
|
||||||
"refId": "A"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"title": "Container Restarts",
|
|
||||||
"type": "stat"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"datasource": "Prometheus",
|
|
||||||
"fieldConfig": {
|
|
||||||
"defaults": {
|
|
||||||
"color": {
|
|
||||||
"mode": "thresholds"
|
|
||||||
},
|
|
||||||
"mappings": [],
|
|
||||||
"max": 100,
|
|
||||||
"min": 0,
|
|
||||||
"thresholds": {
|
|
||||||
"mode": "absolute",
|
|
||||||
"steps": [
|
|
||||||
{
|
|
||||||
"color": "green",
|
|
||||||
"value": null
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"color": "yellow",
|
|
||||||
"value": 75
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"color": "red",
|
|
||||||
"value": 90
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"unit": "percent"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"gridPos": {
|
|
||||||
"h": 4,
|
|
||||||
"w": 6,
|
|
||||||
"x": 12,
|
|
||||||
"y": 0
|
|
||||||
},
|
|
||||||
"id": 3,
|
|
||||||
"options": {
|
|
||||||
"orientation": "auto",
|
|
||||||
"reduceOptions": {
|
|
||||||
"calcs": [
|
|
||||||
"lastNotNull"
|
|
||||||
],
|
|
||||||
"fields": "",
|
|
||||||
"values": false
|
|
||||||
},
|
|
||||||
"showThresholdLabels": false,
|
|
||||||
"showThresholdMarkers": true
|
|
||||||
},
|
|
||||||
"pluginVersion": "8.0.0",
|
|
||||||
"targets": [
|
|
||||||
{
|
|
||||||
"expr": "(kubelet_volume_stats_used_bytes{namespace=\"mail\", persistentvolumeclaim=\"stalwart-data\"} / kubelet_volume_stats_capacity_bytes{namespace=\"mail\", persistentvolumeclaim=\"stalwart-data\"}) * 100",
|
|
||||||
"refId": "A"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"title": "Disk Usage (20Gi PVC)",
|
|
||||||
"type": "gauge"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"datasource": "Prometheus",
|
|
||||||
"fieldConfig": {
|
|
||||||
"defaults": {
|
|
||||||
"color": {
|
|
||||||
"mode": "thresholds"
|
|
||||||
},
|
|
||||||
"mappings": [],
|
|
||||||
"max": 100,
|
|
||||||
"min": 0,
|
|
||||||
"thresholds": {
|
|
||||||
"mode": "absolute",
|
|
||||||
"steps": [
|
|
||||||
{
|
|
||||||
"color": "green",
|
|
||||||
"value": null
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"color": "yellow",
|
|
||||||
"value": 75
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"color": "red",
|
|
||||||
"value": 85
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"unit": "percent"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"gridPos": {
|
|
||||||
"h": 4,
|
|
||||||
"w": 6,
|
|
||||||
"x": 18,
|
|
||||||
"y": 0
|
|
||||||
},
|
|
||||||
"id": 4,
|
|
||||||
"options": {
|
|
||||||
"orientation": "auto",
|
|
||||||
"reduceOptions": {
|
|
||||||
"calcs": [
|
|
||||||
"lastNotNull"
|
|
||||||
],
|
|
||||||
"fields": "",
|
|
||||||
"values": false
|
|
||||||
},
|
|
||||||
"showThresholdLabels": false,
|
|
||||||
"showThresholdMarkers": true
|
|
||||||
},
|
|
||||||
"pluginVersion": "8.0.0",
|
|
||||||
"targets": [
|
|
||||||
{
|
|
||||||
"expr": "(container_memory_working_set_bytes{namespace=\"mail\", pod=~\"stalwart-.*\", container=\"stalwart\"} / container_spec_memory_limit_bytes{namespace=\"mail\", pod=~\"stalwart-.*\", container=\"stalwart\"}) * 100",
|
|
||||||
"refId": "A"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"title": "Memory Usage",
|
|
||||||
"type": "gauge"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"datasource": "Prometheus",
|
|
||||||
"fieldConfig": {
|
|
||||||
"defaults": {
|
|
||||||
"color": {
|
|
||||||
"mode": "palette-classic"
|
|
||||||
},
|
|
||||||
"custom": {
|
|
||||||
"axisLabel": "",
|
|
||||||
"axisPlacement": "auto",
|
|
||||||
"barAlignment": 0,
|
|
||||||
"drawStyle": "line",
|
|
||||||
"fillOpacity": 10,
|
|
||||||
"gradientMode": "none",
|
|
||||||
"hideFrom": {
|
|
||||||
"tooltip": false,
|
|
||||||
"viz": false,
|
|
||||||
"legend": false
|
|
||||||
},
|
|
||||||
"lineInterpolation": "linear",
|
|
||||||
"lineWidth": 1,
|
|
||||||
"pointSize": 5,
|
|
||||||
"scaleDistribution": {
|
|
||||||
"type": "linear"
|
|
||||||
},
|
|
||||||
"showPoints": "never",
|
|
||||||
"spanNulls": true
|
|
||||||
},
|
|
||||||
"mappings": [],
|
|
||||||
"thresholds": {
|
|
||||||
"mode": "absolute",
|
|
||||||
"steps": [
|
|
||||||
{
|
|
||||||
"color": "green",
|
|
||||||
"value": null
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"unit": "percentunit"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"gridPos": {
|
|
||||||
"h": 8,
|
|
||||||
"w": 12,
|
|
||||||
"x": 0,
|
|
||||||
"y": 4
|
|
||||||
},
|
|
||||||
"id": 5,
|
|
||||||
"options": {
|
|
||||||
"legend": {
|
|
||||||
"calcs": [],
|
|
||||||
"displayMode": "list",
|
|
||||||
"placement": "bottom"
|
|
||||||
},
|
|
||||||
"tooltip": {
|
|
||||||
"mode": "multi"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"pluginVersion": "8.0.0",
|
|
||||||
"targets": [
|
|
||||||
{
|
|
||||||
"expr": "rate(container_cpu_usage_seconds_total{namespace=\"mail\", pod=~\"stalwart-.*\", container=\"stalwart\"}[5m]) / (container_spec_cpu_quota{namespace=\"mail\", pod=~\"stalwart-.*\", container=\"stalwart\"} / container_spec_cpu_period{namespace=\"mail\", pod=~\"stalwart-.*\", container=\"stalwart\"})",
|
|
||||||
"legendFormat": "CPU Usage",
|
|
||||||
"refId": "A"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"title": "CPU Usage Over Time",
|
|
||||||
"type": "timeseries"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"datasource": "Prometheus",
|
|
||||||
"fieldConfig": {
|
|
||||||
"defaults": {
|
|
||||||
"color": {
|
|
||||||
"mode": "palette-classic"
|
|
||||||
},
|
|
||||||
"custom": {
|
|
||||||
"axisLabel": "",
|
|
||||||
"axisPlacement": "auto",
|
|
||||||
"barAlignment": 0,
|
|
||||||
"drawStyle": "line",
|
|
||||||
"fillOpacity": 10,
|
|
||||||
"gradientMode": "none",
|
|
||||||
"hideFrom": {
|
|
||||||
"tooltip": false,
|
|
||||||
"viz": false,
|
|
||||||
"legend": false
|
|
||||||
},
|
|
||||||
"lineInterpolation": "linear",
|
|
||||||
"lineWidth": 1,
|
|
||||||
"pointSize": 5,
|
|
||||||
"scaleDistribution": {
|
|
||||||
"type": "linear"
|
|
||||||
},
|
|
||||||
"showPoints": "never",
|
|
||||||
"spanNulls": true
|
|
||||||
},
|
|
||||||
"mappings": [],
|
|
||||||
"thresholds": {
|
|
||||||
"mode": "absolute",
|
|
||||||
"steps": [
|
|
||||||
{
|
|
||||||
"color": "green",
|
|
||||||
"value": null
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"unit": "bytes"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"gridPos": {
|
|
||||||
"h": 8,
|
|
||||||
"w": 12,
|
|
||||||
"x": 12,
|
|
||||||
"y": 4
|
|
||||||
},
|
|
||||||
"id": 6,
|
|
||||||
"options": {
|
|
||||||
"legend": {
|
|
||||||
"calcs": [],
|
|
||||||
"displayMode": "list",
|
|
||||||
"placement": "bottom"
|
|
||||||
},
|
|
||||||
"tooltip": {
|
|
||||||
"mode": "multi"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"pluginVersion": "8.0.0",
|
|
||||||
"targets": [
|
|
||||||
{
|
|
||||||
"expr": "container_memory_working_set_bytes{namespace=\"mail\", pod=~\"stalwart-.*\", container=\"stalwart\"}",
|
|
||||||
"legendFormat": "Memory Usage",
|
|
||||||
"refId": "A"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"expr": "container_spec_memory_limit_bytes{namespace=\"mail\", pod=~\"stalwart-.*\", container=\"stalwart\"}",
|
|
||||||
"legendFormat": "Memory Limit",
|
|
||||||
"refId": "B"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"title": "Memory Usage Over Time",
|
|
||||||
"type": "timeseries"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"datasource": "Prometheus",
|
|
||||||
"fieldConfig": {
|
|
||||||
"defaults": {
|
|
||||||
"color": {
|
|
||||||
"mode": "palette-classic"
|
|
||||||
},
|
|
||||||
"custom": {
|
|
||||||
"axisLabel": "",
|
|
||||||
"axisPlacement": "auto",
|
|
||||||
"barAlignment": 0,
|
|
||||||
"drawStyle": "line",
|
|
||||||
"fillOpacity": 10,
|
|
||||||
"gradientMode": "none",
|
|
||||||
"hideFrom": {
|
|
||||||
"tooltip": false,
|
|
||||||
"viz": false,
|
|
||||||
"legend": false
|
|
||||||
},
|
|
||||||
"lineInterpolation": "linear",
|
|
||||||
"lineWidth": 1,
|
|
||||||
"pointSize": 5,
|
|
||||||
"scaleDistribution": {
|
|
||||||
"type": "linear"
|
|
||||||
},
|
|
||||||
"showPoints": "never",
|
|
||||||
"spanNulls": true
|
|
||||||
},
|
|
||||||
"mappings": [],
|
|
||||||
"thresholds": {
|
|
||||||
"mode": "absolute",
|
|
||||||
"steps": [
|
|
||||||
{
|
|
||||||
"color": "green",
|
|
||||||
"value": null
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"unit": "bytes"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"gridPos": {
|
|
||||||
"h": 8,
|
|
||||||
"w": 12,
|
|
||||||
"x": 0,
|
|
||||||
"y": 12
|
|
||||||
},
|
|
||||||
"id": 7,
|
|
||||||
"options": {
|
|
||||||
"legend": {
|
|
||||||
"calcs": [
|
|
||||||
"lastNotNull"
|
|
||||||
],
|
|
||||||
"displayMode": "table",
|
|
||||||
"placement": "bottom"
|
|
||||||
},
|
|
||||||
"tooltip": {
|
|
||||||
"mode": "multi"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"pluginVersion": "8.0.0",
|
|
||||||
"targets": [
|
|
||||||
{
|
|
||||||
"expr": "kubelet_volume_stats_used_bytes{namespace=\"mail\", persistentvolumeclaim=\"stalwart-data\"}",
|
|
||||||
"legendFormat": "Used Space",
|
|
||||||
"refId": "A"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"expr": "kubelet_volume_stats_capacity_bytes{namespace=\"mail\", persistentvolumeclaim=\"stalwart-data\"}",
|
|
||||||
"legendFormat": "Total Capacity (20Gi)",
|
|
||||||
"refId": "B"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"expr": "kubelet_volume_stats_available_bytes{namespace=\"mail\", persistentvolumeclaim=\"stalwart-data\"}",
|
|
||||||
"legendFormat": "Available Space",
|
|
||||||
"refId": "C"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"title": "Disk Space Over Time (Encrypted Volume)",
|
|
||||||
"type": "timeseries"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"datasource": "Prometheus",
|
|
||||||
"fieldConfig": {
|
|
||||||
"defaults": {
|
|
||||||
"color": {
|
|
||||||
"mode": "palette-classic"
|
|
||||||
},
|
|
||||||
"custom": {
|
|
||||||
"axisLabel": "",
|
|
||||||
"axisPlacement": "auto",
|
|
||||||
"barAlignment": 0,
|
|
||||||
"drawStyle": "line",
|
|
||||||
"fillOpacity": 10,
|
|
||||||
"gradientMode": "none",
|
|
||||||
"hideFrom": {
|
|
||||||
"tooltip": false,
|
|
||||||
"viz": false,
|
|
||||||
"legend": false
|
|
||||||
},
|
|
||||||
"lineInterpolation": "linear",
|
|
||||||
"lineWidth": 1,
|
|
||||||
"pointSize": 5,
|
|
||||||
"scaleDistribution": {
|
|
||||||
"type": "linear"
|
|
||||||
},
|
|
||||||
"showPoints": "never",
|
|
||||||
"spanNulls": true
|
|
||||||
},
|
|
||||||
"mappings": [],
|
|
||||||
"thresholds": {
|
|
||||||
"mode": "absolute",
|
|
||||||
"steps": [
|
|
||||||
{
|
|
||||||
"color": "green",
|
|
||||||
"value": null
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"unit": "none"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"gridPos": {
|
|
||||||
"h": 8,
|
|
||||||
"w": 12,
|
|
||||||
"x": 12,
|
|
||||||
"y": 12
|
|
||||||
},
|
|
||||||
"id": 8,
|
|
||||||
"options": {
|
|
||||||
"legend": {
|
|
||||||
"calcs": [],
|
|
||||||
"displayMode": "list",
|
|
||||||
"placement": "bottom"
|
|
||||||
},
|
|
||||||
"tooltip": {
|
|
||||||
"mode": "multi"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"pluginVersion": "8.0.0",
|
|
||||||
"targets": [
|
|
||||||
{
|
|
||||||
"expr": "kubelet_volume_stats_inodes_used{namespace=\"mail\", persistentvolumeclaim=\"stalwart-data\"}",
|
|
||||||
"legendFormat": "Inodes Used",
|
|
||||||
"refId": "A"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"expr": "kubelet_volume_stats_inodes{namespace=\"mail\", persistentvolumeclaim=\"stalwart-data\"}",
|
|
||||||
"legendFormat": "Total Inodes",
|
|
||||||
"refId": "B"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"title": "Disk Inodes Usage",
|
|
||||||
"type": "timeseries"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"refresh": "30s",
|
|
||||||
"schemaVersion": 27,
|
|
||||||
"style": "dark",
|
|
||||||
"tags": ["stalwart", "mail", "kubernetes"],
|
|
||||||
"templating": {
|
|
||||||
"list": []
|
|
||||||
},
|
|
||||||
"time": {
|
|
||||||
"from": "now-6h",
|
|
||||||
"to": "now"
|
|
||||||
},
|
|
||||||
"timepicker": {},
|
|
||||||
"timezone": "",
|
|
||||||
"title": "Stalwart Mail Server",
|
|
||||||
"uid": "stalwart-mail",
|
|
||||||
"version": 1
|
|
||||||
}
|
|
||||||
kind: ConfigMap
|
|
||||||
metadata:
|
|
||||||
name: stalwart-dashboard
|
|
||||||
namespace: observability
|
|
||||||
labels:
|
|
||||||
grafana_dashboard: "1"
|
|
||||||
|
|
@ -1,231 +0,0 @@
|
||||||
---
|
|
||||||
apiVersion: v1
|
|
||||||
kind: Namespace
|
|
||||||
metadata:
|
|
||||||
name: mail
|
|
||||||
---
|
|
||||||
apiVersion: v1
|
|
||||||
kind: PersistentVolumeClaim
|
|
||||||
metadata:
|
|
||||||
name: stalwart-data
|
|
||||||
namespace: mail
|
|
||||||
spec:
|
|
||||||
accessModes:
|
|
||||||
- ReadWriteOnce
|
|
||||||
storageClassName: hcloud-volumes-encrypted
|
|
||||||
resources:
|
|
||||||
requests:
|
|
||||||
storage: 20Gi
|
|
||||||
---
|
|
||||||
# Secret managed via SealedSecrets
|
|
||||||
# See: stalwart-admin-credentials-sealed.yaml
|
|
||||||
---
|
|
||||||
apiVersion: v1
|
|
||||||
kind: Service
|
|
||||||
metadata:
|
|
||||||
name: stalwart
|
|
||||||
namespace: mail
|
|
||||||
spec:
|
|
||||||
type: ClusterIP
|
|
||||||
clusterIP: None
|
|
||||||
selector:
|
|
||||||
app: stalwart
|
|
||||||
ports:
|
|
||||||
- name: smtp
|
|
||||||
port: 25
|
|
||||||
targetPort: 25
|
|
||||||
protocol: TCP
|
|
||||||
- name: submission
|
|
||||||
port: 587
|
|
||||||
targetPort: 587
|
|
||||||
protocol: TCP
|
|
||||||
- name: submissions
|
|
||||||
port: 465
|
|
||||||
targetPort: 465
|
|
||||||
protocol: TCP
|
|
||||||
- name: imap
|
|
||||||
port: 143
|
|
||||||
targetPort: 143
|
|
||||||
protocol: TCP
|
|
||||||
- name: imaps
|
|
||||||
port: 993
|
|
||||||
targetPort: 993
|
|
||||||
protocol: TCP
|
|
||||||
- name: http
|
|
||||||
port: 8080
|
|
||||||
targetPort: 8080
|
|
||||||
protocol: TCP
|
|
||||||
- name: sieve
|
|
||||||
port: 4190
|
|
||||||
targetPort: 4190
|
|
||||||
protocol: TCP
|
|
||||||
---
|
|
||||||
apiVersion: v1
|
|
||||||
kind: Service
|
|
||||||
metadata:
|
|
||||||
name: stalwart-smtp
|
|
||||||
namespace: mail
|
|
||||||
spec:
|
|
||||||
type: LoadBalancer
|
|
||||||
selector:
|
|
||||||
app: stalwart
|
|
||||||
ports:
|
|
||||||
- name: smtp
|
|
||||||
port: 25
|
|
||||||
targetPort: 25
|
|
||||||
protocol: TCP
|
|
||||||
- name: submission
|
|
||||||
port: 587
|
|
||||||
targetPort: 587
|
|
||||||
protocol: TCP
|
|
||||||
- name: submissions
|
|
||||||
port: 465
|
|
||||||
targetPort: 465
|
|
||||||
protocol: TCP
|
|
||||||
---
|
|
||||||
apiVersion: v1
|
|
||||||
kind: Service
|
|
||||||
metadata:
|
|
||||||
name: stalwart-imap
|
|
||||||
namespace: mail
|
|
||||||
spec:
|
|
||||||
type: LoadBalancer
|
|
||||||
selector:
|
|
||||||
app: stalwart
|
|
||||||
ports:
|
|
||||||
- name: imap
|
|
||||||
port: 143
|
|
||||||
targetPort: 143
|
|
||||||
protocol: TCP
|
|
||||||
- name: imaps
|
|
||||||
port: 993
|
|
||||||
targetPort: 993
|
|
||||||
protocol: TCP
|
|
||||||
---
|
|
||||||
apiVersion: v1
|
|
||||||
kind: Service
|
|
||||||
metadata:
|
|
||||||
name: stalwart-http
|
|
||||||
namespace: mail
|
|
||||||
spec:
|
|
||||||
type: ClusterIP
|
|
||||||
selector:
|
|
||||||
app: stalwart
|
|
||||||
ports:
|
|
||||||
- name: http
|
|
||||||
port: 8080
|
|
||||||
targetPort: 8080
|
|
||||||
protocol: TCP
|
|
||||||
---
|
|
||||||
apiVersion: apps/v1
|
|
||||||
kind: StatefulSet
|
|
||||||
metadata:
|
|
||||||
name: stalwart
|
|
||||||
namespace: mail
|
|
||||||
spec:
|
|
||||||
serviceName: stalwart
|
|
||||||
replicas: 1
|
|
||||||
selector:
|
|
||||||
matchLabels:
|
|
||||||
app: stalwart
|
|
||||||
template:
|
|
||||||
metadata:
|
|
||||||
labels:
|
|
||||||
app: stalwart
|
|
||||||
spec:
|
|
||||||
securityContext:
|
|
||||||
fsGroup: 2000
|
|
||||||
runAsUser: 2000
|
|
||||||
runAsGroup: 2000
|
|
||||||
initContainers:
|
|
||||||
- name: fix-permissions
|
|
||||||
image: busybox
|
|
||||||
command: ["sh", "-c", "chown -R 2000:2000 /opt/stalwart && chmod -R 755 /opt/stalwart"]
|
|
||||||
volumeMounts:
|
|
||||||
- name: data
|
|
||||||
mountPath: /opt/stalwart
|
|
||||||
containers:
|
|
||||||
- name: stalwart
|
|
||||||
image: stalwartlabs/stalwart:latest
|
|
||||||
ports:
|
|
||||||
- containerPort: 25
|
|
||||||
name: smtp
|
|
||||||
- containerPort: 587
|
|
||||||
name: submission
|
|
||||||
- containerPort: 465
|
|
||||||
name: submissions
|
|
||||||
- containerPort: 143
|
|
||||||
name: imap
|
|
||||||
- containerPort: 993
|
|
||||||
name: imaps
|
|
||||||
- containerPort: 8080
|
|
||||||
name: http
|
|
||||||
- containerPort: 4190
|
|
||||||
name: sieve
|
|
||||||
volumeMounts:
|
|
||||||
- name: data
|
|
||||||
mountPath: /opt/stalwart
|
|
||||||
- name: data
|
|
||||||
mountPath: /etc/stalwart
|
|
||||||
subPath: etc
|
|
||||||
livenessProbe:
|
|
||||||
httpGet:
|
|
||||||
path: /healthz/live
|
|
||||||
port: 8080
|
|
||||||
initialDelaySeconds: 30
|
|
||||||
periodSeconds: 10
|
|
||||||
timeoutSeconds: 5
|
|
||||||
failureThreshold: 3
|
|
||||||
readinessProbe:
|
|
||||||
httpGet:
|
|
||||||
path: /healthz/ready
|
|
||||||
port: 8080
|
|
||||||
initialDelaySeconds: 10
|
|
||||||
periodSeconds: 5
|
|
||||||
timeoutSeconds: 3
|
|
||||||
failureThreshold: 3
|
|
||||||
resources:
|
|
||||||
requests:
|
|
||||||
memory: "512Mi"
|
|
||||||
cpu: "250m"
|
|
||||||
limits:
|
|
||||||
memory: "2Gi"
|
|
||||||
cpu: "2000m"
|
|
||||||
securityContext:
|
|
||||||
runAsNonRoot: true
|
|
||||||
allowPrivilegeEscalation: false
|
|
||||||
capabilities:
|
|
||||||
drop: [ALL]
|
|
||||||
add: [NET_BIND_SERVICE]
|
|
||||||
seccompProfile:
|
|
||||||
type: RuntimeDefault
|
|
||||||
volumes:
|
|
||||||
- name: data
|
|
||||||
persistentVolumeClaim:
|
|
||||||
claimName: stalwart-data
|
|
||||||
---
|
|
||||||
apiVersion: networking.k8s.io/v1
|
|
||||||
kind: Ingress
|
|
||||||
metadata:
|
|
||||||
name: stalwart-web
|
|
||||||
namespace: mail
|
|
||||||
annotations:
|
|
||||||
cert-manager.io/cluster-issuer: "letsencrypt-prod"
|
|
||||||
traefik.ingress.kubernetes.io/router.entrypoints: websecure
|
|
||||||
spec:
|
|
||||||
ingressClassName: traefik
|
|
||||||
tls:
|
|
||||||
- hosts:
|
|
||||||
- mail.basicstack.de
|
|
||||||
secretName: stalwart-tls
|
|
||||||
rules:
|
|
||||||
- host: mail.basicstack.de
|
|
||||||
http:
|
|
||||||
paths:
|
|
||||||
- path: /
|
|
||||||
pathType: Prefix
|
|
||||||
backend:
|
|
||||||
service:
|
|
||||||
name: stalwart-http
|
|
||||||
port:
|
|
||||||
number: 8080
|
|
||||||
|
|
@ -1,226 +0,0 @@
|
||||||
---
|
|
||||||
apiVersion: v1
|
|
||||||
kind: Namespace
|
|
||||||
metadata:
|
|
||||||
name: mail
|
|
||||||
---
|
|
||||||
apiVersion: v1
|
|
||||||
kind: PersistentVolumeClaim
|
|
||||||
metadata:
|
|
||||||
name: stalwart-data
|
|
||||||
namespace: mail
|
|
||||||
spec:
|
|
||||||
accessModes:
|
|
||||||
- ReadWriteOnce
|
|
||||||
storageClassName: hcloud-volumes-encrypted
|
|
||||||
resources:
|
|
||||||
requests:
|
|
||||||
storage: 20Gi
|
|
||||||
---
|
|
||||||
# OAuth Configuration ConfigMap
|
|
||||||
apiVersion: v1
|
|
||||||
kind: ConfigMap
|
|
||||||
metadata:
|
|
||||||
name: stalwart-oauth-config
|
|
||||||
namespace: mail
|
|
||||||
data:
|
|
||||||
oauth-config.json: |
|
|
||||||
{
|
|
||||||
"oauth": {
|
|
||||||
"providers": [
|
|
||||||
{
|
|
||||||
"id": "pocket-id",
|
|
||||||
"name": "Pocket ID",
|
|
||||||
"issuer": "https://auth.basicstack.de",
|
|
||||||
"authorization-url": "https://auth.basicstack.de/api/oidc/authorize",
|
|
||||||
"token-url": "https://auth.basicstack.de/api/oidc/token",
|
|
||||||
"userinfo-url": "https://auth.basicstack.de/api/oidc/userinfo",
|
|
||||||
"client-id": "REPLACED_BY_SECRET",
|
|
||||||
"client-secret": "REPLACED_BY_SECRET",
|
|
||||||
"scopes": ["openid", "profile", "email"],
|
|
||||||
"redirect-url": "https://mail.basicstack.de/login/oauth",
|
|
||||||
"user-mapping": {
|
|
||||||
"username": "preferred_username",
|
|
||||||
"email": "email",
|
|
||||||
"name": "name"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"enabled": true,
|
|
||||||
"allow-password-auth": false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
---
|
|
||||||
# Secret managed via SealedSecrets
|
|
||||||
# See: stalwart-oidc-sealed.yaml
|
|
||||||
---
|
|
||||||
apiVersion: v1
|
|
||||||
kind: Service
|
|
||||||
metadata:
|
|
||||||
name: stalwart-smtp
|
|
||||||
namespace: mail
|
|
||||||
spec:
|
|
||||||
type: LoadBalancer
|
|
||||||
selector:
|
|
||||||
app: stalwart
|
|
||||||
ports:
|
|
||||||
- name: smtp
|
|
||||||
port: 25
|
|
||||||
targetPort: 25
|
|
||||||
protocol: TCP
|
|
||||||
- name: submission
|
|
||||||
port: 587
|
|
||||||
targetPort: 587
|
|
||||||
protocol: TCP
|
|
||||||
- name: submissions
|
|
||||||
port: 465
|
|
||||||
targetPort: 465
|
|
||||||
protocol: TCP
|
|
||||||
---
|
|
||||||
apiVersion: v1
|
|
||||||
kind: Service
|
|
||||||
metadata:
|
|
||||||
name: stalwart-imap
|
|
||||||
namespace: mail
|
|
||||||
spec:
|
|
||||||
type: LoadBalancer
|
|
||||||
selector:
|
|
||||||
app: stalwart
|
|
||||||
ports:
|
|
||||||
- name: imap
|
|
||||||
port: 143
|
|
||||||
targetPort: 143
|
|
||||||
protocol: TCP
|
|
||||||
- name: imaps
|
|
||||||
port: 993
|
|
||||||
targetPort: 993
|
|
||||||
protocol: TCP
|
|
||||||
---
|
|
||||||
apiVersion: v1
|
|
||||||
kind: Service
|
|
||||||
metadata:
|
|
||||||
name: stalwart-http
|
|
||||||
namespace: mail
|
|
||||||
spec:
|
|
||||||
type: ClusterIP
|
|
||||||
selector:
|
|
||||||
app: stalwart
|
|
||||||
ports:
|
|
||||||
- name: http
|
|
||||||
port: 8080
|
|
||||||
targetPort: 8080
|
|
||||||
protocol: TCP
|
|
||||||
---
|
|
||||||
apiVersion: apps/v1
|
|
||||||
kind: Deployment
|
|
||||||
metadata:
|
|
||||||
name: stalwart
|
|
||||||
namespace: mail
|
|
||||||
spec:
|
|
||||||
replicas: 1
|
|
||||||
strategy:
|
|
||||||
type: Recreate
|
|
||||||
selector:
|
|
||||||
matchLabels:
|
|
||||||
app: stalwart
|
|
||||||
template:
|
|
||||||
metadata:
|
|
||||||
labels:
|
|
||||||
app: stalwart
|
|
||||||
spec:
|
|
||||||
initContainers:
|
|
||||||
- name: fix-permissions
|
|
||||||
image: busybox
|
|
||||||
command: ["sh", "-c", "chown -R 2000:2000 /opt/stalwart-mail && chmod -R 755 /opt/stalwart-mail"]
|
|
||||||
volumeMounts:
|
|
||||||
- name: data
|
|
||||||
mountPath: /opt/stalwart-mail
|
|
||||||
containers:
|
|
||||||
- name: stalwart
|
|
||||||
image: stalwartlabs/stalwart:v0.16
|
|
||||||
command: ["/usr/local/bin/stalwart"]
|
|
||||||
args: ["--config", "/etc/stalwart/config.json"]
|
|
||||||
ports:
|
|
||||||
- containerPort: 25
|
|
||||||
name: smtp
|
|
||||||
- containerPort: 587
|
|
||||||
name: submission
|
|
||||||
- containerPort: 465
|
|
||||||
name: submissions
|
|
||||||
- containerPort: 143
|
|
||||||
name: imap
|
|
||||||
- containerPort: 993
|
|
||||||
name: imaps
|
|
||||||
- containerPort: 8080
|
|
||||||
name: http
|
|
||||||
env:
|
|
||||||
# Emergency recovery admin (can be disabled after OIDC is working)
|
|
||||||
- name: STALWART_RECOVERY_ADMIN
|
|
||||||
value: "admin@basicstack.de:***REMOVED***"
|
|
||||||
# OAuth configuration
|
|
||||||
- name: STALWART_OAUTH_ENABLED
|
|
||||||
value: "true"
|
|
||||||
- name: STALWART_OAUTH_PROVIDER
|
|
||||||
value: "pocket-id"
|
|
||||||
- name: STALWART_OAUTH_CLIENT_ID
|
|
||||||
valueFrom:
|
|
||||||
secretKeyRef:
|
|
||||||
name: stalwart-oidc-secret
|
|
||||||
key: client-id
|
|
||||||
- name: STALWART_OAUTH_CLIENT_SECRET
|
|
||||||
valueFrom:
|
|
||||||
secretKeyRef:
|
|
||||||
name: stalwart-oidc-secret
|
|
||||||
key: client-secret
|
|
||||||
# Disable password authentication (except recovery admin)
|
|
||||||
- name: STALWART_PASSWORD_AUTH_ENABLED
|
|
||||||
value: "false"
|
|
||||||
volumeMounts:
|
|
||||||
- name: data
|
|
||||||
mountPath: /var/lib/stalwart
|
|
||||||
- name: data
|
|
||||||
mountPath: /etc/stalwart
|
|
||||||
subPath: etc
|
|
||||||
- name: oauth-config
|
|
||||||
mountPath: /etc/stalwart/oauth
|
|
||||||
readOnly: true
|
|
||||||
resources:
|
|
||||||
requests:
|
|
||||||
memory: "512Mi"
|
|
||||||
cpu: "250m"
|
|
||||||
limits:
|
|
||||||
memory: "1Gi"
|
|
||||||
cpu: "1000m"
|
|
||||||
volumes:
|
|
||||||
- name: data
|
|
||||||
persistentVolumeClaim:
|
|
||||||
claimName: stalwart-data
|
|
||||||
- name: oauth-config
|
|
||||||
configMap:
|
|
||||||
name: stalwart-oauth-config
|
|
||||||
---
|
|
||||||
apiVersion: networking.k8s.io/v1
|
|
||||||
kind: Ingress
|
|
||||||
metadata:
|
|
||||||
name: stalwart-web
|
|
||||||
namespace: mail
|
|
||||||
annotations:
|
|
||||||
cert-manager.io/cluster-issuer: "letsencrypt-prod"
|
|
||||||
traefik.ingress.kubernetes.io/router.entrypoints: websecure
|
|
||||||
spec:
|
|
||||||
ingressClassName: traefik
|
|
||||||
tls:
|
|
||||||
- hosts:
|
|
||||||
- mail.basicstack.de
|
|
||||||
secretName: stalwart-tls
|
|
||||||
rules:
|
|
||||||
- host: mail.basicstack.de
|
|
||||||
http:
|
|
||||||
paths:
|
|
||||||
- path: /
|
|
||||||
pathType: Prefix
|
|
||||||
backend:
|
|
||||||
service:
|
|
||||||
name: stalwart-http
|
|
||||||
port:
|
|
||||||
number: 8080
|
|
||||||
|
|
@ -1,186 +0,0 @@
|
||||||
---
|
|
||||||
apiVersion: v1
|
|
||||||
kind: Namespace
|
|
||||||
metadata:
|
|
||||||
name: mail
|
|
||||||
---
|
|
||||||
apiVersion: v1
|
|
||||||
kind: PersistentVolumeClaim
|
|
||||||
metadata:
|
|
||||||
name: stalwart-data
|
|
||||||
namespace: mail
|
|
||||||
spec:
|
|
||||||
accessModes:
|
|
||||||
- ReadWriteOnce
|
|
||||||
storageClassName: hcloud-volumes-encrypted
|
|
||||||
resources:
|
|
||||||
requests:
|
|
||||||
storage: 20Gi
|
|
||||||
---
|
|
||||||
# Secrets managed via SealedSecrets
|
|
||||||
# See: stalwart-admin-credentials-sealed.yaml and stalwart-oidc-sealed.yaml
|
|
||||||
---
|
|
||||||
apiVersion: v1
|
|
||||||
kind: Service
|
|
||||||
metadata:
|
|
||||||
name: stalwart-smtp
|
|
||||||
namespace: mail
|
|
||||||
spec:
|
|
||||||
type: LoadBalancer
|
|
||||||
selector:
|
|
||||||
app: stalwart
|
|
||||||
ports:
|
|
||||||
- name: smtp
|
|
||||||
port: 25
|
|
||||||
targetPort: 25
|
|
||||||
protocol: TCP
|
|
||||||
- name: submission
|
|
||||||
port: 587
|
|
||||||
targetPort: 587
|
|
||||||
protocol: TCP
|
|
||||||
- name: submissions
|
|
||||||
port: 465
|
|
||||||
targetPort: 465
|
|
||||||
protocol: TCP
|
|
||||||
---
|
|
||||||
apiVersion: v1
|
|
||||||
kind: Service
|
|
||||||
metadata:
|
|
||||||
name: stalwart-imap
|
|
||||||
namespace: mail
|
|
||||||
spec:
|
|
||||||
type: LoadBalancer
|
|
||||||
selector:
|
|
||||||
app: stalwart
|
|
||||||
ports:
|
|
||||||
- name: imap
|
|
||||||
port: 143
|
|
||||||
targetPort: 143
|
|
||||||
protocol: TCP
|
|
||||||
- name: imaps
|
|
||||||
port: 993
|
|
||||||
targetPort: 993
|
|
||||||
protocol: TCP
|
|
||||||
---
|
|
||||||
apiVersion: v1
|
|
||||||
kind: Service
|
|
||||||
metadata:
|
|
||||||
name: stalwart-http
|
|
||||||
namespace: mail
|
|
||||||
spec:
|
|
||||||
type: ClusterIP
|
|
||||||
selector:
|
|
||||||
app: stalwart
|
|
||||||
ports:
|
|
||||||
- name: http
|
|
||||||
port: 8080
|
|
||||||
targetPort: 8080
|
|
||||||
protocol: TCP
|
|
||||||
---
|
|
||||||
apiVersion: apps/v1
|
|
||||||
kind: Deployment
|
|
||||||
metadata:
|
|
||||||
name: stalwart
|
|
||||||
namespace: mail
|
|
||||||
spec:
|
|
||||||
replicas: 1
|
|
||||||
strategy:
|
|
||||||
type: Recreate
|
|
||||||
selector:
|
|
||||||
matchLabels:
|
|
||||||
app: stalwart
|
|
||||||
template:
|
|
||||||
metadata:
|
|
||||||
labels:
|
|
||||||
app: stalwart
|
|
||||||
spec:
|
|
||||||
initContainers:
|
|
||||||
- name: fix-permissions
|
|
||||||
image: busybox
|
|
||||||
command: ["sh", "-c", "chown -R 2000:2000 /opt/stalwart-mail && chmod -R 755 /opt/stalwart-mail"]
|
|
||||||
volumeMounts:
|
|
||||||
- name: data
|
|
||||||
mountPath: /opt/stalwart-mail
|
|
||||||
containers:
|
|
||||||
- name: stalwart
|
|
||||||
image: stalwartlabs/stalwart:v0.16
|
|
||||||
command: ["/usr/local/bin/stalwart"]
|
|
||||||
args: ["--config", "/etc/stalwart/config.json"]
|
|
||||||
ports:
|
|
||||||
- containerPort: 25
|
|
||||||
name: smtp
|
|
||||||
- containerPort: 587
|
|
||||||
name: submission
|
|
||||||
- containerPort: 465
|
|
||||||
name: submissions
|
|
||||||
- containerPort: 143
|
|
||||||
name: imap
|
|
||||||
- containerPort: 993
|
|
||||||
name: imaps
|
|
||||||
- containerPort: 8080
|
|
||||||
name: http
|
|
||||||
env:
|
|
||||||
- name: STALWART_RECOVERY_ADMIN
|
|
||||||
value: "admin@basicstack.de:***REMOVED***"
|
|
||||||
# OIDC Configuration
|
|
||||||
- name: STALWART_OAUTH_ENABLE
|
|
||||||
value: "true"
|
|
||||||
- name: STALWART_OAUTH_ISSUER
|
|
||||||
value: "https://auth.basicstack.de"
|
|
||||||
- name: STALWART_OAUTH_CLIENT_ID
|
|
||||||
valueFrom:
|
|
||||||
secretKeyRef:
|
|
||||||
name: stalwart-oidc
|
|
||||||
key: oidc-client-id
|
|
||||||
- name: STALWART_OAUTH_CLIENT_SECRET
|
|
||||||
valueFrom:
|
|
||||||
secretKeyRef:
|
|
||||||
name: stalwart-oidc
|
|
||||||
key: oidc-client-secret
|
|
||||||
- name: STALWART_OAUTH_REDIRECT_URI
|
|
||||||
value: "https://mail.basicstack.de/login/oauth"
|
|
||||||
- name: STALWART_OAUTH_SCOPES
|
|
||||||
value: "openid profile email"
|
|
||||||
volumeMounts:
|
|
||||||
- name: data
|
|
||||||
mountPath: /var/lib/stalwart
|
|
||||||
- name: data
|
|
||||||
mountPath: /etc/stalwart
|
|
||||||
subPath: etc
|
|
||||||
resources:
|
|
||||||
requests:
|
|
||||||
memory: "512Mi"
|
|
||||||
cpu: "250m"
|
|
||||||
limits:
|
|
||||||
memory: "1Gi"
|
|
||||||
cpu: "1000m"
|
|
||||||
volumes:
|
|
||||||
- name: data
|
|
||||||
persistentVolumeClaim:
|
|
||||||
claimName: stalwart-data
|
|
||||||
---
|
|
||||||
apiVersion: networking.k8s.io/v1
|
|
||||||
kind: Ingress
|
|
||||||
metadata:
|
|
||||||
name: stalwart-web
|
|
||||||
namespace: mail
|
|
||||||
annotations:
|
|
||||||
cert-manager.io/cluster-issuer: "letsencrypt-prod"
|
|
||||||
traefik.ingress.kubernetes.io/router.entrypoints: websecure
|
|
||||||
spec:
|
|
||||||
ingressClassName: traefik
|
|
||||||
tls:
|
|
||||||
- hosts:
|
|
||||||
- mail.basicstack.de
|
|
||||||
secretName: stalwart-tls
|
|
||||||
rules:
|
|
||||||
- host: mail.basicstack.de
|
|
||||||
http:
|
|
||||||
paths:
|
|
||||||
- path: /
|
|
||||||
pathType: Prefix
|
|
||||||
backend:
|
|
||||||
service:
|
|
||||||
name: stalwart-http
|
|
||||||
port:
|
|
||||||
number: 8080
|
|
||||||
|
|
@ -1,170 +0,0 @@
|
||||||
---
|
|
||||||
apiVersion: v1
|
|
||||||
kind: Namespace
|
|
||||||
metadata:
|
|
||||||
name: mail
|
|
||||||
---
|
|
||||||
apiVersion: v1
|
|
||||||
kind: PersistentVolumeClaim
|
|
||||||
metadata:
|
|
||||||
name: stalwart-data
|
|
||||||
namespace: mail
|
|
||||||
spec:
|
|
||||||
accessModes:
|
|
||||||
- ReadWriteOnce
|
|
||||||
storageClassName: hcloud-volumes-encrypted
|
|
||||||
resources:
|
|
||||||
requests:
|
|
||||||
storage: 20Gi
|
|
||||||
---
|
|
||||||
# Secret managed via SealedSecrets
|
|
||||||
# See: stalwart-admin-credentials-sealed.yaml
|
|
||||||
---
|
|
||||||
apiVersion: v1
|
|
||||||
kind: Service
|
|
||||||
metadata:
|
|
||||||
name: stalwart-smtp
|
|
||||||
namespace: mail
|
|
||||||
spec:
|
|
||||||
type: LoadBalancer
|
|
||||||
selector:
|
|
||||||
app: stalwart
|
|
||||||
ports:
|
|
||||||
- name: smtp
|
|
||||||
port: 25
|
|
||||||
targetPort: 25
|
|
||||||
protocol: TCP
|
|
||||||
- name: submission
|
|
||||||
port: 587
|
|
||||||
targetPort: 587
|
|
||||||
protocol: TCP
|
|
||||||
- name: submissions
|
|
||||||
port: 465
|
|
||||||
targetPort: 465
|
|
||||||
protocol: TCP
|
|
||||||
---
|
|
||||||
apiVersion: v1
|
|
||||||
kind: Service
|
|
||||||
metadata:
|
|
||||||
name: stalwart-imap
|
|
||||||
namespace: mail
|
|
||||||
spec:
|
|
||||||
type: LoadBalancer
|
|
||||||
selector:
|
|
||||||
app: stalwart
|
|
||||||
ports:
|
|
||||||
- name: imap
|
|
||||||
port: 143
|
|
||||||
targetPort: 143
|
|
||||||
protocol: TCP
|
|
||||||
- name: imaps
|
|
||||||
port: 993
|
|
||||||
targetPort: 993
|
|
||||||
protocol: TCP
|
|
||||||
---
|
|
||||||
apiVersion: v1
|
|
||||||
kind: Service
|
|
||||||
metadata:
|
|
||||||
name: stalwart-http
|
|
||||||
namespace: mail
|
|
||||||
spec:
|
|
||||||
type: ClusterIP
|
|
||||||
selector:
|
|
||||||
app: stalwart
|
|
||||||
ports:
|
|
||||||
- name: http
|
|
||||||
port: 8080
|
|
||||||
targetPort: 8080
|
|
||||||
protocol: TCP
|
|
||||||
---
|
|
||||||
apiVersion: apps/v1
|
|
||||||
kind: Deployment
|
|
||||||
metadata:
|
|
||||||
name: stalwart
|
|
||||||
namespace: mail
|
|
||||||
spec:
|
|
||||||
replicas: 1
|
|
||||||
strategy:
|
|
||||||
type: Recreate
|
|
||||||
selector:
|
|
||||||
matchLabels:
|
|
||||||
app: stalwart
|
|
||||||
template:
|
|
||||||
metadata:
|
|
||||||
labels:
|
|
||||||
app: stalwart
|
|
||||||
spec:
|
|
||||||
initContainers:
|
|
||||||
- name: fix-permissions
|
|
||||||
image: busybox
|
|
||||||
command: ["sh", "-c", "chown -R 2000:2000 /opt/stalwart-mail && chmod -R 755 /opt/stalwart-mail"]
|
|
||||||
volumeMounts:
|
|
||||||
- name: data
|
|
||||||
mountPath: /opt/stalwart-mail
|
|
||||||
containers:
|
|
||||||
- name: stalwart
|
|
||||||
image: stalwartlabs/stalwart:v0.16
|
|
||||||
command: ["/usr/local/bin/stalwart"]
|
|
||||||
args: ["--config", "/etc/stalwart/config.json"]
|
|
||||||
ports:
|
|
||||||
- containerPort: 25
|
|
||||||
name: smtp
|
|
||||||
- containerPort: 587
|
|
||||||
name: submission
|
|
||||||
- containerPort: 465
|
|
||||||
name: submissions
|
|
||||||
- containerPort: 143
|
|
||||||
name: imap
|
|
||||||
- containerPort: 993
|
|
||||||
name: imaps
|
|
||||||
- containerPort: 8080
|
|
||||||
name: http
|
|
||||||
env:
|
|
||||||
- name: STALWART_RECOVERY_ADMIN
|
|
||||||
valueFrom:
|
|
||||||
secretKeyRef:
|
|
||||||
name: stalwart-admin-credentials
|
|
||||||
key: recovery-admin-password
|
|
||||||
volumeMounts:
|
|
||||||
- name: data
|
|
||||||
mountPath: /var/lib/stalwart
|
|
||||||
- name: data
|
|
||||||
mountPath: /etc/stalwart
|
|
||||||
subPath: etc
|
|
||||||
resources:
|
|
||||||
requests:
|
|
||||||
memory: "512Mi"
|
|
||||||
cpu: "250m"
|
|
||||||
limits:
|
|
||||||
memory: "1Gi"
|
|
||||||
cpu: "1000m"
|
|
||||||
volumes:
|
|
||||||
- name: data
|
|
||||||
persistentVolumeClaim:
|
|
||||||
claimName: stalwart-data
|
|
||||||
---
|
|
||||||
apiVersion: networking.k8s.io/v1
|
|
||||||
kind: Ingress
|
|
||||||
metadata:
|
|
||||||
name: stalwart-web
|
|
||||||
namespace: mail
|
|
||||||
annotations:
|
|
||||||
cert-manager.io/cluster-issuer: "letsencrypt-prod"
|
|
||||||
traefik.ingress.kubernetes.io/router.entrypoints: websecure
|
|
||||||
spec:
|
|
||||||
ingressClassName: traefik
|
|
||||||
tls:
|
|
||||||
- hosts:
|
|
||||||
- mail.basicstack.de
|
|
||||||
secretName: stalwart-tls
|
|
||||||
rules:
|
|
||||||
- host: mail.basicstack.de
|
|
||||||
http:
|
|
||||||
paths:
|
|
||||||
- path: /
|
|
||||||
pathType: Prefix
|
|
||||||
backend:
|
|
||||||
service:
|
|
||||||
name: stalwart-http
|
|
||||||
port:
|
|
||||||
number: 8080
|
|
||||||
|
|
@ -120,6 +120,10 @@ spec:
|
||||||
name: stalwart-admin-credentials
|
name: stalwart-admin-credentials
|
||||||
key: admin-password
|
key: admin-password
|
||||||
optional: false
|
optional: false
|
||||||
|
- name: TLS_CERTIFICATE
|
||||||
|
value: "/etc/stalwart/certs/tls.crt"
|
||||||
|
- name: TLS_PRIVATE_KEY
|
||||||
|
value: "/etc/stalwart/certs/tls.key"
|
||||||
volumeMounts:
|
volumeMounts:
|
||||||
- name: data
|
- name: data
|
||||||
mountPath: /var/lib/stalwart
|
mountPath: /var/lib/stalwart
|
||||||
|
|
|
||||||
|
|
@ -1,584 +0,0 @@
|
||||||
{
|
|
||||||
"annotations": {
|
|
||||||
"list": [
|
|
||||||
{
|
|
||||||
"builtIn": 1,
|
|
||||||
"datasource": "-- Grafana --",
|
|
||||||
"enable": true,
|
|
||||||
"hide": true,
|
|
||||||
"iconColor": "rgba(0, 211, 255, 1)",
|
|
||||||
"name": "Annotations & Alerts",
|
|
||||||
"type": "dashboard"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"editable": true,
|
|
||||||
"gnetId": null,
|
|
||||||
"graphTooltip": 0,
|
|
||||||
"id": null,
|
|
||||||
"links": [],
|
|
||||||
"panels": [
|
|
||||||
{
|
|
||||||
"datasource": "Prometheus",
|
|
||||||
"fieldConfig": {
|
|
||||||
"defaults": {
|
|
||||||
"color": {
|
|
||||||
"mode": "thresholds"
|
|
||||||
},
|
|
||||||
"mappings": [
|
|
||||||
{
|
|
||||||
"options": {
|
|
||||||
"0": {
|
|
||||||
"color": "red",
|
|
||||||
"index": 0,
|
|
||||||
"text": "Down"
|
|
||||||
},
|
|
||||||
"1": {
|
|
||||||
"color": "green",
|
|
||||||
"index": 1,
|
|
||||||
"text": "Running"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"type": "value"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"thresholds": {
|
|
||||||
"mode": "absolute",
|
|
||||||
"steps": [
|
|
||||||
{
|
|
||||||
"color": "red",
|
|
||||||
"value": null
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"color": "green",
|
|
||||||
"value": 1
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"unit": "none"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"gridPos": {
|
|
||||||
"h": 4,
|
|
||||||
"w": 6,
|
|
||||||
"x": 0,
|
|
||||||
"y": 0
|
|
||||||
},
|
|
||||||
"id": 1,
|
|
||||||
"options": {
|
|
||||||
"colorMode": "background",
|
|
||||||
"graphMode": "none",
|
|
||||||
"justifyMode": "auto",
|
|
||||||
"orientation": "auto",
|
|
||||||
"reduceOptions": {
|
|
||||||
"calcs": [
|
|
||||||
"lastNotNull"
|
|
||||||
],
|
|
||||||
"fields": "",
|
|
||||||
"values": false
|
|
||||||
},
|
|
||||||
"textMode": "auto"
|
|
||||||
},
|
|
||||||
"pluginVersion": "8.0.0",
|
|
||||||
"targets": [
|
|
||||||
{
|
|
||||||
"expr": "kube_pod_status_phase{namespace=\"mail\", pod=~\"stalwart-.*\", phase=\"Running\"}",
|
|
||||||
"refId": "A"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"title": "Pod Status",
|
|
||||||
"type": "stat"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"datasource": "Prometheus",
|
|
||||||
"fieldConfig": {
|
|
||||||
"defaults": {
|
|
||||||
"color": {
|
|
||||||
"mode": "thresholds"
|
|
||||||
},
|
|
||||||
"mappings": [],
|
|
||||||
"thresholds": {
|
|
||||||
"mode": "absolute",
|
|
||||||
"steps": [
|
|
||||||
{
|
|
||||||
"color": "green",
|
|
||||||
"value": null
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"color": "yellow",
|
|
||||||
"value": 1
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"color": "red",
|
|
||||||
"value": 5
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"unit": "none"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"gridPos": {
|
|
||||||
"h": 4,
|
|
||||||
"w": 6,
|
|
||||||
"x": 6,
|
|
||||||
"y": 0
|
|
||||||
},
|
|
||||||
"id": 2,
|
|
||||||
"options": {
|
|
||||||
"colorMode": "background",
|
|
||||||
"graphMode": "area",
|
|
||||||
"justifyMode": "auto",
|
|
||||||
"orientation": "auto",
|
|
||||||
"reduceOptions": {
|
|
||||||
"calcs": [
|
|
||||||
"lastNotNull"
|
|
||||||
],
|
|
||||||
"fields": "",
|
|
||||||
"values": false
|
|
||||||
},
|
|
||||||
"textMode": "auto"
|
|
||||||
},
|
|
||||||
"pluginVersion": "8.0.0",
|
|
||||||
"targets": [
|
|
||||||
{
|
|
||||||
"expr": "kube_pod_container_status_restarts_total{namespace=\"mail\", pod=~\"stalwart-.*\"}",
|
|
||||||
"refId": "A"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"title": "Container Restarts",
|
|
||||||
"type": "stat"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"datasource": "Prometheus",
|
|
||||||
"fieldConfig": {
|
|
||||||
"defaults": {
|
|
||||||
"color": {
|
|
||||||
"mode": "thresholds"
|
|
||||||
},
|
|
||||||
"mappings": [],
|
|
||||||
"max": 100,
|
|
||||||
"min": 0,
|
|
||||||
"thresholds": {
|
|
||||||
"mode": "absolute",
|
|
||||||
"steps": [
|
|
||||||
{
|
|
||||||
"color": "green",
|
|
||||||
"value": null
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"color": "yellow",
|
|
||||||
"value": 75
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"color": "red",
|
|
||||||
"value": 90
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"unit": "percent"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"gridPos": {
|
|
||||||
"h": 4,
|
|
||||||
"w": 6,
|
|
||||||
"x": 12,
|
|
||||||
"y": 0
|
|
||||||
},
|
|
||||||
"id": 3,
|
|
||||||
"options": {
|
|
||||||
"orientation": "auto",
|
|
||||||
"reduceOptions": {
|
|
||||||
"calcs": [
|
|
||||||
"lastNotNull"
|
|
||||||
],
|
|
||||||
"fields": "",
|
|
||||||
"values": false
|
|
||||||
},
|
|
||||||
"showThresholdLabels": false,
|
|
||||||
"showThresholdMarkers": true
|
|
||||||
},
|
|
||||||
"pluginVersion": "8.0.0",
|
|
||||||
"targets": [
|
|
||||||
{
|
|
||||||
"expr": "(kubelet_volume_stats_used_bytes{namespace=\"mail\", persistentvolumeclaim=\"stalwart-data\"} / kubelet_volume_stats_capacity_bytes{namespace=\"mail\", persistentvolumeclaim=\"stalwart-data\"}) * 100",
|
|
||||||
"refId": "A"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"title": "Disk Usage (20Gi PVC)",
|
|
||||||
"type": "gauge"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"datasource": "Prometheus",
|
|
||||||
"fieldConfig": {
|
|
||||||
"defaults": {
|
|
||||||
"color": {
|
|
||||||
"mode": "thresholds"
|
|
||||||
},
|
|
||||||
"mappings": [],
|
|
||||||
"max": 100,
|
|
||||||
"min": 0,
|
|
||||||
"thresholds": {
|
|
||||||
"mode": "absolute",
|
|
||||||
"steps": [
|
|
||||||
{
|
|
||||||
"color": "green",
|
|
||||||
"value": null
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"color": "yellow",
|
|
||||||
"value": 75
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"color": "red",
|
|
||||||
"value": 85
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"unit": "percent"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"gridPos": {
|
|
||||||
"h": 4,
|
|
||||||
"w": 6,
|
|
||||||
"x": 18,
|
|
||||||
"y": 0
|
|
||||||
},
|
|
||||||
"id": 4,
|
|
||||||
"options": {
|
|
||||||
"orientation": "auto",
|
|
||||||
"reduceOptions": {
|
|
||||||
"calcs": [
|
|
||||||
"lastNotNull"
|
|
||||||
],
|
|
||||||
"fields": "",
|
|
||||||
"values": false
|
|
||||||
},
|
|
||||||
"showThresholdLabels": false,
|
|
||||||
"showThresholdMarkers": true
|
|
||||||
},
|
|
||||||
"pluginVersion": "8.0.0",
|
|
||||||
"targets": [
|
|
||||||
{
|
|
||||||
"expr": "(container_memory_working_set_bytes{namespace=\"mail\", pod=~\"stalwart-.*\", container=\"stalwart\"} / container_spec_memory_limit_bytes{namespace=\"mail\", pod=~\"stalwart-.*\", container=\"stalwart\"}) * 100",
|
|
||||||
"refId": "A"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"title": "Memory Usage",
|
|
||||||
"type": "gauge"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"datasource": "Prometheus",
|
|
||||||
"fieldConfig": {
|
|
||||||
"defaults": {
|
|
||||||
"color": {
|
|
||||||
"mode": "palette-classic"
|
|
||||||
},
|
|
||||||
"custom": {
|
|
||||||
"axisLabel": "",
|
|
||||||
"axisPlacement": "auto",
|
|
||||||
"barAlignment": 0,
|
|
||||||
"drawStyle": "line",
|
|
||||||
"fillOpacity": 10,
|
|
||||||
"gradientMode": "none",
|
|
||||||
"hideFrom": {
|
|
||||||
"tooltip": false,
|
|
||||||
"viz": false,
|
|
||||||
"legend": false
|
|
||||||
},
|
|
||||||
"lineInterpolation": "linear",
|
|
||||||
"lineWidth": 1,
|
|
||||||
"pointSize": 5,
|
|
||||||
"scaleDistribution": {
|
|
||||||
"type": "linear"
|
|
||||||
},
|
|
||||||
"showPoints": "never",
|
|
||||||
"spanNulls": true
|
|
||||||
},
|
|
||||||
"mappings": [],
|
|
||||||
"thresholds": {
|
|
||||||
"mode": "absolute",
|
|
||||||
"steps": [
|
|
||||||
{
|
|
||||||
"color": "green",
|
|
||||||
"value": null
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"unit": "percentunit"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"gridPos": {
|
|
||||||
"h": 8,
|
|
||||||
"w": 12,
|
|
||||||
"x": 0,
|
|
||||||
"y": 4
|
|
||||||
},
|
|
||||||
"id": 5,
|
|
||||||
"options": {
|
|
||||||
"legend": {
|
|
||||||
"calcs": [],
|
|
||||||
"displayMode": "list",
|
|
||||||
"placement": "bottom"
|
|
||||||
},
|
|
||||||
"tooltip": {
|
|
||||||
"mode": "multi"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"pluginVersion": "8.0.0",
|
|
||||||
"targets": [
|
|
||||||
{
|
|
||||||
"expr": "rate(container_cpu_usage_seconds_total{namespace=\"mail\", pod=~\"stalwart-.*\", container=\"stalwart\"}[5m]) / (container_spec_cpu_quota{namespace=\"mail\", pod=~\"stalwart-.*\", container=\"stalwart\"} / container_spec_cpu_period{namespace=\"mail\", pod=~\"stalwart-.*\", container=\"stalwart\"})",
|
|
||||||
"legendFormat": "CPU Usage",
|
|
||||||
"refId": "A"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"title": "CPU Usage Over Time",
|
|
||||||
"type": "timeseries"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"datasource": "Prometheus",
|
|
||||||
"fieldConfig": {
|
|
||||||
"defaults": {
|
|
||||||
"color": {
|
|
||||||
"mode": "palette-classic"
|
|
||||||
},
|
|
||||||
"custom": {
|
|
||||||
"axisLabel": "",
|
|
||||||
"axisPlacement": "auto",
|
|
||||||
"barAlignment": 0,
|
|
||||||
"drawStyle": "line",
|
|
||||||
"fillOpacity": 10,
|
|
||||||
"gradientMode": "none",
|
|
||||||
"hideFrom": {
|
|
||||||
"tooltip": false,
|
|
||||||
"viz": false,
|
|
||||||
"legend": false
|
|
||||||
},
|
|
||||||
"lineInterpolation": "linear",
|
|
||||||
"lineWidth": 1,
|
|
||||||
"pointSize": 5,
|
|
||||||
"scaleDistribution": {
|
|
||||||
"type": "linear"
|
|
||||||
},
|
|
||||||
"showPoints": "never",
|
|
||||||
"spanNulls": true
|
|
||||||
},
|
|
||||||
"mappings": [],
|
|
||||||
"thresholds": {
|
|
||||||
"mode": "absolute",
|
|
||||||
"steps": [
|
|
||||||
{
|
|
||||||
"color": "green",
|
|
||||||
"value": null
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"unit": "bytes"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"gridPos": {
|
|
||||||
"h": 8,
|
|
||||||
"w": 12,
|
|
||||||
"x": 12,
|
|
||||||
"y": 4
|
|
||||||
},
|
|
||||||
"id": 6,
|
|
||||||
"options": {
|
|
||||||
"legend": {
|
|
||||||
"calcs": [],
|
|
||||||
"displayMode": "list",
|
|
||||||
"placement": "bottom"
|
|
||||||
},
|
|
||||||
"tooltip": {
|
|
||||||
"mode": "multi"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"pluginVersion": "8.0.0",
|
|
||||||
"targets": [
|
|
||||||
{
|
|
||||||
"expr": "container_memory_working_set_bytes{namespace=\"mail\", pod=~\"stalwart-.*\", container=\"stalwart\"}",
|
|
||||||
"legendFormat": "Memory Usage",
|
|
||||||
"refId": "A"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"expr": "container_spec_memory_limit_bytes{namespace=\"mail\", pod=~\"stalwart-.*\", container=\"stalwart\"}",
|
|
||||||
"legendFormat": "Memory Limit",
|
|
||||||
"refId": "B"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"title": "Memory Usage Over Time",
|
|
||||||
"type": "timeseries"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"datasource": "Prometheus",
|
|
||||||
"fieldConfig": {
|
|
||||||
"defaults": {
|
|
||||||
"color": {
|
|
||||||
"mode": "palette-classic"
|
|
||||||
},
|
|
||||||
"custom": {
|
|
||||||
"axisLabel": "",
|
|
||||||
"axisPlacement": "auto",
|
|
||||||
"barAlignment": 0,
|
|
||||||
"drawStyle": "line",
|
|
||||||
"fillOpacity": 10,
|
|
||||||
"gradientMode": "none",
|
|
||||||
"hideFrom": {
|
|
||||||
"tooltip": false,
|
|
||||||
"viz": false,
|
|
||||||
"legend": false
|
|
||||||
},
|
|
||||||
"lineInterpolation": "linear",
|
|
||||||
"lineWidth": 1,
|
|
||||||
"pointSize": 5,
|
|
||||||
"scaleDistribution": {
|
|
||||||
"type": "linear"
|
|
||||||
},
|
|
||||||
"showPoints": "never",
|
|
||||||
"spanNulls": true
|
|
||||||
},
|
|
||||||
"mappings": [],
|
|
||||||
"thresholds": {
|
|
||||||
"mode": "absolute",
|
|
||||||
"steps": [
|
|
||||||
{
|
|
||||||
"color": "green",
|
|
||||||
"value": null
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"unit": "bytes"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"gridPos": {
|
|
||||||
"h": 8,
|
|
||||||
"w": 12,
|
|
||||||
"x": 0,
|
|
||||||
"y": 12
|
|
||||||
},
|
|
||||||
"id": 7,
|
|
||||||
"options": {
|
|
||||||
"legend": {
|
|
||||||
"calcs": [
|
|
||||||
"lastNotNull"
|
|
||||||
],
|
|
||||||
"displayMode": "table",
|
|
||||||
"placement": "bottom"
|
|
||||||
},
|
|
||||||
"tooltip": {
|
|
||||||
"mode": "multi"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"pluginVersion": "8.0.0",
|
|
||||||
"targets": [
|
|
||||||
{
|
|
||||||
"expr": "kubelet_volume_stats_used_bytes{namespace=\"mail\", persistentvolumeclaim=\"stalwart-data\"}",
|
|
||||||
"legendFormat": "Used Space",
|
|
||||||
"refId": "A"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"expr": "kubelet_volume_stats_capacity_bytes{namespace=\"mail\", persistentvolumeclaim=\"stalwart-data\"}",
|
|
||||||
"legendFormat": "Total Capacity (20Gi)",
|
|
||||||
"refId": "B"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"expr": "kubelet_volume_stats_available_bytes{namespace=\"mail\", persistentvolumeclaim=\"stalwart-data\"}",
|
|
||||||
"legendFormat": "Available Space",
|
|
||||||
"refId": "C"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"title": "Disk Space Over Time (Encrypted Volume)",
|
|
||||||
"type": "timeseries"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"datasource": "Prometheus",
|
|
||||||
"fieldConfig": {
|
|
||||||
"defaults": {
|
|
||||||
"color": {
|
|
||||||
"mode": "palette-classic"
|
|
||||||
},
|
|
||||||
"custom": {
|
|
||||||
"axisLabel": "",
|
|
||||||
"axisPlacement": "auto",
|
|
||||||
"barAlignment": 0,
|
|
||||||
"drawStyle": "line",
|
|
||||||
"fillOpacity": 10,
|
|
||||||
"gradientMode": "none",
|
|
||||||
"hideFrom": {
|
|
||||||
"tooltip": false,
|
|
||||||
"viz": false,
|
|
||||||
"legend": false
|
|
||||||
},
|
|
||||||
"lineInterpolation": "linear",
|
|
||||||
"lineWidth": 1,
|
|
||||||
"pointSize": 5,
|
|
||||||
"scaleDistribution": {
|
|
||||||
"type": "linear"
|
|
||||||
},
|
|
||||||
"showPoints": "never",
|
|
||||||
"spanNulls": true
|
|
||||||
},
|
|
||||||
"mappings": [],
|
|
||||||
"thresholds": {
|
|
||||||
"mode": "absolute",
|
|
||||||
"steps": [
|
|
||||||
{
|
|
||||||
"color": "green",
|
|
||||||
"value": null
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"unit": "none"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"gridPos": {
|
|
||||||
"h": 8,
|
|
||||||
"w": 12,
|
|
||||||
"x": 12,
|
|
||||||
"y": 12
|
|
||||||
},
|
|
||||||
"id": 8,
|
|
||||||
"options": {
|
|
||||||
"legend": {
|
|
||||||
"calcs": [],
|
|
||||||
"displayMode": "list",
|
|
||||||
"placement": "bottom"
|
|
||||||
},
|
|
||||||
"tooltip": {
|
|
||||||
"mode": "multi"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"pluginVersion": "8.0.0",
|
|
||||||
"targets": [
|
|
||||||
{
|
|
||||||
"expr": "kubelet_volume_stats_inodes_used{namespace=\"mail\", persistentvolumeclaim=\"stalwart-data\"}",
|
|
||||||
"legendFormat": "Inodes Used",
|
|
||||||
"refId": "A"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"expr": "kubelet_volume_stats_inodes{namespace=\"mail\", persistentvolumeclaim=\"stalwart-data\"}",
|
|
||||||
"legendFormat": "Total Inodes",
|
|
||||||
"refId": "B"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"title": "Disk Inodes Usage",
|
|
||||||
"type": "timeseries"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"refresh": "30s",
|
|
||||||
"schemaVersion": 27,
|
|
||||||
"style": "dark",
|
|
||||||
"tags": ["stalwart", "mail", "kubernetes"],
|
|
||||||
"templating": {
|
|
||||||
"list": []
|
|
||||||
},
|
|
||||||
"time": {
|
|
||||||
"from": "now-6h",
|
|
||||||
"to": "now"
|
|
||||||
},
|
|
||||||
"timepicker": {},
|
|
||||||
"timezone": "",
|
|
||||||
"title": "Stalwart Mail Server",
|
|
||||||
"uid": "stalwart-mail",
|
|
||||||
"version": 1
|
|
||||||
}
|
|
||||||
|
|
@ -1,116 +0,0 @@
|
||||||
# Stalwart Helm Fix - Proper Environment Configuration
|
|
||||||
image:
|
|
||||||
repository: stalwartlabs/stalwart
|
|
||||||
tag: "v0.16.9"
|
|
||||||
pullPolicy: Always
|
|
||||||
|
|
||||||
# IMPORTANT: Remove args to let Stalwart use environment variables
|
|
||||||
# The Helm chart should not pass --config if we want env-based config
|
|
||||||
extraArgs: []
|
|
||||||
|
|
||||||
# Enable recovery admin
|
|
||||||
recoveryAdmin:
|
|
||||||
enabled: true
|
|
||||||
username: admin
|
|
||||||
password: ***REMOVED***
|
|
||||||
|
|
||||||
# Persistence
|
|
||||||
persistence:
|
|
||||||
enabled: true
|
|
||||||
storageClass: hcloud-volumes-encrypted
|
|
||||||
accessMode: ReadWriteOnce
|
|
||||||
size: 20Gi
|
|
||||||
|
|
||||||
# Service configuration
|
|
||||||
service:
|
|
||||||
type: LoadBalancer
|
|
||||||
ports:
|
|
||||||
smtp: 25
|
|
||||||
submission: 587
|
|
||||||
smtps: 465
|
|
||||||
imap: 143
|
|
||||||
imaps: 993
|
|
||||||
pop3: 110
|
|
||||||
pop3s: 995
|
|
||||||
sieve: 4190
|
|
||||||
mgmt: 8080
|
|
||||||
|
|
||||||
# Ingress
|
|
||||||
ingress:
|
|
||||||
enabled: true
|
|
||||||
className: traefik
|
|
||||||
annotations:
|
|
||||||
cert-manager.io/cluster-issuer: "letsencrypt-prod"
|
|
||||||
traefik.ingress.kubernetes.io/router.entrypoints: websecure
|
|
||||||
hosts:
|
|
||||||
- host: mail.basicstack.de
|
|
||||||
paths:
|
|
||||||
- path: /
|
|
||||||
pathType: Prefix
|
|
||||||
portName: mgmt
|
|
||||||
tls:
|
|
||||||
- secretName: stalwart-tls
|
|
||||||
hosts:
|
|
||||||
- mail.basicstack.de
|
|
||||||
|
|
||||||
# Minimal config - just database location
|
|
||||||
config:
|
|
||||||
"@type": "RocksDb"
|
|
||||||
path: "/var/lib/stalwart"
|
|
||||||
|
|
||||||
# Security contexts
|
|
||||||
podSecurityContext:
|
|
||||||
fsGroup: 2000
|
|
||||||
runAsUser: 2000
|
|
||||||
runAsGroup: 2000
|
|
||||||
|
|
||||||
containerSecurityContext:
|
|
||||||
runAsNonRoot: true
|
|
||||||
allowPrivilegeEscalation: false
|
|
||||||
capabilities:
|
|
||||||
drop: [ALL]
|
|
||||||
add: [NET_BIND_SERVICE]
|
|
||||||
seccompProfile:
|
|
||||||
type: RuntimeDefault
|
|
||||||
|
|
||||||
# Resources
|
|
||||||
resources:
|
|
||||||
requests:
|
|
||||||
memory: "512Mi"
|
|
||||||
cpu: "250m"
|
|
||||||
limits:
|
|
||||||
memory: "2Gi"
|
|
||||||
cpu: "2"
|
|
||||||
|
|
||||||
# Environment variables for proper configuration
|
|
||||||
env:
|
|
||||||
- name: RUST_LOG
|
|
||||||
value: "debug"
|
|
||||||
- name: STALWART_LOG_LEVEL
|
|
||||||
value: "debug"
|
|
||||||
|
|
||||||
# OIDC Configuration
|
|
||||||
envFrom:
|
|
||||||
- secretRef:
|
|
||||||
name: stalwart-stalwart-env
|
|
||||||
|
|
||||||
# Additional OIDC env vars
|
|
||||||
extraEnv:
|
|
||||||
- name: STALWART_OAUTH_ENABLE
|
|
||||||
value: "true"
|
|
||||||
- name: STALWART_OAUTH_ISSUER
|
|
||||||
value: "https://auth.basicstack.de"
|
|
||||||
- name: STALWART_OAUTH_REDIRECT_URI
|
|
||||||
value: "https://mail.basicstack.de/admin/oauth/callback"
|
|
||||||
- name: STALWART_OAUTH_SCOPES
|
|
||||||
value: "openid profile email"
|
|
||||||
- name: STALWART_OAUTH_CLIENT_ID
|
|
||||||
valueFrom:
|
|
||||||
secretKeyRef:
|
|
||||||
name: stalwart-oidc
|
|
||||||
key: oidc-client-id
|
|
||||||
- name: STALWART_OAUTH_CLIENT_SECRET
|
|
||||||
valueFrom:
|
|
||||||
secretKeyRef:
|
|
||||||
name: stalwart-oidc
|
|
||||||
key: oidc-client-secret
|
|
||||||
|
|
@ -1,100 +0,0 @@
|
||||||
# Stalwart Helm Values with OIDC Configuration
|
|
||||||
image:
|
|
||||||
repository: stalwartlabs/stalwart
|
|
||||||
tag: latest
|
|
||||||
pullPolicy: Always
|
|
||||||
|
|
||||||
replicaCount: 1
|
|
||||||
|
|
||||||
# Recovery admin for initial setup
|
|
||||||
recoveryAdmin:
|
|
||||||
enabled: true
|
|
||||||
username: admin
|
|
||||||
password: ***REMOVED***
|
|
||||||
|
|
||||||
# Persistent storage with encrypted volumes
|
|
||||||
persistence:
|
|
||||||
enabled: true
|
|
||||||
storageClass: hcloud-volumes-encrypted
|
|
||||||
accessMode: ReadWriteOnce
|
|
||||||
size: 20Gi
|
|
||||||
|
|
||||||
# Service configuration
|
|
||||||
service:
|
|
||||||
type: LoadBalancer
|
|
||||||
ports:
|
|
||||||
smtp: 25
|
|
||||||
submission: 587
|
|
||||||
smtps: 465
|
|
||||||
imap: 143
|
|
||||||
imaps: 993
|
|
||||||
pop3: 110
|
|
||||||
pop3s: 995
|
|
||||||
sieve: 4190
|
|
||||||
mgmt: 8080
|
|
||||||
|
|
||||||
# Ingress for web UI
|
|
||||||
ingress:
|
|
||||||
enabled: true
|
|
||||||
className: traefik
|
|
||||||
annotations:
|
|
||||||
cert-manager.io/cluster-issuer: "letsencrypt-prod"
|
|
||||||
traefik.ingress.kubernetes.io/router.entrypoints: websecure
|
|
||||||
hosts:
|
|
||||||
- host: mail.basicstack.de
|
|
||||||
paths:
|
|
||||||
- path: /
|
|
||||||
pathType: Prefix
|
|
||||||
portName: mgmt
|
|
||||||
tls:
|
|
||||||
- secretName: stalwart-tls
|
|
||||||
hosts:
|
|
||||||
- mail.basicstack.de
|
|
||||||
|
|
||||||
# RocksDB configuration
|
|
||||||
config:
|
|
||||||
"@type": "RocksDb"
|
|
||||||
path: "/var/lib/stalwart"
|
|
||||||
|
|
||||||
# Pod security context
|
|
||||||
podSecurityContext:
|
|
||||||
fsGroup: 2000
|
|
||||||
runAsUser: 2000
|
|
||||||
runAsGroup: 2000
|
|
||||||
|
|
||||||
# Container security context
|
|
||||||
containerSecurityContext:
|
|
||||||
runAsNonRoot: true
|
|
||||||
allowPrivilegeEscalation: false
|
|
||||||
capabilities:
|
|
||||||
drop: [ALL]
|
|
||||||
add: [NET_BIND_SERVICE]
|
|
||||||
seccompProfile:
|
|
||||||
type: RuntimeDefault
|
|
||||||
|
|
||||||
# Resources
|
|
||||||
resources:
|
|
||||||
requests:
|
|
||||||
memory: "512Mi"
|
|
||||||
cpu: "250m"
|
|
||||||
limits:
|
|
||||||
memory: "2Gi"
|
|
||||||
cpu: "2000m"
|
|
||||||
|
|
||||||
# OIDC Configuration via environment variables
|
|
||||||
env:
|
|
||||||
- name: STALWART_OAUTH_ENABLE
|
|
||||||
value: "true"
|
|
||||||
- name: STALWART_OAUTH_ISSUER
|
|
||||||
value: "https://auth.basicstack.de"
|
|
||||||
- name: STALWART_OAUTH_CLIENT_ID
|
|
||||||
value: "stalwart-webui"
|
|
||||||
- name: STALWART_OAUTH_CLIENT_SECRET
|
|
||||||
valueFrom:
|
|
||||||
secretKeyRef:
|
|
||||||
name: stalwart-oidc
|
|
||||||
key: oidc-client-secret
|
|
||||||
- name: STALWART_OAUTH_REDIRECT_URI
|
|
||||||
value: "https://mail.basicstack.de/admin/oauth/callback"
|
|
||||||
- name: STALWART_OAUTH_SCOPES
|
|
||||||
value: "openid profile email"
|
|
||||||
|
|
@ -1,100 +0,0 @@
|
||||||
---
|
|
||||||
# PrometheusRule for Stalwart alerting
|
|
||||||
apiVersion: monitoring.coreos.com/v1
|
|
||||||
kind: PrometheusRule
|
|
||||||
metadata:
|
|
||||||
name: stalwart-alerts
|
|
||||||
namespace: mail
|
|
||||||
labels:
|
|
||||||
app: stalwart
|
|
||||||
prometheus: kube-prometheus-stack
|
|
||||||
release: kube-prometheus-stack
|
|
||||||
spec:
|
|
||||||
groups:
|
|
||||||
- name: stalwart.rules
|
|
||||||
interval: 30s
|
|
||||||
rules:
|
|
||||||
# Pod availability alert
|
|
||||||
- alert: StalwartPodDown
|
|
||||||
expr: kube_pod_status_phase{namespace="mail", pod=~"stalwart-.*", phase!="Running"} == 1
|
|
||||||
for: 5m
|
|
||||||
labels:
|
|
||||||
severity: critical
|
|
||||||
component: mail
|
|
||||||
annotations:
|
|
||||||
summary: "Stalwart mail server pod is down"
|
|
||||||
description: "Stalwart pod {{ $labels.pod }} in namespace {{ $labels.namespace }} has been down for more than 5 minutes."
|
|
||||||
|
|
||||||
# Container restart alert
|
|
||||||
- alert: StalwartContainerRestarting
|
|
||||||
expr: rate(kube_pod_container_status_restarts_total{namespace="mail", pod=~"stalwart-.*"}[15m]) > 0
|
|
||||||
for: 5m
|
|
||||||
labels:
|
|
||||||
severity: warning
|
|
||||||
component: mail
|
|
||||||
annotations:
|
|
||||||
summary: "Stalwart container is restarting"
|
|
||||||
description: "Stalwart container in pod {{ $labels.pod }} has restarted {{ $value }} times in the last 15 minutes."
|
|
||||||
|
|
||||||
# Memory usage alert
|
|
||||||
- alert: StalwartHighMemoryUsage
|
|
||||||
expr: |
|
|
||||||
(container_memory_working_set_bytes{namespace="mail", pod=~"stalwart-.*", container="stalwart"}
|
|
||||||
/ container_spec_memory_limit_bytes{namespace="mail", pod=~"stalwart-.*", container="stalwart"}) > 0.85
|
|
||||||
for: 10m
|
|
||||||
labels:
|
|
||||||
severity: warning
|
|
||||||
component: mail
|
|
||||||
annotations:
|
|
||||||
summary: "Stalwart memory usage is high"
|
|
||||||
description: "Stalwart container {{ $labels.pod }} is using {{ $value | humanizePercentage }} of its memory limit."
|
|
||||||
|
|
||||||
# CPU usage alert
|
|
||||||
- alert: StalwartHighCPUUsage
|
|
||||||
expr: |
|
|
||||||
(rate(container_cpu_usage_seconds_total{namespace="mail", pod=~"stalwart-.*", container="stalwart"}[5m])
|
|
||||||
/ container_spec_cpu_quota{namespace="mail", pod=~"stalwart-.*", container="stalwart"}
|
|
||||||
* container_spec_cpu_period{namespace="mail", pod=~"stalwart-.*", container="stalwart"}) > 0.85
|
|
||||||
for: 10m
|
|
||||||
labels:
|
|
||||||
severity: warning
|
|
||||||
component: mail
|
|
||||||
annotations:
|
|
||||||
summary: "Stalwart CPU usage is high"
|
|
||||||
description: "Stalwart container {{ $labels.pod }} is using {{ $value | humanizePercentage }} of its CPU limit."
|
|
||||||
|
|
||||||
# Disk usage alert for PVC
|
|
||||||
- alert: StalwartDiskSpaceLow
|
|
||||||
expr: |
|
|
||||||
(kubelet_volume_stats_used_bytes{namespace="mail", persistentvolumeclaim="stalwart-data"}
|
|
||||||
/ kubelet_volume_stats_capacity_bytes{namespace="mail", persistentvolumeclaim="stalwart-data"}) > 0.75
|
|
||||||
for: 5m
|
|
||||||
labels:
|
|
||||||
severity: warning
|
|
||||||
component: mail
|
|
||||||
annotations:
|
|
||||||
summary: "Stalwart disk space is running low"
|
|
||||||
description: "Stalwart PVC stalwart-data is {{ $value | humanizePercentage }} full. Consider expanding the volume."
|
|
||||||
|
|
||||||
- alert: StalwartDiskSpaceCritical
|
|
||||||
expr: |
|
|
||||||
(kubelet_volume_stats_used_bytes{namespace="mail", persistentvolumeclaim="stalwart-data"}
|
|
||||||
/ kubelet_volume_stats_capacity_bytes{namespace="mail", persistentvolumeclaim="stalwart-data"}) > 0.90
|
|
||||||
for: 5m
|
|
||||||
labels:
|
|
||||||
severity: critical
|
|
||||||
component: mail
|
|
||||||
annotations:
|
|
||||||
summary: "Stalwart disk space is critically low"
|
|
||||||
description: "Stalwart PVC stalwart-data is {{ $value | humanizePercentage }} full. Immediate action required!"
|
|
||||||
|
|
||||||
# PVC availability alert
|
|
||||||
- alert: StalwartPVCNotBound
|
|
||||||
expr: kube_persistentvolumeclaim_status_phase{namespace="mail", persistentvolumeclaim="stalwart-data", phase!="Bound"} == 1
|
|
||||||
for: 5m
|
|
||||||
labels:
|
|
||||||
severity: critical
|
|
||||||
component: mail
|
|
||||||
annotations:
|
|
||||||
summary: "Stalwart PVC is not bound"
|
|
||||||
description: "Stalwart PVC stalwart-data is in {{ $labels.phase }} state. Mail data may be unavailable."
|
|
||||||
|
|
@ -1,18 +0,0 @@
|
||||||
---
|
|
||||||
apiVersion: bitnami.com/v1alpha1
|
|
||||||
kind: SealedSecret
|
|
||||||
metadata:
|
|
||||||
creationTimestamp: null
|
|
||||||
name: stalwart-oauth2-proxy-secret
|
|
||||||
namespace: stalwart
|
|
||||||
spec:
|
|
||||||
encryptedData:
|
|
||||||
client-id: AgARqBsHsoED3MPN81QKic7nfuLpv4CAIpqkPQL3lwUSqmmbxK4JAO2wGuSpS0we+tr7p84VhXWHIe5e7jfe3J+lj5W0JAZdfwEED/5lV/wKD/xNfHgO0iKUTJHvetU7i3ecMogVqJGqG9rX42dE83LfybZ4Ua21AcjTMPSQmakQb8204VXIxqTlOFBD0f7hWNCs7IZ+AfLBjLEZHqmdfh849/TKwXnXFDtYMYzG16o/+zkXBKkOoK/RuPQN8Nt2YdA7Xd1AqU/DggzD8GzMJEvPEFvZQjBnq+hgrxa4lY5dj+S6nJ9/Tt+dLYV6+08vSPLM8mN0jc0Vd73lr2aGnjL8TurKB7s3yDeKsBUgqoilkDJO7YoCBfx3px73lK+a1G+i+KNf7iUcTIGIRq+nIYLHXP3OWSFCIBHe5BhH4oyB3GRUIhHhgBTEMXnHyxALqfzsr3SMHdnRHYnvz9IMLWxw1NTiQhvEP8zF0gZLjpHATWglPvX50vQnESc30HYa0HXc2F9xPX0oTnWucmviuSKGS0DHmNO9zKLFZNR2DJm/FVY6AMaH2YZxpM/3RDqEmCbfGzLgzslaIz8vp432FhR/Isl9yzHV4SJDlXKBlD/EDNo9tthgpqzQilBxx2I79ute1Nbt0Ka71P7doeVyl93NYKCFWhh36hHwlM4vpePUmpRronv4iZl35xzAoV8hNm1RxxLCI79PESH4Qk6XeXiQXBt0s5T7/3ZaMUDnxugY3G9F1U4=
|
|
||||||
client-secret: AgBbziMEuveXDkylbbtppuNIHyJ7rcC6Do+qANtpk89N4Qqo+wFmG/mPzCXDpCjX/i+o7r4vOhwfuzHREy42Pn76VaulYiNpkETiIGFc7266dCIIEwSaHcn353iMHgDYQgnqehsHZejq6EGUEM1Wtv7O+8Bt1O+E7jLxxeAtB6XdaFwyDvvZrpJuN981me1DMPk4tvEfoVkDwtVm+dKogR5GnaN1FK75hojCoqU+SxUulmX6ykj7fdV2GPsxQTOFT1gy/hSWfR3BZ1ZBhHgt3xtfWKWm5DUcn7CFCJGgVxXbe1xspL7ntZu/aDtV01K1udVAVVGnmrckpYr9R54+ATnhcIvt4O8kRBHyJKdeP/Fi9hN7Kupc7yL+gljMaQhMMYu8gtC7Vf9cDPH1ytMfhbyrF2lkKY7MU+iWvFxVPnY6tgd2R2dwhSB0Uxde7iznAiVgD1dObw0JDlHhIKQHn5+Bp9z0gYscK3BJPLbW+xCCIkTOFWXnCu9ahDXD7C+pEpngp1hGdGmMhT1wKjD2GY+8RAlO2Gz9K1Jdix6ukqL2LY35QgdSXrxrJgkD1xRkQP6BEBHefcYcP1QO9gl+xKmxPDZX60MgT0RYAE9ETJBuLuw/11kZKjthlWkgzqzwwKd9S+jk6aghcJueTv89gTcRnDlP9URjjQi8iFk/ag9+Af2Q4ftW7NPu7K141onzA0VPE3ZruwhQVhpWVbLb+cXfmShbn360TECBFFXXAj1oBA==
|
|
||||||
cookie-secret: AgAzT2XhiQrTb2AU5nZAPCJqTZTXk/nSCU3FUgu0g/7N5Wg7vis5MLv8RaUSNrx7nDBFtHO0YJb8cWx+UF69j2tBNmnOYYFmhYIE+U0oK7ZgKuTfbFUiQISmOwKzq/JCbsn9pR/jsjaBXCiIfVBvV15tVDj44iDtVNRtmgjHfatvD/N2YaPn5KP8t/8Xr+Enp8PF58+Z44OO30hG9mXXInAZzPuKbv9kOwMDdRGm+6gWczigyC26Cl3oIVRO4t5TcKDcoEmQD9N7NfEjbZ/D+6GpruYIM7u1W9rEip63SHZ6ty0B2m9BmQZYRWcJPX5ISh5VHPvxY665gAcg1+Qooekp9YcHubb8FJjMHGBuXj2fJVjCbjSZSIuiU6cEoIn9C2yX24rvcVOG7EMDysG9LqDsZnkvqFsQ2uRuQR9d3oKZPkR9xXGibOHDIKPcWVH212hX7QtAo7On0JeeDM/QiSBs+MKuF1l4pdiGfyXqZ0pwrlHsfWwZaZYDYRhMWmftrZTsjTBMDZ9Cw8i2BQpk9NC04wCwJ6EPNySPD9HLJ9HNnet6bEAFmLmMZyN9x6vbW20VJKfx09eOjd99Y4TlEIsVLyUF9S29lVoib09EfzvDMn9axDIYaZ0SKhqZF+eH0lHzhr1vXtMxfecLO9kjBarcAuSilfIS6XqtwcXXUhjiRbz0qGtUNvy3n+xWVCTXtAz0FB4cuUSIqxoV0gXeDRNNQiRdCJNABNGLJxKHYcl4QA==
|
|
||||||
template:
|
|
||||||
metadata:
|
|
||||||
creationTimestamp: null
|
|
||||||
name: stalwart-oauth2-proxy-secret
|
|
||||||
namespace: stalwart
|
|
||||||
type: Opaque
|
|
||||||
|
|
@ -1,40 +0,0 @@
|
||||||
---
|
|
||||||
# Stalwart OIDC Configuration
|
|
||||||
# This ConfigMap configures Pocket ID as the OAuth provider for Stalwart Mail Server
|
|
||||||
|
|
||||||
apiVersion: v1
|
|
||||||
kind: ConfigMap
|
|
||||||
metadata:
|
|
||||||
name: stalwart-oidc-config
|
|
||||||
namespace: mail
|
|
||||||
data:
|
|
||||||
oauth.toml: |
|
|
||||||
# OAuth/OIDC Configuration for Pocket ID
|
|
||||||
[oauth]
|
|
||||||
|
|
||||||
# OAuth provider configuration
|
|
||||||
[oauth.pocket-id]
|
|
||||||
issuer-url = "https://auth.basicstack.de"
|
|
||||||
# client-id and client-secret loaded from SealedSecret: stalwart-oidc-sealed.yaml
|
|
||||||
client-id = "REPLACED_BY_SECRET"
|
|
||||||
client-secret = "REPLACED_BY_SECRET"
|
|
||||||
|
|
||||||
# OAuth endpoints (auto-discovered from issuer-url)
|
|
||||||
authorization-endpoint = "https://auth.basicstack.de/api/oidc/authorize"
|
|
||||||
token-endpoint = "https://auth.basicstack.de/api/oidc/token"
|
|
||||||
userinfo-endpoint = "https://auth.basicstack.de/api/oidc/userinfo"
|
|
||||||
|
|
||||||
# Scopes to request
|
|
||||||
scopes = ["openid", "profile", "email"]
|
|
||||||
|
|
||||||
# Callback URL (must match what's configured in Pocket ID)
|
|
||||||
redirect-uri = "https://mail.basicstack.de/login/oauth"
|
|
||||||
|
|
||||||
# User attribute mapping
|
|
||||||
[oauth.pocket-id.user-mapping]
|
|
||||||
username = "preferred_username"
|
|
||||||
email = "email"
|
|
||||||
name = "name"
|
|
||||||
---
|
|
||||||
# Secret managed via SealedSecrets
|
|
||||||
# See: stalwart-oidc-sealed.yaml
|
|
||||||
|
|
@ -1,17 +0,0 @@
|
||||||
---
|
|
||||||
apiVersion: bitnami.com/v1alpha1
|
|
||||||
kind: SealedSecret
|
|
||||||
metadata:
|
|
||||||
creationTimestamp: null
|
|
||||||
name: stalwart-oidc
|
|
||||||
namespace: stalwart
|
|
||||||
spec:
|
|
||||||
encryptedData:
|
|
||||||
client-id: AgAZPyei756I2qAxQUcZ+PPhTXxTV7pVCK3KuhZoMp6YqrN5tLYQsayRsof7SmT3rjd/kIquWLj+B66cly+JYd5+ejTeaWZV7lxpjvmX80kFa9i6GEI/4KyBczSKZFLMdFdXF4J8YCW3mmQ/Thw+FCvAM9bFcHKXOrKxTJUOR6mDNx5N94G4yc0HH2gfyvfMuzFdzmBhF01wliOCMNpL53RZ5vHc8ysIlNGdReSlovCFZNWwADqxDIGOlyIOd1/dKMsw0QVfPypdkgPGWCH94qcQe8HmzlY+Qh536+AqWLiOzMXv4aZXjO1g7URA1JUPqfrkRWlQ1HMCsiLfHEEzuMq5yNbvs1gY6rj8TBqfSEr+dgbQ2n/dBqhzpiakKxJKLUHNf/6eRipsIV8JAj1/bClH7wxJLmtWgyHkaI1IEy/hCx30K2e+g42ogfrPYo7OZSanT562t6wnzOT2aKfbcPhiBBlZx+6Sw6/aHbOu+7gd30cTm0RnfxAe4/S8GqRn24Hta1/0GK+XHzHDAv5CsDklZYS7IYBd9mtcrDP8xCFnKDAFxATwRYUoxBxbpqJdzf0sysF12GWDUbGcSDgyhukBkzox7Ym2+rh4Xq/kVUcbB+G6v8f3pZrqf/C/Adv4fZKmmAote2Z4h8aCHgLST4xIdwH0nydxurGs/+Pb8UHY2vWBhsJwblUoOU6LywN5c43ooa+AiDnGauLo/yXLsfuElbmUy6y3jPJs20J62Ea+lZR5cX4=
|
|
||||||
client-secret: AgB+khWWpOjDzt8YohAaRmwJ8789tqJD+jvGl/Sitxz9A0yNH1MABPdRa0XbwtmoZkLFAgtCozPJfodCk9HgF4sEOPudrvHdHxE9BL9Tw+ropA56EzvIIOgyvgfOUxKC5gvdiXZN7mty0lldITLMxfeY+IvbxGN4AuEgDnF3YZ0wyKj3i3IfZ47502Xj/aUYiCwO9Fz0K8iI6M5y1ThoeJq1GhHQhPK8Wcq13pJYRIg1c3TGP1PopgpCA7R2O7u6/O6csbJMeOWJpeuUDteZGu4Zr8md79knWz9QNiZR4MNeEjzpD8tfCHqKSaLoR++zqMqN2S+xCQhvBvrS+16E3qupC3BtzidJazylVW3FwrYMhRUguaBDZ7Z/6ueGt7fnaSWDH1KuVYRw+a5AEI/tlJqByWt+C3z4P7G2VAqZLvKNsP1ZUWO2aiYj8o8ROwilAWGoYyukluih2fn3XlJXQUdoFiuLs+ZUrXv2musL+68+Lzv9v+5cBzVGenfBVX5+uCoQmo7lxFR+z4eDTwRjvKrOLEwgdDcwhsWt8tRT2EvQ371SPVaGmiJAk+HN4+o5lh7fzranQ8yPR0kVYvygJwBRUkDda6asqjDo7RvPEW0xkBLSjmD0Sg5eAS8YneRpTaHlUnQadx5Gi19ygX/DPGVsCc2nzhQjwl9jfQHapCzJB3oxC62pkCZPZt9v0Oyo65gApOw/bwFScgfOkNeihNe3ab3hvu95zirUol4leW6a2Q==
|
|
||||||
template:
|
|
||||||
metadata:
|
|
||||||
creationTimestamp: null
|
|
||||||
name: stalwart-oidc
|
|
||||||
namespace: stalwart
|
|
||||||
type: Opaque
|
|
||||||
|
|
@ -1,5 +0,0 @@
|
||||||
spec:
|
|
||||||
externalIPs:
|
|
||||||
- 178.105.17.239 # k3s-cp-1 (where DNS points)
|
|
||||||
- 178.105.216.48 # k3s-worker-1
|
|
||||||
- 49.13.134.255 # k3s-worker-2
|
|
||||||
|
|
@ -1,6 +0,0 @@
|
||||||
spec:
|
|
||||||
template:
|
|
||||||
spec:
|
|
||||||
containers:
|
|
||||||
- name: stalwart
|
|
||||||
args: ["--help"]
|
|
||||||
|
|
@ -1,9 +0,0 @@
|
||||||
spec:
|
|
||||||
template:
|
|
||||||
spec:
|
|
||||||
containers:
|
|
||||||
- name: stalwart
|
|
||||||
args: []
|
|
||||||
env:
|
|
||||||
- name: STALWART_LOG_LEVEL
|
|
||||||
value: "debug"
|
|
||||||
|
|
@ -1,19 +0,0 @@
|
||||||
spec:
|
|
||||||
template:
|
|
||||||
spec:
|
|
||||||
containers:
|
|
||||||
- name: stalwart
|
|
||||||
args: ["--init"]
|
|
||||||
env:
|
|
||||||
- name: STALWART_STORAGE_DATA
|
|
||||||
value: "rocksdb"
|
|
||||||
- name: STALWART_STORAGE_BLOB
|
|
||||||
value: "rocksdb"
|
|
||||||
- name: STALWART_STORAGE_ROCKSDB_PATH
|
|
||||||
value: "/var/lib/stalwart"
|
|
||||||
- name: STALWART_SERVER_HOSTNAME
|
|
||||||
value: "mail.basicstack.de"
|
|
||||||
- name: STALWART_SERVER_HTTP_BIND
|
|
||||||
value: "[::]:8080"
|
|
||||||
- name: STALWART_LOG_LEVEL
|
|
||||||
value: "info"
|
|
||||||
|
|
@ -1,86 +0,0 @@
|
||||||
# Stalwart Helm Values - Fresh Deployment
|
|
||||||
# Domain: mail.basicstack.de
|
|
||||||
# Storage: hcloud-volumes-encrypted
|
|
||||||
|
|
||||||
image:
|
|
||||||
repository: stalwartlabs/stalwart
|
|
||||||
tag: "latest"
|
|
||||||
pullPolicy: Always
|
|
||||||
|
|
||||||
replicaCount: 1
|
|
||||||
|
|
||||||
# Enable recovery admin for initial setup
|
|
||||||
recoveryAdmin:
|
|
||||||
enabled: true
|
|
||||||
username: "admin"
|
|
||||||
password: "***REMOVED***"
|
|
||||||
|
|
||||||
# RocksDB data store
|
|
||||||
config:
|
|
||||||
"@type": RocksDb
|
|
||||||
path: /var/lib/stalwart
|
|
||||||
|
|
||||||
# LoadBalancer service to expose mail ports
|
|
||||||
service:
|
|
||||||
type: LoadBalancer
|
|
||||||
ports:
|
|
||||||
smtp: 25
|
|
||||||
smtps: 465
|
|
||||||
submission: 587
|
|
||||||
imap: 143
|
|
||||||
imaps: 993
|
|
||||||
pop3: 110
|
|
||||||
pop3s: 995
|
|
||||||
sieve: 4190
|
|
||||||
http: 80
|
|
||||||
https: 443
|
|
||||||
mgmt: 8080
|
|
||||||
|
|
||||||
# Ingress for web management interface
|
|
||||||
ingress:
|
|
||||||
enabled: true
|
|
||||||
className: traefik
|
|
||||||
annotations:
|
|
||||||
cert-manager.io/cluster-issuer: "letsencrypt-prod"
|
|
||||||
traefik.ingress.kubernetes.io/router.entrypoints: websecure
|
|
||||||
hosts:
|
|
||||||
- host: mail.basicstack.de
|
|
||||||
paths:
|
|
||||||
- path: /
|
|
||||||
pathType: Prefix
|
|
||||||
portName: mgmt
|
|
||||||
tls:
|
|
||||||
- secretName: stalwart-tls
|
|
||||||
hosts:
|
|
||||||
- mail.basicstack.de
|
|
||||||
|
|
||||||
# Persistent storage with encrypted volumes
|
|
||||||
persistence:
|
|
||||||
enabled: true
|
|
||||||
accessMode: ReadWriteOnce
|
|
||||||
storageClass: "hcloud-volumes-encrypted"
|
|
||||||
size: 20Gi
|
|
||||||
|
|
||||||
# Pod security
|
|
||||||
podSecurityContext:
|
|
||||||
fsGroup: 2000
|
|
||||||
runAsUser: 2000
|
|
||||||
runAsGroup: 2000
|
|
||||||
|
|
||||||
containerSecurityContext:
|
|
||||||
runAsNonRoot: true
|
|
||||||
allowPrivilegeEscalation: false
|
|
||||||
capabilities:
|
|
||||||
drop: [ALL]
|
|
||||||
add: [NET_BIND_SERVICE]
|
|
||||||
seccompProfile:
|
|
||||||
type: RuntimeDefault
|
|
||||||
|
|
||||||
# Resource limits
|
|
||||||
resources:
|
|
||||||
requests:
|
|
||||||
memory: "512Mi"
|
|
||||||
cpu: "250m"
|
|
||||||
limits:
|
|
||||||
memory: "2Gi"
|
|
||||||
cpu: "2000m"
|
|
||||||
|
|
@ -1,90 +0,0 @@
|
||||||
# Stalwart Helm Values - Fixed Port Configuration
|
|
||||||
# Domain: mail.basicstack.de
|
|
||||||
# Storage: hcloud-volumes-encrypted
|
|
||||||
|
|
||||||
image:
|
|
||||||
repository: stalwartlabs/stalwart
|
|
||||||
tag: "latest"
|
|
||||||
pullPolicy: Always
|
|
||||||
|
|
||||||
replicaCount: 1
|
|
||||||
|
|
||||||
# Enable recovery admin for initial setup
|
|
||||||
recoveryAdmin:
|
|
||||||
enabled: true
|
|
||||||
username: "admin"
|
|
||||||
password: "***REMOVED***"
|
|
||||||
|
|
||||||
# RocksDB data store
|
|
||||||
config:
|
|
||||||
"@type": RocksDb
|
|
||||||
path: /var/lib/stalwart
|
|
||||||
|
|
||||||
# LoadBalancer service - ONLY mail ports, NO HTTP ports (80, 443, 8080)
|
|
||||||
# HTTP access is handled by Ingress/Traefik
|
|
||||||
service:
|
|
||||||
type: LoadBalancer
|
|
||||||
ports:
|
|
||||||
# SMTP ports
|
|
||||||
smtp: 25
|
|
||||||
smtps: 465
|
|
||||||
submission: 587
|
|
||||||
# IMAP ports
|
|
||||||
imap: 143
|
|
||||||
imaps: 993
|
|
||||||
# POP3 ports (optional, can be disabled)
|
|
||||||
pop3: 110
|
|
||||||
pop3s: 995
|
|
||||||
# Sieve port
|
|
||||||
sieve: 4190
|
|
||||||
# Management port - exposed internally only, NOT on LoadBalancer
|
|
||||||
mgmt: 8080
|
|
||||||
|
|
||||||
# Ingress for web management interface via Traefik
|
|
||||||
ingress:
|
|
||||||
enabled: true
|
|
||||||
className: traefik
|
|
||||||
annotations:
|
|
||||||
cert-manager.io/cluster-issuer: "letsencrypt-prod"
|
|
||||||
traefik.ingress.kubernetes.io/router.entrypoints: websecure
|
|
||||||
hosts:
|
|
||||||
- host: mail.basicstack.de
|
|
||||||
paths:
|
|
||||||
- path: /
|
|
||||||
pathType: Prefix
|
|
||||||
portName: mgmt
|
|
||||||
tls:
|
|
||||||
- secretName: stalwart-tls
|
|
||||||
hosts:
|
|
||||||
- mail.basicstack.de
|
|
||||||
|
|
||||||
# Persistent storage with encrypted volumes
|
|
||||||
persistence:
|
|
||||||
enabled: true
|
|
||||||
accessMode: ReadWriteOnce
|
|
||||||
storageClass: "hcloud-volumes-encrypted"
|
|
||||||
size: 20Gi
|
|
||||||
|
|
||||||
# Pod security
|
|
||||||
podSecurityContext:
|
|
||||||
fsGroup: 2000
|
|
||||||
runAsUser: 2000
|
|
||||||
runAsGroup: 2000
|
|
||||||
|
|
||||||
containerSecurityContext:
|
|
||||||
runAsNonRoot: true
|
|
||||||
allowPrivilegeEscalation: false
|
|
||||||
capabilities:
|
|
||||||
drop: [ALL]
|
|
||||||
add: [NET_BIND_SERVICE]
|
|
||||||
seccompProfile:
|
|
||||||
type: RuntimeDefault
|
|
||||||
|
|
||||||
# Resource limits
|
|
||||||
resources:
|
|
||||||
requests:
|
|
||||||
memory: "512Mi"
|
|
||||||
cpu: "250m"
|
|
||||||
limits:
|
|
||||||
memory: "2Gi"
|
|
||||||
cpu: "2000m"
|
|
||||||
|
|
@ -1,110 +0,0 @@
|
||||||
# Stalwart Helm Chart Values
|
|
||||||
# Domain: mail.basicstack.de
|
|
||||||
# Storage: hcloud-volumes-encrypted
|
|
||||||
|
|
||||||
image:
|
|
||||||
repository: stalwartlabs/stalwart
|
|
||||||
tag: "latest"
|
|
||||||
pullPolicy: Always
|
|
||||||
|
|
||||||
replicaCount: 1
|
|
||||||
|
|
||||||
# Recovery admin for initial setup (disabled)
|
|
||||||
recoveryAdmin:
|
|
||||||
enabled: false
|
|
||||||
|
|
||||||
# Persistent storage with encrypted volumes
|
|
||||||
persistence:
|
|
||||||
enabled: true
|
|
||||||
storageClassName: hcloud-volumes-encrypted
|
|
||||||
accessMode: ReadWriteOnce
|
|
||||||
size: 20Gi
|
|
||||||
|
|
||||||
# Service configuration
|
|
||||||
service:
|
|
||||||
type: LoadBalancer
|
|
||||||
smtp:
|
|
||||||
enabled: true
|
|
||||||
ports:
|
|
||||||
- port: 25
|
|
||||||
name: smtp
|
|
||||||
- port: 587
|
|
||||||
name: submission
|
|
||||||
- port: 465
|
|
||||||
name: submissions
|
|
||||||
imap:
|
|
||||||
enabled: true
|
|
||||||
ports:
|
|
||||||
- port: 143
|
|
||||||
name: imap
|
|
||||||
- port: 993
|
|
||||||
name: imaps
|
|
||||||
http:
|
|
||||||
enabled: true
|
|
||||||
port: 8080
|
|
||||||
|
|
||||||
# Ingress for web UI
|
|
||||||
ingress:
|
|
||||||
enabled: true
|
|
||||||
className: traefik
|
|
||||||
annotations:
|
|
||||||
cert-manager.io/cluster-issuer: "letsencrypt-prod"
|
|
||||||
traefik.ingress.kubernetes.io/router.entrypoints: websecure
|
|
||||||
hosts:
|
|
||||||
- host: mail.basicstack.de
|
|
||||||
paths:
|
|
||||||
- path: /
|
|
||||||
pathType: Prefix
|
|
||||||
tls:
|
|
||||||
- secretName: stalwart-tls
|
|
||||||
hosts:
|
|
||||||
- mail.basicstack.de
|
|
||||||
|
|
||||||
# RocksDB configuration (default)
|
|
||||||
config:
|
|
||||||
"@type": "RocksDb"
|
|
||||||
path: "/opt/stalwart"
|
|
||||||
|
|
||||||
# Pod security context
|
|
||||||
podSecurityContext:
|
|
||||||
fsGroup: 2000
|
|
||||||
runAsUser: 2000
|
|
||||||
runAsGroup: 2000
|
|
||||||
|
|
||||||
# Container security context
|
|
||||||
containerSecurityContext:
|
|
||||||
runAsNonRoot: true
|
|
||||||
allowPrivilegeEscalation: false
|
|
||||||
capabilities:
|
|
||||||
drop: [ALL]
|
|
||||||
add: [NET_BIND_SERVICE]
|
|
||||||
seccompProfile:
|
|
||||||
type: RuntimeDefault
|
|
||||||
|
|
||||||
# Health probes
|
|
||||||
livenessProbe:
|
|
||||||
httpGet:
|
|
||||||
path: /healthz/live
|
|
||||||
port: 8080
|
|
||||||
initialDelaySeconds: 30
|
|
||||||
periodSeconds: 10
|
|
||||||
timeoutSeconds: 5
|
|
||||||
failureThreshold: 3
|
|
||||||
|
|
||||||
readinessProbe:
|
|
||||||
httpGet:
|
|
||||||
path: /healthz/ready
|
|
||||||
port: 8080
|
|
||||||
initialDelaySeconds: 10
|
|
||||||
periodSeconds: 5
|
|
||||||
timeoutSeconds: 3
|
|
||||||
failureThreshold: 3
|
|
||||||
|
|
||||||
# Resources
|
|
||||||
resources:
|
|
||||||
requests:
|
|
||||||
memory: "512Mi"
|
|
||||||
cpu: "250m"
|
|
||||||
limits:
|
|
||||||
memory: "2Gi"
|
|
||||||
cpu: "2000m"
|
|
||||||
Loading…
Add table
Reference in a new issue