# MAS live provider test (mail-address-semantics MAS-WP-0003). # Founder go for A-D: 2026-10-08. Temporary: delete the namespace after the test. # A namespace + MX pod B LoadBalancer :25 C statute-only ingress D no outbound mail # D superseded 2026-10-18: operator chose replies via Scaleway TEM (Secret mas-relay, # synced by ExternalSecret from OpenBao platform/suppliers/scaleway/mas-tem-relay). # IMAGE is replaced with the pushed digest at apply time. apiVersion: v1 kind: Namespace metadata: name: mas labels: {app.kubernetes.io/part-of: mail-address-semantics} --- apiVersion: v1 kind: PersistentVolumeClaim metadata: {name: mas-evidence, namespace: mas} spec: accessModes: [ReadWriteOnce] storageClassName: local-path resources: {requests: {storage: 1Gi}} --- # TEM SMTP login from OpenBao (ops-mason MASON-WP-0014, CCR-2026-0051). # OpenBao role external-secrets-mas-tem-relay and its read policy are # cluster-scoped. Retire them at MAS-WP-0003 close; deleting the namespace # does not remove them. apiVersion: v1 kind: ServiceAccount metadata: {name: mas-tem-relay-eso, namespace: mas} automountServiceAccountToken: false --- apiVersion: external-secrets.io/v1 kind: SecretStore metadata: {name: openbao-mas-tem-relay, namespace: mas} spec: provider: vault: server: http://openbao.openbao.svc:8200 path: platform version: v2 auth: kubernetes: mountPath: kubernetes role: external-secrets-mas-tem-relay serviceAccountRef: {name: mas-tem-relay-eso, audiences: [openbao]} --- apiVersion: external-secrets.io/v1 kind: ExternalSecret metadata: {name: mas-relay, namespace: mas} spec: refreshInterval: 1h secretStoreRef: {name: openbao-mas-tem-relay, kind: SecretStore} target: name: mas-relay creationPolicy: Owner # Explicit target metadata stops ESO copying the last-applied annotation, # which policy reject-secret-last-applied forbids on Secrets. template: metadata: labels: {app.kubernetes.io/name: mas-mx} data: - {secretKey: username, remoteRef: {key: suppliers/scaleway/mas-tem-relay, property: username}} - {secretKey: password, remoteRef: {key: suppliers/scaleway/mas-tem-relay, property: password}} --- apiVersion: apps/v1 kind: Deployment metadata: name: mas-mx namespace: mas labels: {app.kubernetes.io/name: mas-mx} spec: replicas: 1 strategy: {type: Recreate} selector: {matchLabels: {app.kubernetes.io/name: mas-mx}} template: metadata: {labels: {app.kubernetes.io/name: mas-mx}} spec: automountServiceAccountToken: false containers: - name: mx image: IMAGE args: [mx] # Outbound replies via Scaleway TEM, enabled only when Secret mas-relay # (keys username, password) exists. Port 25 out is blocked on the node. # MAS_MAX_SENT: lifetime cap on replies sent (counted in the ticket db). env: - {name: MAS_RELAY, value: "[smtp.tem.scaleway.com]:587"} - {name: MAS_MAX_SENT, value: "300"} ports: [{name: smtp, containerPort: 25}] volumeMounts: - {name: evidence, mountPath: /var/lib/mas} - {name: relay, mountPath: /etc/mas-relay, readOnly: true} readinessProbe: {tcpSocket: {port: 25}, periodSeconds: 10} resources: requests: {cpu: 20m, memory: 64Mi} limits: {memory: 256Mi} volumes: - name: evidence persistentVolumeClaim: {claimName: mas-evidence} - name: relay secret: {secretName: mas-relay, optional: true} --- apiVersion: v1 kind: Service metadata: {name: mas-smtp, namespace: mas} spec: type: LoadBalancer externalTrafficPolicy: Local # keep real client IPs in the Postfix log selector: {app.kubernetes.io/name: mas-mx} ports: [{name: smtp, port: 25, targetPort: 25, protocol: TCP}] --- apiVersion: apps/v1 kind: Deployment metadata: name: mas-statute namespace: mas labels: {app.kubernetes.io/name: mas-statute} spec: replicas: 1 selector: {matchLabels: {app.kubernetes.io/name: mas-statute}} template: metadata: {labels: {app.kubernetes.io/name: mas-statute}} spec: automountServiceAccountToken: false securityContext: {runAsNonRoot: true, runAsUser: 65534} containers: - name: statute image: IMAGE args: [statute] ports: [{name: http, containerPort: 8080}] readinessProbe: {httpGet: {path: /.well-known/mas, port: 8080}} resources: requests: {cpu: 5m, memory: 32Mi} limits: {memory: 64Mi} --- apiVersion: v1 kind: Service metadata: {name: mas-statute, namespace: mas} spec: selector: {app.kubernetes.io/name: mas-statute} ports: [{name: http, port: 80, targetPort: 8080}] --- apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: mas-statute namespace: mas annotations: cert-manager.io/cluster-issuer: letsencrypt-prod spec: ingressClassName: traefik rules: - host: mas.coulomb.social http: paths: - path: /.well-known/mas pathType: Exact backend: {service: {name: mas-statute, port: {number: 80}}} tls: - hosts: [mas.coulomb.social] secretName: mas-coulomb-social-tls