variable "tls_secret_name" {} variable "tier" { type = string } variable "mailserver_accounts" {} variable "postfix_account_aliases" {} variable "opendkim_key" {} variable "sasl_passwd" {} # For sendgrid i.e relayhost variable "nfs_server" { type = string } variable "mailgun_api_key" { type = string sensitive = true } variable "email_monitor_imap_password" { type = string sensitive = true } resource "kubernetes_namespace" "mailserver" { metadata { name = "mailserver" labels = { tier = var.tier } # connecting via localhost does not seem to work? # labels = { # "istio-injection" : "enabled" # } } } module "tls_secret" { source = "../../../../modules/kubernetes/setup_tls_secret" namespace = kubernetes_namespace.mailserver.metadata[0].name tls_secret_name = var.tls_secret_name } resource "kubernetes_config_map" "mailserver_env_config" { metadata { name = "mailserver.env.config" namespace = kubernetes_namespace.mailserver.metadata[0].name labels = { app = "mailserver" } annotations = { "reloader.stakater.com/match" = "true" } } data = { DMS_DEBUG = "0" # LOG_LEVEL = "debug" ENABLE_CLAMAV = "0" ENABLE_AMAVIS = "0" ENABLE_FAIL2BAN = "0" ENABLE_FETCHMAIL = "0" ENABLE_POSTGREY = "0" ENABLE_SASLAUTHD = "0" ENABLE_SPAMASSASSIN = "0" ENABLE_RSPAMD = "1" ENABLE_OPENDKIM = "0" ENABLE_OPENDMARC = "0" RSPAMD_LEARN = "1" ENABLE_SRS = "1" FETCHMAIL_POLL = "120" ONE_DIR = "1" OVERRIDE_HOSTNAME = "mail.viktorbarzin.me" POSTFIX_MESSAGE_SIZE_LIMIT = 1024 * 1024 * 200 # 200 MB POSTFIX_REJECT_UNKNOWN_CLIENT_HOSTNAME = "1" # TLS_LEVEL = "intermediate" # DEFAULT_RELAY_HOST = "[smtp.sendgrid.net]:587" DEFAULT_RELAY_HOST = "[smtp.eu.mailgun.org]:587" SPOOF_PROTECTION = "1" SSL_TYPE = "manual" SSL_CERT_PATH = "/tmp/ssl/tls.crt" SSL_KEY_PATH = "/tmp/ssl/tls.key" } } resource "kubernetes_config_map" "mailserver_config" { metadata { name = "mailserver.config" namespace = kubernetes_namespace.mailserver.metadata[0].name labels = { app = "mailserver" } annotations = { "reloader.stakater.com/match" = "true" } } data = { # Actual mail settings "postfix-accounts.cf" = join("\n", [for user, pass in var.mailserver_accounts : "${user}|${bcrypt(pass, 6)}"]) "postfix-main.cf" = var.postfix_cf "postfix-virtual.cf" = format("%s%s", var.postfix_account_aliases, file("${path.module}/extra/aliases.txt")) KeyTable = "mail._domainkey.viktorbarzin.me viktorbarzin.me:mail:/etc/opendkim/keys/viktorbarzin.me-mail.key\n" SigningTable = "*@viktorbarzin.me mail._domainkey.viktorbarzin.me\n" TrustedHosts = "127.0.0.1\nlocalhost\n" "sasl_passwd" = var.sasl_passwd # Rspamd DKIM signing configuration "dkim_signing.conf" = <<-EOF enabled = true; sign_authenticated = true; sign_local = true; use_domain = "header"; use_redis = false; use_esld = true; selector = "mail"; path = "/tmp/docker-mailserver/rspamd/dkim/viktorbarzin.me/mail.private"; domain { viktorbarzin.me { path = "/tmp/docker-mailserver/rspamd/dkim/viktorbarzin.me/mail.private"; selector = "mail"; } } EOF fail2ban_conf = <<-EOF [DEFAULT] #logtarget = /var/log/fail2ban.log logtarget = SYSOUT EOF } # Password hashes are different each time and avoid changing secret constantly. # Either 1.Create consistent hashes or 2.Find a way to ignore_changes on per password lifecycle { ignore_changes = [data["postfix-accounts.cf"]] } } # resource "kubernetes_config_map" "user_patches" { # metadata { # name = "user-patches" # namespace = kubernetes_namespace.mailserver.metadata[0].name # labels = { # "app" = "mailserver" # } # } # data = { # user_patches = < spam@) resp = requests.post( f"https://api.eu.mailgun.net/v3/{DOMAIN}/messages", auth=("api", MAILGUN_API_KEY), data={ "from": f"monitoring@{DOMAIN}", "to": f"smoke-test@{DOMAIN}", "subject": subject, "text": f"E2E email monitoring probe {marker}. Auto-generated, will be deleted.", }, timeout=30, ) resp.raise_for_status() print(f"Sent test email via Mailgun: {resp.status_code} marker={marker}") # Step 2: Wait for delivery, retry IMAP up to 3 min ctx = ssl.create_default_context() ctx.check_hostname = False ctx.verify_mode = ssl.CERT_NONE found = False for attempt in range(9): time.sleep(20) try: imap = imaplib.IMAP4_SSL(IMAP_HOST, 993, ssl_context=ctx) imap.login(IMAP_USER, IMAP_PASS) imap.select("INBOX") _, msg_ids = imap.search(None, "SUBJECT", marker) if msg_ids[0]: found = True print(f"Found test email after {attempt+1} attempts") # Delete the test email try: for mid in msg_ids[0].split(): imap.store(mid, "+FLAGS", "\\Deleted") imap.expunge() print("Deleted test email") except Exception as de: print(f"Delete failed (non-critical): {de}") imap.logout() if found: break except Exception as e: print(f"IMAP attempt {attempt+1} failed: {e}") duration = time.time() - start if found: success = 1 print(f"Round-trip SUCCESS in {duration:.1f}s") else: print(f"Round-trip FAILED - email not found after {duration:.1f}s") except Exception as e: duration = time.time() - start print(f"ERROR: {e}") # Push metrics to Pushgateway metrics = f"""# HELP email_roundtrip_success Whether the last e2e email probe succeeded # TYPE email_roundtrip_success gauge email_roundtrip_success {success} # HELP email_roundtrip_duration_seconds Duration of the last e2e email probe # TYPE email_roundtrip_duration_seconds gauge email_roundtrip_duration_seconds {duration:.2f} # HELP email_roundtrip_last_success_timestamp Unix timestamp of last successful probe # TYPE email_roundtrip_last_success_timestamp gauge email_roundtrip_last_success_timestamp {int(time.time()) if success else 0} """ try: requests.put(PUSHGATEWAY, data=metrics, timeout=10) print("Pushed metrics to Pushgateway") except Exception as e: print(f"Failed to push metrics: {e}") # Push to Uptime Kuma on success if success: try: requests.get("http://uptime-kuma.uptime-kuma.svc.cluster.local/api/push/hLtyRKgeZO?status=up&msg=OK&ping=" + str(int(duration)), timeout=10) print("Pushed to Uptime Kuma") except Exception as e: print(f"Failed to push to Uptime Kuma: {e}") sys.exit(0 if success else 1) ' EOT ] env { name = "MAILGUN_API_KEY" value = var.mailgun_api_key } env { name = "EMAIL_MONITOR_IMAP_PASSWORD" value = var.email_monitor_imap_password } resources { requests = { memory = "64Mi" cpu = "10m" } limits = { memory = "128Mi" } } } dns_config { option { name = "ndots" value = "2" } } } } } } } }