Browse Source

docs: handover mailcow fetchmail sogo relay session

Co-authored-by: Cursor <cursoragent@cursor.com>
master
Budi Setiawan 2 weeks ago
parent
commit
729988d030
  1. 93
      Mailcow/.env.example
  2. 15
      Mailcow/.gitignore
  3. BIN
      Mailcow/docs/ai-session-summary.md
  4. 15
      Mailcow/docs/changelog.md
  5. 149
      Mailcow/docs/context-index.md
  6. 150
      Mailcow/docs/deployment.md
  7. 149
      Mailcow/docs/environment.md
  8. 23
      Mailcow/docs/issues.md
  9. 193
      Mailcow/docs/notes/deployment-sizing.md
  10. 248
      Mailcow/docs/notes/email-sync-migration-concept.md
  11. 298
      Mailcow/docs/notes/ldap-ad-mailcow.md
  12. 245
      Mailcow/docs/notes/storage-nas-qnap.md
  13. 74
      Mailcow/docs/project-status.md
  14. 204
      Mailcow/docs/server.md
  15. 47
      Mailcow/docs/todo.md
  16. BIN
      Mailcow/infra/nginx/sync.geonet.co.id.conf
  17. BIN
      Mailcow/scripts/fix-mailcow-dns-ufw.sh
  18. 41
      Mailcow/scripts/fix-ssh-key.ps1
  19. 33
      Mailcow/scripts/install-dockge-agent.sh
  20. 88
      Mailcow/scripts/install-fetchmail-support.sh
  21. BIN
      Mailcow/scripts/install-mailcow.sh
  22. 116
      Mailcow/scripts/prepare-mailserver.sh
  23. 35
      Mailcow/scripts/remote-reset-authorized-keys.sh
  24. 13
      Mailcow/scripts/setup-dockge-agent-user.sh
  25. 13
      Mailcow/scripts/setup-ssh-key.ps1
  26. BIN
      Workspace-Session.md

93
Mailcow/.env.example

@ -0,0 +1,93 @@
# Mailcow — template konfigurasi (copy ke .env, isi secret)
# Jangan commit .env — lihat docs/environment.md
# =============================================================================
# Core install (mailcow-dockerized generate_config.sh)
# =============================================================================
MAILCOW_HOSTNAME=sync.geonet.co.id
MAILCOW_VM_IP=10.100.1.28
MAILCOW_TIMEZONE=Asia/Jakarta
MAILCOW_PUBLIC_IP=117.102.73.102
MAILCOW_STACK_PATH=/opt/mailcow-dockerized
# API password Mailcow (generate: openssl rand -base64 32)
MAILCOW_API_KEY=
# Let's Encrypt — set y hanya untuk lab tanpa DNS publik
SKIP_LETS_ENCRYPT=n
ADDITIONAL_SAN=
# =============================================================================
# Admin UI Mailcow (https://mail.gisportal.id)
# =============================================================================
MAILCOW_ADMIN_USER=admin
MAILCOW_ADMIN_PASSWORD=
# =============================================================================
# Mailbox utama — integrasi Zammad
# =============================================================================
MAILCOW_SUPPORT_EMAIL=support@geonet.co.id
MAILCOW_SUPPORT_PASSWORD=
# =============================================================================
# Domain email
# =============================================================================
MAILCOW_DOMAIN_PRIMARY=geonet.co.id
MAILCOW_DOMAIN_SECONDARY=gegeo.my.id
# =============================================================================
# Port layanan (default Mailcow — jangan ubah kecuali ada konflik)
# =============================================================================
MAILCOW_PORT_SMTP=25
MAILCOW_PORT_SMTPS=465
MAILCOW_PORT_SUBMISSION=587
MAILCOW_PORT_IMAPS=993
MAILCOW_PORT_POP3S=995
MAILCOW_PORT_HTTPS=443
# =============================================================================
# Zammad integration (dari container Zammad → Mailcow)
# Gunakan hostname internal Docker setelah stack up
# =============================================================================
ZAMMAD_SMTP_HOST=mailcowdockerized-postfix-mailcow-1
ZAMMAD_SMTP_PORT=587
ZAMMAD_SMTP_SSL=starttls
ZAMMAD_SMTP_USER=support@geonet.co.id
ZAMMAD_SMTP_PASSWORD=
ZAMMAD_IMAP_HOST=mailcowdockerized-dovecot-mailcow-1
ZAMMAD_IMAP_PORT=993
ZAMMAD_IMAP_SSL=yes
ZAMMAD_IMAP_USER=support@geonet.co.id
ZAMMAD_IMAP_PASSWORD=
# =============================================================================
# LDAP — Active Directory (Identity Provider Mailcow)
# Panduan: docs/notes/ldap-ad-mailcow.md
# =============================================================================
LDAP_HOST=10.100.1.40
LDAP_PORT=389
LDAP_BASE_DN=DC=gisportal,DC=id
LDAP_BIND_DN=Administrator@gisportal.id
LDAP_BIND_PASSWORD=
LDAP_GROUP_DN=CN=Mail-Users,CN=Users,DC=gisportal,DC=id
LDAP_USER_FILTER=(&(objectClass=user)(objectCategory=person)(memberOf=CN=Mail-Users,CN=Users,DC=gisportal,DC=id))
LDAP_LOGIN_ATTRIBUTE=mail
LDAP_MAIL_ATTRIBUTE=mail
LDAP_SYNC_INTERVAL_MIN=15
# =============================================================================
# Legacy SMTP cPanel (sumber migrasi — Zammad pakai ini sampai Mailcow OK)
# =============================================================================
LEGACY_SMTP_HOST=sc138.idcloudhosting.cloud
LEGACY_SMTP_PORT=465
LEGACY_SMTP_SSL=yes
LEGACY_SMTP_SSL_VERIFY=yes
LEGACY_SMTP_USER=support@geonet.co.id
LEGACY_SMTP_PASSWORD=
# =============================================================================
# DNS (referensi — set di panel DNS domain, bukan di Mailcow)
# =============================================================================
DNS_MX_HOST=sync.geonet.co.id
DNS_SPF_INCLUDE=include:sync.geonet.co.id

15
Mailcow/.gitignore vendored

@ -0,0 +1,15 @@
# Environment & secrets — never commit
.env
.env.*
!.env.example
# Mailcow generated config on server
mailcow.conf
data/
# OS / editor
.DS_Store
Thumbs.db
*.swp
.idea/
.vscode/

BIN
Mailcow/docs/ai-session-summary.md

Binary file not shown.

15
Mailcow/docs/changelog.md

