mirror of
https://codeberg.org/polyteknisk-radiogruppe/the_prg_server_configuration.git
synced 2026-10-21 03:15:46 +02:00
Compare commits
18 commits
fe289e0600
...
cab31335ff
| Author | SHA1 | Date | |
|---|---|---|---|
| cab31335ff | |||
| 84d2ce3d2f | |||
| acdd65a5fb | |||
| 1bc85803a9 | |||
| ba7e32a2a7 | |||
| c884036b7a | |||
| e389ebb672 | |||
| 6f536f306f | |||
| 8c6571727b | |||
| a3249aa479 | |||
| a8c4486c20 | |||
| 64d59dd636 | |||
| 8963c8d048 | |||
| 5289fc4a20 | |||
| e7d4f3d4a1 | |||
| 70f6cc7930 | |||
| 1565b21d8b | |||
| 4618c3f7fb |
6 changed files with 130 additions and 48 deletions
|
|
@ -5,6 +5,7 @@ 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:
|
||||
|
|
@ -38,4 +39,5 @@ creation_rules:
|
|||
- path_regex: nix-system-configs/secrets/mail/[^/]+\.(yaml|json|env|ini)$
|
||||
key_groups:
|
||||
- age:
|
||||
- *admin_christine
|
||||
- *admin_christine
|
||||
- *server_mail
|
||||
|
|
@ -1,3 +1,11 @@
|
|||
## 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
|
||||
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"
|
||||
#```
|
||||
|
|
@ -130,6 +130,22 @@ 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;
|
||||
}
|
||||
];
|
||||
};
|
||||
};
|
||||
|
|
|
|||
|
|
@ -5,6 +5,14 @@
|
|||
...
|
||||
}: 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 {
|
||||
|
|
@ -41,6 +49,26 @@ 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";
|
||||
|
|
@ -61,22 +89,16 @@ 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";
|
||||
|
|
@ -84,7 +106,7 @@ in {
|
|||
protocol = "http";
|
||||
};
|
||||
management = {
|
||||
bind = ["127.0.0.1:8080"];
|
||||
bind = ["127.0.0.1:8080" "[::]:8081"];
|
||||
protocol = "http";
|
||||
};
|
||||
};
|
||||
|
|
@ -93,20 +115,21 @@ in {
|
|||
hostname = "mail.prg-radio.org";
|
||||
domain = "prg-radio.org";
|
||||
};
|
||||
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;
|
||||
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}%";
|
||||
};
|
||||
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";
|
||||
|
|
@ -114,20 +137,20 @@ in {
|
|||
{
|
||||
class = "individual";
|
||||
name = "Polyteknisk Radiogruppe Board Member";
|
||||
secret = config.sops.secrets."board-member-password".path;
|
||||
secret = "%{file:/etc/stalwart/mail-pw1}%";
|
||||
email = ["board@prg-radio.org"];
|
||||
}
|
||||
{
|
||||
class = "individual";
|
||||
name = "postmaster";
|
||||
secret = config.sops.secrets."board-member-password".path;
|
||||
secret = "%{file:/etc/stalwart/mail-pw1}%";
|
||||
email = ["postmaster@prg-radio.org"];
|
||||
}
|
||||
];
|
||||
};
|
||||
authentication.fallback-admin = {
|
||||
user = "admin";
|
||||
secret = config.sops.secrets."admin-password".path;
|
||||
secret = "%{file:/etc/stalwart/admin-pw}%";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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,6 +309,10 @@ in {
|
|||
scheme = "https";
|
||||
};
|
||||
};
|
||||
# Middleware to strip the /management prefix when proxying to the mail admin
|
||||
mail-strip-management = {
|
||||
stripPrefix = {prefixes = ["/management"];};
|
||||
};
|
||||
};
|
||||
|
||||
http.routers = {
|
||||
|
|
@ -372,6 +376,16 @@ 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 = {
|
||||
|
|
@ -414,6 +428,13 @@ 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
|
||||
|
|
@ -453,12 +474,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
|
||||
|
|
@ -495,15 +516,18 @@ in {
|
|||
};
|
||||
|
||||
# Mail TCP services
|
||||
# mail-smtp.loadBalancer = {
|
||||
# servers = [ { address = "10.1.1.15:25"; } ];
|
||||
# };
|
||||
mail-smtp.loadBalancer = {
|
||||
proxyProtocol = {version = 2;}; # Add this line
|
||||
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";}];
|
||||
};
|
||||
};
|
||||
|
|
@ -546,8 +570,8 @@ in {
|
|||
wants = ["acme-finished-prg-radio.org.target"];
|
||||
};
|
||||
|
||||
networking.firewall.allowedTCPPorts = [80 443 10022 30033 8448 9001 3001];
|
||||
networking.firewall.allowedUDPPorts = [80 443 9987 9001 3001];
|
||||
networking.firewall.allowedTCPPorts = [80 443 10022 30033 8448 9001 3001 993 465 25];
|
||||
networking.firewall.allowedUDPPorts = [80 443 9987 9001 3001 993 465 25];
|
||||
|
||||
system.stateVersion = "25.11";
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,19 +1,28 @@
|
|||
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]
|
||||
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]
|
||||
sops:
|
||||
age:
|
||||
- recipient: age1746rvsvsc3snxfl7cndm222wd5kck4aqj3x7nednlegq0gdjhfcqx0qv7m
|
||||
enc: |
|
||||
-----BEGIN AGE ENCRYPTED FILE-----
|
||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBseTM3bTJtamFBVzRkOVpp
|
||||
bEZNQlJoNUVjTFd0NGl3VktmUUdHWi9wMTM0CnE3bTh5QmM1KzVqTnI3RHdmbXhN
|
||||
a0NWRkthbG9OZ3pZMEZkU3hjWUNyd1UKLS0tIE5KdXgzQmRRWTVBbDFTSEkzQW9o
|
||||
ZWE0ci9wSmhpWmc1OHZwb09aTjk1TUUKitmILkhief6sapPh3gZAEDsaHqcv3se0
|
||||
+6w+hs05ChkXHQ+JlXOTznd5ZNS4hwOAk5KOconNbauBaKWDplnHhA==
|
||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBYRFU5S1EvUmFTeFoxQ0dZ
|
||||
Ymx5Tm92U2NtS2JsaGlzS3Y2Z3dsRGpkOW5BCkxCRXJWMjBkTjdiejg0bGpRcXg0
|
||||
UzBzSHUwajRSM2s4bjFWRGNCVDVXclEKLS0tICtxYnZJQm9KcCs1eURCYTUyRHYx
|
||||
SFQxT0hyZ3A5d1BPbEhWUVJJNFVFM1EKI7Srn4G6r0mKIfPd+VsCCAQGlshirxLS
|
||||
cvMHhG3WqMV/esuHfi/0xpQuKoCQpBcS2sW/vtJk5Au+uV3dVfv/jg==
|
||||
-----END AGE ENCRYPTED FILE-----
|
||||
lastmodified: "2026-02-15T14:13:19Z"
|
||||
mac: ENC[AES256_GCM,data:7hFjuvictSbcXLqXwG0VgWErKJpFsy1PfDyepQQXpszpMT4Z/BwvXlk4ppKo8C0PaCLv2qi86yBmFm/O6xUBhsMEFWYHQ+mJpYtqLX0GDvj1cn4LwDEnRa+2SiHkkZeHSwrtOHCBw8vE2R2sXBaNMkUoSXkcQ4lPS6YjulpO1vw=,iv:5aZxEnPymcvNpsUyGvvRI3o7hnfExSFWlBrzoIhQkFQ=,tag:6JJGvBL9XeiPw+TdN2qEgA==,type:str]
|
||||
- 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]
|
||||
unencrypted_suffix: _unencrypted
|
||||
version: 3.11.0
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue