diff --git a/README.md b/README.md new file mode 100644 index 0000000..831ed87 --- /dev/null +++ b/README.md @@ -0,0 +1,244 @@ +# 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.