locals { mail_home = "${local.persistent_folder}/mail" mailboxes_path = "${local.mail_home}/mailboxes" getmail_path = "${local.mail_home}/getmail" } resource "kubernetes_namespace" "mail" { metadata { annotations = { name = "mail" } labels = { name = "mail" } name = "mail" } } resource "kubernetes_secret" "dovecot_users" { metadata { name = "dovecot-users" namespace = kubernetes_namespace.mail.metadata[0].name } data = { "users" = join("\n", [for account in local.email_accounts : "${account.username}:{plain}${account.password}:1000:1000::/var/mail/${account.username}::"]) } } resource "kubernetes_service_account" "mail_service_account" { metadata { name = "mail-app-sa" namespace = kubernetes_namespace.mail.metadata[0].name } automount_service_account_token = false } resource "kubernetes_config_map" "getmail_config" { metadata { name = "getmail-config" namespace = kubernetes_namespace.mail.metadata[0].name } data = { for job in local.getmail_jobs : "getmailrc_${job.name}" => templatefile("${path.module}/resources/mail/getmailrc.tpl", { recipient = job.recipient domain = job.domain server = job.server port = job.port username = job.username password = job.password mailbox = job.mailbox delete_after = job.delete_after dovecot_lmtp_address = "dovecot-lmtp.mail.svc.cluster.local" dovecot_lmtp_port = 31024 }) } } resource "kubernetes_config_map" "dovecot_config" { metadata { name = "dovecot-config" namespace = kubernetes_namespace.mail.metadata[0].name } data = { "dovecot.conf" = file("${path.module}/resources/mail/dovecot.conf") } } resource "kubernetes_config_map" "dovecot_sieve" { metadata { name = "dovecot-sieve" namespace = kubernetes_namespace.mail.metadata[0].name } data = { "sieve" = file("${path.module}/resources/mail/sieve") } } resource "kubernetes_manifest" "mail_certificate" { manifest = { apiVersion = "cert-manager.io/v1" kind = "Certificate" metadata = { name = "mail-cert" namespace = kubernetes_namespace.mail.metadata[0].name } spec = { secretName = "mail-tls" issuerRef = { name = "letsencrypt-prod" kind = "ClusterIssuer" } commonName = "imap.${local.domain_name}" dnsNames = ["imap.${local.domain_name}", "smtp.${local.domain_name}"] } } } resource "kubernetes_cron_job_v1" "fetch_emails" { metadata { namespace = kubernetes_namespace.mail.metadata[0].name name = "fetch-emails" } spec { schedule = local.fetch_emails_schedule job_template { metadata { name = "fetch-emails" } spec { backoff_limit = 1 parallelism = 1 completions = 1 active_deadline_seconds = 300 template { metadata { labels = { app = "fetch-emails" } } spec { container { name = "getmail" image = "a13labs/getmail6:${local.getmail_version}" args = concat(["--getmaildir", "/var/getmail"], flatten([for job in local.getmail_jobs : ["-r", "/getmail/getmailrc_${job.name}"]])) volume_mount { name = "getmail-config" mount_path = "/getmail" read_only = true } security_context { run_as_user = 1000 run_as_group = 1000 } volume_mount { name = "getmail-cache" mount_path = "/var/getmail" } } restart_policy = "Never" volume { name = "getmail-config" config_map { name = "getmail-config" } } volume { name = "getmail-cache" host_path { path = local.getmail_path } } } } } } } } resource "kubernetes_deployment" "mail" { metadata { namespace = kubernetes_namespace.mail.metadata[0].name name = "mail" labels = { app = "mail" } } spec { replicas = 1 selector { match_labels = { app = "mail" } } template { metadata { labels = { app = "mail" } } spec { service_account_name = kubernetes_service_account.mail_service_account.metadata[0].name container { name = "dovecot" image = "dovecot/dovecot:latest" port { container_port = 31993 host_port = 993 } port { container_port = 31024 } volume_mount { name = "email-storage" mount_path = "/var/mail" } volume_mount { name = "users" mount_path = "/etc/dovecot/users" sub_path = "users" read_only = true } volume_mount { name = "dovecot-config" mount_path = "/etc/dovecot/dovecot.conf" sub_path = "dovecot.conf" read_only = true } volume_mount { name = "dovecot-sieve" mount_path = "/etc/dovecot/sieve/default.sieve" sub_path = "sieve" read_only = true } volume_mount { name = "certs" mount_path = "/etc/letsencrypt" read_only = true } } volume { name = "email-storage" host_path { path = local.mailboxes_path } } volume { name = "users" secret { secret_name = kubernetes_secret.dovecot_users.metadata[0].name } } volume { name = "dovecot-config" config_map { name = kubernetes_config_map.dovecot_config.metadata[0].name } } volume { name = "certs" secret { secret_name = "mail-tls" } } volume { name = "dovecot-sieve" config_map { name = kubernetes_config_map.dovecot_sieve.metadata[0].name } } } } } depends_on = [kubernetes_manifest.mail_certificate, kubernetes_service_account.mail_service_account] lifecycle { replace_triggered_by = [ kubernetes_config_map.dovecot_config, kubernetes_secret.dovecot_users, kubernetes_config_map.dovecot_sieve, ] } } resource "kubernetes_service" "dovecot" { metadata { namespace = kubernetes_namespace.mail.metadata[0].name name = "mail" } spec { selector = { app = "mail" } port { name = "imap" port = 993 target_port = 31993 } type = "LoadBalancer" } lifecycle { ignore_changes = [ metadata[0].annotations ] } } resource "kubernetes_service" "dovecot_lmtp" { metadata { name = "dovecot-lmtp" namespace = kubernetes_namespace.mail.metadata[0].name } spec { selector = { app = "mail" } port { port = 31024 } cluster_ip = "None" } }