@ -0,0 +1,15 @@
# Changelog — Mailcow
---
## 2026-07-03 — fetchmail, SOGo, outbound relay
**Ringkasan:** Email flow support@geonet.co.id end-to-end via Mailcow.
**Fitur:** fetchmail POP3 drain; relay cPanel :587; SOGo operational.
**Fix:** SOGo Unauthorized; Gmail 550 smarthost.
**File:** scripts/install-fetchmail-support.sh, docs/*
**Author:** AI Agent (Cursor)

149
Mailcow/docs/context-index.md

@ -0,0 +1,149 @@
# Context Index — Mailcow ⭐ AI Entry Point
> **Terakhir Diperbarui:** 2026-07-03
> **Fase Aktif:** Fase 1 — Mailcow live, transisi MX cPanel → sync 📧
**Sesi terakhir:** [docs/ai-session-summary.md](./ai-session-summary.md)
---
## Filosofi
Proyek ini menerapkan **AI Native Development**:
- Repository = sumber kebenaran utama (Single Source of Truth)
- AI tidak boleh mengandalkan riwayat percakapan
- Developer sering berpindah perangkat — konteks harus tersimpan di repo
- Implementasi tanpa dokumentasi = belum selesai
---
## Urutan Membaca
1. **File ini** — peta navigasi
2. **`docs/ai-session-summary.md`** — ringkasan sesi terakhir ⭐
3. `docs/environment.md` — variabel `.env` (tanpa secret)
3. `.env.example` → copy ke `.env` — isi password & secret
4. Dokumen spesifik task (lihat tabel di bawah)
5. `Zammad/docs/context-index.md` — integrasi email channel
---
## Posisi dalam Super Apps
```
Internet → MX geonet.co.id → cPanel (staging, inbox kosong setelah POP3)
fetchmail POP3 + delete (VM .27)
Mailcow sync.geonet.co.id — inbox canonical (IMAP)
│ │
Outlook user Zammad
IMAP+SMTP sync IMAP+SMTP sync
```
| Repo terkait | Entry point | Kapan baca |
|--------------|-------------|------------|
| **Zammad** | `Zammad/docs/context-index.md` | Channel email inbound/outbound |
| **server-connection** | `server-connection/docs/context-index.md` | LDAP AD, nginx pattern |
| **mysupperapps_FE_PWA** | `docs/context-index.md` | Mail UI `/mail` (fase berikutnya) |
---
## Peta Dokumentasi
### `docs/` — Flat Files
| File | Isi | Status | Baca Saat |
|------|-----|--------|-----------|
| `docs/context-index.md` | **File ini** — navigasi | ✅ | Selalu |
| `docs/environment.md` | Env vars, port, integrasi Zammad | ✅ | Setup, deploy |
| `docs/project-status.md` | Status modul, milestone | ✅ | Planning |
| `docs/ai-session-summary.md` | **Ringkasan sesi terakhir** | ✅ | Lanjut kerja |
| `docs/roadmap.md` | Fase Mailcow dalam super apps | 📋 | Planning |
| `docs/todo.md` | Next actions berprioritas | ✅ | Sebelum coding |
| `docs/changelog.md` | Riwayat perubahan penting | ✅ | Referensi |
| `docs/issues.md` | Known bugs + workaround | ✅ | Debug |
| `docs/server.md` | Server, SSH, spec VM `.28` | ✅ | Deploy |
| `docs/deployment.md` | Step-by-step install Mailcow | ✅ | Deploy |
| `docs/security.md` | Credential, DNS, port 25 | 📋 | Security |
### `docs/notes/`
| File | Isi |
|------|-----|
| `email-sync-migration-concept.md` | **Konsep sync hosting → Mailcow → Zammad** (MX tetap cPanel) |
| `zammad-email-channel.md` | Step-by-step SMTP + IMAP Zammad ↔ Mailcow |
| `dns-cutover-geonet-co-id.md` | MX, SPF, DKIM, DMARC cutover dari cPanel |
| `storage-nas-qnap.md` | **Email di NAS QNAP** — iSCSI / NFS / backup |
| `deployment-sizing.md` | **Linux + Docker, spec VM 30+ user** |
| `ldap-ad-mailcow.md` | **LDAP AD → Mailcow** — auth + provisioning user terpusat |
### Root repo
| File | Isi |
|------|-----|
| `.env.example` | Template konfigurasi — **aman di-commit** |
| `.env` | Nilai aktual (password) — **gitignored** |
| `.gitignore` | Exclude secret & data Mailcow |
| `scripts/install-fetchmail-support.sh` | Install POP3 drain + systemd timer |
---
## Quick Reference
| Fungsi | URL / Path |
|--------|------------|
| Mailcow UI (target) | `https://sync.geonet.co.id` |
| Hostname mail | `sync.geonet.co.id` |
| Mailbox support (fase sync) | `support@sync.geonet.co.id` |
| Mailbox produksi (hosting) | `support@geonet.co.id` @ cPanel |
| Domain utama | `geonet.co.id` (MX tetap hosting) |
| Docker stack (target) | `/opt/stacks/mailcow/` |
| Server Mailcow | `10.100.1.28` (16 vCPU, 32 GB, 2×512 GB SSD) |
| Server Zammad | `10.100.1.24` |
### Port layanan
| Port | Layanan |
|------|---------|
| 25 | SMTP inbound |
| 465 | SMTPS |
| 587 | Submission (Zammad outbound) |
| 993 | IMAPS (Zammad inbound) |
| 443 | HTTPS Web UI |
### SSH
```bash
ssh mailcow # 10.100.1.28
```
> SSH ke server **wajib minta persetujuan user** sebelum dijalankan.
---
## Status saat ini (2026-07-03)
| Item | Status |
|------|--------|
| Mailcow stack `/opt/stacks/mailcow` | ✅ |
| Domain `geonet.co.id` + DKIM generated | ✅ |
| fetchmail POP3 drain `support@` | ✅ timer 2 menit |
| Outbound relay cPanel `:587` | ✅ transisi |
| SOGo kirim/terima `support@` | ✅ verified user |
| LDAP IdP (user AD lain) | ✅ |
| Zammad → Mailcow channel | ❌ |
| DKIM/SPF DNS publish | ❌ |
| MX cutover ke sync | ❌ |
Detail: [project-status.md](./project-status.md)
---
## Aturan Utama AI
1. Baca `docs/context-index.md` ini terlebih dahulu
2. **Jangan commit** `.env` — hanya update `.env.example` (placeholder) dan `docs/environment.md`
3. Integrasi Zammad → referensi `Zammad/docs/` — jangan duplikasi panjang
4. LDAP umum → referensi `server-connection/docs/environment.md`
5. Setelah deploy → buat/update `docs/deployment.md` + `docs/project-status.md`

150
Mailcow/docs/deployment.md

@ -0,0 +1,150 @@
# Deployment — Mailcow
> **Terakhir Diperbarui:** 2026-07-03
> **Target VM:** `10.100.1.28` · `sync.geonet.co.id`
Step-by-step install — **Mailcow + nginx proxy selesai 2026-07-03**.
---
## Prasyarat
- [x] VM spec diset (16 vCPU, 32 GB RAM, 2×512 GB SSD)
- [x] Keputusan storage: local SSD vmail + backup QNAP
- [x] Grup AD `Mail-Users` + `support@geonet.co.id`
- [x] VM `10.100.1.28` provisioned & SSH OK
- [x] DNS A `sync.geonet.co.id``117.102.73.102`
- [x] NAT mail `25/465/587/993``.28`
- [x] NAT `80/443``.24` (nginx)
- [x] Nginx vhost `sync.geonet.co.id` → proxy ke `.28:443` + certbot LE
- [ ] PTR `117.102.73.102``sync.geonet.co.id`
Lihat `docs/server.md` untuk detail VM.
---
## 1 — OS & disk
1. Install **Debian 12** atau **Ubuntu 22.04 LTS**
2. Disk 2 → format `ext4` → mount `/data/mailcow/vmail`
3. Tambah ke `/etc/fstab`
4. Swap 4 GiB
```bash
# Contoh mount disk 2 (sesuaikan device)
mkfs.ext4 /dev/sdb
mkdir -p /data/mailcow/vmail
echo '/dev/sdb /data/mailcow/vmail ext4 defaults 0 2' >> /etc/fstab
mount -a
```
---
## 2 — Docker
```bash
apt update && apt install -y ca-certificates curl gnupg
# Install Docker CE + compose plugin (lihat docs.docker.com)
docker --version # >= 24
docker compose version # >= 2
```
---
## 3 — Install Mailcow
```bash
cd /opt/stacks
git clone https://github.com/mailcow/mailcow-dockerized mailcow
cd mailcow
./generate_config.sh
# Hostname: sync.geonet.co.id
# Timezone: Asia/Jakarta
```
Edit `mailcow.conf`:
```bash
# Port 80/443 publik masuk nginx .24 — bukan langsung ke Mailcow
SKIP_LETS_ENCRYPT=y
```
Opsi lain: DNS-01 challenge di Mailcow jika ingin cert internal Mailcow (tanpa nginx SSL).
> Stack path `/opt/stacks/mailcow` agar terlihat di Dockge agent `.28`.
---
## 4 — Bind vmail ke disk 2
Buat `docker-compose.override.yml`:
```yaml
volumes:
vmail-vol-1:
driver: local
driver_opts:
type: none
device: /data/mailcow/vmail
o: bind
```
```bash
docker compose pull
docker compose up -d
```
---
## 4b — Nginx reverse proxy (`.24`)
Karena `80/443` publik masuk ke nginx, buat vhost `sync.geonet.co.id`:
```nginx
upstream mailcow_backend {
server 10.100.1.28:443;
}
server {
listen 80;
server_name sync.geonet.co.id;
return 301 https://$host$request_uri;
}
server {
listen 443 ssl;
server_name sync.geonet.co.id;
include /etc/nginx/conf.d/ssl.conf; # atau certbot sync.geonet.co.id
proxy_pass https://mailcow_backend;
proxy_ssl_verify off;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_read_timeout 3600s;
client_max_body_size 50M;
}
```
Cert publik: `certbot --nginx -d sync.geonet.co.id` di `.24`.
---
## 5 — Post-install
1. Login admin UI `https://sync.geonet.co.id` *(via nginx `.24`)*
2. Tambah domain `geonet.co.id`
3. Identity Provider → LDAP → AD `10.100.1.40`
4. fetchmail POP3 drain dari cPanel
5. Backup cron → QNAP
6. Zammad channel IMAP/SMTP → `10.100.1.28`
---
## Referensi
- [Mailcow Install](https://docs.mailcow.email/getstarted/install/)
- [Move vmail volume](https://docs.mailcow.email/manual-guides/Dovecot/u_e-dovecot-vmail-volume/)
- `docs/notes/ldap-ad-mailcow.md`
- `docs/notes/email-sync-migration-concept.md`

149
Mailcow/docs/environment.md

@ -0,0 +1,149 @@
# Environment Variables — Mailcow
> **Terakhir Diperbarui:** 2026-07-03
> ⚠ **Jangan commit `.env`** — hanya `.env.example` di repo. Nilai secret di server: `/opt/stacks/mailcow/.env`
---
## Core install
| Variable | Contoh | Keterangan |
|----------|--------|------------|
| `MAILCOW_HOSTNAME` | `sync.geonet.co.id` | FQDN utama Mailcow (Let's Encrypt) |
| `MAILCOW_VM_IP` | `10.100.1.28` | IP LAN VM Mailcow |
| `MAILCOW_TIMEZONE` | `Asia/Jakarta` | Timezone container |
| `MAILCOW_PUBLIC_IP` | *(IP publik NAT)* | IP publik untuk MX/PTR — set di DNS |
| `MAILCOW_STACK_PATH` | `/opt/mailcow-dockerized` | Path deploy di VM `.27` |
| `MAILCOW_API_KEY` | *(generate)* | Password API Mailcow (`mailcow.conf` → `MAILCOW_PASS`) |
| `SKIP_LETS_ENCRYPT` | `n` | `y` hanya lab tanpa DNS |
| `ADDITIONAL_SAN` | | Hostname tambahan di cert (opsional) |
---
## Admin UI
| Variable | Contoh | Keterangan |
|----------|--------|------------|
| `MAILCOW_ADMIN_USER` | `admin` | User default panel Mailcow |
| `MAILCOW_ADMIN_PASSWORD` | **SECRET** | Password admin — set saat install / reset di UI |
---
## Mailbox support (Zammad)
| Variable | Contoh | Keterangan |
|----------|--------|------------|
| `MAILCOW_SUPPORT_EMAIL` | `support@geonet.co.id` | Mailbox inbound/outbound Zammad |
| `MAILCOW_SUPPORT_PASSWORD` | **SECRET** | Password mailbox — sama untuk SMTP & IMAP di Zammad |
---
## Domain
| Variable | Contoh | Keterangan |
|----------|--------|------------|
| `MAILCOW_DOMAIN_PRIMARY` | `geonet.co.id` | Domain utama |
| `MAILCOW_DOMAIN_SECONDARY` | `gegeo.my.id` | Domain tambahan (multi-domain) |
---
## Port layanan (default Mailcow)
| Variable | Port | Protokol | Keterangan |
|----------|------|----------|------------|
| `MAILCOW_PORT_SMTP` | 25 | SMTP | Inbound dari internet — cek firewall & provider |
| `MAILCOW_PORT_SMTPS` | 465 | SMTPS | Implicit SSL |
| `MAILCOW_PORT_SUBMISSION` | 587 | STARTTLS | **Disarankan** untuk Zammad outbound |
| `MAILCOW_PORT_IMAPS` | 993 | IMAPS | **Zammad inbound** fetch email |
| `MAILCOW_PORT_POP3S` | 995 | POP3S | Opsional |
| `MAILCOW_PORT_HTTPS` | 443 | HTTPS | Web UI + API |
---
## Integrasi Zammad
Setelah Mailcow running, konfigurasi di **Zammad → Channels → Email**:
### Outbound (notifikasi tiket)
| Variable | Nilai | Keterangan |
|----------|-------|------------|
| `ZAMMAD_SMTP_HOST` | `mailcowdockerized-postfix-mailcow-1` | Hostname container Postfix (dari jaringan Docker Zammad) |
| `ZAMMAD_SMTP_PORT` | `587` | STARTTLS |
| `ZAMMAD_SMTP_SSL` | `starttls` | |
| `ZAMMAD_SMTP_USER` | `support@geonet.co.id` | |
| `ZAMMAD_SMTP_PASSWORD` | **SECRET** | Sama dengan `MAILCOW_SUPPORT_PASSWORD` |
> Alternatif dari host: `127.0.0.1:587` jika port di-bind ke localhost + nginx tidak mem-proxy SMTP.
### Inbound (email → tiket)
| Variable | Nilai | Keterangan |
|----------|-------|------------|
| `ZAMMAD_IMAP_HOST` | `mailcowdockerized-dovecot-mailcow-1` | Container Dovecot |
| `ZAMMAD_IMAP_PORT` | `993` | SSL |
| `ZAMMAD_IMAP_SSL` | `yes` | |
| `ZAMMAD_IMAP_USER` | `support@geonet.co.id` | |
| `ZAMMAD_IMAP_PASSWORD` | **SECRET** | |
**Ref Zammad:** `Zammad/docs/notes/smtp-notification-setup.md`, `Zammad/docs/roadmap.md` Fase 9
---
## LDAP (Active Directory)
Mailcow **bisa** pakai LDAP `10.100.1.40` — sama dengan geonet-console & Zammad.
| Variable | Nilai |
|----------|-------|
| `LDAP_HOST` | `10.100.1.40` |
| `LDAP_PORT` | `389` |
| `LDAP_BASE_DN` | `DC=gisportal,DC=id` |
| `LDAP_BIND_DN` | `Administrator@gisportal.id` |
| `LDAP_BIND_PASSWORD` | **SECRET** — reuse geonet-console |
**Panduan lengkap:** `docs/notes/ldap-ad-mailcow.md`
| Terpusat di AD | Tetap di Mailcow UI |
|----------------|---------------------|
| Password, nama, on/off user | Domain, alias, template quota |
| Grup siapa dapat mailbox | Admin panel (SQL only) |
| Atribut `mail``@geonet.co.id` | fetchmail POP3 drain |
---
## Legacy SMTP (migrasi dari cPanel)
Zammad saat ini memakai hosting idcloudhosting — tetap didokumentasikan sampai cutover Mailcow.
| Variable | Nilai |
|----------|-------|
| `LEGACY_SMTP_HOST` | `sc138.idcloudhosting.cloud` |
| `LEGACY_SMTP_PORT` | `465` |
| `LEGACY_SMTP_SSL` | `yes` |
| `LEGACY_SMTP_SSL_VERIFY` | `yes` |
| `LEGACY_SMTP_USER` | `support@geonet.co.id` |
| `LEGACY_SMTP_PASSWORD` | **SECRET** |
Error historis DNS container: `Zammad/docs/notes/email-geonet-co-id.md`
---
## DNS (panel domain — bukan env Mailcow)
| Variable | Contoh | Record |
|----------|--------|--------|
| `DNS_MX_HOST` | `mail.gisportal.id` | MX → IP publik server |
| `DNS_SPF_INCLUDE` | `include:mail.gisportal.id` | TXT SPF |
| — | *(dari Mailcow UI)* | TXT DKIM |
| — | | TXT DMARC |
---
## Lokasi secret
| Secret | Lokasi |
|--------|--------|
| Mailcow `.env` (repo lokal) | `Mailcow/.env` — gitignored |
| Mailcow di production | `/opt/stacks/mailcow/mailcow.conf` + `.env` |
| Zammad SMTP saat ini | UI Zammad + `Zammad/docs/notes/smtp-notification-setup.md` |

23
Mailcow/docs/issues.md

@ -0,0 +1,23 @@
# Issues — Mailcow
---
## Open Issues
### ISS-001: _sogo_static_view tidak auto-sync setelah edit password
| Field | Value |
|-------|-------|
| Status | Open |
| Severity | Medium |
| Found | 2026-07-03 |
**Workaround:** UPDATE _sogo_static_view FROM mailbox.password; restart sogo container.
---
## Resolved Issues
### ISS-003: SOGo Unauthorized — authsource mailcow + sync view (2026-07-03)
### ISS-004: Gmail 550 — relay sc138:587 (2026-07-03)
### ISS-005: fetchmailrc parse — ssl after user, no sslcertck (2026-07-03)

193
Mailcow/docs/notes/deployment-sizing.md

@ -0,0 +1,193 @@
# Deployment & Sizing — Mailcow (jangka panjang)
> **Terakhir Diperbarui:** 2026-07-03
> **Target:** ~30+ user email aktif · hostname `sync.geonet.co.id` · VM **`10.100.1.28`**
> **Keputusan storage:****Local SSD VM** (prioritas uptime) + backup QNAP harian
---
## Linux atau Docker?
**Keduanya — bukan pilihan salah satu.**
| Lapisan | Teknologi | Keterangan |
|---------|-----------|------------|
| **Host OS** | Linux (VM) | Wajib — Debian 12 / Ubuntu 22.04 LTS / Alma 9 |
| **Aplikasi mail** | **Docker Compose** | Mailcow hanya didistribusikan sebagai `mailcow-dockerized` |
| **Windows** | ❌ | Tidak didukung sebagai host Mailcow |
| **Install native tanpa Docker** | ❌ | Tidak ada paket Mailcow untuk Linux biasa |
Alur install resmi:
```
Proxmox (.102) → VM Linux → Docker + Compose → git clone mailcow-dockerized → ./generate_config.sh → docker compose up -d
```
**Kesimpulan:** VM **Linux dedicated** yang menjalankan stack **Docker** Mailcow.
---
## Rekomendasi proper (30+ user aktif)
### Jangan taruh Mailcow di server `.24`
Server `10.100.1.24` sudah penuh microservice (Zammad, geonet-console, Grafana, n8n, …). Mail server butuh:
- Port 25/465/587/993 stabil
- RAM predictable
- Isolasi jika satu service restart tidak ganggu mail
**Taruh di VM dedicated `10.100.1.28`** — terpisah dari app server `.24`.
### Spesifikasi VM (diset 2026-07-03)
| Resource | Nilai |
|----------|-------|
| **IP** | `10.100.1.28` |
| **vCPU** | 16 core |
| **RAM** | 32 GiB |
| **Disk 1** | 512 GiB SSD — OS + Docker + MariaDB + Redis |
| **Disk 2** | 512 GiB SSD — vmail live |
> Spec ini **melampaui** minimum resmi Mailcow — sangat nyaman untuk 30+ user dan headroom growth.
### Jangan Proxmox LXC + Docker (untuk production)
| Opsi | Produksi mail? |
|------|----------------|
| **VM Linux penuh** | ✅ Disarankan |
| LXC nested Docker | ⚠ Bisa, tapi kurang bersih untuk mail jangka panjang |
---
## Spesifikasi VM (rekomendasi → **diset user**)
Untuk **30+ mailbox aktif** — nilai **tebal** = yang akan dipakai:
| Resource | Minimum | Rekomendasi doc | **VM `.27` (final)** |
|----------|---------|-----------------|----------------------|
| **vCPU** | 2 | 4 | **16** |
| **RAM** | 8 GiB | 12–16 GiB | **32 GiB** |
| **Disk OS+DB** | 40 GiB | 60 GiB | **512 GiB SSD** |
| **Disk vmail** | 100 GiB | 200–300 GiB | **512 GiB SSD** |
| **Swap** | 2 GiB | 4 GiB | **4 GiB** |
### Perkiraan storage email
| Asumsi | Per user/tahun | 30 user × 3 tahun |
|--------|----------------|-------------------|
| Ringan (5 GB/user) | 5 GB | ~150 GB |
| Sedang (10 GB/user) | 10 GB | ~300 GB |
| Berat (attachment) | 20 GB+ | 600 GB+ |
Mount `mailcow` data (`./data`, terutama `vmail`) di **disk/volume terpisah** agar mudah expand & backup.
---
## OS & software host
| Item | Pilihan |
|------|---------|
| OS | **Debian 12** atau **Ubuntu 22.04 LTS** |
| Docker | ≥ 24.x |
| Docker Compose | **Plugin v2** (≥ 2.0) |
| Firewall | `ufw` / nftables — buka 25, 80, 443, 465, 587, 993, 995 |
| Hostname VM | `sync.geonet.co.id` |
| PTR (reverse DNS) | IP publik → `sync.geonet.co.id` *(wajib untuk deliverability)* |
---
## Optimasi `mailcow.conf` (30 user, stabil)
Default Mailcow memuat banyak komponen. Untuk 30 user dengan Outlook (bukan ActiveSync massal):
| Setting | Rekomendasi | Alasan |
|---------|-------------|--------|
| `SKIP_CLAMD` | `n` jika RAM ≥12 GiB, `y` jika ketat | ClamAV ~1 GiB RAM |
| `SKIP_FTS` / Solr | `y` *(opsional)* | Hemat RAM jika tidak pakai search SOGo berat |
| SOGo | Tetap default | Webmail cadangan; Outlook user utama |
| Redis maxmemory | Set batas di override | Cegah Redis membengkak |
Resmi Mailcow: [Prerequisite System](https://docs.mailcow.email/getstarted/prerequisite-system/)
---
## Arsitektur target di `.102`
```
VM mail-sync — 10.100.1.28
├── 16 vCPU, 32 GiB RAM, 4 GiB swap
├── Disk 1: 512 GiB SSD (OS + Docker + MariaDB + Redis)
├── Disk 2: 512 GiB SSD (vmail live — tidak via NAS)
├── Docker: mailcow-dockerized
│ └── MAILCOW_HOSTNAME=sync.geonet.co.id
├── fetchmail (POP3 drain cPanel → Mailcow)
└── Backup cron → QNAP 10.100.1.10
```
**Koneksi ke layanan lain:**
| Dari | Ke | Port |
|------|-----|------|
| VM Mailcow | AD `10.100.1.40` | 389 LDAP |
| VM Mailcow | Internet | 995 POP3 (cPanel drain) |
| Zammad `.24` | VM Mailcow `.27` | 993 IMAP, 587 SMTP |
| User Outlook | `sync.geonet.co.id` | 993 / 587 |
---
## Backup jangka panjang (wajib)
| Item | Cara |
|------|------|
| Mailcow built-in | `helper-scripts/backup_and_restore.sh` |
| Jadwal | Harian → QNAP NFS/SMB atau rsync |
| Retensi | 7 harian + 4 mingguan |
| Uji restore | Kuartalan |
Tanpa backup, VM mail = single point of failure data.
---
## Monitoring
| Cek | Tool |
|-----|------|
| Container health | `docker compose ps` + Uptime Kuma |
| Disk vmail | Alert >80% |
| fetchmail | Log + alert jika cPanel inbox menumpuk |
| Queue Postfix | `mailq` |
| LDAP sync | Mailcow crontask logs |
---
## Yang **tidak** disarankan
| Pilihan | Kenapa |
|---------|--------|
| Mailcow di `.24` bersama 15+ container | RAM/port conflict, restart cascade |
| RAM 6 GiB untuk 30 user aktif | OOM / swap thrashing |
| 1 disk kecil tanpa pisah vmail | Sulit expand & backup |
| Skip PTR / SPF / DKIM | Email masuk spam |
| Thunderbird di PC sebagai bridge POP3 | Tidak 24/7 — pakai fetchmail di VM |
---
## Checklist sebelum install
- [ ] VM di Proxmox `.102` dengan spec di atas
- [ ] DNS A `sync.geonet.co.id` → IP publik
- [ ] PTR record ke `sync.geonet.co.id`
- [ ] Port 25 outbound/inbound dicek (provider tidak blokir)
- [ ] Firewall MikroTik NAT 25, 443, 465, 587, 993
- [ ] Volume data mail terpisah
- [ ] Rencana backup ke QNAP
---
## Referensi
- [Mailcow — Prepare your system](https://docs.mailcow.email/getstarted/prerequisite-system/)
- [Mailcow — Install](https://docs.mailcow.email/getstarted/install/)
- `docs/notes/email-sync-migration-concept.md`
- `docs/notes/ldap-ad-mailcow.md`

248
Mailcow/docs/notes/email-sync-migration-concept.md

@ -0,0 +1,248 @@
# Konsep — POP3 Drain cPanel → Mailcow (inbox tunggal)
> **Terakhir Diperbarui:** 2026-07-03
> **Status:** Rencana arsitektur — **keputusan user**
> **Konteks:** MX `geonet.co.id` tetap cPanel; inbox canonical di Mailcow `sync.geonet.co.id`
---
## Ringkasan keputusan
| Aspek | Rencana |
|-------|---------|
| MX `geonet.co.id` | Tetap cPanel (belum cutover) |
| Inbox cPanel | **Kosong** — hanya staging sementara |
| POP3 drain | VM/service tarik POP3 dari cPanel, **hapus** setelah sukses |
| Inbox canonical | Mailcow `sync.geonet.co.id` (IMAP) |
| Outlook user | IMAP + SMTP **hanya** `sync.geonet.co.id` |
| Zammad | IMAP + SMTP ke Mailcow (mailbox sama dengan user) |
| Mailcow host | `10.100.1.28` |
**Prinsip:** cPanel hanya pintu masuk internet; **semua orang baca/tulis email lewat Mailcow**.
---
## Diagram alur
```mermaid
flowchart TB
subgraph internet [Internet]
S[Pengirim / penerima balasan]
end
subgraph cpanel [cPanel — staging kosong]
MX[MX geonet.co.id]
STG[(Inbox cPanel\nkosong setelah POP3)]
end
subgraph vm [VM 10.100.1.28]
FM[fetchmail / getmail\nPOP3 drain + delete]
MC[Mailcow sync.geonet.co.id]
MBX[(Inbox IMAP\ncanonical)]
end
subgraph clients [Klien]
OL[Outlook user\nIMAP + SMTP]
Z[Zammad\nIMAP + SMTP]
end
S -->|email baru| MX --> STG
STG -->|POP3 :995\nhapus setelah OK| FM -->|SMTP inject :587| MC --> MBX
MBX -->|IMAP :993| OL
MBX -->|IMAP poll| Z
OL -->|SMTP :587| MC
Z -->|SMTP :587| MC
MC -->|kirim ke internet| S
```
---
## Peran masing-masing komponen
### 1. cPanel (`mail.geonet.co.id`)
| Fungsi | Detail |
|--------|--------|
| MX record | Masih menunjuk ke hosting — email dari internet masuk sini dulu |
| Inbox | **Staging** — harus kosong setelah POP3 drain berjalan |
| User Outlook | **Tidak** connect ke cPanel lagi |
| POP3 | `mail.geonet.co.id:995` SSL — hanya untuk service VM |
### 2. VM — POP3 drain (fetchmail)
Service di server (bisa di VM yang sama dengan Mailcow), **bukan** di PC user.
| Setting | Nilai |
|---------|-------|
| Protokol sumber | POP3 SSL `:995` |
| Host | `mail.geonet.co.id` |
| User | `nama@geonet.co.id` (per mailbox) |
| Setelah sukses | **Hapus** dari cPanel (`fetchall` + tidak `keep`) |
| Tujuan | SMTP ke Mailcow `127.0.0.1:587` atau `sync.geonet.co.id:587` |
| Jadwal | Cron/systemd tiap **1–2 menit** |
**Contoh `fetchmailrc` (produksi di `/opt/fetchmail/fetchmailrc`):**
```
poll mail.geonet.co.id with proto POP3 port 995
user "support@geonet.co.id" there with password "..." is "support@geonet.co.id" here
ssl
no sslcertck
smtphost 127.0.0.1
smtpname sync.geonet.co.id
fetchall
nokeep
```
> Password dengan `#` atau `$` harus di-escape (generate via Python `repr()`).
> `ssl` harus **setelah** baris `user`, bukan sebelum.
> `no sslcertck` — cert cPanel `*.idcloudhosting.cloud``mail.geonet.co.id`.
> SMTP inject ke Mailcow via `127.0.0.1:25` (tanpa auth).
> Install: `scripts/install-fetchmail-support.sh` + systemd timer 2 menit.
### 3. Mailcow (`sync.geonet.co.id`)
| Fungsi | Detail |
|--------|--------|
| Hostname | `sync.geonet.co.id` |
| Inbox canonical | Semua pesan hidup di sini |
| Domain mailbox | Tambah `geonet.co.id` di Mailcow agar alamat tetap `user@geonet.co.id` |
| Dovecot IMAP | `:993` — Outlook & Zammad |
| Postfix SMTP | `:587` — kirim balasan |
### 4. Outlook user
Satu akun saja — **tidak** ada akun cPanel.
| Setting | Nilai |
|---------|-------|
| **Incoming** | IMAP |
| Server | `sync.geonet.co.id` |
| Port | `993` |
| Encryption | SSL/TLS |
| **Outgoing** | SMTP |
| Server | `sync.geonet.co.id` |
| Port | `587` (STARTTLS) atau `465` (SSL) |
| Auth | Ya — username = email penuh |
| Username | `nama@geonet.co.id` |
### 5. Zammad
Sama seperti Outlook — satu mailbox di Mailcow:
| Arah | Konfigurasi |
|------|-------------|
| Inbound | IMAP `sync.geonet.co.id:993``support@geonet.co.id` |
| Outbound | SMTP `sync.geonet.co.id:587``support@geonet.co.id` |
---
## Mengapa POP3 drain (bukan imapsync mirror)
| | imapsync (salin) | POP3 drain (pilihan user) |
|--|------------------|---------------------------|
| Inbox cPanel | Masih berisi salinan | **Kosong** |
| Sumber kebenaran | Dua tempat | **Satu** — Mailcow saja |
| Outlook connect ke | Harus Mailcow saja juga | Mailcow saja ✅ |
| Risiko duplikat | Ada jika lupa aturan | Rendah |
Cocok dengan tujuan: **user hanya tahu `sync.geonet.co.id`**, cPanel tidak dipakai sehari-hari.
---
## SPF / DKIM outbound (penting)
Balasan dari Mailcow dengan `From: user@geonet.co.id` butuh DNS mengizinkan server `.102`:
| Record | Fase transisi | Fase akhir |
|--------|---------------|------------|
| SPF `geonet.co.id` | Tambah IP publik `.102` **atau** relay SMTP cPanel sebagai smarthost | Mailcow only |
| DKIM | Generate di Mailcow, publish TXT | Sama |
| DMARC | Opsional awal | Disarankan |
**Opsi jika belum siap SPF:**
- Mailcow **smarthost**`sc138.idcloudhosting.cloud:465` untuk outbound `@geonet.co.id`
- Atau Zammad SMTP tetap hosting sementara, Outlook SMTP Mailcow (campuran — kurang ideal)
**Target user:** Outlook incoming **dan** outgoing `sync.geonet.co.id` → siapkan SPF/DKIM `geonet.co.id` untuk IP Mailcow.
---
## Multi-mailbox
Setiap `user@geonet.co.id` di cPanel perlu:
1. Entry fetchmail POP3 sendiri (atau satu script loop)
2. Mailbox matching di Mailcow
3. Outlook user connect ke Mailcow
Contoh tim:
| cPanel POP3 | Mailcow mailbox | Klien |
|-------------|-----------------|-------|
| `support@geonet.co.id` | `support@geonet.co.id` | Zammad + Outlook IT |
| `info@geonet.co.id` | `info@geonet.co.id` | Outlook admin |
| `nama@geonet.co.id` | `nama@geonet.co.id` | Outlook user |
---
## Risiko & mitigasi
| Risiko | Dampak | Mitigasi |
|--------|--------|----------|
| fetchmail mati | Email menumpuk di cPanel, user tidak lihat | Monitor cron + alert (Uptime Kuma / script) |
| Latency 1–2 menit | Email tidak instant di Outlook | Kurangi interval cron; terima sebagai trade-off |
| POP3 gagal mid-batch | Pesan bisa hilang jika delete sebelum SMTP OK | Pakai fetchmail `nokeep` hanya setelah SMTP sukses (default behavior) |
| Port 25 blokir | Outbound Mailcow gagal | Smarthost cPanel atau port 587 |
| VM `.102` down | Semua mail flow stop | HA / backup VM; monitor |
---
## Perbedaan dengan pola Thunderbird manual
| | Thunderbird di PC | fetchmail di VM |
|--|-------------------|-----------------|
| POP3 drain | User PC harus nyala | Server 24/7 |
| Deliver ke Mailcow | Manual / filter | Otomatis SMTP inject |
| Outlook user | Hanya IMAP Mailcow | Sama ✅ |
Thunderbird **tidak** dipakai untuk bridge — hanya Outlook (atau client lain) ke Mailcow.
---
## Roadmap
| Tahap | Aksi |
|-------|------|
| **1** | Install Mailcow di VM `.102`, hostname `sync.geonet.co.id` |
| **2** | Tambah domain `geonet.co.id` + buat mailbox |
| **3** | Deploy fetchmail POP3 drain per mailbox |
| **4** | Kosongkan inbox cPanel (tes drain) |
| **5** | Konfigurasi Outlook user → IMAP/SMTP Mailcow |
| **6** | Zammad IMAP + SMTP Mailcow |
| **7** | SPF/DKIM untuk outbound `@geonet.co.id` |
| **8** | Stabil 2–4 minggu |
| **9** | Cutover MX → `sync.geonet.co.id`, matikan fetchmail |
---
## Checklist
- [ ] VM `10.100.1.28` provisioned (16 vCPU, 32 GB, 2×512 GB)
- [ ] DNS A `sync.geonet.co.id` → IP publik
- [ ] Mailcow + domain `geonet.co.id`
- [x] fetchmail + timer 2 menit (`support@geonet.co.id`) — `scripts/install-fetchmail-support.sh`
- [ ] Verifikasi inbox cPanel kosong setelah drain
- [ ] Outlook: IMAP+SMTP `sync.geonet.co.id`
- [ ] Zammad channel email → Mailcow
- [ ] SPF/DKIM `geonet.co.id`
- [ ] Monitoring fetchmail
---
## Referensi
- `Mailcow/docs/environment.md`
- `Zammad/docs/notes/email-geonet-co-id.md`
- `fetchmail` man: https://www.fetchmail.info/

298
Mailcow/docs/notes/ldap-ad-mailcow.md

@ -0,0 +1,298 @@
# LDAP — Mailcow ke Active Directory
> **Terakhir Diperbarui:** 2026-07-03
> **AD:** `10.100.1.40` · domain `gisportal.id`
> **Status:** Grup AD siap ✅ · LDAP Identity Provider aktif ✅ (2026-07-03)
> ⚠ Bind password **tidak** didokumentasikan di repo — reuse `geonet-console` `.env`
---
## Status AD — Mail-Users (2026-07-03)
| Item | Nilai | Status |
|------|-------|--------|
| Grup LDAP | `Mail-Users` | ✅ Dibuat user |
| DN grup *(default AD)* | `CN=Mail-Users,CN=Users,DC=gisportal,DC=id` | ⚠ Verifikasi jika filter gagal |
| Member | `support` | ✅ |
| Atribut `mail` | `support@geonet.co.id` | ✅ |
| sAMAccountName | `support` | *(untuk login LDAP)* |
**LDAP filter siap pakai:**
```
(&(objectClass=user)(objectCategory=person)(memberOf=CN=Mail-Users,CN=Users,DC=gisportal,DC=id))
```
Mailbox pertama yang akan di-provision otomatis: **`support@geonet.co.id`** — sekaligus mailbox Zammad + fetchmail POP3 drain.
---
## Jawaban singkat
**Ya, Mailcow bisa pakai LDAP (Active Directory)** sebagai Identity Provider — login IMAP/SMTP/SOGo dan provisioning mailbox otomatis.
User **tidak** perlu password terpisah di Mailcow; autentikasi ke AD sama seperti geonet-console dan Zammad.
**Tapi:** manajemen akun **tidak 100% identik** dengan microservice lain — Mailcow tetap perlu konfigurasi domain email, template mailbox, dan grup siapa yang dapat mailbox `@geonet.co.id`.
---
## Posisi dalam ekosistem Super Apps
```mermaid
flowchart LR
AD[(Active Directory\n10.100.1.40\ngisportal.id)]
AD -->|LDAP bind| GC[geonet-console\nJWT login]
AD -->|LDAP sync| ZM[Zammad\nagent/customer]
AD -->|LDAP IdP| MC[Mailcow\nmailbox @geonet.co.id]
AD -->|LDAP| FE[mysupperapps PWA\nvia console API]
MC --> OL[Outlook IMAP/SMTP]
MC --> ZM
```
| Layanan | LDAP dipakai untuk | User lokal? |
|---------|-------------------|-------------|
| **geonet-console** | Auth only → JWT | Tidak |
| **Zammad** | Sync user + role/group | Dibuat otomatis dari AD |
| **Mailcow** | Auth mailbox + import/sync | Mailbox auto-provision (bukan admin) |
| **Super Apps FE** | Via geonet-console API | Tidak |
**Sumber kebenaran identitas:** AD `10.100.1.40` — password, nama, grup.
---
## Fitur LDAP native Mailcow (2025+)
Mailcow menyediakan **System → Configuration → Access → Identity Provider → LDAP**.
| Fitur | Keterangan |
|-------|------------|
| LDAP bind login | User login pakai kredensial AD |
| Auto-provision | Mailbox dibuat saat login pertama (IMAP/SMTP/UI) |
| Periodic import | Cron impor user dari LDAP |
| Periodic full sync | Update template/quota dari perubahan AD |
| Attribute mapping | Map atribut LDAP → template mailbox Mailcow |
**Dokumentasi resmi:** [mailcow LDAP guide](https://docs.mailcow.email/manual-guides/mailcow-UI/u_e-mailcow_ui-ldap/)
---
## Batasan penting
| Item | Perilaku |
|------|----------|
| **Admin Mailcow** | Tetap auth **SQL** (bukan LDAP) |
| **Domain Admin** | Tetap auth **SQL** |
| **Mailbox user** | Bisa LDAP ✅ |
| **2FA** | LDAP user bisa set 2FA di UI Mailcow |
| **Outlook** | Login pakai password AD; tidak perlu app password khusus (beda dengan OIDC) |
| **Active Directory** | Pakai filter AD (`objectClass=user`), **bukan** `posixAccount` |
> **Catatan AD:** Ada laporan issue Dovecot + filter `posixAccount` untuk AD. Pastikan filter di Identity Provider memakai sintaks AD. Cek log IMAP setelah setup.
---
## Parameter LDAP (selaras Zammad / geonet-console)
| Field Mailcow UI | Nilai |
|----------------|-------|
| **URI / Host** | `ldap://10.100.1.40:389` |
| **Bind DN** | `Administrator@gisportal.id` |
| **Bind password** | `LDAP_BIND_PASSWORD` dari geonet-console |
| **Base DN** | `DC=gisportal,DC=id` |
| **LDAP Filter** | `(&(objectClass=user)(objectCategory=person)(memberOf=CN=Mail-Users,OU=Groups,DC=gisportal,DC=id))` *(contoh — sesuaikan grup)* |
| **Username field** | `mail` *(bukan `sAMAccountName` — sync butuh alamat email penuh)* |
| **Attribute Field** (mapping) | `mail` atau atribut custom |
Reuse credential: `/opt/stacks/geonet-console/.env` — pola sama dengan Zammad (`Zammad/docs/notes/ldap-ad-setup.md`).
### Prasyarat jaringan
```bash
# Dari VM Mailcow (.102)
nc -zv -w5 10.100.1.40 389
```
Firewall: `10.100.1.28``10.100.1.40:389`
---
## Tantangan: AD `gisportal.id` vs email `geonet.co.id`
AD domain = `gisportal.id`, tetapi mailbox operasional = `@geonet.co.id`.
| Strategi | Cara | Rekomendasi |
|----------|------|-------------|
| **A — Atribut `mail` di AD** | Set `mail` = `user@geonet.co.id` per user | ✅ Paling bersih |
| **B — Grup + template** | Hanya user di grup `Mail-Users` AD yang di-import | ✅ Kontrol siapa dapat email |
| **C — proxyAddresses** | `SMTP:user@geonet.co.id` di AD | Enterprise, butuh script |
| **D — Dual domain** | Login AD `user@gisportal.id`, alias `user@geonet.co.id` | Manual per user di Mailcow |
**Rekomendasi:** Kombinasi **A + B**:
1. Buat grup AD `Mail-Users` (atau `IT-Mail`, `Domain Users` subset)
2. Isi atribut `mail` = `nama@geonet.co.id` untuk anggota grup
3. LDAP filter: `memberOf=CN=Mail-Users,...`
4. Hanya user dengan `mail` @ `geonet.co.id` yang di-provision
User tanpa `mail` atau di luar grup → tidak dapat mailbox (sama seperti 3 user gagal sync di Zammad).
---
## Attribute mapping & mailbox template
Mailcow memakai **template** untuk quota, spam policy, dll.
| LDAP Attribute Field | Attribute value | Template Mailcow |
|---------------------|-----------------|------------------|
| `mail` | `*@geonet.co.id` | `default` *(buat template di UI)* |
| `department` | `IT` | `it-mailbox` *(opsional)* |
**Cron sync** (disarankan aktif):
| Opsi | Fungsi |
|------|--------|
| **Import Users** | Impor user baru dari LDAP tiap N menit |
| **Periodic Full Sync** | Update mailbox jika template/atribut berubah |
| **Sync / Import Interval** | `15` menit (sesuaikan) |
Log: `System → Information → Logs → Crontasks`
---
## Alur provisioning user baru
```
1. Admin tambah user di AD (+ grup Mail-Users + atribut mail)
2. Cron Mailcow LDAP sync (atau login pertama)
3. Mailbox user@geonet.co.id dibuat otomatis
4. User buka Outlook → IMAP sync.geonet.co.id + password AD
5. Zammad (jika mailbox support@) poll IMAP mailbox yang sama
```
**Tidak perlu** buat user manual di Mailcow UI untuk user standar.
---
## Apa yang tetap dikelola di Mailcow UI
| Item | LDAP? | Keterangan |
|------|-------|------------|
| Password user | ✅ AD | Central |
| Nama / display | ✅ AD | Sync dari LDAP |
| Buat/hapus user | ✅ AD (+ grup) | Hapus = disable di AD + hapus mailbox |
| Domain `geonet.co.id` | ❌ Mailcow | Tambah domain sekali |
| Alias email | ❌ Mailcow | `info@``support@` |
| Shared mailbox | ❌ Mailcow | Manual |
| Quota / template | ⚙ Hybrid | Template di Mailcow, assign via LDAP mapping |
| fetchmail POP3 drain | ❌ Mailcow | Konfig terpisah (staging cPanel) |
| Admin panel Mailcow | ❌ SQL | `admin` lokal — jangan LDAP |
---
## Perbandingan manajemen terpusat
| Aksi admin | Di AD saja? | Catatan |
|------------|-------------|---------|
| Onboard karyawan baru | Hampir ✅ | + grup `Mail-Users` + `mail` attribute |
| Reset password | ✅ | Satu password untuk AD, Outlook, SOGo |
| Nonaktifkan karyawan | ✅ | Disable AD → sync → blokir login mail |
| Ubah alamat email | ✅ | Ubah `mail` di AD + sync |
| Tambah domain email baru | ❌ | Mailcow UI |
| Forward / alias | ❌ | Mailcow UI |
**Kesimpulan:** LDAP **cukup untuk 90%** manajemen user harian; Mailcow UI untuk kebijakan mail (domain, alias, template).
---
## Integrasi dengan Outlook & Zammad
Setelah LDAP aktif:
| Klien | Login | Server |
|-------|-------|--------|
| **Outlook** | `user@geonet.co.id` + **password AD** | IMAP/SMTP `sync.geonet.co.id` |
| **Zammad channel** | Service account `support@geonet.co.id` | Bisa tetap password mailbox khusus *(bukan LDAP user login)* |
> Mailbox `support@` untuk Zammad/fetchmail bisa tetap **authsource mailcow (SQL)** dengan app password, sementara user biasa pakai LDAP — menghindari konflik service account.
---
## Step-by-step (setelah Mailcow terinstall)
### 1 — Tambah domain
**E-Mail → Configuration → Domains** → tambah `geonet.co.id`
### 2 — Buat mailbox template
**E-Mail → Configuration → Templates** → `default` (quota, ratelimit)
### 3 — Identity Provider LDAP
**System → Configuration → Access → Identity Provider**
1. Pilih **LDAP**
2. Isi connection (lihat tabel parameter di atas)
3. **Connection Test** → harus OK
4. Set LDAP filter + attribute mapping
5. Aktifkan **Import Users** + **Periodic Full Sync**
6. Interval: 15 menit
### 4 — Grup AD ✅ (2026-07-03)
| Item | Status |
|------|--------|
| Grup `Mail-Users` | ✅ |
| Member `support` | ✅ |
| `mail` = `support@geonet.co.id` | ✅ |
Tambahkan member baru dengan aturan sama: masuk grup `Mail-Users` + atribut `mail` = `nama@geonet.co.id`.
### 5 — Uji
1. Login SOGo / Mailcow UI dengan user AD uji
2. Cek mailbox muncul di **E-Mail → Mailboxes**
3. Tes Outlook IMAP/SMTP
4. Cek log jika IMAP gagal: `System → Information → Logs`
### 6 — Admin Mailcow
Pertahankan akun `admin` SQL terpisah — **jangan** pakai LDAP untuk admin panel.
---
## Troubleshooting
| Gejala | Penyebab | Tindakan |
|--------|----------|----------|
| Web login OK, IMAP gagal | Filter LDAP tidak cocok AD | Ubah filter ke `objectClass=user`; cek log Dovecot |
| User tidak ter-import | Tidak match filter / grup | Cek `memberOf`, atribut `mail` |
| Email salah domain | `mail` di AD masih `@gisportal.id` | Update AD → `@geonet.co.id` |
| Connection test gagal | Firewall / bind password | `nc` ke `.40:389`, cek bind DN |
| Semua user AD dapat mailbox | Filter terlalu luas | Perketat `memberOf=CN=Mail-Users,...` |
---
## Roadmap LDAP Mailcow
| # | Task | Status |
|---|------|--------|
| 1 | Install Mailcow di `.102` | ❌ |
| 2 | Tes koneksi LDAP dari container | ❌ |
| 3 | Buat grup AD `Mail-Users` | ✅ 2026-07-03 |
| 4 | Member `support` + `mail` @ `geonet.co.id` | ✅ |
| 5 | Konfigurasi Identity Provider | ✅ 2026-07-03 |
| 6 | Uji Outlook + SOGo | ⏳ |
| 7 | `docs/project-status.md` | ✅ |
---
## Referensi
- [Mailcow LDAP docs](https://docs.mailcow.email/manual-guides/mailcow-UI/u_e-mailcow_ui-ldap/)
- `Zammad/docs/notes/ldap-ad-setup.md` — parameter AD sama
- `server-connection/docs/environment.md``LDAP_*` vars
- `Mailcow/docs/notes/email-sync-migration-concept.md` — alur POP3 drain + Mailcow

245
Mailcow/docs/notes/storage-nas-qnap.md

@ -0,0 +1,245 @@
# Storage Email — NAS QNAP
> **Terakhir Diperbarui:** 2026-07-03
> **NAS:** QNAP `10.100.1.10`
> **Keputusan (2026-07-03):****Local SSD VM** untuk live vmail — prioritas **uptime layanan mail**. QNAP hanya untuk **backup**, bukan iSCSI/NFS live.
---
## Jawaban singkat
**Ya, bisa** — tapi hanya untuk **filesystem Maildir** (`vmail`), **bukan** lewat Object Storage (S3/OSS) seperti attachment Zammad.
| Metode | Live mailbox di NAS | Rekomendasi |
|--------|---------------------|-------------|
| **iSCSI** (LUN QNAP → VM) | ✅ | 🥇 **Paling proper** — seperti disk lokal |
| **NFS** v4 + bind mount | ✅ | 🥈 Bisa, perlu tuning Dovecot |
| **Backup/rsync** ke NAS | Hanya salinan | 🥇 **Wajib** — kombinasi dengan disk lokal |
| **SMB/CIFS** mount langsung | ⚠ | Hindari untuk Maildir aktif |
| **QNAP OSS / S3** | ❌ | Mailcow tidak support object storage untuk vmail |
---
## Apa yang disimpan di NAS vs tetap lokal VM
| Data | NAS live? | Catatan |
|------|-----------|---------|
| `vmail` (email Maildir) | ✅ Bisa | Target utama pindah storage |
| `vmail-attachments` | ✅ Bisa | Attachment besar |
| **MariaDB** (database Mailcow) | ❌ **Jangan** | NFS + MySQL = corrupt risk |
| **Redis** | ❌ **Jangan** | Tetap disk lokal VM |
| Index Dovecot | ⚠ | Bisa di NAS jika vmail di NAS + `mail_nfs_*` |
**Aturan:** Hanya **file email** ke NAS; database & cache tetap di VM.
---
## Perbandingan: iSCSI QNAP vs Local Storage VM
> Konteks: VM Mailcow di Proxmox `.102`, NAS QNAP `.10`, ~30 user IMAP aktif, Maildir (banyak file kecil).
### Kecepatan
| Aspek | **Local storage VM** | **iSCSI LUN QNAP** |
|-------|----------------------|---------------------|
| Latency per operasi file | **~0,1–0,5 ms** (VirtIO SSD) | **~1–3 ms** (LAN + iSCSI stack) |
| Throughput maksimum | Sesuai disk host (SSD: 500+ MB/s) | **~110 MB/s** (1 GbE) · ~1 GB/s (10 GbE) |
| IMAP / Outlook sync (banyak file kecil) | **Lebih responsif** | Cukup, terasa sedikit lebih lambat saat sync awal |
| Beban 30 user concurrent | **Lebih nyaman** | Bisa, bottleneck di jaringan 1 GbE |
| Postfix queue / spike | **Lebih stabil** | Lonjakan I/O + traffic LAN bersamaan |
**Pola beban Mailcow:** Dovecot membaca/ menulis ribuan file kecil (Maildir) — **sangat sensitif latency**, bukan hanya throughput. Local SSD menang jelas untuk kecepatan sehari-hari.
### Keandalan
| Aspek | **Local storage VM** | **iSCSI LUN QNAP** |
|-------|----------------------|---------------------|
| Kegagalan disk VM saja | ❌ Data ikut hilang *(tanpa backup)* | ✅ Data tetap di QNAP RAID |
| Kegagalan NAS / jaringan | ✅ VM tetap jalan *(jika disk lokal)* | ❌ **Mail down** — I/O hang |
| Reboot VM | ✅ Stabil | ⚠ Perlu iSCSI reconnect otomatis |
| RAID / redundansi | Tergantung disk Proxmox host | ✅ QNAP RAID5/6 biasanya lebih baik |
| Restore disaster | Dari backup QNAP | LUN snapshot / replicate NAS |
| Single point of failure | Disk VM (tipis) | **NAS + switch + kabel LAN** |
**Keandalan data mentah:** iSCSI ke QNAP RAID lebih aman jika disk Proxmox `.102` tidak RAID.
**Keandalan layanan mail online:** local storage lebih andal — tidak bergantung jaringan ke NAS.
### Skor ringkas (30 user produksi)
| Kriteria | Local VM (SSD) | iSCSI QNAP |
|----------|----------------|------------|
| **Kecepatan** | ⭐⭐⭐⭐⭐ | ⭐⭐⭐ |
| **Uptime layanan** | ⭐⭐⭐⭐⭐ | ⭐⭐⭐ |
| **Keamanan data (hardware)** | ⭐⭐⭐ | ⭐⭐⭐⭐⭐ |
| **Kapasitas / expand** | ⭐⭐⭐ | ⭐⭐⭐⭐⭐ |
| **Kesederhanaan ops** | ⭐⭐⭐⭐⭐ | ⭐⭐⭐ |
---
## Rekomendasi terbaik (Geonet)
### 🥇 **Hybrid — dipilih Geonet (2026-07-03)**
Prioritas: **uptime layanan mail** → live storage **local SSD VM**, bukan NAS.
```
Live vmail → Local SSD VM `10.100.1.28` disk 2 (512 GiB)
Backup → QNAP harian (rsync / Mailcow backup)
DB + Redis → Local SSD VM (selalu)
```
| Mengapa | |
|---------|--|
| Kecepatan IMAP/Outlook | Maksimal — tanpa hop jaringan |
| **Uptime mail** | Mail tetap jalan meski NAS maintenance / LAN ke QNAP down |
| Keandalan data | Backup harian + snapshot QNAP |
| Kompleksitas | Rendah — tidak perlu iSCSI tuning |
### 🥈 **iSCSI QNAP** — tidak dipilih *(trade-off uptime)*
- Disk lokal Proxmox `.102` **kecil atau HDD lambat**
- QNAP punya **SSD pool** dan kapasitas besar
- Ingin **satu pool storage terpusat** + snapshot LUN
- Bisa terima trade-off: sedikit lebih lambat, risiko mail down saat NAS/LAN bermasalah
### ❌ Hindari:
- NFS/SMB live untuk vmail (lebih lemah dari iSCSI untuk Maildir)
- Hanya local tanpa backup ke QNAP
- Hanya iSCSI tanpa monitoring path NAS
---
QNAP expose LUN iSCSI → Proxmox/VM mount sebagai block device.
```
QNAP 10.100.1.10
└── iSCSI LUN 300GB "mailcow-vmail"
└── VM mail-sync (Debian)
└── /dev/sdb → ext4 → /data/mailcow/vmail
└── docker-compose.override.yml bind mount
```
**Kelebihan:**
- Performa mendekati disk lokal
- Tidak ada masalah file locking seperti NFS
- Dovecot tidak perlu mode NFS khusus
- Expand LUN dari QNAP saat penuh
**Setup ringkas:**
1. QNAP → iSCSI Storage → buat LUN + target
2. VM → `iscsiadm` connect → format `ext4``/etc/fstab`
3. Mailcow `docker-compose.override.yml` bind ke path tersebut
---
## Opsi 2 — NFS (bisa, butuh hati-hati)
Mount NFS di host VM, bind ke volume Docker — **jangan pakai symlink** ke `_data` Docker (risiko inbox kosong setelah reboot).
### Host: `/etc/fstab`
```
10.100.1.10:/mailcow-vmail /mnt/mailcow-vmail nfs nfsvers=4.1,noac,rw,hard,timeo=600,retrans=2,_netdev 0 0
```
> `noac` = disable attribute cache — penting untuk konsistensi Maildir di NFS.
### Mailcow: `docker-compose.override.yml`
```yaml
volumes:
vmail-vol-1:
driver: local
driver_opts:
type: none
device: /mnt/mailcow-vmail
o: bind
```
### Dovecot: aktifkan mode NFS
Edit `data/conf/dovecot/dovecot.conf` (uncomment):
```
mmap_disable = yes
mail_fsync = always
mail_nfs_index = yes
mail_nfs_storage = yes
```
Restart: `docker compose restart dovecot-mailcow`
**Risiko NFS:** permission intermittent, latency IMAP, issue saat reboot jika mount order salah.
Ref: [Mailcow — Move Maildir](https://docs.mailcow.email/manual-guides/Dovecot/u_e-dovecot-vmail-volume/), [GitHub #5339](https://github.com/mailcow/mailcow-dockerized/issues/5339)
---
## Opsi 3 — Disk lokal VM + backup ke NAS (paling sederhana)
| Live | Backup |
|------|--------|
| Disk VM 512 GiB vmail (`.27`) | Harian rsync/NFS ke QNAP |
```
/opt/stacks/mailcow/helper-scripts/backup_and_restore.sh backup all
# → rsync ke 10.100.1.10:/backup/mailcow/
```
**Kelebihan:** Stabil, tidak ada kompleksitas NFS/iSCSI.
**Kekurangan:** Kapasitas terbatas ukuran disk VM — expand manual.
Cocok jika awal 30 user dan belum butuh ratusan GB segera.
---
## Perbandingan untuk 30+ user aktif
| Kriteria | iSCSI | NFS live | Lokal + backup NAS |
|----------|-------|----------|---------------------|
| Stabilitas IMAP | ⭐⭐⭐⭐⭐ | ⭐⭐⭐ | ⭐⭐⭐⭐⭐ |
| Expand storage | Mudah (grow LUN) | Mudah | Expand disk VM |
| Kompleksitas | Sedang | Tinggi | Rendah |
| Performa Outlook sync | Baik | Sedang | Baik |
| Disaster recovery | Snapshot LUN QNAP | Snapshot share | Restore dari backup |
**Rekomendasi Geonet:** **iSCSI LUN dari QNAP** untuk vmail live + **backup harian** script Mailcow ke share NFS terpisah.
---
## QNAP — share yang disarankan
| Share QNAP | Protokol | Fungsi |
|------------|----------|--------|
| `mailcow-vmail` | iSCSI LUN atau NFS | Data email live |
| `backup-mailcow` | NFS / SMB | Backup harian (rsync) |
Jaringan: VM `.102` ↔ NAS `.10` lewat LAN `10.100.1.x` (low latency).
---
## Yang tidak bisa: OSS seperti Zammad
Zammad attachment pakai **S3** (`http://10.100.1.10:8010`). Mailcow **tidak** punya driver S3 untuk mailbox — Dovecot butuh **Maildir di filesystem POSIX**.
---
## Checklist implementasi NAS
- [ ] Pilih: iSCSI (preferred) atau NFS atau lokal+backup
- [ ] Buat LUN/share di QNAP
- [ ] Mount di VM **sebelum** Docker start (`_netdev` di fstab)
- [ ] `docker-compose.override.yml` bind mount (bukan symlink)
- [ ] Dovecot NFS settings jika pakai NFS
- [ ] MariaDB/Redis tetap volume lokal
- [ ] Tes reboot VM — inbox tidak boleh kosong
- [ ] Backup harian ke share terpisah
---
## Referensi
- `docs/notes/deployment-sizing.md`
- [Mailcow vmail volume](https://docs.mailcow.email/manual-guides/Dovecot/u_e-dovecot-vmail-volume/)
- QNAP iSCSI: Storage & Snapshots → iSCSI

74
Mailcow/docs/project-status.md

@ -0,0 +1,74 @@
# Project Status — Mailcow
> **Terakhir Diperbarui:** 2026-07-03
---
## Keputusan arsitektur
| Keputusan | Pilihan | Tanggal |
|-----------|---------|---------|
| **VM IP** | `10.100.1.28` | 2026-07-03 |
| **vCPU / RAM** | 16 core / 32 GiB | 2026-07-03 |
| **Disk 1** | 512 GiB SSD — OS + DB | 2026-07-03 |
| **Disk 2** | 512 GiB SSD — vmail live | 2026-07-03 |
| **Prioritas storage** | Uptime layanan mail — local SSD | 2026-07-03 |
| **Backup** | QNAP `10.100.1.10` harian | 2026-07-03 |
Mail tetap jalan meski NAS maintenance atau gangguan LAN ke QNAP.
---
## Ringkasan
| Item | Status |
|------|--------|
| Dokumentasi repo | ✅ |
| Konsep POP3 drain → Mailcow | ✅ `docs/notes/email-sync-migration-concept.md` |
| Konsep LDAP AD | ✅ `docs/notes/ldap-ad-mailcow.md` |
| Grup AD `Mail-Users` | ✅ 2026-07-03 |
| Member `support` + `mail` | ✅ `support@geonet.co.id` |
| **Storage live** | Local SSD VM + backup QNAP | ✅ Keputusan 2026-07-03 |
| **VM Mailcow** | `10.100.1.28` — 16 vCPU, 32 GB, 2×512 GB SSD | ✅ |
| SSH key login | `ssh mailcow` | ✅ 2026-07-03 |
| OS prep (hostname, disk, docker) | ✅ 2026-07-03 |
| Desktop GNOME (Proxmox console) | ✅ ubuntu-desktop-minimal |
| Install Mailcow stack | ✅ 2026-07-03 — `/opt/stacks/mailcow` |
| Domain `geonet.co.id` | ✅ aktif, DKIM generated |
| Mailbox `support@geonet.co.id` | ✅ auth mailcow (service account) |
| SOGo login + kirim/terima | ✅ verified user 2026-07-03 |
| Outbound relay cPanel | ✅ `sc138.idcloudhosting.cloud:587` |
| Nginx proxy `sync.geonet.co.id` | ✅ `.24` + Let's Encrypt |
| LDAP Identity Provider | ✅ 2026-07-03 (user AD lain) |
| fetchmail POP3 drain | ✅ timer 2 menit — `scripts/install-fetchmail-support.sh` |
| Zammad IMAP → Mailcow | ❌ |
| DKIM/SPF DNS publish | ❌ |
---
## Active Directory — Mail-Users
| Item | Nilai |
|------|-------|
| Grup | `Mail-Users` |
| DN (asumsi default) | `CN=Mail-Users,CN=Users,DC=gisportal,DC=id` |
| Member pertama | `support` |
| Email (`mail`) | `support@geonet.co.id` |
| Login AD | `support` (sAMAccountName) |
> Verifikasi DN grup jika LDAP filter gagal: PowerShell di AD → `(Get-ADGroup 'Mail-Users').DistinguishedName`
---
## Berikutnya
1. Provision VM + Mailcow + LDAP | ✅
2. Domain `geonet.co.id` + mailbox `support@` | ✅
3. DNS email (DKIM/SPF) untuk `geonet.co.id` | ⏳
4. fetchmail POP3 drain dari cPanel | ✅
5. Outbound relay cPanel (transisi) | ✅
6. SOGo `support@` operational | ✅
7. DNS DKIM/SPF + PTR | ⏳
8. Zammad IMAP/SMTP → Mailcow | ⏳
Lihat `docs/todo.md`, `docs/ai-session-summary.md`.

204
Mailcow/docs/server.md

@ -0,0 +1,204 @@
# Server — Mailcow VM
> **Terakhir Diperbarui:** 2026-07-03 (Dockge agent)
---
## VM Mailcow
| Item | Nilai |
|------|-------|
| **IP LAN** | `10.100.1.28` |
| Hostname | `mailserver` |
| FQDN mail (Mailcow nanti) | `sync.geonet.co.id` |
| **Role** | Mail server (Mailcow dockerized) |
| **Hypervisor** | Proxmox Opr `10.100.1.102` *(asumsi — sesuaikan jika beda)* |
| **Stack path** | `/opt/stacks/mailcow` (Dockge) |
| **Dockge agent** | `http://10.100.1.28:5001` — dikelola dari `dockge.gisportal.id` (`.24`) |
| **Web UI** | https://sync.geonet.co.id *(nginx `.24` → Mailcow `.28`)* |
## Desktop GNOME (opsional)
| | |
|--|--|
| **Boleh dipakai?** | ✅ Ya — dengan **32 GB RAM** tidak masalah |
| **Paket** | `ubuntu-desktop-minimal` + GDM |
| **Akses** | Proxmox noVNC / console |
| **Mailcow** | Tetap di Docker — desktop tidak mengganggu |
Yang tetap **tidak perlu** (boleh hapus): CUPS, Avahi, Snap, LibreOffice.
Prep script: `KEEP_DESKTOP=1 ./prepare-mailserver.sh` agar desktop tidak di-purge.
---
| Item | Nilai |
|------|-------|
| Hostname | `mailserver` |
| OS | Ubuntu 22.04.5 LTS |
| Desktop GNOME | ✅ `ubuntu-desktop-minimal` — akses Proxmox console |
| Snap | ❌ Dihapus (tidak wajib untuk desktop) |
| CUPS / Avahi services | ❌ Dihapus (tidak perlu mail server) |
| Docker | ✅ 29.x + Compose v5 |
| Dockge agent | ✅ `/opt/dockge` — port `5001` (LAN only) |
| UFW firewall | ✅ Aktif |
| `vm.max_map_count` | 262144 |
| Swap | 4 GiB |
| `/data/mailcow/vmail` | `/dev/sdb1` 503 GiB |
Script prep: `Mailcow/scripts/prepare-mailserver.sh`
---
### Spesifikasi hardware
| Resource | Nilai |
|----------|-------|
| **vCPU** | 16 core |
| **RAM** | 32 GiB |
| **Swap** | 4 GiB *(disarankan)* |
| **Disk 1** | 512 GiB SSD — OS + Docker + MariaDB + Redis |
| **Disk 2** | 512 GiB SSD — `vmail` (email live, local) |
| **Storage live** | Local SSD — prioritas uptime *(bukan NAS)* |
| **Backup** | QNAP `10.100.1.10` harian |
> Kapasitas 512 GiB vmail cukup untuk 30+ user aktif bertahun-tahun (asumsi ~10–15 GB/user).
---
## Host terkait
| Role | IP | Keterangan |
|------|-----|------------|
| **Mailcow VM** | `10.100.1.28` | Server ini |
| Dockge primary | `10.100.1.24` | `dockge.gisportal.id` — UI manajemen Docker |
| Zammad | `10.100.1.24` | IMAP/SMTP ke `.28` |
| AD / LDAP | `10.100.1.40` | Identity Provider Mailcow |
| QNAP NAS | `10.100.1.10` | Backup harian |
| cPanel POP3 | `mail.geonet.co.id` | fetchmail drain |
| Proxmox Opr | `10.100.1.102` | Host VM *(jika applicable)* |
---
## SSH
### Login (passwordless)
```bash
ssh mailcow
# atau
ssh root@10.100.1.28
```
Key: `~/.ssh/id_ed25519_mailcow` · Host alias `mailcow` di `~/.ssh/config`
### Setup key (sekali — sudah selesai 2026-07-03)
Jika perlu ulang di mesin lain: `Mailcow/scripts/fix-ssh-key.ps1`
**Catatan:** Hindari pipe script PowerShell ke `bash -s` (CRLF). Pakai SCP upload file `.sh` + `.pub`.
| File key | Lokasi |
|----------|--------|
| Private | `~/.ssh/id_ed25519_mailcow` |
| Public | `~/.ssh/id_ed25519_mailcow.pub` |
| SSH config | Host `mailcow``10.100.1.28` |
---
## Dockge agent (monitoring dari `.24`)
Docker di VM ini dikelola lewat **Dockge Agents** — instance Dockge di `.28` didaftarkan ke Dockge utama di `10.100.1.24` (`dockge.gisportal.id`).
| Item | Nilai |
|------|-------|
| Agent URL | `http://10.100.1.28:5001` |
| Compose | `/opt/dockge/compose.yaml` |
| Stacks dir | `/opt/stacks/` |
| Bind port | `10.100.1.28:5001` *(bukan `0.0.0.0` — tidak listen di localhost)* |
| Primary | Dockge v1.5.0 di `.24` |
### Instalasi (sudah selesai 2026-07-03)
```bash
# Di mailserver — atau ulang dari repo:
bash Mailcow/scripts/install-dockge-agent.sh
bash Mailcow/scripts/setup-dockge-agent-user.sh # samakan user admin dengan primary
```
### Daftarkan agent di UI primary *(sekali)*
1. Buka https://dockge.gisportal.id
2. **Settings → Dockge Agents → Add Agent**
3. URL: `http://10.100.1.28:5001`
4. Username / password: sama dengan login Dockge primary (`admin`)
5. Status agent harus **online** — stack Mailcow nanti bisa dipilih agent **mailserver**
> Port `5001` **tidak** di-expose ke internet; hanya `10.100.1.24` yang diizinkan UFW.
---
## Firewall / port (VM `.28`)
UFW aktif — default deny incoming, allow outgoing.
| Port | Protokol | Layanan | Sumber |
|------|----------|---------|--------|
| 22 | TCP | SSH | Anywhere |
| 25 | TCP | SMTP inbound | Anywhere |
| 80 | TCP | HTTP (ACME redirect) | Anywhere |
| 443 | TCP | HTTPS — Mailcow UI | Anywhere |
| 465 | TCP | SMTPS | Anywhere |
| 587 | TCP | Submission | Anywhere |
| 993 | TCP | IMAPS | Anywhere |
| 995 | TCP | POP3S *(opsional)* | Anywhere |
| 5001 | TCP | Dockge agent | **Hanya** `10.100.1.24` |
### Akses internal yang diperlukan
| Dari | Ke | Port |
|------|-----|------|
| `10.100.1.28` | `10.100.1.40` | 389 LDAP |
| `10.100.1.28` | Internet | 995 POP3 (cPanel drain) |
| `10.100.1.24` | `10.100.1.28` | 5001 Dockge, 993, 587 |
| User LAN/VPN | `10.100.1.28` atau IP publik | 993, 587 |
---
## DNS & NAT publik
**IP publik:** `117.102.73.102`
| Record | Target |
|--------|--------|
| A `sync.geonet.co.id` | `117.102.73.102` |
| PTR | `sync.geonet.co.id` *(verifikasi di provider)* |
### Port forwarding (MikroTik / gateway)
| Port | NAT ke | Layanan |
|------|--------|---------|
| 25, 465, 587, 993 | `10.100.1.28` | Mailcow — SMTP / IMAP langsung |
| 80, 443 | `10.100.1.24` | Nginx reverse proxy |
> **HTTPS Mailcow UI** tidak langsung ke `.28` dari internet — Nginx di `.24` harus proxy `sync.geonet.co.id``https://10.100.1.28:443`. Saat install Mailcow set `SKIP_LETS_ENCRYPT=y`; sertifikat publik diurus Nginx `.24` (certbot).
Akses internal (Zammad, LAN): langsung ke `10.100.1.28:587` / `:993` tanpa lewat NAT publik.
---
## Partition disk (rencana)
| Device | Mount | Isi |
|--------|-------|-----|
| Disk 1 | `/` | Debian/Ubuntu + Docker |
| Disk 1 | `/var/lib/docker` | Container layers, MariaDB, Redis volumes |
| Disk 2 | `/data/mailcow/vmail` | Bind mount → `vmail-vol-1` Mailcow |
---
## Referensi
- `docs/notes/deployment-sizing.md`
- `docs/notes/storage-nas-qnap.md`
- `docs/project-status.md`

47
Mailcow/docs/todo.md

@ -0,0 +1,47 @@
# TODO — Mailcow
> Last updated: 2026-07-03
---
## High Priority
- [ ] Publish DKIM TXT dkim._domainkey.geonet.co.id (key dari Mailcow API)
- [ ] Update SPF geonet.co.id — include IP 117.102.73.102 atau relay sementara
- [ ] Zammad channel email → IMAP/SMTP Mailcow 10.100.1.28 (ganti cPanel)
---
## Medium Priority
- [ ] PTR/rDNS 117.102.73.102 → sync.geonet.co.id
- [ ] Buat scripts/install-relay-cpanel.sh (mirror fetchmail installer)
- [ ] Automasi sync _sogo_static_view setelah password mailbox mailcow berubah
- [ ] Monitoring fetchmail (Uptime Kuma atau alert log)
---
## Low Priority
- [ ] fetchmail entry untuk mailbox tambahan (info@, dll.)
- [ ] LDAP user lain di Mail-Users — provisioning + Outlook guide
- [ ] Domain sekunder gegeo.my.id
- [ ] MX cutover ke sync.geonet.co.id (setelah 2–4 minggu stabil)
- [ ] Matikan fetchmail + relay cPanel pasca cutover
---
## Selesai (Sesi 2026-07-03)
- [x] fetchmail POP3 drain support@ + timer 2 menit
- [x] Relay outbound cPanel :587 untuk geonet.co.id
- [x] SOGo login + kirim/terima email verified
- [x] scripts/install-fetchmail-support.sh
- [x] Dokumentasi handover sesi
---
## Technical Debt
- [ ] support@ service account vs LDAP — dokumentasikan keputusan
- [ ] context-index diagram IP — align ke .28

BIN
Mailcow/infra/nginx/sync.geonet.co.id.conf

Binary file not shown.

BIN
Mailcow/scripts/fix-mailcow-dns-ufw.sh

Binary file not shown.

41
Mailcow/scripts/fix-ssh-key.ps1

@ -0,0 +1,41 @@
# Fix SSH key — upload LF scripts via SCP (no CRLF pipe issues)
$ErrorActionPreference = 'Stop'
$hostIp = '10.100.1.28'
$key = Join-Path $env:USERPROFILE '.ssh\id_ed25519_mailcow'
$pub = "$key.pub"
$remoteSh = Join-Path $PSScriptRoot 'remote-reset-authorized-keys.sh'
# Perketat ACL private key (Windows OpenSSH)
Write-Host "Fixing private key ACL..."
icacls $key /inheritance:r 2>$null | Out-Null
icacls $key /grant:r "${env:USERNAME}:(R)" 2>$null | Out-Null
# Pub key LF-only
$tempPub = Join-Path $env:TEMP 'geonet_mailcow_key.pub'
$keyLine = (Get-Content $pub -Raw).Trim() -replace "`r", ""
[System.IO.File]::WriteAllText($tempPub, $keyLine + "`n")
# Remote script LF-only
$shContent = (Get-Content $remoteSh -Raw) -replace "`r", ""
$tempSh = Join-Path $env:TEMP 'remote-reset-authorized-keys.sh'
[System.IO.File]::WriteAllText($tempSh, $shContent)
Write-Host "Upload + reset on root@${hostIp} (password 2x: scp + ssh)..."
scp -o PreferredAuthentications=password -o PubkeyAuthentication=no $tempPub "root@${hostIp}:/tmp/geonet_mailcow_key.pub"
scp -o PreferredAuthentications=password -o PubkeyAuthentication=no $tempSh "root@${hostIp}:/tmp/remote-reset-authorized-keys.sh"
ssh -o PreferredAuthentications=password -o PubkeyAuthentication=no "root@${hostIp}" 'chmod +x /tmp/remote-reset-authorized-keys.sh && bash /tmp/remote-reset-authorized-keys.sh && rm -f /tmp/remote-reset-authorized-keys.sh'
Remove-Item $tempPub, $tempSh -Force -ErrorAction SilentlyContinue
Write-Host ""
Write-Host "Test key-only..."
ssh -i $key -o BatchMode=yes -o IdentitiesOnly=yes -o PreferredAuthentications=publickey "root@${hostIp}" "echo KEY_AUTH_OK"
if ($LASTEXITCODE -ne 0) {
Write-Host ""
Write-Host "Masih gagal — ambil auth.log (password sekali):"
Write-Host " ssh root@$hostIp `"grep sshd /var/log/auth.log | tail -20`""
exit 1
}
Write-Host "Success. Login: ssh mailcow"

33
Mailcow/scripts/install-dockge-agent.sh

@ -0,0 +1,33 @@
#!/bin/bash
# Install Dockge agent on mailserver (10.100.1.28) for remote management from Dockge primary (.24).
set -euo pipefail
DOCKGE_PRIMARY_IP="${DOCKGE_PRIMARY_IP:-10.100.1.24}"
BIND_IP="${BIND_IP:-10.100.1.28}"
mkdir -p /opt/stacks /opt/dockge/data
cat > /opt/dockge/compose.yaml << EOF
services:
dockge:
image: louislam/dockge:1
restart: unless-stopped
ports:
- ${BIND_IP}:5001:5001
volumes:
- /var/run/docker.sock:/var/run/docker.sock
- ./data:/app/data
- /opt/stacks:/opt/stacks
environment:
- DOCKGE_STACKS_DIR=/opt/stacks
EOF
cd /opt/dockge
docker compose up -d
if command -v ufw >/dev/null 2>&1; then
ufw allow from "${DOCKGE_PRIMARY_IP}" to any port 5001 proto tcp comment 'Dockge primary' || true
fi
echo "Dockge agent started on ${BIND_IP}:5001"
echo "Register from Dockge primary: Settings -> Agents -> Add -> http://${BIND_IP}:5001"

88
Mailcow/scripts/install-fetchmail-support.sh

@ -0,0 +1,88 @@
#!/usr/bin/env bash
# Install fetchmail POP3 drain: cPanel mail.geonet.co.id -> Mailcow support@geonet.co.id
# Run on Mailcow VM (10.100.1.28) as root.
set -euo pipefail
PASS_FILE="${PASS_FILE:-/opt/fetchmail/support.pass}"
FETCHMAILRC="${FETCHMAILRC:-/opt/fetchmail/fetchmailrc}"
POP3_USER="${POP3_USER:-support@geonet.co.id}"
POP3_HOST="${POP3_HOST:-mail.geonet.co.id}"
LOCAL_USER="${LOCAL_USER:-support@geonet.co.id}"
usage() {
echo "Usage: POP3_PASSWORD='...' $0" >&2
echo " or: PASS_FILE=/path/to/secret $0" >&2
exit 1
}
if [[ -n "${POP3_PASSWORD:-}" ]]; then
install -d -m 700 /opt/fetchmail
printf '%s' "$POP3_PASSWORD" >"$PASS_FILE"
chmod 600 "$PASS_FILE"
elif [[ ! -f "$PASS_FILE" ]]; then
usage
fi
apt-get install -y -qq fetchmail
python3 - "$PASS_FILE" "$FETCHMAILRC" "$POP3_USER" "$POP3_HOST" "$LOCAL_USER" <<'PY'
import pathlib, sys
pass_file, rc_path, pop3_user, pop3_host, local_user = sys.argv[1:6]
password = pathlib.Path(pass_file).read_text()
cfg = f'''set postmaster "{local_user}"
set syslog
set logfile /var/log/fetchmail-support.log
poll {pop3_host} with proto POP3 port 995
user "{pop3_user}" there with password {password!r} is "{local_user}" here
ssl
no sslcertck
smtphost 127.0.0.1
smtpname sync.geonet.co.id
fetchall
nokeep
'''
path = pathlib.Path(rc_path)
path.write_text(cfg)
path.chmod(0o600)
PY
touch /var/log/fetchmail-support.log
chmod 600 /var/log/fetchmail-support.log
cat >/etc/systemd/system/fetchmail-support.service <<EOF
[Unit]
Description=fetchmail POP3 drain cPanel to Mailcow (${LOCAL_USER})
After=network-online.target docker.service
Wants=network-online.target
[Service]
Type=oneshot
ExecStart=/usr/bin/fetchmail -f ${FETCHMAILRC} --nodetach
SuccessExitStatus=0 1
EOF
cat >/etc/systemd/system/fetchmail-support.timer <<'EOF'
[Unit]
Description=Poll cPanel POP3 every 2 minutes
[Timer]
OnBootSec=90s
OnUnitActiveSec=2min
AccuracySec=30s
Persistent=true
[Install]
WantedBy=timers.target
EOF
systemctl daemon-reload
systemctl enable --now fetchmail-support.timer
echo "fetchmail installed. Timer status:"
systemctl status fetchmail-support.timer --no-pager || true
echo "Test run:"
fetchmail -f "$FETCHMAILRC" -c && fetchmail -f "$FETCHMAILRC" -v -N
echo "Log: /var/log/fetchmail-support.log"

BIN
Mailcow/scripts/install-mailcow.sh

Binary file not shown.

116
Mailcow/scripts/prepare-mailserver.sh

@ -0,0 +1,116 @@
#!/bin/bash
# Prepare Ubuntu 22.04 VM for Mailcow — run as root on 10.100.1.28
# Set KEEP_DESKTOP=1 to skip desktop purge (Proxmox console UI)
set -euo pipefail
export DEBIAN_FRONTEND=noninteractive
KEEP_DESKTOP="${KEEP_DESKTOP:-0}"
log() { echo "[$(date +%H:%M:%S)] $*"; }
log "=== 1. Hostname mailserver ==="
hostnamectl set-hostname mailserver
grep -q '^127.0.1.1' /etc/hosts && sed -i 's/^127.0.1.1.*/127.0.1.1 mailserver sync.geonet.co.id/' /etc/hosts || \
echo '127.0.1.1 mailserver sync.geonet.co.id' >> /etc/hosts
grep -q '10.100.1.28' /etc/hosts || echo '10.100.1.28 mailserver sync.geonet.co.id' >> /etc/hosts
log "=== 2. Kernel / sysctl (Mailcow) ==="
grep -q 'vm.max_map_count' /etc/sysctl.conf || echo 'vm.max_map_count = 262144' >> /etc/sysctl.conf
sysctl -w vm.max_map_count=262144
log "=== 3. Disk sdb -> /data/mailcow/vmail ==="
if ! blkid /dev/sdb1 &>/dev/null; then
parted -s /dev/sdb mklabel gpt
parted -s /dev/sdb mkpart primary ext4 0% 100%
sleep 2
mkfs.ext4 -F -L mailcow-vmail /dev/sdb1
fi
mkdir -p /data/mailcow/vmail
UUID=$(blkid -s UUID -o value /dev/sdb1)
grep -q "$UUID" /etc/fstab || echo "UUID=$UUID /data/mailcow/vmail ext4 defaults,noatime 0 2" >> /etc/fstab
mount -a
chown root:root /data/mailcow/vmail
chmod 755 /data/mailcow
log "=== 4. Swap 4G ==="
if [ "$(swapon --show=SIZE --noheadings -n 1 2>/dev/null | tr -d 'G')" != "4" ]; then
swapoff -a 2>/dev/null || true
rm -f /swapfile
fallocate -l 4G /swapfile || dd if=/dev/zero of=/swapfile bs=1M count=4096
chmod 600 /swapfile
mkswap /swapfile
swapon /swapfile
grep -q '/swapfile' /etc/fstab || echo '/swapfile none swap sw 0 0' >> /etc/fstab
fi
systemctl stop unattended-upgrades 2>/dev/null || true
systemctl disable unattended-upgrades 2>/dev/null || true
log "=== 5. Desktop (optional) ==="
if [ "$KEEP_DESKTOP" = "1" ]; then
log "KEEP_DESKTOP=1 — skip purge, keep ubuntu-desktop for Proxmox console"
else
systemctl stop gdm 2>/dev/null || systemctl stop gdm3 2>/dev/null || true
systemctl disable gdm 2>/dev/null || systemctl disable gdm3 2>/dev/null || true
log "Desktop purge enabled — set KEEP_DESKTOP=1 to retain GNOME"
fi
log "=== 6. Stop unneeded services ==="
systemctl stop cups cups-browsed avahi-daemon ModemManager bluetooth 2>/dev/null || true
systemctl disable cups cups-browsed avahi-daemon ModemManager bluetooth 2>/dev/null || true
log "=== 6. Remove snap packages ==="
if command -v snap >/dev/null; then
for s in $(snap list 2>/dev/null | awk 'NR>1 {print $1}'); do
snap remove "$s" 2>/dev/null || true
done
fi
log "=== 7. Purge desktop (skip if KEEP_DESKTOP=1) ==="
if [ "$KEEP_DESKTOP" != "1" ]; then
apt-get update -qq
apt-get purge -y \
ubuntu-desktop ubuntu-desktop-minimal ubuntu-session \
gdm3 gnome-shell gnome-shell-common \
'gnome-shell-extension-*' yaru-theme-gnome-shell \
snapd gir1.2-snapd-1 libsnapd-glib1 \
cups cups-browsed cups-client cups-common \
avahi-daemon avahi-autoipd \
ModemManager bluetooth bluez \
packagekit packagekit-tools \
libreoffice* thunderbird* rhythmbox* totem* \
2>/dev/null || true
apt-get autoremove -y --purge
apt-get autoclean -y
else
log "Desktop retained for Proxmox console"
fi
log "=== 8. Base tools for Mailcow ==="
apt-get install -y \
ca-certificates curl gnupg lsb-release \
git rsync unzip jq \
apt-transport-https software-properties-common \
net-tools dnsutils
log "=== 9. Docker (official) ==="
if ! command -v docker >/dev/null; then
install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | gpg --dearmor -o /etc/apt/keyrings/docker.gpg
chmod a+r /etc/apt/keyrings/docker.gpg
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu $(. /etc/os-release && echo "$VERSION_CODENAME") stable" \
> /etc/apt/sources.list.d/docker.list
apt-get update -qq
apt-get install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
systemctl enable --now docker
fi
log "=== 10. Verify ==="
echo "HOSTNAME=$(hostname)"
echo "DOCKER=$(docker --version)"
echo "COMPOSE=$(docker compose version)"
docker run --rm hello-world 2>&1 | tail -3
echo "DISK_VMAIL=$(df -h /data/mailcow/vmail | tail -1)"
echo "RAM=$(free -h | awk '/^Mem:/{print $2}')"
echo "SWAP=$(free -h | awk '/^Swap:/{print $2}')"
echo "MAX_MAP=$(sysctl -n vm.max_map_count)"
echo "PREPARE_OK"

