diff --git a/hieradata/country/au/region/syd1/infra/halb/haproxy2.yaml b/hieradata/country/au/region/syd1/infra/halb/haproxy2.yaml index 55cc6d2..37cab85 100644 --- a/hieradata/country/au/region/syd1/infra/halb/haproxy2.yaml +++ b/hieradata/country/au/region/syd1/infra/halb/haproxy2.yaml @@ -11,6 +11,10 @@ profiles::haproxy::dns::vrrp_cnames: - fafflix.unkin.net - grafana.unkin.net - dashboard.ceph.unkin.net + - mail-webadmin.main.unkin.net + - mail-in.main.unkin.net + - imap.main.unkin.net + - imaps.main.unkin.net profiles::haproxy::mappings: fe_http: @@ -29,6 +33,7 @@ profiles::haproxy::mappings: - 'git.unkin.net be_gitea' - 'grafana.unkin.net be_grafana' - 'dashboard.ceph.unkin.net be_ceph_dashboard' + - 'mail-webadmin.main.unkin.net be_stalwart_webadmin' fe_https: ensure: present mappings: @@ -45,6 +50,7 @@ profiles::haproxy::mappings: - 'git.unkin.net be_gitea' - 'grafana.unkin.net be_grafana' - 'dashboard.ceph.unkin.net be_ceph_dashboard' + - 'mail-webadmin.main.unkin.net be_stalwart_webadmin' profiles::haproxy::frontends: fe_http: @@ -66,6 +72,7 @@ profiles::haproxy::frontends: - 'acl_gitea req.hdr(host) -i git.unkin.net' - 'acl_grafana req.hdr(host) -i grafana.unkin.net' - 'acl_ceph_dashboard req.hdr(host) -i dashboard.ceph.unkin.net' + - 'acl_stalwart_webadmin req.hdr(host) -i mail-webadmin.main.unkin.net' - 'acl_internalsubnets src 198.18.0.0/16 10.10.12.0/24' use_backend: - "%[req.hdr(host),lower,map(/etc/haproxy/fe_https.map,be_default)]" @@ -84,6 +91,7 @@ profiles::haproxy::frontends: - 'set-header X-Frame-Options DENY if acl_gitea' - 'set-header X-Frame-Options DENY if acl_grafana' - 'set-header X-Frame-Options DENY if acl_ceph_dashboard' + - 'set-header X-Frame-Options DENY if acl_stalwart_webadmin' - 'set-header X-Content-Type-Options nosniff' - 'set-header X-XSS-Protection 1;mode=block' @@ -286,7 +294,69 @@ profiles::haproxy::backends: - add-header X-Forwarded-Proto https if { dst_port 9443 } redirect: 'scheme https if !{ ssl_fc }' stick-table: 'type ip size 200k expire 30m' + be_stalwart_webadmin: + description: Backend for Stalwart Webadmin + collect_exported: false # handled in custom function + options: + balance: roundrobin + option: + - httpchk GET / + - forwardfor + - http-keep-alive + - prefer-last-server + cookie: SRVNAME insert indirect nocache + http-reuse: always + http-check: + - expect status 200 + http-request: + - set-header X-Forwarded-Port %[dst_port] + - add-header X-Forwarded-Proto https if { dst_port 9443 } + redirect: 'scheme https if !{ ssl_fc }' + stick-table: 'type ip size 200k expire 30m' + be_stalwart_imap: + description: Backend for Stalwart IMAP (STARTTLS) + collect_exported: false + options: + mode: tcp + balance: roundrobin + option: + - tcp-check + - prefer-last-server + stick-table: 'type ip size 200k expire 30m' stick: 'on src' + tcp-check: + - connect port 143 + - expect string "* OK" + - send "A001 STARTTLS\r\n" + - expect rstring "A001 (OK|2.0.0)" + be_stalwart_imaps: + description: Backend for Stalwart IMAPS (implicit TLS) + collect_exported: false + options: + mode: tcp + balance: roundrobin + option: + - tcp-check + - prefer-last-server + stick-table: 'type ip size 200k expire 30m' + stick: 'on src' + tcp-check: + - connect ssl + - expect string "* OK" + be_stalwart_smtp: + description: Backend for Stalwart SMTP + collect_exported: false + options: + mode: tcp + balance: roundrobin + option: + - tcp-check + - prefer-last-server + stick-table: 'type ip size 200k expire 30m' + stick: 'on src' + tcp-check: + - connect port 25 + - expect string "220 " profiles::haproxy::certlist::enabled: true profiles::haproxy::certlist::certificates: @@ -309,6 +379,7 @@ profiles::pki::vault::alt_names: - au-syd1-pve.main.unkin.net - au-syd1-pve-api.main.unkin.net - jellyfin.main.unkin.net + - mail-webadmin.main.unkin.net # additional cnames profiles::haproxy::dns::cnames: diff --git a/hieradata/roles/infra/halb/haproxy2.yaml b/hieradata/roles/infra/halb/haproxy2.yaml index 297dd16..f58be10 100644 --- a/hieradata/roles/infra/halb/haproxy2.yaml +++ b/hieradata/roles/infra/halb/haproxy2.yaml @@ -163,6 +163,39 @@ profiles::haproxy::frontends: - 'set-header X-Forwarded-Proto https' - 'set-header X-Real-IP %[src]' - 'use-service prometheus-exporter if { path /metrics }' + fe_imap: + description: 'Frontend for Stalwart IMAP (STARTTLS)' + bind: + 0.0.0.0:143: [] + mode: 'tcp' + options: + log: global + default_backend: be_stalwart_imap + tcp-request: + - inspect-delay 5s + - content accept if { req_len 0 } + fe_imaps: + description: 'Frontend for Stalwart IMAPS (implicit TLS)' + bind: + 0.0.0.0:993: [] + mode: 'tcp' + options: + log: global + default_backend: be_stalwart_imaps + tcp-request: + - inspect-delay 5s + - content accept if { req_len 0 } + fe_smtp: + description: 'Frontend for Stalwart SMTP' + bind: + 0.0.0.0:25: [] + mode: 'tcp' + options: + log: global + default_backend: be_stalwart_smtp + tcp-request: + - inspect-delay 5s + - content accept if { req_len 0 } profiles::haproxy::backends: be_letsencrypt: diff --git a/hieradata/roles/infra/mail/backend.yaml b/hieradata/roles/infra/mail/backend.yaml index 73501b3..79ac007 100644 --- a/hieradata/roles/infra/mail/backend.yaml +++ b/hieradata/roles/infra/mail/backend.yaml @@ -2,6 +2,7 @@ hiera_include: - stalwart - profiles::sql::postgresdb + - profiles::stalwart::haproxy # additional altnames profiles::pki::vault::alt_names: @@ -13,6 +14,8 @@ profiles::sql::postgresdb::cluster_name: "patroni-shared-%{facts.environment}" profiles::sql::postgresdb::dbname: stalwart profiles::sql::postgresdb::dbuser: stalwart +# export backends to haproxy +profiles::stalwart::haproxy::enable: true # Cluster role for node discovery stalwart::cluster_role: "%{facts.enc_role}" @@ -32,7 +35,7 @@ stalwart::s3_region: "%{facts.region}" stalwart::domains: - 'mail.unkin.net' stalwart::postfix_relay_host: 'out-mta.main.unkin.net' -stalwart::manage_dns_records: true # DNS records point to individual servers +stalwart::manage_dns_records: false ## With load balancer: #stalwart::manage_dns_records: true diff --git a/site/profiles/manifests/stalwart/haproxy.pp b/site/profiles/manifests/stalwart/haproxy.pp new file mode 100644 index 0000000..e8375e9 --- /dev/null +++ b/site/profiles/manifests/stalwart/haproxy.pp @@ -0,0 +1,59 @@ +# enable external access via haproxy +class profiles::stalwart::haproxy ( + Boolean $enable = false, +){ + + # webadmin + profiles::haproxy::balancemember { "${facts['networking']['fqdn']}_443": + service => 'be_stalwart_webadmin', + ports => [443], + options => [ + "cookie ${facts['networking']['hostname']}", + 'ssl', + 'verify none', + 'check', + 'inter 2s', + 'rise 3', + 'fall 2', + ] + } + + # imap + profiles::haproxy::balancemember { "${facts['networking']['fqdn']}_143": + service => 'be_stalwart_imap', + ports => [143], + options => [ + 'check', + 'inter 3s', + 'rise 2', + 'fall 3', + ] + } + + # imaps + profiles::haproxy::balancemember { "${facts['networking']['fqdn']}_993": + service => 'be_stalwart_imaps', + ports => [993], + options => [ + 'check', + 'ssl', + 'verify none', + 'inter 3s', + 'rise 2', + 'fall 3', + ] + } + + # smtp + profiles::haproxy::balancemember { "${facts['networking']['fqdn']}_25": + service => 'be_stalwart_smtp', + ports => [25], + options => [ + 'check', + 'inter 3s', + 'rise 2', + 'fall 3', + ] + } + +}