diff --git a/.sops.yaml b/.sops.yaml index a519d2d..fc66233 100644 --- a/.sops.yaml +++ b/.sops.yaml @@ -5,7 +5,6 @@ keys: - &server_database age1k9ddvzypz986a7dt403ja6evql2agz0gehll79mx64zceteya38smxph8m - &server_build_machine age1xtsm7kmql8794756ls53eu8pusyadk4lauadxx867tgsjhd38y5sj7elca - &server_christine age1kzsrlz86y5nqe4vaufv8chupq2hrf3avlmxsp3d5p9t5xj593deszslkmp - - &server_mail age1pkn38zzyckxqgeud7s7y3uuhd7pau3csh56rn3ue4altzkressdsaxs4et creation_rules: - path_regex: nix-system-configs/secrets/songsheet/[^/]+\.(yaml|json|env|ini)$ key_groups: @@ -39,5 +38,4 @@ creation_rules: - path_regex: nix-system-configs/secrets/mail/[^/]+\.(yaml|json|env|ini)$ key_groups: - age: - - *admin_christine - - *server_mail \ No newline at end of file + - *admin_christine \ No newline at end of file diff --git a/nix-system-configs/modules/scripts/compose_two_nix.zsh b/nix-system-configs/modules/scripts/compose_two_nix.zsh index 1e01a26..b0e71b0 100755 --- a/nix-system-configs/modules/scripts/compose_two_nix.zsh +++ b/nix-system-configs/modules/scripts/compose_two_nix.zsh @@ -1,11 +1,3 @@ ## Todo, make a proper script for this EDITOR=micro nix-shell -p sops --run "SOPS_AGE_KEY_FILE=$HOME/.config/sops/age/keys.txt sops nix-system-configs/secrets/songsheet/secrets.yaml" -compose2nix --inputs nix-system-configs/modules/songsheet/wavelog/docker-compose.yml --sops_file nix-system-configs/secrets/songsheet/secrets.yaml --project=wavelog --output nix-system-configs/modules/songsheet/wavelog/docker-compose.nix --root_path nix-system-configs/modules/songsheet/wavelog - - -# Todo make a proper script for this: -NIXOS_CONFIG="/home/nixosbm/the_prg_server_configuration/nix-system-configs/modules/system/mail-server.nix" nixos-rebuild build-image --image-variant proxmox -#Mind the ``` -# - In `/home/nixosbm/the_prg_server_configuration/nix-system-configs/modules/bootloader/seabios-assigned-proxmox-at-birth.nix': "/dev/vda1" -# - In `/nix/var/nix/profiles/per-user/root/channels/nixos/nixos/modules/virtualisation/proxmox-image.nix': "/dev/disk/by-label/nixos" -#``` \ No newline at end of file +compose2nix --inputs nix-system-configs/modules/songsheet/wavelog/docker-compose.yml --sops_file nix-system-configs/secrets/songsheet/secrets.yaml --project=wavelog --output nix-system-configs/modules/songsheet/wavelog/docker-compose.nix --root_path nix-system-configs/modules/songsheet/wavelog \ No newline at end of file diff --git a/nix-system-configs/modules/system/gramethus.nix b/nix-system-configs/modules/system/gramethus.nix index 0f3a636..d26361f 100644 --- a/nix-system-configs/modules/system/gramethus.nix +++ b/nix-system-configs/modules/system/gramethus.nix @@ -130,22 +130,6 @@ in { isDefault = false; editable = true; } - - { - name = "Prometheus - Mail Server"; - type = "prometheus"; - url = "http://10.1.1.15:9001"; - isDefault = false; - editable = true; - } - - { - name = "Loki - Mail Server"; - type = "loki"; - url = "http://10.1.1.15:3100"; - isDefault = false; - editable = true; - } ]; }; }; diff --git a/nix-system-configs/modules/system/mail-server.nix b/nix-system-configs/modules/system/mail-server.nix index 0b18ed0..257f6a7 100644 --- a/nix-system-configs/modules/system/mail-server.nix +++ b/nix-system-configs/modules/system/mail-server.nix @@ -5,14 +5,6 @@ ... }: let choose = paths: lib.findFirst builtins.pathExists null paths; - # Read secrets from sops-managed files and trim trailing newlines so the - # generated stalwart config contains the actual secret values (not the - # literal "$FOO" placeholders). Using builtins.readFile here ensures the - # values are placed into the TOML at build time. - adminPassword = builtins.replaceStrings ["\n"] [""] (builtins.readFile config.sops.secrets."admin-password".path); - boardPassword = builtins.replaceStrings ["\n"] [""] (builtins.readFile config.sops.secrets."board-member-password".path); - cloudflareUsername = builtins.replaceStrings ["\n"] [""] (builtins.readFile config.sops.secrets."cloudflare-username".path); - cloudflareToken = builtins.replaceStrings ["\n"] [""] (builtins.readFile config.sops.secrets."cloudflare-dns-token".path); in { options.local = { hostname = lib.mkOption { @@ -49,26 +41,6 @@ in { ]; config = { - # Create /etc/stalwart secret files so the generated TOML can reference them - environment.etc = { - "stalwart/mail-pw1".text = boardPassword; # principal password (board) - "stalwart/mail-pw1".mode = "0777"; - "stalwart/admin-pw".text = adminPassword; # admin fallback password - "stalwart/admin-pw".mode = "0777"; - "stalwart/acme-secret".text = cloudflareToken; # API token for ACME (Cloudflare) - "stalwart/acme-secret".mode = "0777"; - "stalwart/cloudflare-username".text = cloudflareUsername; # contact email for ACME - "stalwart/cloudflare-username".mode = "0777"; - }; - - systemd.tmpfiles.rules = [ - # z = create/modify file or directory, set mode and owner - "z /etc/stalwart 0555 root root - -" - ]; - - # Enable Tailscale for remote access to Traefik dashboard and configuration - services.tailscale.enable = true; - local.hostname = "nixos-mailserver"; local.username = "mailprg"; local.userDescription = "NixOS PRG Mailing Service"; @@ -89,16 +61,22 @@ in { smtp = { protocol = "smtp"; bind = "[::]:25"; + proxy.trusted-networks = [ + "10.1.1.250/32" + ]; }; submissions = { bind = "[::]:465"; protocol = "smtp"; tls.implicit = true; + # Also trust proxy for SMTPS + proxy.trusted-networks = ["10.1.1.250/32"]; }; imaps = { bind = "[::]:993"; protocol = "imap"; tls.implicit = true; + proxy.trusted-networks = ["10.1.1.250/32"]; }; jmap = { bind = "[::]:8080"; @@ -106,7 +84,7 @@ in { protocol = "http"; }; management = { - bind = ["127.0.0.1:8080" "[::]:8081"]; + bind = ["127.0.0.1:8080"]; protocol = "http"; }; }; @@ -115,21 +93,20 @@ in { hostname = "mail.prg-radio.org"; domain = "prg-radio.org"; }; - acme."letsencrypt" = { - directory = "https://acme-v02.api.letsencrypt.org/directory"; - challenge = "dns-01"; - # reference the contact and secret via files under /etc/stalwart - contact = "%{file:/etc/stalwart/cloudflare-username}%"; - domains = ["prg-radio.org" "mail.prg-radio.org"]; - provider = "cloudflare"; - secret = "%{file:/etc/stalwart/acme-secret}%"; + acme."letsencrypt" = { + directory = "https://acme-v02.api.letsencrypt.org/directory"; + challenge = "dns-01"; + contact = config.sops.secrets."cloudflare-username".path; + domains = ["prg-radio.org" "mail.prg-radio.org"]; + provider = "cloudflare"; + secret = config.sops.secrets."cloudflare-dns-token".path; }; session.auth = { - mechanisms = ["plain"]; - directory = "in-memory"; + mechanisms = "[plain]"; + directory = "'in-memory'"; }; storage.directory = "in-memory"; - session.rcpt.directory = "in-memory"; + session.rcpt.directory = "'in-memory'"; directory."imap".lookup.domains = ["prg-radio.org"]; directory."in-memory" = { type = "memory"; @@ -137,20 +114,20 @@ in { { class = "individual"; name = "Polyteknisk Radiogruppe Board Member"; - secret = "%{file:/etc/stalwart/mail-pw1}%"; + secret = config.sops.secrets."board-member-password".path; email = ["board@prg-radio.org"]; } { class = "individual"; name = "postmaster"; - secret = "%{file:/etc/stalwart/mail-pw1}%"; + secret = config.sops.secrets."board-member-password".path; email = ["postmaster@prg-radio.org"]; } ]; }; authentication.fallback-admin = { user = "admin"; - secret = "%{file:/etc/stalwart/admin-pw}%"; + secret = config.sops.secrets."admin-password".path; }; }; }; diff --git a/nix-system-configs/modules/system/traefik.nix b/nix-system-configs/modules/system/traefik.nix index 5c20f91..7275a45 100644 --- a/nix-system-configs/modules/system/traefik.nix +++ b/nix-system-configs/modules/system/traefik.nix @@ -267,10 +267,10 @@ in { }; # Mail entrypoints: plain SMTP, implicit SMTPS (465) and IMAPS (993) - smtp = { - # plain SMTP (port 25) - TCP passthrough to backend - address = "[::]:25"; - }; + #smtp = { + # # plain SMTP (port 25) - TCP passthrough to backend + # address = "[::]:25"; + # }; smtps = { # implicit TLS SMTP (port 465) - passthrough to backend address = "[::]:465"; @@ -309,10 +309,6 @@ in { scheme = "https"; }; }; - # Middleware to strip the /management prefix when proxying to the mail admin - mail-strip-management = { - stripPrefix = {prefixes = ["/management"];}; - }; }; http.routers = { @@ -376,16 +372,6 @@ in { entryPoints = ["websecure"]; tls = {certresolver = "acme";}; }; - - # Mail web administration UI (Stalwart management) - exposed under /management - mail-webadmin = { - rule = "Host(`mail.prg-radio.org`) && PathPrefix(`/management`)"; - service = "mail-webadmin"; - entryPoints = ["websecure"]; - tls = {certresolver = "acme";}; - # strip the /management prefix before proxying to the backend - middlewares = ["mail-strip-management"]; - }; }; http.services = { @@ -428,13 +414,6 @@ in { mail-jmap.loadBalancer = { servers = [{url = "http://10.1.1.15:8080";}]; }; - - # Mail webadmin backend (management). Traefik will strip the /management - # prefix and forward the remainder to the management listener on the mail - # server which is bound to port 8081. - mail-webadmin.loadBalancer = { - servers = [{url = "http://10.1.1.15:8081";}]; - }; }; # TCP routing for TeamSpeak @@ -474,12 +453,12 @@ in { }; # Mail TCP routers: SMTP (25), SMTPS (465 implicit TLS), IMAPS (993 implicit TLS) - mail-smtp = { - # catch-all TCP router for port 25 - rule = "HostSNI(`*`)"; - service = "mail-smtp"; - entryPoints = ["smtp"]; - }; + #mail-smtp = { + # # catch-all TCP router for port 25 + # rule = "HostSNI(`*`)"; + # service = "mail-smtp"; + # entryPoints = ["smtp"]; + # }; mail-smtps = { # SMTPS implicit TLS - passthrough to backend @@ -516,18 +495,15 @@ in { }; # Mail TCP services - mail-smtp.loadBalancer = { - proxyProtocol = {version = 2;}; # Add this line - servers = [{address = "10.1.1.15:25";}]; - }; + # mail-smtp.loadBalancer = { + # servers = [ { address = "10.1.1.15:25"; } ]; + # }; mail-smtps.loadBalancer = { - proxyProtocol = {version = 2;}; # Add this line servers = [{address = "10.1.1.15:465";}]; }; mail-imaps.loadBalancer = { - proxyProtocol = {version = 2;}; # Add this line servers = [{address = "10.1.1.15:993";}]; }; }; @@ -570,8 +546,8 @@ in { wants = ["acme-finished-prg-radio.org.target"]; }; - networking.firewall.allowedTCPPorts = [80 443 10022 30033 8448 9001 3001 993 465 25]; - networking.firewall.allowedUDPPorts = [80 443 9987 9001 3001 993 465 25]; + networking.firewall.allowedTCPPorts = [80 443 10022 30033 8448 9001 3001]; + networking.firewall.allowedUDPPorts = [80 443 9987 9001 3001]; system.stateVersion = "25.11"; }; diff --git a/nix-system-configs/secrets/mail/secrets.yaml b/nix-system-configs/secrets/mail/secrets.yaml index fc3b5c2..a085774 100644 --- a/nix-system-configs/secrets/mail/secrets.yaml +++ b/nix-system-configs/secrets/mail/secrets.yaml @@ -1,28 +1,19 @@ -admin-password: ENC[AES256_GCM,data:036xX0rMzmQn0iFBYEU2u6Xa,iv:fsWHfakdNdNf8r0PrazuTbBeHmaeVqP+9l4Ip6Yx3nM=,tag:sbYB+wMOckVtBaac84zEow==,type:str] -board-member-password: ENC[AES256_GCM,data:e8jdc6FVBwB2Ieqty9s4d/aASdY=,iv:E09RhJOB84ad+L7zRXROLGD1RgDUYe/DOJCbz/zFN08=,tag:fboTZGEnrBz5pMGqwIXSTA==,type:str] -cloudflare-dns-token: ENC[AES256_GCM,data:IpgU7An3IW/LFL+5OJ3oYH4c3eZjZFP+qK8/oFsNCorYKVaWPVOIVA==,iv:49UwRT8DfbC9ZIXgx7nCSxjHeIIAkiHD70ti3rWUexA=,tag:Vn9wVUhTgRlGG9E+i9NzGw==,type:str] -cloudflare-username: ENC[AES256_GCM,data:AMgWBFP90f1ML/I6es0HIUoW,iv:64GGNMSdTrmurEsgdI82iTqD9FUW8leKu/JvT7Ls6Og=,tag:3rTWNF3NfTExCnV+RRwyVA==,type:str] +admin-password: ENC[AES256_GCM,data:o8nD9CDNzufxVjfH7TqUY4QO6Xkz1xbpFu74jMVRR57NDKpKsg3ijRmBvvq1/QuJ2TM=,iv:xVGjzrRFpCyjriW6yB/UjuGiNhK4oeBiDA+Pk/BTOWc=,tag:H/ZuqffTDGsGTWTTKplvvA==,type:str] +board-member-password: ENC[AES256_GCM,data:8KLyqyGFi/ehvbNU6HsWilMO42UnXYL6puc/OOPgMDiU2fuDM2o7TibUCn1BXi9UyiE=,iv:UyLOFSARK/5p/zin7vqQic9C5qlryd/O/mjcw3eZ3ao=,tag:N2RJC6nZC+V3XoAw/eWYfA==,type:str] +cloudflare-dns-token: ENC[AES256_GCM,data:W0S9WWmhQMwjKbKI60vBjYlSb0chZUYcVs3A4DsVRfJwErcIqDWoJg==,iv:nRH8hLvCavydVC16M93N+r/t9WwNY4339CE4zGSINsk=,tag:yIUKs/XB3arhApGeik6p1A==,type:str] +cloudflare-username: ENC[AES256_GCM,data:5QCWOZcv6RKlk1/5LWjjCW0m,iv:9VzhRDlLq36pMfv0tu7POxheLFKdXP8tLLiKlTOr/30=,tag:MiijON1+mKJj6VMdnaJTkA==,type:str] sops: age: - recipient: age1746rvsvsc3snxfl7cndm222wd5kck4aqj3x7nednlegq0gdjhfcqx0qv7m enc: | -----BEGIN AGE ENCRYPTED FILE----- - YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBYRFU5S1EvUmFTeFoxQ0dZ - Ymx5Tm92U2NtS2JsaGlzS3Y2Z3dsRGpkOW5BCkxCRXJWMjBkTjdiejg0bGpRcXg0 - UzBzSHUwajRSM2s4bjFWRGNCVDVXclEKLS0tICtxYnZJQm9KcCs1eURCYTUyRHYx - SFQxT0hyZ3A5d1BPbEhWUVJJNFVFM1EKI7Srn4G6r0mKIfPd+VsCCAQGlshirxLS - cvMHhG3WqMV/esuHfi/0xpQuKoCQpBcS2sW/vtJk5Au+uV3dVfv/jg== + YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBseTM3bTJtamFBVzRkOVpp + bEZNQlJoNUVjTFd0NGl3VktmUUdHWi9wMTM0CnE3bTh5QmM1KzVqTnI3RHdmbXhN + a0NWRkthbG9OZ3pZMEZkU3hjWUNyd1UKLS0tIE5KdXgzQmRRWTVBbDFTSEkzQW9o + ZWE0ci9wSmhpWmc1OHZwb09aTjk1TUUKitmILkhief6sapPh3gZAEDsaHqcv3se0 + +6w+hs05ChkXHQ+JlXOTznd5ZNS4hwOAk5KOconNbauBaKWDplnHhA== -----END AGE ENCRYPTED FILE----- - - recipient: age1pkn38zzyckxqgeud7s7y3uuhd7pau3csh56rn3ue4altzkressdsaxs4et - enc: | - -----BEGIN AGE ENCRYPTED FILE----- - YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSAvOFYyWGYrUWNDdzBkajlh - RVdENTloUDcvVnhPeHgvcDIzRnNIWUNnS21BCmFhMlVSSk1kNmpUek0yZDh3cGcx - WHltUWVqREhZdlJ2bWJtczJjTXZ3dHcKLS0tIDRweTY1RVdVZVQ1ZlNsc3FwdVdL - OFE3aWxZNThlWUUrUWlwZmtGYjJGT2sKFkoNZt6ThwzwQ2MMFjncrVrLKEhJ1hxh - uJuOfYFlQI80k3etChD64mTRMSK7Cr/BIc2625+jGJK4kOc+JpFDEQ== - -----END AGE ENCRYPTED FILE----- - lastmodified: "2026-02-15T17:53:57Z" - mac: ENC[AES256_GCM,data:ZdDyFxbaUq/FPSe6z3+FhRJ6GeG4YjGc0fIVof/Sf/gsfKwZAStZs3CKxZtJXxUGBtkw93aguYjnNfxjUH+YxTemZPhWfJZo0pcR9YwWzXvB9fRLM95v/TzWY9qzqqbqVUSz+2SSfUJJG2+dKuzEzA3c1lE1pwOFXdfWOm0+uCE=,iv:mQp+yIVMDBY3tAPlDShk3IlIJ3cb4zrTn18x4HY+YsI=,tag:QLq34VwtxN5t66aE31LcYA==,type:str] + lastmodified: "2026-02-15T14:13:19Z" + mac: ENC[AES256_GCM,data:7hFjuvictSbcXLqXwG0VgWErKJpFsy1PfDyepQQXpszpMT4Z/BwvXlk4ppKo8C0PaCLv2qi86yBmFm/O6xUBhsMEFWYHQ+mJpYtqLX0GDvj1cn4LwDEnRa+2SiHkkZeHSwrtOHCBw8vE2R2sXBaNMkUoSXkcQ4lPS6YjulpO1vw=,iv:5aZxEnPymcvNpsUyGvvRI3o7hnfExSFWlBrzoIhQkFQ=,tag:6JJGvBL9XeiPw+TdN2qEgA==,type:str] unencrypted_suffix: _unencrypted version: 3.11.0