# --------------------------------------------------------------------------- # 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 → # - 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 → # # 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: | # Global directives — both the top-level `hostname`/`tls` statements AND # the macros of the same name are required. Macros are substituted into # module blocks below; the bare directives configure the process. $(hostname) = mail.anydrop.arthurbarre.fr $(primary_domain) = anydrop.arthurbarre.fr $(local_domains) = $(primary_domain) hostname $(hostname) tls off # Outbound delivery pipeline ---------------------------------------------- target.queue local_queue { target &remote_delivery autogenerated_msg_domain $(primary_domain) bounce { destination postmaster $(local_domains) { reject 550 5.0.0 "Bounces ignored — outbound only" } default_destination { reject 550 5.0.0 "Bounces ignored — outbound only" } } } 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 listener — internal ClusterIP service only. # No auth enforced: the service is not reachable outside the cluster # network. If you ever expose this externally, add an `auth` block. smtp tcp://0.0.0.0:587 { hostname $(hostname) tls off limits { all rate 100 1s all concurrency 50 } source $(local_domains) { destination postmaster $(local_domains) { reject 550 5.1.1 "Local delivery disabled" } default_destination { modify { dkim $(primary_domain) default } deliver_to &local_queue } } default_source { reject 501 5.1.8 "Non-local sender refused" } } --- 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", "run"] 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