{ config, pkgs, lib, ... }: let mailSecret = key: { sopsFile = ../secrets/mailserver.yaml; inherit key; owner = "nginx"; group = "nginx"; mode = "0440"; restartUnits = [ "phpfpm-roundcube.service" "dovecot.service" ]; }; clientIdSecret = config.sops.secrets."mailserver/oauth/client-id".path; clientSecret = config.sops.secrets."mailserver/oauth/client-secret".path; in { sops.secrets."mailserver/oauth/client-id" = mailSecret "mailserver/oauth/client-id"; sops.secrets."mailserver/oauth/client-secret" = mailSecret "mailserver/oauth/client-secret"; imports = [ (builtins.fetchTarball { url = "https://gitlab.com/simple-nixos-mailserver/nixos-mailserver/-/archive/51726d7b7fd94aa69829fd42749a803914cbf3b7/nixos-mailserver-51726d7b7fd94aa69829fd42749a803914cbf3b7.tar.gz"; sha256 = "0449ssnpwbgfv0cyddzb8s6g641gy5xh1hd3shg32x8pv0a5qy3a"; }) ]; security.acme.acceptTerms = false; services.roundcube = { enable = true; hostName = "mail.mizuki.guru"; package = pkgs.roundcube.withPlugins ( plugins: with plugins; [ persistent_login ] ); plugins = [ "persistent_login" "managesieve" ]; dicts = with pkgs.aspellDicts; [ en ]; maxAttachmentSize = config.mailserver.messageSizeLimit / 1024 / 1024; extraConfig = '' $_SERVER['HTTPS'] = 'on'; $_SERVER['SERVER_PORT'] = 443; $_SERVER['HTTP_X_FORWARDED_PORT'] = 443; $_SERVER['HTTP_X_FORWARDED_PROTO'] = 'https'; $_SERVER['HTTP_HOST'] = 'mail.mizuki.guru'; $config['proxy_ips'] = '127.0.0.1'; $config['imap_host'] = "ssl://${config.mailserver.fqdn}"; $config['smtp_host'] = "ssl://${config.mailserver.fqdn}"; $config['smtp_user'] = "%u"; $config['smtp_pass'] = "%p"; $config['managesieve_host'] = "tls://${config.mailserver.fqdn}"; $config['managesieve_port'] = 4190; $config['managesieve_usetls'] = true; $config['oauth_provider'] = 'generic'; $config['oauth_provider_name'] = 'authentik'; $config['oauth_client_id'] = trim(file_get_contents('${clientIdSecret}')); $config['oauth_client_secret'] = trim(file_get_contents('${clientSecret}')); $config['oauth_auth_uri'] = 'https://auth.mizuki.guru/application/o/authorize/'; $config['oauth_token_uri'] = 'https://auth.mizuki.guru/application/o/token/'; $config['oauth_identity_uri'] = 'https://auth.mizuki.guru/application/o/userinfo/'; $config['oauth_scope'] = "email openid dovecotprofile offline_access"; $config['oauth_auth_parameters'] = []; $config['oauth_identity_fields'] = ['email']; ''; }; services.nginx.virtualHosts.${config.services.roundcube.hostName} = { forceSSL = false; enableACME = false; listen = [ { addr = "127.0.0.1"; port = 5656; } ]; extraConfig = '' set_real_ip_from 127.0.0.1; real_ip_header X-Forwarded-For; real_ip_recursive on; ''; }; services.caddy = { enable = true; virtualHosts."mail.mizuki.guru" = { extraConfig = '' reverse_proxy localhost:5656 ''; }; }; services.dovecot2.settings = { auth_mechanisms = lib.mkForce [ "plain" "login" "xoauth2" "oauthbearer" ]; oauth2_tokeninfo_url = "https://auth.mizuki.guru/application/o/userinfo/?access_token="; # 주의: # 여기는 아직 secret '값'이 아니라 secret '경로'가 들어감. # Dovecot이 URL 안에서 file_get_contents 같은 처리를 안 하므로 # 이 방식은 제대로 동작하지 않을 가능성이 큼. oauth2_introspection_url = "https://auth.mizuki.guru/application/o/introspect/"; oauth2_introspection_mode = "post"; oauth2_force_introspection = "yes"; oauth2_active_attribute = "active"; oauth2_active_value = "true"; oauth2_username_attribute = "email"; }; users.users.dovecot = { isSystemUser = true; group = "dovecot"; extraGroups = [ "caddy" ]; }; users.groups.dovecot = { }; users.users.postfix.extraGroups = [ "caddy" ]; mailserver = { enable = true; stateVersion = 4; fqdn = "mail.mizuki.guru"; domains = [ "mizuki.guru" ]; x509 = { certificateFile = "/var/lib/caddy/.local/share/caddy/certificates/acme-v02.api.letsencrypt.org-directory/mail.mizuki.guru/mail.mizuki.guru.crt"; privateKeyFile = "/var/lib/caddy/.local/share/caddy/certificates/acme-v02.api.letsencrypt.org-directory/mail.mizuki.guru/mail.mizuki.guru.key"; }; ldap = { enable = true; uris = [ "ldap://10.11.8.102" ]; bind = { dn = "cn=mailserver,ou=users,dc=ldap,dc=goauthentik,dc=io"; passwordFile = "/var/lib/secrets/authentik-ldap-bind-pw"; }; base = "ou=users,dc=ldap,dc=goauthentik,dc=io"; scope = "sub"; attributes = { uuid = "uid"; username = "mail"; mail = "mail"; }; dovecot = { passFilter = "mail=%{user}\n bind = yes"; }; }; accounts = { }; }; }