Add AIO Dockerfile

This commit is contained in:
nemunaire 2025-10-16 17:47:37 +07:00
commit c56fc2e6d5
15 changed files with 793 additions and 5 deletions

27
.dockerignore Normal file
View file

@ -0,0 +1,27 @@
# Git files
.git
.gitignore
# Documentation
*.md
!README.md
# Build artifacts
happyDeliver
*.db
*.sqlite
*.sqlite3
# IDE and editor files
.vscode/
.idea/
*.swp
*.swo
*~
# Logs files
logs/
# Test files
*_test.go
testdata/

3
.gitignore vendored
View file

@ -17,6 +17,9 @@ vendor/
.env.local
*.local
# Logs files
logs/
# Database files
*.db
*.sqlite

85
Dockerfile Normal file
View file

@ -0,0 +1,85 @@
# Multi-stage Dockerfile for happyDeliver with integrated MTA
# Stage 1: Build the Go application
FROM golang:1-alpine AS builder
WORKDIR /build
# Install build dependencies
RUN apk add --no-cache ca-certificates git gcc musl-dev
# Copy go mod files
COPY go.mod go.sum ./
RUN go mod download
# Copy source code
COPY . .
# Build the application
RUN CGO_ENABLED=1 GOOS=linux go build -a -installsuffix cgo -ldflags="-w -s" -o happyDeliver ./cmd/happyDeliver
# Stage 2: Runtime image with Postfix and all filters
FROM alpine:3
# Install all required packages
RUN apk add --no-cache \
bash \
ca-certificates \
opendkim \
opendkim-utils \
opendmarc \
postfix \
postfix-pcre \
postfix-policyd-spf-perl \
spamassassin \
spamassassin-client \
supervisor \
sqlite \
tzdata \
&& rm -rf /var/cache/apk/*
# Get test-only version of postfix-policyd-spf-perl
ADD https://git.nemunai.re/happyDomain/postfix-policyd-spf-perl/raw/branch/master/postfix-policyd-spf-perl /usr/bin/postfix-policyd-spf-perl
# Create happydeliver user and group
RUN addgroup -g 1000 happydeliver && \
adduser -D -u 1000 -G happydeliver happydeliver
# Create necessary directories
RUN mkdir -p /etc/happydeliver \
/var/lib/happydeliver \
/var/log/happydeliver \
/var/spool/postfix/opendkim \
/var/spool/postfix/opendmarc \
/etc/opendkim/keys \
&& chown -R happydeliver:happydeliver /var/lib/happydeliver /var/log/happydeliver \
&& chown -R opendkim:postfix /var/spool/postfix/opendkim \
&& chown -R opendmarc:postfix /var/spool/postfix/opendmarc
# Copy the built application
COPY --from=builder /build/happyDeliver /usr/local/bin/happyDeliver
RUN chmod +x /usr/local/bin/happyDeliver
# Copy configuration files
COPY docker/postfix/ /etc/postfix/
COPY docker/opendkim/ /etc/opendkim/
COPY docker/opendmarc/ /etc/opendmarc/
COPY docker/spamassassin/ /etc/mail/spamassassin/
COPY docker/supervisor/ /etc/supervisor/
COPY docker/entrypoint.sh /entrypoint.sh
RUN chmod +x /entrypoint.sh
# Expose ports
# 25 - SMTP
# 8080 - API server
EXPOSE 25 8080
# Default configuration
ENV HAPPYDELIVER_DATABASE_TYPE=sqlite HAPPYDELIVER_DATABASE_DSN=/var/lib/happydeliver/happydeliver.db HAPPYDELIVER_DOMAIN=happydeliver.local HAPPYDELIVER_ADDRESS_PREFIX=test- HAPPYDELIVER_DNS_TIMEOUT=5s HAPPYDELIVER_HTTP_TIMEOUT=10s HAPPYDELIVER_RBL=zen.spamhaus.org,bl.spamcop.net,b.barracudacentral.org,dnsbl.sorbs.net,dnsbl-1.uceprotect.net,bl.mailspike.net
# Volume for persistent data
VOLUME ["/var/lib/happydeliver", "/var/log/happydeliver"]
# Set entrypoint
ENTRYPOINT ["/entrypoint.sh"]
CMD ["supervisord", "-c", "/etc/supervisor/supervisord.conf"]

View file

@ -13,7 +13,62 @@ An open-source email deliverability testing platform that analyzes test emails a
## Quick Start
### 1. Build
### With Docker (Recommended)
The easiest way to run happyDeliver is using the all-in-one Docker container that includes Postfix, OpenDKIM, OpenDMARC, SpamAssassin, and the happyDeliver application.
#### What's included in the Docker container:
- **Postfix MTA**: Receives emails on port 25
- **OpenDKIM**: DKIM signature verification
- **OpenDMARC**: DMARC policy validation
- **SpamAssassin**: Spam scoring and analysis
- **happyDeliver API**: REST API server on port 8080
- **SQLite Database**: Persistent storage for tests and reports
#### 1. Using docker-compose
```bash
# Clone the repository
git clone https://git.nemunai.re/happyDomain/happyDeliver.git
cd happydeliver
# Edit docker-compose.yml to set your domain
# Change HAPPYDELIVER_DOMAIN and HOSTNAME environment variables
# Build and start
docker-compose up -d
# View logs
docker-compose logs -f
# Stop
docker-compose down
```
The API will be available at `http://localhost:8080` and SMTP at `localhost:25`.
#### 2. Using docker build directly
```bash
# Build the image
docker build -t happydeliver:latest .
# Run the container
docker run -d \
--name happydeliver \
-p 25:25 \
-p 8080:8080 \
-e HAPPYDELIVER_DOMAIN=yourdomain.com \
-e HOSTNAME=mail.yourdomain.com \
-v $(pwd)/data:/var/lib/happydeliver \
-v $(pwd)/logs:/var/log/happydeliver \
happydeliver:latest
```
### Manual Build
#### 1. Build
```bash
go generate
@ -28,7 +83,7 @@ go build -o happyDeliver ./cmd/happyDeliver
The server will start on `http://localhost:8080` by default.
### 3. Integrate with your existing e-mail setup
#### 3. Integrate with your existing e-mail setup
It is expected your setup annotate the email with eg. opendkim, spamassassin, ...
happyDeliver will not perform thoses checks, it relies instead on standard software to have real world annotations.
@ -84,7 +139,7 @@ Add the following line in your `/etc/postfix/aliases`:
Note that the recipient address has to be present in header.
### 4. Create a Test
#### 4. Create a Test
```bash
curl -X POST http://localhost:8080/api/test
@ -100,11 +155,11 @@ Response:
}
```
### 5. Send Test Email
#### 5. Send Test Email
Send a test email to the address provided (you'll need to configure your MTA to route emails to the analyzer - see MTA Integration below).
### 6. Get Report
#### 6. Get Report
```bash
curl http://localhost:8080/api/report/550e8400-e29b-41d4-a716-446655440000

40
docker-compose.yml Normal file
View file

@ -0,0 +1,40 @@
services:
happydeliver:
build:
context: .
dockerfile: Dockerfile
image: happydeliver:latest
container_name: happydeliver
hostname: mail.happydeliver.local
environment:
# Set your domain and hostname
DOMAIN: happydeliver.local
HOSTNAME: mail.happydeliver.local
ports:
# SMTP port
- "25:25"
# API port
- "8080:8080"
volumes:
# Persistent database storage
- ./data:/var/lib/happydeliver
# Log files
- ./logs:/var/log/happydeliver
# Optional: Override config
# - ./custom-config.yaml:/etc/happydeliver/config.yaml
restart: unless-stopped
healthcheck:
test: ["CMD", "wget", "--quiet", "--tries=1", "--spider", "http://localhost:8080/api/status"]
interval: 30s
timeout: 10s
retries: 3
start_period: 40s
volumes:
data:
logs:

164
docker/README.md Normal file
View file

@ -0,0 +1,164 @@
# happyDeliver Docker Configuration
This directory contains all configuration files for the all-in-one Docker container.
## Architecture
The Docker container integrates multiple components:
- **Postfix**: Mail Transfer Agent (MTA) that receives emails on port 25
- **OpenDKIM**: DKIM signature verification
- **OpenDMARC**: DMARC policy validation
- **SpamAssassin**: Spam scoring and content analysis
- **happyDeliver**: Go application (API server + email analyzer)
- **Supervisor**: Process manager that runs all services
## Directory Structure
```
docker/
├── postfix/
│ ├── main.cf # Postfix main configuration
│ ├── master.cf # Postfix service definitions
│ └── transport_maps # Email routing rules
├── opendkim/
│ └── opendkim.conf # DKIM verification config
├── opendmarc/
│ └── opendmarc.conf # DMARC validation config
├── spamassassin/
│ └── local.cf # SpamAssassin rules and scoring
├── supervisor/
│ └── supervisord.conf # Supervisor service definitions
├── entrypoint.sh # Container initialization script
└── config.docker.yaml # happyDeliver default config
```
## Configuration Details
### Postfix (postfix/)
**main.cf**: Core Postfix settings
- Configures hostname, domain, and network interfaces
- Sets up milter integration for OpenDKIM and OpenDMARC
- Configures SPF policy checking
- Routes emails through SpamAssassin content filter
- Uses transport_maps to route test emails to happyDeliver
**master.cf**: Service definitions
- Defines SMTP service with content filtering
- Sets up SPF policy service (postfix-policyd-spf-perl)
- Configures SpamAssassin content filter
- Defines happydeliver pipe for email analysis
**transport_maps**: PCRE-based routing
- Matches test-UUID@domain emails
- Routes them to the happydeliver pipe
### OpenDKIM (opendkim/)
**opendkim.conf**: DKIM verification settings
- Operates in verification-only mode
- Adds Authentication-Results headers
- Socket communication with Postfix via milter
- 5-second DNS timeout
### OpenDMARC (opendmarc/)
**opendmarc.conf**: DMARC validation settings
- Validates DMARC policies
- Adds results to Authentication-Results headers
- Does not reject emails (analysis mode only)
- Socket communication with Postfix via milter
### SpamAssassin (spamassassin/)
**local.cf**: Spam detection rules
- Enables network tests (RBL checks)
- SPF and DKIM checking
- Required score: 5.0 (standard threshold)
- Adds detailed spam report headers
- 5-second RBL timeout
### Supervisor (supervisor/)
**supervisord.conf**: Service orchestration
- Runs all services as daemons
- Start order: OpenDKIM → OpenDMARC → SpamAssassin → Postfix → API
- Automatic restart on failure
- Centralized logging
### Entrypoint Script (entrypoint.sh)
Initialization script that:
1. Creates required directories and sets permissions
2. Replaces configuration placeholders with environment variables
3. Initializes Postfix (aliases, transport maps)
4. Updates SpamAssassin rules
5. Starts Supervisor to launch all services
### happyDeliver Config (config.docker.yaml)
Default configuration for the Docker environment:
- API server on 0.0.0.0:8080
- SQLite database at /var/lib/happydeliver/happydeliver.db
- Configurable domain for test emails
- RBL servers for blacklist checking
- Timeouts for DNS and HTTP checks
## Environment Variables
The container accepts these environment variables:
- `DOMAIN`: Email domain for test addresses (default: happydeliver.local)
- `HOSTNAME`: Container hostname (default: mail.happydeliver.local)
Example:
```bash
docker run -e DOMAIN=example.com -e HOSTNAME=mail.example.com ...
```
## Volumes
**Required volumes:**
- `/var/lib/happydeliver`: Database and persistent data
- `/var/log/happydeliver`: Log files from all services
**Optional volumes:**
- `/etc/happydeliver/config.yaml`: Custom configuration file
## Ports
- **25**: SMTP (Postfix)
- **8080**: HTTP API (happyDeliver)
## Service Startup Order
Supervisor ensures services start in the correct order:
1. **OpenDKIM** (priority 10): DKIM verification milter
2. **OpenDMARC** (priority 11): DMARC validation milter
3. **SpamAssassin** (priority 12): Spam scoring daemon
4. **Postfix** (priority 20): MTA that uses the above services
5. **happyDeliver API** (priority 30): REST API server
## Email Processing Flow
1. Email arrives at Postfix on port 25
2. Postfix sends to OpenDKIM milter
- Verifies DKIM signature
- Adds `Authentication-Results: ... dkim=pass/fail`
3. Postfix sends to OpenDMARC milter
- Validates DMARC policy
- Adds `Authentication-Results: ... dmarc=pass/fail`
4. Postfix routes through SpamAssassin content filter
- Checks SPF record
- Scores email for spam
- Adds `X-Spam-Status` and `X-Spam-Report` headers
5. Postfix checks transport_maps
- If recipient matches test-UUID pattern, route to happydeliver pipe
6. happyDeliver analyzer receives email
- Extracts test ID from recipient
- Parses all headers added by filters
- Performs additional analysis (DNS, RBL, content)
- Generates deliverability score
- Stores report in database

66
docker/entrypoint.sh Normal file
View file

@ -0,0 +1,66 @@
#!/bin/bash
set -e
echo "Starting happyDeliver container..."
# Get environment variables with defaults
HOSTNAME="${HOSTNAME:-mail.happydeliver.local}"
HAPPYDELIVER_DOMAIN="${HAPPYDELIVER_DOMAIN:-happydeliver.local}"
echo "Hostname: $HOSTNAME"
echo "Domain: $HAPPYDELIVER_DOMAIN"
# Create runtime directories
mkdir -p /var/run/opendkim /var/run/opendmarc
chown opendkim:postfix /var/run/opendkim
chown opendmarc:postfix /var/run/opendmarc
# Create socket directories
mkdir -p /var/spool/postfix/opendkim /var/spool/postfix/opendmarc
chown opendkim:postfix /var/spool/postfix/opendkim
chown opendmarc:postfix /var/spool/postfix/opendmarc
chmod 750 /var/spool/postfix/opendkim /var/spool/postfix/opendmarc
# Create log directory
mkdir -p /var/log/happydeliver
chown happydeliver:happydeliver /var/log/happydeliver
# Replace placeholders in Postfix configuration
echo "Configuring Postfix..."
sed -i "s/__HOSTNAME__/${HOSTNAME}/g" /etc/postfix/main.cf
sed -i "s/__DOMAIN__/${HAPPYDELIVER_DOMAIN}/g" /etc/postfix/main.cf
# Replace placeholders in OpenDMARC configuration
sed -i "s/__HOSTNAME__/${HOSTNAME}/g" /etc/opendmarc/opendmarc.conf
# Initialize Postfix aliases
if [ -f /etc/postfix/aliases ]; then
echo "Initializing Postfix aliases..."
postalias /etc/postfix/aliases || true
fi
# Compile transport maps
if [ -f /etc/postfix/transport_maps ]; then
echo "Compiling transport maps..."
postmap /etc/postfix/transport_maps
fi
# Update SpamAssassin rules
echo "Updating SpamAssassin rules..."
sa-update || echo "SpamAssassin rules update failed (might be first run)"
# Compile SpamAssassin rules
sa-compile || echo "SpamAssassin compilation skipped"
# Initialize database if it doesn't exist
if [ ! -f /var/lib/happydeliver/happydeliver.db ]; then
echo "Database will be initialized on first API startup..."
fi
# Set proper permissions
chown -R happydeliver:happydeliver /var/lib/happydeliver
echo "Configuration complete, starting services..."
# Execute the main command (supervisord)
exec "$@"

View file

@ -0,0 +1,39 @@
# OpenDKIM configuration for happyDeliver
# Verifies DKIM signatures on incoming emails
# Log to syslog
Syslog yes
SyslogSuccess yes
LogWhy yes
# Run as this user and group
UserID opendkim:mail
UMask 002
# Socket for Postfix communication
Socket unix:/var/spool/postfix/opendkim/opendkim.sock
# Process ID file
PidFile /var/run/opendkim/opendkim.pid
# Operating mode - verify only (not signing)
Mode v
# Canonicalization methods
Canonicalization relaxed/simple
# DNS timeout
DNSTimeout 5
# Add header for verification results
AlwaysAddARHeader yes
# Accept unsigned mail
On-NoSignature accept
# Always add Authentication-Results header
AlwaysAddARHeader yes
# Maximum verification attempts
MaximumSignaturesToVerify 3

View file

@ -0,0 +1,41 @@
# OpenDMARC configuration for happyDeliver
# Verifies DMARC policies on incoming emails
# Socket for Postfix communication
Socket unix:/var/spool/postfix/opendmarc/opendmarc.sock
# Process ID file
PidFile /var/run/opendmarc/opendmarc.pid
# Run as this user and group
UserID opendmarc:mail
UMask 002
# Syslog configuration
Syslog true
SyslogFacility mail
# Ignore authentication results from other hosts
IgnoreAuthenticatedClients true
# Accept mail even if DMARC fails (we're analyzing, not filtering)
RejectFailures false
# Trust Authentication-Results headers from localhost only
TrustedAuthservIDs __HOSTNAME__
# Add DMARC results to Authentication-Results header
#AddAuthenticationResults true
# DNS timeout
DNSTimeout 5
# History file (for reporting)
# HistoryFile /var/spool/opendmarc/opendmarc.dat
# Ignore hosts file
# IgnoreHosts /etc/opendmarc/ignore.hosts
# Public suffix list
# PublicSuffixList /usr/share/publicsuffix/public_suffix_list.dat

10
docker/postfix/aliases Normal file
View file

@ -0,0 +1,10 @@
# Postfix aliases for happyDeliver
# This file is processed by postalias to create aliases.db
# Standard aliases
postmaster: root
abuse: root
mailer-daemon: postmaster
# Root mail can be redirected if needed
# root: admin@example.com

41
docker/postfix/main.cf Normal file
View file

@ -0,0 +1,41 @@
# Postfix main configuration for happyDeliver
# This configuration receives emails and routes them through authentication filters
# Basic settings
compatibility_level = 3.6
myhostname = __HOSTNAME__
mydomain = __DOMAIN__
myorigin = $mydomain
inet_interfaces = all
inet_protocols = ipv4
# Recipient settings
mydestination = $myhostname, localhost.$mydomain, localhost
mynetworks = 127.0.0.0/8 [::1]/128
# Relay settings - accept mail for our test domain
relay_domains = $mydomain
# Queue and size limits
message_size_limit = 10485760
mailbox_size_limit = 0
queue_minfree = 50000000
# Transport maps - route test emails to happyDeliver analyzer
transport_maps = pcre:/etc/postfix/transport_maps
# Authentication milters
# OpenDKIM for DKIM verification
milter_default_action = accept
milter_protocol = 6
smtpd_milters = unix:/var/spool/postfix/opendkim/opendkim.sock, unix:/var/spool/postfix/opendmarc/opendmarc.sock
non_smtpd_milters = $smtpd_milters
# SPF policy checking
smtpd_recipient_restrictions =
permit_mynetworks,
reject_unauth_destination,
check_policy_service unix:private/policy-spf
# Logging
debug_peer_level = 2

87
docker/postfix/master.cf Normal file
View file

@ -0,0 +1,87 @@
# Postfix master process configuration for happyDeliver
# SMTP service
smtp inet n - n - - smtpd
-o content_filter=spamassassin
# Pickup service
pickup unix n - n 60 1 pickup
# Cleanup service
cleanup unix n - n - 0 cleanup
# Queue manager
qmgr unix n - n 300 1 qmgr
# Rewrite service
rewrite unix - - n - - trivial-rewrite
# Bounce service
bounce unix - - n - 0 bounce
# Defer service
defer unix - - n - 0 bounce
# Trace service
trace unix - - n - 0 bounce
# Verify service
verify unix - - n - 1 verify
# Flush service
flush unix n - n 1000? 0 flush
# Proxymap service
proxymap unix - - n - - proxymap
# Proxywrite service
proxywrite unix - - n - 1 proxymap
# SMTP client
smtp unix - - n - - smtp
# Relay service
relay unix - - n - - smtp
# Showq service
showq unix n - n - - showq
# Error service
error unix - - n - - error
# Retry service
retry unix - - n - - error
# Discard service
discard unix - - n - - discard
# Local delivery
local unix - n n - - local
# Virtual delivery
virtual unix - n n - - virtual
# LMTP delivery
lmtp unix - - n - - lmtp
# Anvil service
anvil unix - - n - 1 anvil
# Scache service
scache unix - - n - 1 scache
# Maildrop service
maildrop unix - n n - - pipe
flags=DRXhu user=vmail argv=/usr/bin/maildrop -d ${recipient}
# SPF policy service
policy-spf unix - n n - 0 spawn
user=nobody argv=/usr/bin/postfix-policyd-spf-perl
# SpamAssassin content filter
spamassassin unix - n n - - pipe
user=mail argv=/usr/bin/spamc -f -e /usr/sbin/sendmail -oi -f ${sender} ${recipient}
# happyDeliver analyzer - receives emails matching transport_maps
happydeliver unix - n n - - pipe
flags=DRXhu user=happydeliver argv=/usr/local/bin/happyDeliver analyze -config /etc/happydeliver/config.yaml -recipient ${recipient}

View file

@ -0,0 +1,4 @@
# Transport map - route test emails to happyDeliver analyzer
# Pattern: test-<uuid>@domain.com -> happydeliver pipe
/^test-[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}@.*$/ happydeliver:

View file

@ -0,0 +1,50 @@
# SpamAssassin configuration for happyDeliver
# Scores emails for spam characteristics
# Network tests
# Enable network tests for RBL checks, Razor, Pyzor, etc.
use_network_tests 1
# RBL checks
# Enable DNS-based blacklist checks
use_rbls 1
# SPF checking
use_spf 1
# DKIM checking
use_dkim 1
# Bayes filtering
# Disable bayes learning (we're not maintaining a persistent spam database)
use_bayes 0
bayes_auto_learn 0
# Scoring thresholds
# Lower thresholds for testing purposes
required_score 5.0
# Report settings
# Add detailed spam report to headers
add_header all Status "_YESNO_, score=_SCORE_ required=_REQD_ tests=_TESTS_ autolearn=_AUTOLEARN_ version=_VERSION_"
add_header all Level _STARS(*)_
add_header all Report _REPORT_
# Rewrite subject line
rewrite_header Subject [SPAM:_SCORE_]
# Whitelisting and blacklisting
# Accept all mail for analysis (don't reject)
skip_rbl_checks 0
# Language settings
# Accept all languages
ok_languages all
# Network timeout
rbl_timeout 5
# User preferences
# Don't use user-specific rules
user_scores_dsn_timeout 3
user_scores_sql_override 0

View file

@ -0,0 +1,76 @@
[supervisord]
nodaemon=true
user=root
logfile=/var/log/happydeliver/supervisord.log
pidfile=/run/supervisord.pid
loglevel=info
[unix_http_server]
file=/run/supervisord.sock
chmod=0700
[rpcinterface:supervisor]
supervisor.rpcinterface_factory = supervisor.rpcinterface:make_main_rpcinterface
[supervisorctl]
serverurl=unix:///run/supervisord.sock
# syslogd service
[program:syslogd]
command=/sbin/syslogd -n
autostart=true
autorestart=true
priority=9
# OpenDKIM service
[program:opendkim]
command=/usr/sbin/opendkim -f -x /etc/opendkim/opendkim.conf
autostart=true
autorestart=true
priority=10
stdout_logfile=/var/log/happydeliver/opendkim.log
stderr_logfile=/var/log/happydeliver/opendkim_error.log
user=opendkim
group=mail
# OpenDMARC service
[program:opendmarc]
command=/usr/sbin/opendmarc -f -c /etc/opendmarc/opendmarc.conf
autostart=true
autorestart=true
priority=11
stdout_logfile=/var/log/happydeliver/opendmarc.log
stderr_logfile=/var/log/happydeliver/opendmarc_error.log
user=opendmarc
group=mail
# SpamAssassin daemon
[program:spamd]
command=/usr/sbin/spamd --max-children 5 --helper-home-dir /var/lib/spamassassin --syslog stderr --pidfile /var/run/spamd.pid
autostart=true
autorestart=true
priority=12
stdout_logfile=/var/log/happydeliver/spamd.log
stderr_logfile=/var/log/happydeliver/spamd_error.log
user=root
# Postfix service
[program:postfix]
command=/usr/sbin/postfix start-fg
autostart=true
autorestart=true
priority=20
stdout_logfile=/var/log/happydeliver/postfix.log
stderr_logfile=/var/log/happydeliver/postfix_error.log
user=root
# happyDeliver API server
[program:happydeliver-api]
command=/usr/local/bin/happyDeliver server -config /etc/happydeliver/config.yaml
autostart=true
autorestart=true
priority=30
stdout_logfile=/var/log/happydeliver/api.log
stderr_logfile=/var/log/happydeliver/api_error.log
user=happydeliver
environment=GIN_MODE="release"