Files
ouranos/docs/smtp4dev.md
Robert Helewka b4d60f2f38 docs: rewrite README with structured overview and quick start guide
Replaces the minimal project description with a comprehensive README
including a component overview table, quick start instructions, common
Ansible operations, and links to detailed documentation. Aligns with
Red Panda Approval™ standards.
2026-03-03 12:49:06 +00:00

192 lines
6.7 KiB
Markdown

# 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 Agathos 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` | `Agathos` | 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": "Agathos"
}
```
> ⚠️ 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
```