README!
This commit is contained in:
244
README.md
Normal file
244
README.md
Normal file
@@ -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.<domain>` (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 <repo-url>
|
||||
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 <PORTAINER_WEBHOOK_URL>
|
||||
```
|
||||
|
||||
### 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-<domain-slug>.rule=Host(`mta-sts.<domain>`)"
|
||||
- "traefik.http.routers.mta-sts-<domain-slug>.entrypoints=websecure"
|
||||
- "traefik.http.routers.mta-sts-<domain-slug>.tls=true"
|
||||
- "traefik.http.routers.mta-sts-<domain-slug>.tls.certresolver=letsencrypt"
|
||||
- "traefik.http.routers.mta-sts-<domain-slug>.service=mta-sts-<domain-slug>"
|
||||
- "traefik.http.services.mta-sts-<domain-slug>.loadbalancer.server.port=80"
|
||||
```
|
||||
3. Ensure DNS is configured with `mta-sts.<domain>` pointing to your server
|
||||
4. Add a TXT record `_mta-sts.<domain>` with value `v=STSv1; id=<unique-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.<domain>` exists
|
||||
- Check that the policy file is accessible at `https://mta-sts.<domain>/.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.
|
||||
Reference in New Issue
Block a user