locals { haraka-labels = merge(local.common-labels, { "app.kubernetes.io/component" = "haraka" }) } resource "kubectl_manifest" "haraka_deploy" { yaml_body = <<-EOF apiVersion: apps/v1 kind: Deployment metadata: name: "${var.instance}-haraka" namespace: "${var.namespace}" labels: ${jsonencode(local.haraka-labels)} spec: replicas: 1 selector: matchLabels: ${jsonencode(local.haraka-labels)} template: metadata: labels: ${jsonencode(local.haraka-labels)} spec: securityContext: fsGroup: 1000 containers: - name: haraka securityContext: capabilities: drop: - ALL readOnlyRootFilesystem: true runAsNonRoot: true runAsUser: 1000 image: "${var.images.haraka.registry}/${var.images.haraka.repository}:${var.images.haraka.tag}" imagePullPolicy: "${var.images.haraka.pullPolicy}" ports: - name: smtp containerPort: 2500 protocol: TCP livenessProbe: tcpSocket: port: smtp initialDelaySeconds: 20 periodSeconds: 30 readinessProbe: tcpSocket: port: smtp initialDelaySeconds: 20 periodSeconds: 30 resources: {} volumeMounts: - name: config mountPath: /app/config/me subPath: me - name: config mountPath: /app/config/host_list subPath: host_list - name: config mountPath: /app/config/rspamd.ini subPath: rspamd.ini - name: config mountPath: /app/config/wildduck.yaml subPath: wildduck.yaml - name: config mountPath: /app/config/smtp.ini subPath: smtp.ini - name: config mountPath: /app/config/dkim_sign.ini subPath: dkim_sign.ini - name: tls mountPath: /app/config/tls_key.pem subPath: tls.key - name: tls mountPath: /app/config/tls_cert.pem subPath: tls.crt - mountPath: /app/queue name: queue volumes: - name: queue emptyDir: {} - name: config configMap: name: "${var.instance}-haraka" - name: tls secret: secretName: "${var.instance}-cert" EOF } resource "kubectl_manifest" "haraka_config" { yaml_body = <<-EOF apiVersion: v1 kind: ConfigMap metadata: name: "${var.instance}-haraka" namespace: "${var.namespace}" labels: ${jsonencode(local.haraka-labels)} data: me: |- ${var.sub-domain}.${var.domain-name} host_list: |- # add hosts in here we want to accept mail for ${var.sub-domain}.${var.domain-name} ${var.domain-name} ${join("\n ",var.additional-domains)} rspamd.ini: |- host = ${var.instance}-rspamd.${var.namespace}.svc.cluster.local port = 11333 add_headers = always [dkim] enabled = true [header] bar = X-Rspamd-Bar report = X-Rspamd-Report score = X-Rspamd-Score spam = X-Rspamd-Spam [check] authenticated=true private_ip=true [reject] spam = false [soft_reject] enabled = true [rmilter_headers] enabled = true [spambar] positive = + negative = - neutral = /' smtp.ini: |- listen=[::0]:2500 ; public IP address (default: none) ; If your machine is behind a NAT, some plugins (SPF, GeoIP) gain features ; if they know the servers public IP. If 'stun' is installed, Haraka will ; try to figure it out. If that doesn't work, set it here. ;public_ip=N.N.N.N ; Time in seconds to let sockets be idle with no activity ;inactivity_timeout=300 ; Drop privileges to this user/group ;user=smtp ;group=smtp ; Don't stop Haraka if plugins fail to compile ;ignore_bad_plugins=0 ; Run using cluster to fork multiple backend processes ;nodes=cpus ; Daemonize ;daemonize=true ;daemon_log_file=/var/log/haraka.log ;daemon_pid_file=/var/run/haraka.pid ; Spooling ; Save memory by spooling large messages to disk ;spool_dir=/var/spool/haraka ; Specify -1 to never spool to disk ; Specify 0 to always spool to disk ; Otherwise specify a size in bytes, once reached the ; message will be spooled to disk to save memory. ;spool_after= ; Force Shutdown Timeout ; - Haraka tries to close down gracefully, but if everything is shut down ; after this time it will hard close. 30s is usually long enough to ; wait for outbound connections to finish. ;force_shutdown_timeout=30 ; SMTP service extensions: https://tools.ietf.org/html/rfc1869 ; strict_rfc1869 = false ; Advertise support for SMTPTUF8 (RFC-6531) ;smtputf8=true [headers] ;add_received=true ;clean_auth_results=true ; replace header_hide_version ;show_version=true ; replace max_header_lines max_lines=1000 ; replace max_received_count max_received=100 dkim_sign.ini: |- disabled = true selector = mail domain = ${var.domain-name} headers_to_sign = From, Sender, Reply-To, Subject, Date, Message-ID, To, Cc, MIME-Version wildduck.yaml: |- redis: port: 6379 host: "${var.instance}-${var.component}-redis.${var.namespace}.svc" db: 3 mongo: # connection string for main messages database url: 'mongodb://${var.component}:${local.mongo-password}@${var.instance}-${var.component}-mongo-svc.${var.namespace}.svc:27017/wildduck' ## database name or connection string for the users db #users: "users" ## database name or connection string for the attachments db #gridfs: "attachments" ## database name or connection string for the outbound queue sender: 'zone-mta' sender: # Push messages to ZoneMTA queue for delivery # if `false` then no messages are sent enabled: true # which ZoneMTA queue to use by default. This mostly affects forwarded messages zone: 'default' # Collection name for GridFS storage gfs: 'mail' # Collection name for the queue # see [dbs].sender option for choosing correct database to use for ZoneMTA queues # by default the main wildduck database is used collection: 'zone-queue' # Hashing secret for loop detection # Must be shared with wildduck # If not set then looping is not tracked loopSecret: '${local.secrets.srs}' srs: # must be shared with ZoneMTA SRS config, otherwise messages sent from ZoneMTA are not recognized by Haraka secret: '${local.secrets.srs}' attachments: type: 'gridstore' bucket: 'attachments' decodeBase64: true log: authlogExpireDays: 30 limits: windowSize: 3600 # 1 hour rcptIp: 100 # allowed messages for the same recipient from same IP during window size rcptWindowSize: 60 # 1 minute rcpt: 60 # allowed messages for the same recipient during window size gelf: enabled: false component: 'mx' options: graylogPort: 12201 graylogHostname: '127.0.0.1' connection: 'lan' rspamd: # do not process forwarding addresses for messages with the following spam score forwardSkip: 10 # if a message has one of the tags listed here with positive score, the message will be rejected blacklist: - DMARC_POLICY_REJECT # if a message has one of the tags listed here with positive score, the message will be soft rejected softlist: - RBL_ZONE # define special responses responses: DMARC_POLICY_REJECT: "Unauthenticated email from {host} is not accepted due to domain's DMARC policy" RBL_ZONE: '[{host}] was found from Zone RBL' EOF } resource "kubectl_manifest" "haraka_service" { yaml_body = <<-EOF apiVersion: v1 kind: Service metadata: name: "${var.instance}-haraka" namespace: "${var.namespace}" labels: ${jsonencode(local.haraka-labels)} spec: type: LoadBalancer ports: - port: 25 targetPort: smtp protocol: TCP name: smtp selector: ${jsonencode(local.haraka-labels)} EOF }