Initial commit: Postfix mail server with SES relay
- Containerized Postfix configuration for mailing list management - Environment-based configuration for SES credentials - Template-based config generation for flexibility - Static virtual aliases (Phase 1) - Prepared for future web interface and SQL backend (Phase 2+) Features: - Docker Compose orchestration - Secure credential management via .env - Configurable SMTP host/port - Git-ignored sensitive files - Comprehensive documentation
This commit is contained in:
11
.env.example
Normal file
11
.env.example
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
# SES/SMTP Configuration
|
||||||
|
# Copy this file to .env and fill in your actual credentials
|
||||||
|
|
||||||
|
# Required: Your AWS SES credentials
|
||||||
|
SES_USER=your_ses_access_key_id
|
||||||
|
SES_PASS=your_ses_secret_access_key
|
||||||
|
|
||||||
|
# Optional: SMTP server configuration
|
||||||
|
# Default is EU West 2 - change if using different region
|
||||||
|
SMTP_HOST=email-smtp.eu-west-2.amazonaws.com
|
||||||
|
SMTP_PORT=587
|
||||||
97
.github/copilot-instructions.md
vendored
Normal file
97
.github/copilot-instructions.md
vendored
Normal file
@@ -0,0 +1,97 @@
|
|||||||
|
# Copilot Instructions: Mail List Manager (Postfix + SES + Web Interface)
|
||||||
|
|
||||||
|
## Architecture Overview
|
||||||
|
|
||||||
|
This is a containerized mailing list management system built around Postfix as an SMTP relay through Amazon SES. **Currently in Phase 1** with static configuration; **planned expansion** includes web frontend and SQL database for dynamic member management.
|
||||||
|
|
||||||
|
**Current Components (Phase 1):**
|
||||||
|
- `docker-compose.yaml`: Single-service orchestration with SES credentials
|
||||||
|
- `postfix/`: Complete Postfix container configuration
|
||||||
|
- Static virtual aliases system for mailing list distribution
|
||||||
|
|
||||||
|
**Planned Architecture (Phase 2+):**
|
||||||
|
- Web frontend for list management (view/add/remove members)
|
||||||
|
- SQL database backend for member storage
|
||||||
|
- Dynamic Postfix configuration generation
|
||||||
|
- Multi-service Docker Compose setup
|
||||||
|
|
||||||
|
## Configuration Pattern
|
||||||
|
|
||||||
|
**Current (Static):** Environment variable substitution for secure credential management:
|
||||||
|
|
||||||
|
1. **Credentials Flow**: SES credentials passed via environment → `sasl_passwd.template` → runtime generation in `entrypoint.sh`
|
||||||
|
2. **Config Files**: Static configs (`main.cf`, `virtual_aliases.cf`) + dynamic SASL auth file
|
||||||
|
3. **Postfix Maps**: Hash databases generated at build time (virtual aliases) and runtime (SASL)
|
||||||
|
|
||||||
|
**Future (Dynamic):** Database-driven configuration:
|
||||||
|
- Member lists stored in SQL database
|
||||||
|
- Web interface for CRUD operations on members
|
||||||
|
- `virtual_aliases.cf` generated from database at runtime
|
||||||
|
- Postfix reload triggered by configuration changes
|
||||||
|
|
||||||
|
## Key Files and Their Roles
|
||||||
|
|
||||||
|
- `main.cf`: Core Postfix config - relay through SES, domain settings, security
|
||||||
|
- `sasl_passwd.template`: Template for SES authentication (uses `${SES_USER}:${SES_PASS}`)
|
||||||
|
- `virtual_aliases.cf`: Static email forwarding rules (one mailing list currently)
|
||||||
|
- `entrypoint.sh`: Runtime credential processing and Postfix startup
|
||||||
|
|
||||||
|
## Development Workflows
|
||||||
|
|
||||||
|
**Building and Running:**
|
||||||
|
```bash
|
||||||
|
docker-compose up --build # Build and start mail server
|
||||||
|
docker-compose logs -f # Monitor mail delivery logs
|
||||||
|
```
|
||||||
|
|
||||||
|
**Adding Mailing Lists (Current):**
|
||||||
|
1. Edit `virtual_aliases.cf` with new list → recipients mapping
|
||||||
|
2. Rebuild container (postmap runs at build time)
|
||||||
|
3. No restart needed for existing virtual alias changes
|
||||||
|
|
||||||
|
**Future Web Interface Workflow:**
|
||||||
|
1. Access web frontend at configured port
|
||||||
|
2. Use CRUD interface to manage mailing lists and members
|
||||||
|
3. Changes automatically update database and regenerate Postfix configs
|
||||||
|
|
||||||
|
**Testing Mail Delivery:**
|
||||||
|
```bash
|
||||||
|
# From inside container
|
||||||
|
echo "Test message" | mail -s "Subject" community@lists.sasalliance.org
|
||||||
|
|
||||||
|
# Check logs for SES relay status
|
||||||
|
docker-compose logs postfix | grep -E "(sent|bounced|deferred)"
|
||||||
|
```
|
||||||
|
|
||||||
|
## Security Considerations
|
||||||
|
|
||||||
|
- SES credentials exposed in docker-compose.yaml (consider using Docker secrets)
|
||||||
|
- SASL password file permissions set to 600 in entrypoint
|
||||||
|
- TLS encryption enforced for SES relay (`smtp_tls_security_level = encrypt`)
|
||||||
|
- Only localhost and configured hostname accepted for local delivery
|
||||||
|
|
||||||
|
## Configuration Conventions
|
||||||
|
|
||||||
|
- **Hostname Pattern**: `lists.sasalliance.org` for mailing lists, origin domain `sasalliance.org`
|
||||||
|
- **Virtual Aliases**: Simple format `listname@lists.domain → recipient1, recipient2`
|
||||||
|
- **SES Region**: EU West 2 (`email-smtp.eu-west-2.amazonaws.com`)
|
||||||
|
- **Port Mapping**: Standard SMTP port 25 exposed to host
|
||||||
|
|
||||||
|
## Common Modifications
|
||||||
|
|
||||||
|
**Adding Recipients to Existing List (Current):**
|
||||||
|
Edit `virtual_aliases.cf`, add comma-separated emails, rebuild container.
|
||||||
|
|
||||||
|
**New Mailing List (Current):**
|
||||||
|
Add line to `virtual_aliases.cf`: `newlist@lists.sasalliance.org recipients...`
|
||||||
|
|
||||||
|
**Credential Updates:**
|
||||||
|
Update `SES_USER`/`SES_PASS` in docker-compose.yaml, restart container.
|
||||||
|
|
||||||
|
## Migration Considerations
|
||||||
|
|
||||||
|
When implementing the web frontend and database backend:
|
||||||
|
- Preserve existing `community@lists.sasalliance.org` list during migration
|
||||||
|
- Consider migration script to import current virtual aliases into database
|
||||||
|
- Plan for zero-downtime deployment with database-driven config generation
|
||||||
|
- Web interface should validate email addresses and handle duplicate members
|
||||||
15
.gitignore
vendored
Normal file
15
.gitignore
vendored
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
# Environment variables with sensitive credentials
|
||||||
|
.env
|
||||||
|
|
||||||
|
# Docker build artifacts
|
||||||
|
.dockerignore
|
||||||
|
|
||||||
|
# IDE files
|
||||||
|
.vscode/
|
||||||
|
.idea/
|
||||||
|
*.swp
|
||||||
|
*.swo
|
||||||
|
|
||||||
|
# OS files
|
||||||
|
.DS_Store
|
||||||
|
Thumbs.db
|
||||||
21
LICENSE
Normal file
21
LICENSE
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2024 Mail List Manager
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
124
README.md
Normal file
124
README.md
Normal file
@@ -0,0 +1,124 @@
|
|||||||
|
# Mail List Manager
|
||||||
|
|
||||||
|
A containerized mailing list management system built around Postfix as an SMTP relay through Amazon SES.
|
||||||
|
|
||||||
|
## Architecture
|
||||||
|
|
||||||
|
**Current (Phase 1):** Static configuration with environment-based credentials
|
||||||
|
- Postfix container configured as SES relay
|
||||||
|
- Static virtual aliases for mailing list distribution
|
||||||
|
- Environment variable configuration for security
|
||||||
|
|
||||||
|
**Planned (Phase 2+):** Web interface with SQL backend
|
||||||
|
- Web frontend for list management (view/add/remove members)
|
||||||
|
- SQL database for member storage
|
||||||
|
- Dynamic Postfix configuration generation
|
||||||
|
|
||||||
|
## Quick Start
|
||||||
|
|
||||||
|
1. Copy the environment template:
|
||||||
|
```bash
|
||||||
|
cp .env.example .env
|
||||||
|
```
|
||||||
|
|
||||||
|
2. Edit `.env` with your SES credentials and configuration:
|
||||||
|
```bash
|
||||||
|
# Required: Your SES credentials
|
||||||
|
SES_USER=your_ses_access_key
|
||||||
|
SES_PASS=your_ses_secret_key
|
||||||
|
|
||||||
|
# Optional: SMTP configuration (defaults to EU West 2)
|
||||||
|
SMTP_HOST=email-smtp.eu-west-2.amazonaws.com
|
||||||
|
SMTP_PORT=587
|
||||||
|
```
|
||||||
|
|
||||||
|
3. Build and start the mail server:
|
||||||
|
```bash
|
||||||
|
docker-compose up --build
|
||||||
|
```
|
||||||
|
|
||||||
|
4. Test mail delivery:
|
||||||
|
```bash
|
||||||
|
# From inside container
|
||||||
|
docker-compose exec postfix bash
|
||||||
|
echo "Test message" | mail -s "Subject" community@lists.sasalliance.org
|
||||||
|
|
||||||
|
# Check logs
|
||||||
|
docker-compose logs -f postfix
|
||||||
|
```
|
||||||
|
|
||||||
|
## Configuration
|
||||||
|
|
||||||
|
### Adding Mailing Lists (Current)
|
||||||
|
|
||||||
|
Edit `postfix/virtual_aliases.cf`:
|
||||||
|
```
|
||||||
|
newlist@lists.sasalliance.org recipient1@domain.com, recipient2@domain.com
|
||||||
|
```
|
||||||
|
|
||||||
|
Then rebuild the container:
|
||||||
|
```bash
|
||||||
|
docker-compose up --build
|
||||||
|
```
|
||||||
|
|
||||||
|
### Domain Configuration
|
||||||
|
|
||||||
|
The system is configured for:
|
||||||
|
- **Hostname**: `lists.sasalliance.org` (mailing lists)
|
||||||
|
- **Origin Domain**: `sasalliance.org`
|
||||||
|
- **SES Region**: EU West 2 (configurable via `SMTP_HOST`)
|
||||||
|
|
||||||
|
## Security
|
||||||
|
|
||||||
|
- SES credentials are stored in `.env` (git-ignored)
|
||||||
|
- SASL password files have restricted permissions (600)
|
||||||
|
- TLS encryption enforced for SES relay
|
||||||
|
- Only localhost and configured hostname accepted for local delivery
|
||||||
|
|
||||||
|
## Development
|
||||||
|
|
||||||
|
### Project Structure
|
||||||
|
```
|
||||||
|
├── docker-compose.yaml # Service orchestration
|
||||||
|
├── .env # Environment configuration (not in git)
|
||||||
|
├── postfix/
|
||||||
|
│ ├── Dockerfile # Postfix container build
|
||||||
|
│ ├── entrypoint.sh # Runtime configuration processing
|
||||||
|
│ ├── main.cf.template # Postfix main configuration template
|
||||||
|
│ ├── sasl_passwd.template # SES authentication template
|
||||||
|
│ └── virtual_aliases.cf # Static mailing list definitions
|
||||||
|
└── .github/
|
||||||
|
└── copilot-instructions.md # AI agent guidance
|
||||||
|
```
|
||||||
|
|
||||||
|
### Environment Variables
|
||||||
|
- `SES_USER`: AWS SES access key ID
|
||||||
|
- `SES_PASS`: AWS SES secret access key
|
||||||
|
- `SMTP_HOST`: SMTP server hostname (default: email-smtp.eu-west-2.amazonaws.com)
|
||||||
|
- `SMTP_PORT`: SMTP server port (default: 587)
|
||||||
|
|
||||||
|
### Debugging
|
||||||
|
|
||||||
|
Monitor mail delivery:
|
||||||
|
```bash
|
||||||
|
# View all logs
|
||||||
|
docker-compose logs -f
|
||||||
|
|
||||||
|
# Filter for delivery status
|
||||||
|
docker-compose logs postfix | grep -E "(sent|bounced|deferred)"
|
||||||
|
|
||||||
|
# Check Postfix queue
|
||||||
|
docker-compose exec postfix postqueue -p
|
||||||
|
```
|
||||||
|
|
||||||
|
## Roadmap
|
||||||
|
|
||||||
|
- [ ] Web frontend for mailing list management
|
||||||
|
- [ ] SQL database backend for member storage
|
||||||
|
- [ ] Dynamic configuration generation from database
|
||||||
|
- [ ] Multi-service Docker Compose architecture
|
||||||
|
- [ ] Migration tools for static → dynamic configuration
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
MIT License - see LICENSE file for details.
|
||||||
8
docker-compose.yaml
Normal file
8
docker-compose.yaml
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
version: "3.9"
|
||||||
|
services:
|
||||||
|
postfix:
|
||||||
|
build: ./postfix
|
||||||
|
container_name: postfix
|
||||||
|
env_file: .env
|
||||||
|
ports:
|
||||||
|
- "25:25"
|
||||||
25
postfix/Dockerfile
Normal file
25
postfix/Dockerfile
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
FROM debian:stable-slim
|
||||||
|
|
||||||
|
# Install Postfix and tools
|
||||||
|
RUN apt-get update && \
|
||||||
|
DEBIAN_FRONTEND=noninteractive apt-get install -y \
|
||||||
|
postfix \
|
||||||
|
libsasl2-modules \
|
||||||
|
mailutils \
|
||||||
|
gettext-base \
|
||||||
|
&& apt-get clean && rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
|
# Copy configs
|
||||||
|
COPY main.cf.template /etc/postfix/main.cf.template
|
||||||
|
COPY sasl_passwd.template /etc/postfix/sasl_passwd.template
|
||||||
|
COPY virtual_aliases.cf /etc/postfix/virtual_aliases.cf
|
||||||
|
COPY entrypoint.sh /entrypoint.sh
|
||||||
|
RUN chmod +x /entrypoint.sh
|
||||||
|
|
||||||
|
# Generate Postfix maps for virtual aliases
|
||||||
|
RUN postmap /etc/postfix/virtual_aliases.cf
|
||||||
|
|
||||||
|
# Expose SMTP
|
||||||
|
EXPOSE 25
|
||||||
|
|
||||||
|
ENTRYPOINT ["/entrypoint.sh"]
|
||||||
15
postfix/entrypoint.sh
Normal file
15
postfix/entrypoint.sh
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
set -e
|
||||||
|
|
||||||
|
# Generate main.cf from template with environment variables
|
||||||
|
envsubst < /etc/postfix/main.cf.template > /etc/postfix/main.cf
|
||||||
|
|
||||||
|
# Generate SASL password file from environment variables
|
||||||
|
envsubst < /etc/postfix/sasl_passwd.template > /etc/postfix/sasl_passwd
|
||||||
|
|
||||||
|
# Generate Postfix hash
|
||||||
|
postmap /etc/postfix/sasl_passwd
|
||||||
|
chmod 600 /etc/postfix/sasl_passwd /etc/postfix/sasl_passwd.db
|
||||||
|
|
||||||
|
# Start Postfix in foreground
|
||||||
|
exec postfix start-fg
|
||||||
21
postfix/main.cf.template
Normal file
21
postfix/main.cf.template
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
# Basic
|
||||||
|
myhostname = lists.sasalliance.org
|
||||||
|
myorigin = sasalliance.org
|
||||||
|
mydestination = $myhostname, localhost.$mydomain, localhost
|
||||||
|
|
||||||
|
# Relay through SES
|
||||||
|
relayhost = [${SMTP_HOST}]:${SMTP_PORT}
|
||||||
|
smtp_tls_security_level = encrypt
|
||||||
|
smtp_tls_note_starttls_offer = yes
|
||||||
|
|
||||||
|
# SASL auth for SES
|
||||||
|
smtp_sasl_auth_enable = yes
|
||||||
|
smtp_sasl_password_maps = hash:/etc/postfix/sasl_passwd
|
||||||
|
smtp_sasl_security_options = noanonymous
|
||||||
|
|
||||||
|
# Virtual aliases (static for now)
|
||||||
|
virtual_alias_maps = hash:/etc/postfix/virtual_aliases.cf
|
||||||
|
|
||||||
|
# Other recommended settings
|
||||||
|
alias_maps = hash:/etc/aliases
|
||||||
|
alias_database = hash:/etc/aliases
|
||||||
1
postfix/sasl_passwd.template
Normal file
1
postfix/sasl_passwd.template
Normal file
@@ -0,0 +1 @@
|
|||||||
|
[${SMTP_HOST}]:${SMTP_PORT} ${SES_USER}:${SES_PASS}
|
||||||
1
postfix/virtual_aliases.cf
Normal file
1
postfix/virtual_aliases.cf
Normal file
@@ -0,0 +1 @@
|
|||||||
|
community@lists.sasalliance.org james@pattinson.org, james.pattinson@sasalliance.org
|
||||||
Reference in New Issue
Block a user