202 lines
5.2 KiB
YAML
202 lines
5.2 KiB
YAML
# ---------------------------------------------------------------------------
|
|
# Maddy — self-hosted SMTP server for AnyDrop magic-link emails.
|
|
#
|
|
# BEFORE FIRST DEPLOYMENT, the following DNS records must exist for
|
|
# anydrop.arthurbarre.fr (OVH):
|
|
# - A mail.anydrop.arthurbarre.fr → <cluster public IP>
|
|
# - MX 10 anydrop.arthurbarre.fr → mail.anydrop.arthurbarre.fr
|
|
# - TXT anydrop.arthurbarre.fr → "v=spf1 a mx ~all"
|
|
# - TXT _dmarc.anydrop.arthurbarre.fr → "v=DMARC1; p=none; rua=mailto:arthurbarre.js@gmail.com"
|
|
#
|
|
# The DKIM public key is generated by maddy on first start and stored in the
|
|
# PVC. Extract it once with:
|
|
# kubectl -n anydrop exec -it deploy/maddy -- cat /data/dkim_keys/anydrop.arthurbarre.fr_default.dns
|
|
# Then publish it as:
|
|
# TXT default._domainkey.anydrop.arthurbarre.fr → <public key record>
|
|
#
|
|
# Also make sure PTR (reverse DNS) for the public IP points to
|
|
# mail.anydrop.arthurbarre.fr — configure at the Proxmox / ISP level.
|
|
# ---------------------------------------------------------------------------
|
|
apiVersion: v1
|
|
kind: ConfigMap
|
|
metadata:
|
|
name: maddy-config
|
|
namespace: anydrop
|
|
data:
|
|
maddy.conf: |
|
|
$(hostname) = mail.anydrop.arthurbarre.fr
|
|
$(primary_domain) = anydrop.arthurbarre.fr
|
|
$(local_domains) = $(primary_domain)
|
|
|
|
tls off
|
|
|
|
# -------------------------------------------------------------------
|
|
# Outbound pipeline — sign with DKIM, send directly to destination MX.
|
|
# -------------------------------------------------------------------
|
|
(local_routing) {
|
|
destination postmaster $(local_domains) {
|
|
reject 550 5.1.1 "No local mailboxes — outbound only"
|
|
}
|
|
default_destination {
|
|
modify {
|
|
dkim $(primary_domain) default (1024)
|
|
}
|
|
deliver_to &remote_queue
|
|
}
|
|
}
|
|
|
|
target.queue remote_queue {
|
|
target &remote_delivery
|
|
max_parallelism 16
|
|
max_tries 20
|
|
}
|
|
|
|
target.remote remote_delivery {
|
|
limits {
|
|
destination rate 20 1s
|
|
destination concurrency 10
|
|
}
|
|
mx_auth {
|
|
dane
|
|
mtasts {
|
|
cache fs
|
|
fs_dir mtasts_cache/
|
|
}
|
|
local_policy {
|
|
min_tls_level none
|
|
min_mx_level none
|
|
}
|
|
}
|
|
}
|
|
|
|
# -------------------------------------------------------------------
|
|
# SMTP submission endpoint (internal only — cluster-ip service).
|
|
# No TLS required in-cluster; the server process talks to maddy over
|
|
# the flat pod network.
|
|
# -------------------------------------------------------------------
|
|
smtp tcp://0.0.0.0:587 {
|
|
limits {
|
|
all rate 100 1s
|
|
all concurrency 50
|
|
}
|
|
source $(local_domains) {
|
|
reject 501 5.1.8 "Non-local sender refused"
|
|
}
|
|
default_source {
|
|
destination postmaster $(local_domains) {
|
|
reject 550 5.1.1 "Cannot send to local — outbound only"
|
|
}
|
|
default_destination {
|
|
modify {
|
|
dkim $(primary_domain) default (1024)
|
|
}
|
|
deliver_to &remote_queue
|
|
}
|
|
}
|
|
}
|
|
|
|
---
|
|
apiVersion: v1
|
|
kind: PersistentVolumeClaim
|
|
metadata:
|
|
name: maddy-data
|
|
namespace: anydrop
|
|
spec:
|
|
accessModes: ["ReadWriteOnce"]
|
|
resources:
|
|
requests:
|
|
storage: 1Gi
|
|
|
|
---
|
|
apiVersion: apps/v1
|
|
kind: Deployment
|
|
metadata:
|
|
name: maddy
|
|
namespace: anydrop
|
|
spec:
|
|
replicas: 1
|
|
strategy:
|
|
type: Recreate
|
|
selector:
|
|
matchLabels:
|
|
app: maddy
|
|
template:
|
|
metadata:
|
|
labels:
|
|
app: maddy
|
|
spec:
|
|
containers:
|
|
- name: maddy
|
|
image: foxcpp/maddy:0.8
|
|
args: ["-config", "/etc/maddy/maddy.conf"]
|
|
ports:
|
|
- containerPort: 587
|
|
name: submission
|
|
- containerPort: 25
|
|
name: smtp
|
|
volumeMounts:
|
|
- name: config
|
|
mountPath: /etc/maddy
|
|
- name: data
|
|
mountPath: /data
|
|
workingDir: /data
|
|
readinessProbe:
|
|
tcpSocket:
|
|
port: 587
|
|
initialDelaySeconds: 5
|
|
periodSeconds: 10
|
|
livenessProbe:
|
|
tcpSocket:
|
|
port: 587
|
|
initialDelaySeconds: 30
|
|
periodSeconds: 30
|
|
resources:
|
|
requests:
|
|
memory: "64Mi"
|
|
cpu: "50m"
|
|
limits:
|
|
memory: "256Mi"
|
|
cpu: "200m"
|
|
volumes:
|
|
- name: config
|
|
configMap:
|
|
name: maddy-config
|
|
- name: data
|
|
persistentVolumeClaim:
|
|
claimName: maddy-data
|
|
|
|
---
|
|
apiVersion: v1
|
|
kind: Service
|
|
metadata:
|
|
name: maddy
|
|
namespace: anydrop
|
|
spec:
|
|
type: ClusterIP
|
|
selector:
|
|
app: maddy
|
|
ports:
|
|
- name: submission
|
|
port: 587
|
|
targetPort: 587
|
|
|
|
---
|
|
# Optional: expose port 25 on a NodePort if you want maddy to also receive
|
|
# inbound mail (bounces, replies). For Phase 1 outbound-only, this can stay
|
|
# commented out — direct-to-MX delivery does not require inbound.
|
|
#
|
|
# apiVersion: v1
|
|
# kind: Service
|
|
# metadata:
|
|
# name: maddy-smtp
|
|
# namespace: anydrop
|
|
# spec:
|
|
# type: NodePort
|
|
# selector:
|
|
# app: maddy
|
|
# ports:
|
|
# - name: smtp
|
|
# port: 25
|
|
# targetPort: 25
|
|
# nodePort: 30025
|