lobon (Mailman-VM) #30

Merged
Gonne merged 5 commits from Gonne/nixConfig:lobon into main 2024-10-12 14:10:02 +00:00
7 changed files with 286 additions and 0 deletions

View file

@ -4,6 +4,7 @@ keys:
- &nyarlathotep age1s99d0vlj5qlm287n98jratql5fypvjrxxal0k5jl2aw9dcc8kyvqw5yyt4
- &bragi age1lqvgpmlemyg9095ujck64u59ma29656zs7a4yxgz4s6u5cld2ccss69jwe
- &lobon age12nz7dtc0m5wasxm4r9crtkgwnzvauyfp0xh0n8z8jld0arn9ea9qe0agvn
creation_rules:
- path_regex: nixos/machines/nyarlathotep/.*
@ -18,6 +19,12 @@ creation_rules:
- *nerf
- *gonne
- *bragi
- path_regex: nixos/machines/lobon/.*
key_groups:
- age:
- *nerf
- *gonne
- *lobon
# this is the catchall clause if nothing above machtes. Encrypt to users but not
# to machines
- key_groups:

View file

@ -0,0 +1,39 @@
allowlistPass: ENC[AES256_GCM,data:bb9jXSvWeDnZqqiY/IarwA==,iv:qeFAYvXYdh2uEleg8kpCd77u4PTbwM8ydEkbMhyPz1I=,tag:1/eysyZb2mJ0mYHXIrpihw==,type:str]
nerf marked this conversation as resolved Outdated
Outdated
Review

See comment on the other sops file.

See comment on the other sops file.
sops:
kms: []
gcp_kms: []
azure_kv: []
hc_vault: []
age:
- recipient: age1rasjnr2tlv9y70sj0z0hwpgpxdc974wzg5umtx2pnc6z0p05u3js6r8sln
enc: |
-----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSAySVhjV0xXdGE2am85RVJh
NXJLRy92blkzeENuWHh3QSsxNHBXcUpibGxnCnVHUEVoYVgxbk5WSmxQRXNzMC9i
Y1g4MUFrNEVjVjJWM0xhU0JzTzNZTk0KLS0tIFIrdmhrbXFHb2VaQ1p2dDJMMmlR
Um5CcGlZanBBRzJKOVNZeWVPTmsrcVUK905uViHD7uZMVQHPfFraIHXYTHaT+ERl
ZvyRDdjjRCyxu0qcIpYVpPAmfGCo0++bXSRUX8rCp48YN20MbPNjgA==
-----END AGE ENCRYPTED FILE-----
- recipient: age1xv5rfxkxg9jyqx5jg2j82cxv7w7ep4a3795p4yl5fuqf38f3m3eqfnefju
enc: |
-----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBLNkNpN2RlcHBuOUxoYmkx
QzdOM1E0cFBSc1I0NzVRbmhiUXhjM3dQOWhnCmlOQzJ3b2Q5NFJkb2haMDNGSFBv
SkdySWtRUzhic1FNeXhiUFBPRVNoWmcKLS0tIGNaVW5xUmxWOEtXVkRqVEJJSEVv
NFBWREFQbnFXclhiNW51M0ZsOEMxdnMKdOPVRbD42q7MRw1CX1M30Xdil7VFLDVD
G8j4sjxlDkcwQK/3WjZdBLXAzJcrvAp0okGzw8lymC812CXTSEfmxw==
-----END AGE ENCRYPTED FILE-----
- recipient: age12nz7dtc0m5wasxm4r9crtkgwnzvauyfp0xh0n8z8jld0arn9ea9qe0agvn
enc: |
-----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBKVVN2THloaU1pVnhtWDhm
TWpPaHNLSXlud0RLU3ovS0s4REtUTzQwMHhZClF5OFZQVHB2VG9BeThSYzVSMUFJ
VDNkT0Y1Y3RUemkwSmxlM0drUlNDR1UKLS0tIDYrcVhXMWJxR2dhcXhjdTQ3MjV1
Y3lWbHdLOGRGamhRY0xoRnVJczc2aFUKWWAflRwoszNw5bEDTSaVI65FtQve/HrC
uY1JvYwXLq4m4hu76dyrplDpzb8ant/YAUXpG6F4U7nn9GiLBaoyUQ==
-----END AGE ENCRYPTED FILE-----
lastmodified: "2024-03-31T14:34:54Z"
mac: ENC[AES256_GCM,data:sjWiO96NcFUT4L9mdBuQwt6Zl5cS16o73zes30SYJxzM1R3ZBIg9oOmhXxY9BC3yKjEb6bVuemj/bnnopSR/m3RPH7xfaYCBfz97Zgc4SGtoqLIra5OUCRpWnKSsD6Nf09Qss5Pbla9EIrI0kQt7fpf4iKLF7VJwrQryslnvfcM=,iv:ilnbLK6sttweEyqszVHxVnjbTq8jF5ZTO24OEIPMprE=,tag:3XgAlXMl/RIaUfkVwHJeBQ==,type:str]
pgp: []
unencrypted_suffix: _unencrypted
version: 3.8.1

