# smtp4dev - Development SMTP Server ## Overview smtp4dev is a fake SMTP server for development and testing. It accepts all incoming email without delivering it, capturing messages for inspection via a web UI and IMAP client. All services in the Ouranos sandbox that send email (Casdoor, Gitea, etc.) are wired to smtp4dev so email flows can be tested without a real mail server. **Host:** Oberon (container_orchestration) **Web UI Port:** 22085 → `https://smtp4dev.ouranos.helu.ca` **SMTP Port:** 22025 (used by all services as `smtp_host:smtp_port`) **IMAP Port:** 22045 **Syslog Port:** 51405 (Alloy) ## Architecture ``` ┌─────────────────────────────────────────────────────────┐ │ Oberon Host │ │ │ │ ┌──────────────────────────────────────────────────┐ │ │ │ smtp4dev Container (Docker) │ │ │ │ │ │ │ │ Port 80 → host 22085 (Web UI) │ │ │ │ Port 25 → host 22025 (SMTP) │ │ │ │ Port 143 → host 22045 (IMAP) │ │ │ │ │ │ │ │ Volume: smtp4dev_data → /smtp4dev │ │ │ │ Logs: syslog → Alloy:51405 → Loki │ │ │ └──────────────────────────────────────────────────┘ │ │ │ └─────────────────────────────────────────────────────────┘ ▲ ▲ │ SMTP :22025 │ SMTP :22025 ┌──────┴──────┐ ┌──────┴──────┐ │ Casdoor │ │ Gitea │ │ (Titania) │ │ (Rosalind) │ └─────────────┘ └─────────────┘ External access: https://smtp4dev.ouranos.helu.ca → HAProxy (Titania) → oberon.incus:22085 ``` ## Shared SMTP Variables smtp4dev connection details are defined once in `ansible/inventory/group_vars/all/vars.yml` and consumed by all service templates: | Variable | Value | Purpose | |----------|-------|---------| | `smtp_host` | `oberon.incus` | SMTP server hostname | | `smtp_port` | `22025` | SMTP server port | | `smtp_from` | `noreply@ouranos.helu.ca` | Default sender address | | `smtp_from_name` | `Ouranos` | Default sender display name | Any service that needs to send email references these shared variables rather than defining its own SMTP config. This means switching to a real SMTP server only requires changing `group_vars/all/vars.yml`. ## Ansible Deployment ### Playbook ```bash # Deploy smtp4dev on Oberon ansible-playbook smtp4dev/deploy.yml # Redeploy HAProxy to activate the smtp4dev.ouranos.helu.ca backend ansible-playbook haproxy/deploy.yml ``` ### Files | File | Purpose | |------|---------| | `ansible/smtp4dev/deploy.yml` | Main deployment playbook | | `ansible/smtp4dev/docker-compose.yml.j2` | Docker Compose template | ### Deployment Steps The `deploy.yml` playbook: 1. Filters hosts — only runs on hosts with `smtp4dev` in their `services` list (Oberon) 2. Creates `smtp4dev` system group and user 3. Adds `ponos` user to the `smtp4dev` group (for `docker compose` access) 4. Creates `/srv/smtp4dev` directory owned by `smtp4dev:smtp4dev` 5. Templates `docker-compose.yml` into `/srv/smtp4dev/` 6. Resets SSH connection to apply group membership 7. Starts the service with `community.docker.docker_compose_v2: state: present` ### Host Variables Defined in `ansible/inventory/host_vars/oberon.incus.yml`: ```yaml # smtp4dev Configuration smtp4dev_user: smtp4dev smtp4dev_group: smtp4dev smtp4dev_directory: /srv/smtp4dev smtp4dev_port: 22085 # Web UI (container port 80) smtp4dev_smtp_port: 22025 # SMTP (container port 25) smtp4dev_imap_port: 22045 # IMAP (container port 143) smtp4dev_syslog_port: 51405 # Alloy syslog collector ``` ## Service Integrations ### Casdoor The Casdoor email provider is declared in `ansible/casdoor/init_data.json.j2` and seeded automatically on a **fresh** Casdoor deployment: ```json { "owner": "admin", "name": "provider-email-smtp4dev", "displayName": "smtp4dev Email", "category": "Email", "type": "SMTP", "host": "oberon.incus", "port": 22025, "disableSsl": true, "fromAddress": "noreply@ouranos.helu.ca", "fromName": "Ouranos" } ``` > ⚠️ For **existing** Casdoor installs, create the provider manually: > 1. Log in to `https://id.ouranos.helu.ca` as admin > 2. Navigate to **Identity → Providers → Add** > 3. Set **Category**: `Email`, **Type**: `SMTP` > 4. Fill host `oberon.incus`, port `22025`, disable SSL, from `noreply@ouranos.helu.ca` > 5. Save and assign the provider to the `heluca` organization under **Organizations → heluca → Edit → Default email provider** ### Gitea Configured directly in `ansible/gitea/app.ini.j2`: ```ini [mailer] ENABLED = true SMTP_ADDR = {{ smtp_host }} SMTP_PORT = {{ smtp_port }} FROM = {{ smtp_from }} ``` Redeploy Gitea to apply: ```bash ansible-playbook gitea/deploy.yml ``` ## External Access smtp4dev's web UI is exposed via HAProxy on Titania at `https://smtp4dev.ouranos.helu.ca`. Backend entry in `ansible/inventory/host_vars/titania.incus.yml`: ```yaml - subdomain: "smtp4dev" backend_host: "oberon.incus" backend_port: 22085 health_path: "/" ``` ## Verification ```bash # Check container is running ssh oberon.incus "cd /srv/smtp4dev && docker compose ps" # Check logs ssh oberon.incus "cd /srv/smtp4dev && docker compose logs --tail=50" # Test SMTP delivery (sends a test message) ssh oberon.incus "echo 'Subject: test' | sendmail -S oberon.incus:22025 test@example.com" # Check web UI is reachable internally curl -s -o /dev/null -w "%{http_code}" http://oberon.incus:22085 # Check external HTTPS route curl -sk -o /dev/null -w "%{http_code}" https://smtp4dev.ouranos.helu.ca ``` ## site.yml Order smtp4dev is deployed after Docker (it requires the Docker engine) and before Casdoor (so the SMTP endpoint exists when Casdoor initialises): ```yaml - name: Deploy Docker import_playbook: docker/deploy.yml - name: Deploy smtp4dev import_playbook: smtp4dev/deploy.yml - name: Deploy PPLG Stack # ...continues ```