26 changed files with 2242 additions and 0 deletions
@ -0,0 +1,93 @@
@@ -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 |
||||
@ -0,0 +1,15 @@
@@ -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/ |
||||
Binary file not shown.
@ -0,0 +1,15 @@
@@ -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) |
||||
@ -0,0 +1,149 @@
@@ -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` |
||||
@ -0,0 +1,150 @@
@@ -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` |
||||
@ -0,0 +1,23 @@
@@ -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) |
||||
@ -0,0 +1,248 @@
@@ -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/ |
||||
@ -0,0 +1,74 @@
@@ -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`. |
||||
@ -0,0 +1,204 @@
@@ -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` |
||||
@ -0,0 +1,47 @@
@@ -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 |
||||
Binary file not shown.
Binary file not shown.
@ -0,0 +1,41 @@
@@ -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" |
||||
@ -0,0 +1,33 @@
@@ -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" |
||||
@ -0,0 +1,88 @@
@@ -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" |
||||
Binary file not shown.
@ -0,0 +1,116 @@
@@ -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" |
||||
@ -0,0 +1,35 @@
@@ -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 |
||||
@ -0,0 +1,13 @@
@@ -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;' |
||||
@ -0,0 +1,13 @@
@@ -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" |
||||
Binary file not shown.
Loading…
Reference in new issue