View file

@ -0,0 +1,39 @@
backupKey: ENC[AES256_GCM,data:/PErHUVZDTyqK+GKI2inDoEBQpSmezeBTgXWnrthc8IPtUFn4Ur2CkDo+MqfiAlSn9vT2ksHmyS5qmoGANG01e1Cm50qpt/BdoC2hh15jOVuc0uUBNOq7f5YBVeYtbemwjPcmbF7dgUeRlEAvxhqtX3/ntzxSB1inew/SsEgPrU4Yl0FF+CHhqgbeB/NJOhQY29/3hBGwMksfTUDymUmX6pUgIN1M26crIKFCn5IyqAXl10F+zL4PThZPnhmks7Y8BsGUbKkiE6ghdaUjEjBjGOGgbaGAjolG+nJ17xyM1Pc2speT4E/3VgAC34dpaByveGcf2SfsXir0KavcI86mUkjzaNF9u7GjGO0Szn742/aqbdUoOkJl41unb0Enf2/D4Up3fy6LrUqVqrHIM4Dea9WLQd0poD0FWSN12IKh+ylkouMkmhwLXUXFzIHOePS92/MsPM+9fLhH4cU64qxr9UzmfYRnNBpAHrjlxdkK9WZ1Oj4mdtu6R20vYkYcMIQgU38FvSN6uWGvPxJj+Ij,iv:ghvgkC6qFO/0tvsc7igCoZy7am8eNsd21WYCSAKiZDs=,tag:MFnk/Nnw+cloN+x7sd4LLg==,type:str]
nerf marked this conversation as resolved Outdated
Outdated
Review

Hmm, I didn't have thought about the following:

Which secrets get different sops files and which go into the same file?
can we put both of this secrets into the same yaml, is this desirable?

Hmm, I didn't have thought about the following: Which secrets get different sops files and which go into the same file? can we put both of this secrets into the same yaml, is this desirable?
Outdated
Review

I think splitting per desired system user is useful (might even be necessary). Apart from that it feels more about conventions which I don't know.

Currently the secrets are used by root and mailman but giving the backup service root-access in itself feels like a mistake.

I think splitting per desired system user is useful (might even be necessary). Apart from that it feels more about conventions which I don't know. Currently the secrets are used by `root` and `mailman` but giving the backup service `root`-access in itself feels like a mistake.
Outdated
Review

refactoring the backup process sounds like something, that we should do uniform across all VM. So I think we should do that
in a dedicated pull request. I will open an issue for that. Finding a solution for this is probably out of scope here.