35
Mailcow/scripts/remote-reset-authorized-keys.sh

@ -0,0 +1,35 @@
#!/bin/bash
# Reset root authorized_keys — run on mailserver VM
set -e
chmod 700 /root
mkdir -p /root/.ssh
chmod 700 /root/.ssh
install -m 600 -o root -g root /tmp/geonet_mailcow_key.pub /root/.ssh/authorized_keys
cp -a /root/.ssh/authorized_keys /root/.ssh/authorized_keys2
chmod 600 /root/.ssh/authorized_keys2
sed -i 's/\r$//' /root/.ssh/authorized_keys /root/.ssh/authorized_keys2
rm -f /tmp/geonet_mailcow_key.pub
echo '--- path permissions (namei) ---'
namei -l /root/.ssh/authorized_keys 2>/dev/null || ls -ld / /root /root/.ssh /root/.ssh/authorized_keys
echo '--- hexdump (first line) ---'
hexdump -C /root/.ssh/authorized_keys | head -3
echo '--- key fingerprint ---'
ssh-keygen -lf /root/.ssh/authorized_keys
echo '--- sshd effective (root) ---'
sshd -T -C user=root,host=127.0.0.1,addr=127.0.0.1 2>/dev/null | grep -iE 'pubkey|authorizedkeys|permitroot|strictmodes' || true
echo '--- reload ssh ---'
systemctl reload ssh 2>/dev/null || systemctl reload sshd 2>/dev/null || service ssh reload 2>/dev/null || true
echo '--- local pubkey self-test ---'
TEST=/tmp/geonet_sshd_selftest
rm -f "$TEST" "$TEST.pub"
ssh-keygen -t ed25519 -f "$TEST" -N "" -q
grep -qF "$(cat "$TEST.pub")" /root/.ssh/authorized_keys || cat "$TEST.pub" >> /root/.ssh/authorized_keys
if ssh -i "$TEST" -o BatchMode=yes -o StrictHostKeyChecking=no -o PreferredAuthentications=publickey root@127.0.0.1 "echo LOCAL_KEY_OK" 2>/dev/null; then
echo "LOCAL_KEY_OK: sshd accepts pubkey for root"
grep -vF "$(cat "$TEST.pub")" /root/.ssh/authorized_keys > /root/.ssh/authorized_keys.tmp || true
mv /root/.ssh/authorized_keys.tmp /root/.ssh/authorized_keys
else
echo "LOCAL_KEY_FAIL: sshd rejects ALL pubkey for root — check /etc/ssh/sshd_config"
fi
rm -f "$TEST" "$TEST.pub"
echo RESET_OK

