# MTA-STS Policy Server A lightweight nginx-based server that hosts MTA-STS (Mail Transfer Agent Strict Transport Security) policies for multiple domains, ensuring secure email delivery through enforced TLS encryption. ## What is MTA-STS? MTA-STS is a security standard ([RFC 8461](https://tools.ietf.org/html/rfc8461)) that helps protect email in transit by: - **Enforcing TLS encryption** for email delivery between mail servers - **Preventing downgrade attacks** that could force unencrypted connections - **Validating certificates** to ensure emails are delivered to legitimate servers - **Providing a policy** that sending servers can cache and enforce ## Purpose This repository serves MTA-STS policies for all domains under the `mifi-holdings` organization. Each domain has a subdomain `mta-sts.` (e.g., `mta-sts.mifi.holdings`) that serves: 1. **Policy file**: `/.well-known/mta-sts.txt` - defines the MTA-STS policy 2. **Landing page**: `/` - provides information about the service ### Domains Covered This server currently provides MTA-STS protection for: - mifi.holdings - mifi.com.br - mifi.dev - mifi.ventures - mifi.vix.br - mifi.me - blackice.vix.br - fitz.guru - umlautpress.com - camilla-rena.com - officelift.net - mylocalpro.biz - mylocalpro.online - happybeardedcarpenter.com - thenewenglandpalletguy.com - dining-it.com ## Architecture ### Components ``` ┌─────────────────────────────────────────┐ │ Docker Container │ │ ┌───────────────────────────────────┐ │ │ │ nginx:alpine │ │ │ │ │ │ │ │ /etc/nginx/conf.d/default.conf │ │ │ │ /usr/share/nginx/html/ │ │ │ │ ├── index.html │ │ │ │ └── .well-known/ │ │ │ │ └── mta-sts.txt │ │ │ └───────────────────────────────────┘ │ └─────────────────────────────────────────┘ │ ▼ ┌─────────────┐ │ Traefik │ (Reverse Proxy) └─────────────┘ │ ▼ Multiple mta-sts.* domains ``` ### How It Works 1. **Build**: Docker image is built with nginx, custom configuration, and static HTML content baked in 2. **Registry**: Image is pushed to `git.mifi.dev/mifi-holdings/mta-sts` 3. **Deploy**: Portainer pulls the latest image and restarts the container 4. **Routing**: Traefik routes all `mta-sts.*` domains to the container 5. **Serving**: nginx serves the policy file and landing page ## Project Structure ``` . ├── Dockerfile # Docker image definition ├── docker-compose.yml # Portainer stack configuration ├── package.json # Build and push scripts ├── nginx/ │ └── default.conf # nginx server configuration ├── html/ │ ├── index.html # Landing page │ └── .well-known/ │ └── mta-sts.txt # MTA-STS policy file └── .woodpecker/ ├── build.yaml # CI: Build and push image └── deploy.yaml # CI: Trigger Portainer webhook ``` ## Setup & Deployment ### Prerequisites - Docker installed and logged into `git.mifi.dev` - pnpm (for manual builds) - Access to the Gitea package registry - Portainer webhook configured (for deployments) ### Local Development 1. **Clone the repository**: ```bash git clone cd mta-sts ``` 2. **Build the Docker image**: ```bash pnpm build ``` 3. **Test locally**: ```bash docker run -p 8080:80 git.mifi.dev/mifi-holdings/mta-sts:latest ``` Visit `http://localhost:8080` to see the landing page and `http://localhost:8080/.well-known/mta-sts.txt` for the policy file. ### Manual Deployment 1. **Build and push**: ```bash pnpm build:push ``` 2. **Trigger Portainer webhook** (or let Portainer auto-pull): ```bash curl -X POST ``` ### Automated CI/CD The repository uses Woodpecker CI for automated builds and deployments: #### Build Pipeline (`.woodpecker/build.yaml`) Triggers on push to `main` branch: 1. **Build Docker image** with commit SHA and `latest` tags 2. **Send Discord notification** about build status 3. **Push to registry** at `git.mifi.dev/mifi-holdings/mta-sts` 4. **Send Discord notification** about push status #### Deploy Pipeline (`.woodpecker/deploy.yaml`) Depends on successful build: 1. **Trigger Portainer webhook** to redeploy the stack 2. **Send Discord notification** about deployment status ### Updating the Policy To modify the MTA-STS policy: 1. Edit `html/.well-known/mta-sts.txt` 2. Commit and push to `main` 3. Woodpecker CI will automatically build and deploy **Important**: The `max_age` directive in the policy determines how long mail servers will cache the policy. Plan policy changes accordingly. ### Adding New Domains To add MTA-STS support for a new domain: 1. Add the domain's MX records to `html/.well-known/mta-sts.txt` 2. Add Traefik labels in `docker-compose.yml`: ```yaml - "traefik.http.routers.mta-sts-.rule=Host(`mta-sts.`)" - "traefik.http.routers.mta-sts-.entrypoints=websecure" - "traefik.http.routers.mta-sts-.tls=true" - "traefik.http.routers.mta-sts-.tls.certresolver=letsencrypt" - "traefik.http.routers.mta-sts-.service=mta-sts-" - "traefik.http.services.mta-sts-.loadbalancer.server.port=80" ``` 3. Ensure DNS is configured with `mta-sts.` pointing to your server 4. Add a TXT record `_mta-sts.` with value `v=STSv1; id=` ## Package Scripts | Script | Description | |--------|-------------| | `pnpm build` | Build Docker image locally | | `pnpm push` | Push Docker image to registry | | `pnpm build:push` | Build and push in one command | ## Technology Stack - **nginx:alpine** - Lightweight web server (base image) - **Docker** - Containerization - **Traefik** - Reverse proxy and SSL termination - **Portainer** - Container orchestration - **Woodpecker CI** - Automated build and deployment - **Gitea** - Package registry ## Health Checks The container includes health checks that: - Run every 30 seconds - Check if nginx is responding on port 80 - Allow 10 seconds for startup - Retry 3 times before marking as unhealthy ## Security Considerations - All content is served over HTTPS (enforced by Traefik) - MTA-STS policy enforces TLS for mail delivery - No sensitive data is stored in the container - nginx runs as non-root user (alpine default) - Container filesystem is read-only safe (all content is static) ## Troubleshooting ### Container won't start - Check Portainer logs for the `mta-sts` container - Verify the image was pushed successfully to the registry - Ensure Traefik network exists: `docker network ls | grep traefik` ### Policy not being respected - Verify DNS TXT record `_mta-sts.` exists - Check that the policy file is accessible at `https://mta-sts./.well-known/mta-sts.txt` - Ensure the certificate is valid (no HTTPS errors) - Wait for cache expiry if you recently changed the policy ### Build fails in Woodpecker - Check Docker daemon is accessible - Verify registry credentials are configured in Woodpecker secrets - Review build logs for specific error messages ## License ISC ## Contributing 1. Create a feature branch 2. Make your changes 3. Test locally with `pnpm build` 4. Submit a pull request to `main` Changes merged to `main` will automatically deploy via Woodpecker CI.