refactoring the backup process sounds like something, that we should do uniform across all VM. So I think we should do that in a dedicated pull request. I will open an issue for that. Finding a solution for this is probably out of scope here.
sops:
kms: []
gcp_kms: []
azure_kv: []
hc_vault: []
age:
- recipient: age1rasjnr2tlv9y70sj0z0hwpgpxdc974wzg5umtx2pnc6z0p05u3js6r8sln
enc: |
-----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBaNVExTHQrY2h1M3RZOEdU
Wm5kdDBHZ1NmZGpQU0VOYWtjOTdBVURQZkJnCmZzWTEvSWxvMFk4NlFOVnBDbm9q
Tncva0VyMGVDL29ZZ0YxeGE3RFBUS3cKLS0tIDluK05NMUNNM3pEUmlCNE9BV3lT
L0dPYTBwbjJzUmJnYktiM2JBME5LM3cKvPwth4DxQgFYhvr9vJLfeaiNc+UfAo4c
RdXPLkwtq3vksrU1IR54tHcUJ0yZiZ1HxxGp3PCPaXXJiUykllnJPw==
-----END AGE ENCRYPTED FILE-----
- recipient: age1epz92k2rkp43hkrg3u0jgkzhnkwx8y43kag7rvfzwl9wcddelvusyetxl7
enc: |
-----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBRdFV0OVA5VTBuOVhsL3lp
MFpDN2RHVDExck5vcWpDNDNPM2k3S1FqQUFFCjNreXdSbDFXOHJ4b21mNGlZb0xQ
YUh0WVNGN2o1aFVaaGpxbmk4aUQ4ZTAKLS0tIHhtci84Zk1zZlBOOHk4a3VKUlM1
MXNZbWdpVEJiTTlIRERLYzBlNWxBMlUK4Z8JLlN5FOegfdg5njhHjbCwAm/f+kJS
buOHGWzWirW0ZibOP+fikzJwdIzIsX8v8tGaV89nQwf0hrxK0748Hg==
-----END AGE ENCRYPTED FILE-----
- recipient: age12nz7dtc0m5wasxm4r9crtkgwnzvauyfp0xh0n8z8jld0arn9ea9qe0agvn
enc: |
-----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBZa2pUZlZsVmhPU242d0Nj
bi9BSlJBU1Q1cFU4ZjA4NnlJNmdwaVFBc2xNCjJlSG5UaDFnSzFHZ01RVVNjOHY5
L1JVUit6SThvbGRIU0loNmtZanllNXcKLS0tIFhMR1pxRmlGQWFEQURiRFJoMWJZ
dlExV2xTVWR6bWI3VCtSdU81SmtqYncKLFQczlIj89vzlfgE33w6ktotYFdxaWr9
YyewbY8qZmOUGQ4xKlZmhojeMh/FEH8dGNEf1AxnKbuQdnW6lqGR/w==
-----END AGE ENCRYPTED FILE-----
lastmodified: "2024-03-31T16:01:00Z"
mac: ENC[AES256_GCM,data:AawTzIXyX+3FyFpw8pXFeVJJtXN7ZpTFnUqhedC2vcbbNUzMMt1X0SaxtNNJ5chZI/tYHn59FT6zznl1eO4Xn29Zc2Up4dkT1BE4yqkEG0hiCFXrXMz/PaHfROzBhIWCVyF4fYj6MZKg1iBBxhWRqhJlQ1q4UVkoaITRUKpFJgs=,iv:3lTPOQ8VjmP3WNGbFK2yLU4Ks1KviNS/l7TH4SnvSUs=,tag:KUbAU6+76/Uxj2Wn9EnqnA==,type:str]
pgp: []
unencrypted_suffix: _unencrypted
version: 3.8.1

View file

@ -0,0 +1,36 @@
{
imports = [
./hardware-configuration.nix
../../modules/mailman.nix
../../roles
../../roles/vm.nix
../../modules/vmNetwork.nix
];
# System configuration here
services.mathebau-mailman = {
enable = true;
hostName = "lists.mathebau.de";
siteOwner = "root@mathebau.de";
};
networking.hostName = "lobon";
vmNetwork.ipv4 = "192.168.0.22";
system.stateVersion = "23.11";
sops.secrets = {
allowlistPass = {
sopsFile = ./allowlistPass.yaml;
owner = "mailman";
group = "mailman";
mode = "0400";
};
backupKey = {
sopsFile = ./backupKey.yaml;
owner = "root";
group = "root";
mode = "0400";
};
};
}

View file

@ -0,0 +1,30 @@
{
lib,
pkgs,
...
}: {
imports = [];
fileSystems."/" = {
device = "root";
fsType = "tmpfs";
options = ["size=1G" "mode=755"];
};
fileSystems."/persist" = {
device = "/dev/disk/by-label/nixos";
fsType = "btrfs";
options = ["subvol=persist"];
neededForBoot = true;
};
fileSystems."/boot" = {
device = "/dev/disk/by-label/boot";
fsType = "ext4";
};
fileSystems."/nix" = {
device = "/dev/disk/by-label/nixos";
fsType = "btrfs";
options = ["subvol=nix"];
};
nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux";
}

View file