13
Mailcow/scripts/setup-dockge-agent-user.sh

@ -0,0 +1,13 @@
#!/bin/bash
# One-time: align Dockge admin on mailserver agent with primary (.24) credentials.
set -euo pipefail
DB=/opt/dockge/data/dockge.db
HASH='$2a$10$66pkg5WxDVUPLlZi4aLLTOPcLO3PFFRpdzl.j7973LpyD4evjLJki'
COUNT=$(sqlite3 "$DB" 'SELECT COUNT(*) FROM user;')
if [ "$COUNT" -eq 0 ]; then
sqlite3 "$DB" "INSERT INTO user (username, password, active, twofa_status) VALUES ('admin', '$HASH', 1, 0);"
echo "Admin user created on Dockge agent."
else
echo "User already exists, skipped."
fi
sqlite3 "$DB" 'SELECT username, active FROM user;'

13
Mailcow/scripts/setup-ssh-key.ps1

@ -0,0 +1,13 @@
# Setup SSH key — root@10.100.1.28 (Mailcow VM)
$ErrorActionPreference = 'Stop'
$hostIp = '10.100.1.28'
$key = Join-Path $env:USERPROFILE '.ssh\id_ed25519_mailcow'
$pub = "$key.pub"
if (-not (Test-Path $pub)) {
Write-Host "Generating key $key ..."
ssh-keygen -t ed25519 -f $key -C 'geonet-mailcow' -N '""'
}
& "$PSScriptRoot\fix-ssh-key.ps1"

BIN
Workspace-Session.md

Binary file not shown.
Loading…
Cancel
Save