@ -83,6 +83,13 @@ in {
path = "/var/lib/backups/ithaqua";
allowSubRepos = true;
};
lobon = {
authorizedKeysAppendOnly = [
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAICEptjf1UWRlo6DG9alAIRwkSDUAVHwDKkHC6/DeYKzi Lobon Backup"
];
path = "/var/lib/backups/lobon";
allowSubRepos = true;
};
sanctamariamaterdei = {
authorizedKeysAppendOnly = [
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIH9Le5OI4ympQ0mQKYHmxgxGF598rzpD5VVpWK1mGfd8 Sanctamariamaterdei Backupsystem"

128
nixos/modules/mailman.nix Normal file
View file

@ -0,0 +1,128 @@
# Adapted and simplified from https://nixos.wiki/wiki/Mailman
{
config,
lib,
pkgs,
...
}: let
inherit
(lib)
mkIf
mkEnableOption
mkOption
;
inherit (lib.types) nonEmptyStr;
cfg = config.services.mathebau-mailman;
in {
options.services.mathebau-mailman = {
enable = mkEnableOption "mathebau mailman service";
hostName = mkOption {
type = nonEmptyStr;
Gonne marked this conversation as resolved Outdated
Outdated
Review

are these allowed to be empty?

are these allowed to be empty?
Outdated
Review

Probably not. No idea how to achieve that.

Probably not. No idea how to achieve that.
Outdated
Review

there is lib.types.nonEmptyStr

there is `lib.types.nonEmptyStr`
};
siteOwner = mkOption {
type = nonEmptyStr;
Gonne marked this conversation as resolved Outdated
Outdated
Review

same as above?

same as above?
};
};
config = mkIf cfg.enable {
services = {
postfix = {
enable = true;
relayDomains = ["hash:/var/lib/mailman/data/postfix_domains"];
config = {
transport_maps = ["hash:/var/lib/mailman/data/postfix_lmtp"];
local_recipient_maps = ["hash:/var/lib/mailman/data/postfix_lmtp"];
proxy_interfaces = "130.83.2.184";
smtputf8_enable = "no"; # HRZ does not know SMTPUTF8
};
relayHost = "192.168.0.24"; # Relay to eihort which relays to HRZ (see https://www.hrz.tu-darmstadt.de/services/it_services/email_infrastruktur/index.de.jsp)
Gonne marked this conversation as resolved Outdated
Outdated
Review

m(

m(
};
mailman = {
enable = true;
inherit (cfg) siteOwner;
hyperkitty.enable = true;
webHosts = [cfg.hostName];
serve.enable = true; #
# Don't include confirmation tokens in reply addresses, because we would need to send them to HRZ otherwise.
settings.mta.verp_confirmations = "no";
};
};
environment.persistence.${config.impermanence.name} = {
directories = [
Gonne marked this conversation as resolved Outdated
Outdated
Review

I'm not sure how to stand on this, and it is a purely idiomatic question (not a technical one).
If we want the certs mainly for non nginx use, should we config them separately and not in the nginx block?

Also how does the webinterface work, are we proxied by cthulhu? If yes who handles tls certs?

I'm not sure how to stand on this, and it is a purely idiomatic question (not a technical one). If we want the certs mainly for non nginx use, should we config them separately and not in the nginx block? Also how does the webinterface work, are we proxied by cthulhu? If yes who handles tls certs?
Outdated
Review

“Automatic cert validation and configuration for Apache and Nginx virtual hosts is included in NixOS, however if you would like to generate a wildcard cert or you are not using a web server you will have to configure DNS based validation.” – https://nixos.org/manual/nixos/stable/index.html#module-security-acme

I don't want to do DNS validation so this is the way. The services.mailman.serve.enable = true enables nginx anyway.

Web requests get proxied through cthulhu and reach this VM via http. Mail gets proxied via eihort and also probably reaches this VM in plaintext (possibly with STARTTLS).

On the other hand this VM is not supposed to be communicating with anything besides cthulhu and eihort so we might as well try to disable all TLS stuff.

“Automatic cert validation and configuration for Apache and Nginx virtual hosts is included in NixOS, however if you would like to generate a wildcard cert or you are not using a web server you will have to configure DNS based validation.” – https://nixos.org/manual/nixos/stable/index.html#module-security-acme I don't want to do DNS validation so this is the way. The `services.mailman.serve.enable = true` enables nginx anyway. Web requests get proxied through cthulhu and reach this VM via http. Mail gets proxied via eihort and also probably reaches this VM in plaintext (possibly with STARTTLS). On the other hand this VM is not supposed to be communicating with anything besides cthulhu and eihort so we might as well try to disable all TLS stuff.
"/var/lib/mailman"
"/var/lib/mailman-web"
];
files = ["/root/.ssh/known_hosts"]; # for the backup server bragi
Gonne marked this conversation as resolved Outdated
Outdated
Review

TODO: Backups?

TODO: Backups?
Outdated
Review

Done for mailman data. The certificates can be regenerated on hardware failure.

Done for mailman data. The certificates can be regenerated on hardware failure.
};
networking.firewall.allowedTCPPorts = [25 80];
# Update HRZ allowlist
# For account details see https://www-cgi.hrz.tu-darmstadt.de/mail/
# will stop working if no valid TUIDs are associated to our domain.
Outdated
Review

we could move this to the general vm role, right? Every vm makes backups this way, (or am i missing something?)

we could move this to the general vm role, right? Every vm makes backups this way, (or am i missing something?)
Outdated
Review

Maybe. This is in scope for the backup refactoring.

Maybe. This is in scope for the backup refactoring.
systemd.timers."mailAllowlist" = {
wantedBy = ["timers.target"];
timerConfig = {
OnBootSec = "5m"; # Run every 5 minutes
OnUnitActiveSec = "5m";
RandomizedDelaySec = "2m"; # prevent overload on regular intervals
Gonne marked this conversation as resolved Outdated
Outdated
Review

See above comment about who handles tls, we might not need 443 and 80

See above comment about who handles tls, we might not need 443 and 80
Unit = "mailAllowlist.service";
};
};
systemd.services."mailAllowlist" = {
description = "Allowlist update: Post the mail addresses used by mailman to the HRZ allowllist";
script = ''
# Get the mail addresses' local-part
cut -d '@' -f 1 /var/lib/mailman/data/postfix_lmtp | grep -v '#' | grep "\S" > /tmp/addresses
# Post local-parts to HRZ
${pkgs.curl}/bin/curl https://www-cgi.hrz.tu-darmstadt.de/mail/whitelist-update.php -F emaildomain=${cfg.hostName} -F password=$(cat /run/secrets/allowlistPass) -F emailliste=@/tmp/addresses -F meldungen=voll
# Cleanup
rm /tmp/addresses
'';
serviceConfig = {
Type = "oneshot";
User = "mailman";
NoNewPrivileges = true;
# See https://www.man7.org/linux/man-pages/man5/systemd.exec.5.html
PrivateTmp = true;
ProtectHome = true;
ReadOnlyPaths = "/";
ReadWritePaths = "/tmp";
InaccessiblePaths = "-/lost+found";
Gonne marked this conversation as resolved Outdated
Outdated
Review

I love and hate this so much,maybe we should/could make this script its own package? maybe idiomatically correct but overkill.

I love and hate this so much,maybe we should/could make this script its own package? maybe idiomatically correct but overkill.
Outdated
Review

I think it's overkill and I don't want to do it.

I think it's overkill and I don't want to do it.
PrivateDevices = true;
PrivateUsers = true;
ProtectHostname = true;
ProtectClock = true;
Outdated
Review

can we set NoNewPrivileges = true;? or does it break the service?

And one could read the Sandboxing part of systemd.exec(5)

can we set `NoNewPrivileges = true;`? or does it break the service? And one could read the Sandboxing part of `systemd.exec(5)`
Outdated
Review

Yes, I added a bunch of them in f334e00d01 that seemed reasonable and don't break the updater. Notably, I left out ExecPaths= and NoExecPaths= because the correct values are unclear to me.

Yes, I added a bunch of them in f334e00d01 that seemed reasonable and don't break the updater. Notably, I left out `ExecPaths=` and `NoExecPaths=` because the correct values are unclear to me.
ProtectKernelTunables = true;
ProtectKernelModules = true;
ProtectKernelLogs = true;
ProtectControlGroups = true;
LockPersonality = true;
MemoryDenyWriteExecute = true;
RestrictRealtime = true;
RestrictSUIDSGID = true;
};
};
# Backups
services.borgbackup.jobs.mailman = {
paths = [
"/var/lib/mailman/data"
"/var/lib/mailman-web"
];
encryption.mode = "none"; # Otherwise the key is next to the backup or we have human interaction.
environment = {
BORG_RSH = "ssh -i /run/secrets/backupKey";
nerf marked this conversation as resolved Outdated
Outdated
Review

This is probably a bigger refactoring pull request and outside the scope of this one.
We could think making it easier configurable that backups are made by a user that can
basically read everything but not write.

This is probably a bigger refactoring pull request and outside the scope of this one. We could think making it easier configurable that backups are made by a user that can basically read everything but not write.
# “Borg ensures that backups are not created on random drives that just happen to contain a Borg repository.”
# https://borgbackup.readthedocs.io/en/stable/deployment/automated-local.html
# We don't want this in order to not need to persist borg cache and simplify new deployments.
BORG_UNKNOWN_UNENCRYPTED_REPO_ACCESS_IS_OK = "yes";
};
repo = "borg@192.168.1.11:lobon"; # TODO for https://gitea.mathebau.de/Fachschaft/nixConfig/issues/33
startAt = "daily";
user = "root";
group = "root";
};
};
}