diff options
Diffstat (limited to 'hosts')
35 files changed, 1027 insertions, 250 deletions
diff --git a/hosts/eostre/default.nix b/hosts/eostre/default.nix index fd4b15f2..d4113024 100644 --- a/hosts/eostre/default.nix +++ b/hosts/eostre/default.nix | |||
@@ -37,14 +37,10 @@ with lib; | |||
37 | powerManagement.enable = true; | 37 | powerManagement.enable = true; |
38 | }; | 38 | }; |
39 | 39 | ||
40 | opengl.enable = true; | 40 | graphics.enable = true; |
41 | }; | 41 | }; |
42 | 42 | ||
43 | environment.etc."machine-id".text = "f457b21333f1491e916521151ff5d468"; | ||
44 | |||
45 | networking = { | 43 | networking = { |
46 | hostId = "f457b213"; | ||
47 | |||
48 | domain = "lan.yggdrasil"; | 44 | domain = "lan.yggdrasil"; |
49 | search = [ "lan.yggdrasil" "yggdrasil" ]; | 45 | search = [ "lan.yggdrasil" "yggdrasil" ]; |
50 | 46 | ||
@@ -83,19 +79,14 @@ with lib; | |||
83 | ]; | 79 | ]; |
84 | }; | 80 | }; |
85 | 81 | ||
86 | 82 | services.displayManager.sddm = { | |
87 | services.xserver = { | ||
88 | enable = true; | 83 | enable = true; |
89 | displayManager.sddm = { | 84 | wayland.enable = true; |
90 | enable = true; | 85 | settings = { |
91 | settings = { | 86 | Users.HideUsers = "gkleen"; |
92 | Users.HideUsers = "gkleen"; | ||
93 | }; | ||
94 | }; | 87 | }; |
95 | desktopManager.plasma5.enable = true; | ||
96 | |||
97 | videoDrivers = [ "nvidia" ]; | ||
98 | }; | 88 | }; |
89 | services.desktopManager.plasma6.enable = true; | ||
99 | 90 | ||
100 | 91 | ||
101 | services.openssh = { | 92 | services.openssh = { |
diff --git a/hosts/sif/default.nix b/hosts/sif/default.nix index f4de24e8..b436542f 100644 --- a/hosts/sif/default.nix +++ b/hosts/sif/default.nix | |||
@@ -12,8 +12,8 @@ let | |||
12 | in { | 12 | in { |
13 | imports = with flake.nixosModules.systemProfiles; [ | 13 | imports = with flake.nixosModules.systemProfiles; [ |
14 | ./hw.nix | 14 | ./hw.nix |
15 | ./mail ./libvirt ./greetd | 15 | ./email ./libvirt ./greetd |
16 | tmpfs-root bcachefs initrd-all-crypto-modules default-locale openssh rebuild-machines niri-unstable networkmanager | 16 | tmpfs-root bcachefs initrd-all-crypto-modules default-locale openssh rebuild-machines niri-unstable networkmanager lanzaboote |
17 | flakeInputs.nixos-hardware.nixosModules.lenovo-thinkpad-p1 | 17 | flakeInputs.nixos-hardware.nixosModules.lenovo-thinkpad-p1 |
18 | flakeInputs.impermanence.nixosModules.impermanence | 18 | flakeInputs.impermanence.nixosModules.impermanence |
19 | flakeInputs.nixVirt.nixosModules.default | 19 | flakeInputs.nixVirt.nixosModules.default |
@@ -34,6 +34,10 @@ in { | |||
34 | initrd = { | 34 | initrd = { |
35 | systemd = { | 35 | systemd = { |
36 | emergencyAccess = config.users.users.root.hashedPassword; | 36 | emergencyAccess = config.users.users.root.hashedPassword; |
37 | extraBin = { | ||
38 | "vim" = lib.getExe pkgs.vim; | ||
39 | "grep" = lib.getExe pkgs.gnugrep; | ||
40 | }; | ||
37 | }; | 41 | }; |
38 | luks.devices = { | 42 | luks.devices = { |
39 | nvm0 = { device = "/dev/disk/by-uuid/bef17e86-d929-4a60-97cb-6bfa133face7"; bypassWorkqueues = true; }; | 43 | nvm0 = { device = "/dev/disk/by-uuid/bef17e86-d929-4a60-97cb-6bfa133face7"; bypassWorkqueues = true; }; |
@@ -47,13 +51,8 @@ in { | |||
47 | 51 | ||
48 | blacklistedKernelModules = [ "nouveau" ]; | 52 | blacklistedKernelModules = [ "nouveau" ]; |
49 | 53 | ||
50 | # Use the systemd-boot EFI boot loader. | 54 | lanzaboote.configurationLimit = 15; |
51 | loader = { | 55 | loader = { |
52 | systemd-boot = { | ||
53 | enable = true; | ||
54 | configurationLimit = 15; | ||
55 | netbootxyz.enable = true; | ||
56 | }; | ||
57 | efi.canTouchEfiVariables = true; | 56 | efi.canTouchEfiVariables = true; |
58 | timeout = null; | 57 | timeout = null; |
59 | }; | 58 | }; |
@@ -77,6 +76,14 @@ in { | |||
77 | }; | 76 | }; |
78 | } | 77 | } |
79 | ]; | 78 | ]; |
79 | consoleLogLevel = 3; | ||
80 | kernelParams = [ | ||
81 | "quiet" | ||
82 | "boot.shell_on_fail" | ||
83 | "udev.log_priority=3" | ||
84 | "rd.systemd.show_status=auto" | ||
85 | "plymouth.use-simpledrm" | ||
86 | ]; | ||
80 | 87 | ||
81 | tmp.useTmpfs = true; | 88 | tmp.useTmpfs = true; |
82 | 89 | ||
@@ -98,6 +105,8 @@ in { | |||
98 | server ptbtime2.ptb.de prefer iburst nts | 105 | server ptbtime2.ptb.de prefer iburst nts |
99 | server ptbtime3.ptb.de prefer iburst nts | 106 | server ptbtime3.ptb.de prefer iburst nts |
100 | server ptbtime4.ptb.de prefer iburst nts | 107 | server ptbtime4.ptb.de prefer iburst nts |
108 | pool ntppool1.time.nl prefer iburst nts | ||
109 | pool ntppool2.time.nl prefer iburst nts | ||
101 | 110 | ||
102 | authselectmode require | 111 | authselectmode require |
103 | minsources 3 | 112 | minsources 3 |
@@ -130,6 +139,12 @@ in { | |||
130 | useNetworkd = true; | 139 | useNetworkd = true; |
131 | }; | 140 | }; |
132 | 141 | ||
142 | environment.etc."NetworkManager/dnsmasq.d/dnssec.conf" = { | ||
143 | text = '' | ||
144 | conf-file=${pkgs.dnsmasq}/share/dnsmasq/trust-anchors.conf | ||
145 | dnssec | ||
146 | ''; | ||
147 | }; | ||
133 | environment.etc."NetworkManager/dnsmasq.d/libvirt_dnsmasq.conf" = { | 148 | environment.etc."NetworkManager/dnsmasq.d/libvirt_dnsmasq.conf" = { |
134 | text = '' | 149 | text = '' |
135 | except-interface=virbr0 | 150 | except-interface=virbr0 |
@@ -372,19 +387,6 @@ in { | |||
372 | ]; | 387 | ]; |
373 | 388 | ||
374 | services = { | 389 | services = { |
375 | uucp = { | ||
376 | enable = true; | ||
377 | nodeName = "sif"; | ||
378 | remoteNodes = { | ||
379 | "ymir" = { | ||
380 | publicKeys = ["ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIG6KNtsCOl5fsZ4rV7udTulGMphJweLBoKapzerWNoLY root@ymir"]; | ||
381 | hostnames = ["ymir.yggdrasil.li" "ymir.niflheim.yggdrasil"]; | ||
382 | }; | ||
383 | }; | ||
384 | |||
385 | defaultCommands = lib.mkForce []; | ||
386 | }; | ||
387 | |||
388 | avahi.enable = true; | 390 | avahi.enable = true; |
389 | 391 | ||
390 | fwupd.enable = true; | 392 | fwupd.enable = true; |
@@ -403,8 +405,8 @@ in { | |||
403 | 405 | ||
404 | logind = { | 406 | logind = { |
405 | lidSwitch = "suspend"; | 407 | lidSwitch = "suspend"; |
406 | lidSwitchDocked = "lock"; | 408 | lidSwitchDocked = "ignore"; |
407 | lidSwitchExternalPower = "lock"; | 409 | lidSwitchExternalPower = "ignore"; |
408 | }; | 410 | }; |
409 | 411 | ||
410 | atd = { | 412 | atd = { |
@@ -446,11 +448,6 @@ in { | |||
446 | 448 | ||
447 | systemd.tmpfiles.settings = { | 449 | systemd.tmpfiles.settings = { |
448 | "10-localtime"."/etc/localtime".L.argument = "/.bcachefs/etc/localtime"; | 450 | "10-localtime"."/etc/localtime".L.argument = "/.bcachefs/etc/localtime"; |
449 | |||
450 | # "10-regreet"."/var/cache/regreet/cache.toml".C.argument = toString ((pkgs.formats.toml {}).generate "cache.toml" { | ||
451 | # last_user = "gkleen"; | ||
452 | # user_to_last_sess.gkleen = "Niri"; | ||
453 | # }); | ||
454 | }; | 451 | }; |
455 | 452 | ||
456 | users = { | 453 | users = { |
@@ -610,25 +607,6 @@ in { | |||
610 | 607 | ||
611 | environment.etc."X11/xorg.conf.d/50-wacom.conf".source = lib.mkForce ./wacom.conf; | 608 | environment.etc."X11/xorg.conf.d/50-wacom.conf".source = lib.mkForce ./wacom.conf; |
612 | 609 | ||
613 | systemd.services."ac-plugged" = { | ||
614 | description = "Inhibit handling of lid-switch and sleep"; | ||
615 | |||
616 | path = with pkgs; [ systemd coreutils ]; | ||
617 | |||
618 | script = '' | ||
619 | exec systemd-inhibit --what=handle-lid-switch --why="AC is connected" --mode=block sleep infinity | ||
620 | ''; | ||
621 | |||
622 | serviceConfig = { | ||
623 | Type = "simple"; | ||
624 | }; | ||
625 | }; | ||
626 | |||
627 | services.udev.extraRules = with pkgs; lib.mkAfter '' | ||
628 | SUBSYSTEM=="power_supply", ENV{POWER_SUPPLY_ONLINE}=="0", RUN+="${systemd}/bin/systemctl --no-block stop ac-plugged.service" | ||
629 | SUBSYSTEM=="power_supply", ENV{POWER_SUPPLY_ONLINE}=="1", RUN+="${systemd}/bin/systemctl --no-block start ac-plugged.service" | ||
630 | ''; | ||
631 | |||
632 | systemd.services."nix-daemon".serviceConfig = { | 610 | systemd.services."nix-daemon".serviceConfig = { |
633 | MemoryAccounting = true; | 611 | MemoryAccounting = true; |
634 | MemoryHigh = "50%"; | 612 | MemoryHigh = "50%"; |
@@ -688,7 +666,7 @@ in { | |||
688 | directories = [ | 666 | directories = [ |
689 | "/nix" | 667 | "/nix" |
690 | "/root" | 668 | "/root" |
691 | "/home" | 669 | "/home" |
692 | "/var/log" | 670 | "/var/log" |
693 | "/var/lib/sops-nix" | 671 | "/var/lib/sops-nix" |
694 | "/var/lib/nixos" | 672 | "/var/lib/nixos" |
@@ -698,9 +676,9 @@ in { | |||
698 | "/var/lib/bluetooth" | 676 | "/var/lib/bluetooth" |
699 | "/var/lib/upower" | 677 | "/var/lib/upower" |
700 | "/var/lib/postfix" | 678 | "/var/lib/postfix" |
679 | "/var/lib/regreet" | ||
701 | "/etc/NetworkManager/system-connections" | 680 | "/etc/NetworkManager/system-connections" |
702 | { directory = "/var/uucp"; user = "uucp"; group = "uucp"; mode = "0700"; } | 681 | config.boot.lanzaboote.pkiBundle |
703 | { directory = "/var/spool/uucp"; user = "uucp"; group = "uucp"; mode = "0750"; } | ||
704 | ]; | 682 | ]; |
705 | files = [ | 683 | files = [ |
706 | ]; | 684 | ]; |
diff --git a/hosts/sif/email/default.nix b/hosts/sif/email/default.nix new file mode 100644 index 00000000..bebf7980 --- /dev/null +++ b/hosts/sif/email/default.nix | |||
@@ -0,0 +1,111 @@ | |||
1 | { config, lib, pkgs, ... }: | ||
2 | { | ||
3 | services.postfix = { | ||
4 | enable = true; | ||
5 | enableSmtp = false; | ||
6 | enableSubmission = false; | ||
7 | setSendmail = true; | ||
8 | # networksStyle = "host"; | ||
9 | settings.main = { | ||
10 | recpipient_delimiter = "+"; | ||
11 | mydestination = []; | ||
12 | myhostname = "sif.midgard.yggdrasil"; | ||
13 | |||
14 | mydomain = "yggdrasil.li"; | ||
15 | |||
16 | local_transport = "error:5.1.1 No local delivery"; | ||
17 | alias_database = []; | ||
18 | alias_maps = []; | ||
19 | local_recipient_maps = []; | ||
20 | |||
21 | inet_interfaces = "loopback-only"; | ||
22 | |||
23 | message_size_limit = 0; | ||
24 | |||
25 | authorized_submit_users = "inline:{ gkleen= }"; | ||
26 | authorized_flush_users = "inline:{ gkleen= }"; | ||
27 | authorized_mailq_users = "inline:{ gkleen= }"; | ||
28 | |||
29 | smtp_generic_maps = "inline:{ root=root+sif }"; | ||
30 | |||
31 | mynetworks = ["127.0.0.0/8" "[::1]/128"]; | ||
32 | smtpd_client_restrictions = ["permit_mynetworks" "reject"]; | ||
33 | smtpd_relay_restrictions = ["permit_mynetworks" "reject"]; | ||
34 | |||
35 | sender_dependent_default_transport_maps = ''regexp:${pkgs.writeText "sender_relay" '' | ||
36 | /@(cip|stud)\.ifi\.(lmu|uni-muenchen)\.de$/ smtp:smtp.ifi.lmu.de | ||
37 | /@ifi\.(lmu|uni-muenchen)\.de$/ smtp:smtpin1.ifi.lmu.de:587 | ||
38 | /@math(ematik)?\.(lmu|uni-muenchen)\.de$/ smtps:smtp.math.lmu.de:465 | ||
39 | /@(campus\.)?lmu\.de$/ smtp:postout.lrz.de | ||
40 | ''}''; | ||
41 | sender_bcc_maps = ''regexp:${pkgs.writeText "sender_bcc" '' | ||
42 | /^uni2work(-[^@]*)?@ifi\.lmu\.de$/ uni2work@ifi.lmu.de | ||
43 | /@ifi\.lmu\.de$/ gregor.kleen@ifi.lmu.de | ||
44 | ''}''; | ||
45 | relayhost = ["[surtr.yggdrasil.li]:465"]; | ||
46 | default_transport = "relay"; | ||
47 | |||
48 | smtp_sasl_auth_enable = true; | ||
49 | smtp_sender_dependent_authentication = true; | ||
50 | smtp_sasl_tls_security_options = "noanonymous"; | ||
51 | smtp_sasl_mechanism_filter = ["plain"]; | ||
52 | smtp_sasl_password_maps = "regexp:/run/credentials/postfix.service/sasl_passwd"; | ||
53 | smtp_cname_overrides_servername = false; | ||
54 | smtp_always_send_ehlo = true; | ||
55 | smtp_tls_security_level = "dane"; | ||
56 | |||
57 | smtp_tls_loglevel = "1"; | ||
58 | smtp_dns_support_level = "dnssec"; | ||
59 | }; | ||
60 | settings.master = { | ||
61 | submission = { | ||
62 | type = "inet"; | ||
63 | private = false; | ||
64 | command = "smtpd"; | ||
65 | args = [ | ||
66 | "-o" "syslog_name=postfix/$service_name" | ||
67 | ]; | ||
68 | }; | ||
69 | smtp = { }; | ||
70 | smtps = { | ||
71 | type = "unix"; | ||
72 | private = true; | ||
73 | privileged = true; | ||
74 | chroot = false; | ||
75 | command = "smtp"; | ||
76 | args = [ | ||
77 | "-o" "smtp_tls_wrappermode=yes" | ||
78 | "-o" "smtp_tls_security_level=encrypt" | ||
79 | ]; | ||
80 | }; | ||
81 | relay = { | ||
82 | command = "smtp"; | ||
83 | args = [ | ||
84 | "-o" "smtp_fallback_relay=" | ||
85 | "-o" "smtp_tls_security_level=verify" | ||
86 | "-o" "smtp_tls_wrappermode=yes" | ||
87 | "-o" "smtp_tls_cert_file=${./relay.crt}" | ||
88 | "-o" "smtp_tls_key_file=/run/credentials/postfix.service/relay.key" | ||
89 | ]; | ||
90 | }; | ||
91 | }; | ||
92 | }; | ||
93 | |||
94 | systemd.services.postfix = { | ||
95 | serviceConfig.LoadCredential = [ | ||
96 | "sasl_passwd:${config.sops.secrets."postfix-sasl-passwd".path}" | ||
97 | "relay.key:${config.sops.secrets."relay-key".path}" | ||
98 | ]; | ||
99 | }; | ||
100 | |||
101 | sops.secrets = { | ||
102 | postfix-sasl-passwd = { | ||
103 | key = "sasl-passwd"; | ||
104 | sopsFile = ./secrets.yaml; | ||
105 | }; | ||
106 | relay-key = { | ||
107 | format = "binary"; | ||
108 | sopsFile = ./relay.key; | ||
109 | }; | ||
110 | }; | ||
111 | } | ||
diff --git a/hosts/sif/email/relay.crt b/hosts/sif/email/relay.crt new file mode 100644 index 00000000..ac13e7cb --- /dev/null +++ b/hosts/sif/email/relay.crt | |||
@@ -0,0 +1,11 @@ | |||
1 | -----BEGIN CERTIFICATE----- | ||
2 | MIIBjDCCAQygAwIBAgIPQAAAAGgLfNoL/PSMAsutMAUGAytlcTAXMRUwEwYDVQQD | ||
3 | DAx5Z2dkcmFzaWwubGkwHhcNMjUwNDI1MTIwOTQ1WhcNMzUwNDI2MTIxNDQ1WjAR | ||
4 | MQ8wDQYDVQQDDAZna2xlZW4wKjAFBgMrZXADIQB3outi3/3F4YO7Q97WAAaMHW0a | ||
5 | m+Blldrgee+EZnWnD6N1MHMwHwYDVR0jBBgwFoAUTtn+VjMw6Ge1f68KD8dT1CWn | ||
6 | l3YwHQYDVR0OBBYEFFOa4rYZYMbXUVdKv98NB504GUhjMA4GA1UdDwEB/wQEAwID | ||
7 | 6DAMBgNVHRMBAf8EAjAAMBMGA1UdJQQMMAoGCCsGAQUFBwMCMAUGAytlcQNzABC0 | ||
8 | 0UgIt7gLZrU1TmzGoqPBris8R1DbKOJacicF5CU0MIIjHcX7mPFW8KtB4qm6KcPq | ||
9 | kF6IaEPmgKpX3Nubk8HJik9vhIy9ysfINcVTvzXx8pO1bxbvREJRyA/apj10nzav | ||
10 | yauId0cXHvN6g5RLAMsMAA== | ||
11 | -----END CERTIFICATE----- | ||
diff --git a/hosts/sif/email/relay.key b/hosts/sif/email/relay.key new file mode 100644 index 00000000..412a44e0 --- /dev/null +++ b/hosts/sif/email/relay.key | |||
@@ -0,0 +1,19 @@ | |||
1 | { | ||
2 | "data": "ENC[AES256_GCM,data:lBlTuzOS75pvRmcTKT4KhHMH44RlE2SvCFAUP+GfsXws1Uai7DZ1MmbhvxxCa+pcLW19+sQYxrXLRNZWby1yOeKBJ2UQeYV5LOk9LSL/WIE3FZkCo5Dv0O0gSFKjjb61WN22a4JnHbLWADf/mLT3GZv91XfvFDo=,iv:ho8wQH3UNzX9JPW5gVcUGtxZzdVwsMFus0Z4KYe5t48=,tag:dAgZyHOva2xVVhE1nTl+lg==,type:str]", | ||
3 | "sops": { | ||
4 | "age": [ | ||
5 | { | ||
6 | "recipient": "age1rmmhetcmllq0ahl5qznlr0eya2zdxwl9h6y5wnl97d2wtyx5t99sm2u866", | ||
7 | "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSB6eTVRSUdFNUZGZmcxSUlT\nWmlsOGNyWXIzMGNTZjlKbXlhcEdZUXFRVkR3Cll0T0RMd0h2UW16QkR3SHlhYmNZ\nNDFrYXh3Rkp5NWsvcWc3UFJJaHVwT1UKLS0tIHhXVEI0VHBZVkpDQ1FzWENjMmJH\nb1FQWXVUUTBiZ1pKWG00MTNqVEo2SjAKK3VOU+QgRuxWYWEcrJiVMRFCprBICz4F\ngD+9zuPUzPezyJkYwTs+M+wX5GYkXppqm5W58yQLS2UDD38sr+SRjg==\n-----END AGE ENCRYPTED FILE-----\n" | ||
8 | }, | ||
9 | { | ||
10 | "recipient": "age1fj65apkhfkrwyv5tx6zcs9nkjg8267fy733qph30sc7zfn7vapjqkd5kne", | ||
11 | "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBzWmJmZDVFazN2bDY1TkNG\nNXpJN2twMFFjZUxMTVdSNzJwQTFiYktrcGdrCjk4eFVHTko0bFVMSlFFWm9tbjMr\nbWNHMEQ1Rm1qUVhodlB1RGw2aDc4TUEKLS0tIERBK0J5NkN4OXJEZ1ZOZXhNc1Jm\naWNnUmZGbTIxdmNkYi9TZ2h2bGs3MVEKPQGaEf7M/5/xvSOfawpIp50fB3QfFSuz\nPgkrPMneaBeUx+uBYMyEFX4rpzLIBR3pnYMjAfoc+bjWaOtGQuEqyQ==\n-----END AGE ENCRYPTED FILE-----\n" | ||
12 | } | ||
13 | ], | ||
14 | "lastmodified": "2025-04-25T12:14:44Z", | ||
15 | "mac": "ENC[AES256_GCM,data:pObl2bJA93az9E3Ya+hA3ekI8TKKZ9NNTi0KzmWZBOiQwi9FuQYtpnmmT80L1KXWyOKJV6wGdAri3mNe/ue2S0TziSbQ/4+Dj4ubFKgkH7thb5q2dFyxw5FzhYzRQiXFqD/pxcNN9uL0lQI2Al0Eci0zX8Kcd1rAQ6RzLEoSmco=,iv:zo/3QFKTUEDxLy1k5yyU7Z1JMZ7cKdYUc6GHjaTTZKQ=,tag:f63Eja3lBfwJCYAOyEt56g==,type:str]", | ||
16 | "unencrypted_suffix": "_unencrypted", | ||
17 | "version": "3.10.2" | ||
18 | } | ||
19 | } | ||
diff --git a/hosts/sif/mail/secrets.yaml b/hosts/sif/email/secrets.yaml index 3c74b710..3c74b710 100644 --- a/hosts/sif/mail/secrets.yaml +++ b/hosts/sif/email/secrets.yaml | |||
diff --git a/hosts/sif/hw.nix b/hosts/sif/hw.nix index 1bcf0261..e567c37d 100644 --- a/hosts/sif/hw.nix +++ b/hosts/sif/hw.nix | |||
@@ -25,7 +25,7 @@ | |||
25 | # system.etc.overlay.enable = false; | 25 | # system.etc.overlay.enable = false; |
26 | 26 | ||
27 | boot.initrd.systemd.packages = [ | 27 | boot.initrd.systemd.packages = [ |
28 | (pkgs.writeTextDir "/etc/systemd/system/\\x2ebcachefs.mount.d/block_scan.conf" '' | 28 | (pkgs.writeTextDir "/etc/systemd/system/sysroot-.bcachefs.mount.d/block_scan.conf" '' |
29 | [Mount] | 29 | [Mount] |
30 | Environment=BCACHEFS_BLOCK_SCAN=1 | 30 | Environment=BCACHEFS_BLOCK_SCAN=1 |
31 | '') | 31 | '') |
diff --git a/hosts/sif/mail/default.nix b/hosts/sif/mail/default.nix deleted file mode 100644 index 8d6cd705..00000000 --- a/hosts/sif/mail/default.nix +++ /dev/null | |||
@@ -1,70 +0,0 @@ | |||
1 | { config, lib, pkgs, ... }: | ||
2 | { | ||
3 | services.postfix = { | ||
4 | enable = true; | ||
5 | enableSmtp = true; | ||
6 | enableSubmission = false; | ||
7 | setSendmail = true; | ||
8 | networksStyle = "host"; | ||
9 | hostname = "sif.midgard.yggdrasil"; | ||
10 | destination = []; | ||
11 | relayHost = "uucp:ymir"; | ||
12 | recipientDelimiter = "+"; | ||
13 | masterConfig = { | ||
14 | uucp = { | ||
15 | type = "unix"; | ||
16 | private = true; | ||
17 | privileged = true; | ||
18 | chroot = false; | ||
19 | command = "pipe"; | ||
20 | args = [ "flags=Fqhu" "user=uucp" ''argv=${config.security.wrapperDir}/uux -z -a $sender - $nexthop!rmail ($recipient)'' ]; | ||
21 | }; | ||
22 | smtps = { | ||
23 | type = "unix"; | ||
24 | private = true; | ||
25 | privileged = true; | ||
26 | chroot = false; | ||
27 | command = "smtp"; | ||
28 | args = [ "-o" "smtp_tls_wrappermode=yes" "-o" "smtp_tls_security_level=encrypt" ]; | ||
29 | }; | ||
30 | }; | ||
31 | config = { | ||
32 | default_transport = "uucp:ymir"; | ||
33 | |||
34 | inet_interfaces = "loopback-only"; | ||
35 | |||
36 | authorized_submit_users = ["!uucp" "static:anyone"]; | ||
37 | message_size_limit = "0"; | ||
38 | |||
39 | sender_dependent_default_transport_maps = ''regexp:${pkgs.writeText "sender_relay" '' | ||
40 | /@(cip|stud)\.ifi\.(lmu|uni-muenchen)\.de$/ smtp:smtp.ifi.lmu.de | ||
41 | /@ifi\.(lmu|uni-muenchen)\.de$/ smtp:smtpin1.ifi.lmu.de:587 | ||
42 | /@math(ematik)?\.(lmu|uni-muenchen)\.de$/ smtps:smtp.math.lmu.de:465 | ||
43 | /@(campus\.)?lmu\.de$/ smtp:postout.lrz.de | ||
44 | ''}''; | ||
45 | sender_bcc_maps = ''regexp:${pkgs.writeText "sender_bcc" '' | ||
46 | /^uni2work(-[^@]*)?@ifi\.lmu\.de$/ uni2work@ifi.lmu.de | ||
47 | /@ifi\.lmu\.de$/ gregor.kleen@ifi.lmu.de | ||
48 | ''}''; | ||
49 | |||
50 | smtp_sasl_auth_enable = true; | ||
51 | smtp_sender_dependent_authentication = true; | ||
52 | smtp_sasl_tls_security_options = "noanonymous"; | ||
53 | smtp_sasl_mechanism_filter = ["plain"]; | ||
54 | smtp_sasl_password_maps = "regexp:/var/db/postfix/sasl_passwd"; | ||
55 | smtp_cname_overrides_servername = false; | ||
56 | smtp_always_send_ehlo = true; | ||
57 | smtp_tls_security_level = "dane"; | ||
58 | |||
59 | smtp_tls_loglevel = "1"; | ||
60 | smtp_dns_support_level = "dnssec"; | ||
61 | }; | ||
62 | }; | ||
63 | |||
64 | sops.secrets.postfix-sasl-passwd = { | ||
65 | key = "sasl-passwd"; | ||
66 | path = "/var/db/postfix/sasl_passwd"; | ||
67 | owner = "postfix"; | ||
68 | sopsFile = ./secrets.yaml; | ||
69 | }; | ||
70 | } | ||
diff --git a/hosts/surtr/bifrost/default.nix b/hosts/surtr/bifrost/default.nix index fbfde757..52ab43f5 100644 --- a/hosts/surtr/bifrost/default.nix +++ b/hosts/surtr/bifrost/default.nix | |||
@@ -18,7 +18,7 @@ in { | |||
18 | ListenPort = 51822; | 18 | ListenPort = 51822; |
19 | }; | 19 | }; |
20 | wireguardPeers = [ | 20 | wireguardPeers = [ |
21 | { AllowedIPs = [ "2a03:4000:52:ada:4:1::/96" ]; | 21 | { AllowedIPs = [ "2a03:4000:52:ada:4:1::/96" "2a03:4000:52:ada:6::/80" ]; |
22 | PublicKey = trim (readFile ../../vidhar/network/bifrost/vidhar.pub); | 22 | PublicKey = trim (readFile ../../vidhar/network/bifrost/vidhar.pub); |
23 | } | 23 | } |
24 | ]; | 24 | ]; |
@@ -34,6 +34,8 @@ in { | |||
34 | routes = [ | 34 | routes = [ |
35 | { Destination = "2a03:4000:52:ada:4::/80"; | 35 | { Destination = "2a03:4000:52:ada:4::/80"; |
36 | } | 36 | } |
37 | { Destination = "2a03:4000:52:ada:6::/80"; | ||
38 | } | ||
37 | ]; | 39 | ]; |
38 | linkConfig = { | 40 | linkConfig = { |
39 | RequiredForOnline = false; | 41 | RequiredForOnline = false; |
diff --git a/hosts/surtr/default.nix b/hosts/surtr/default.nix index d420040a..63beece3 100644 --- a/hosts/surtr/default.nix +++ b/hosts/surtr/default.nix | |||
@@ -7,7 +7,7 @@ with lib; | |||
7 | tmpfs-root qemu-guest openssh rebuild-machines zfs | 7 | tmpfs-root qemu-guest openssh rebuild-machines zfs |
8 | ./zfs.nix ./dns ./tls ./http ./bifrost ./matrix ./postgresql | 8 | ./zfs.nix ./dns ./tls ./http ./bifrost ./matrix ./postgresql |
9 | ./prometheus ./email ./vpn ./borg.nix ./etebase ./immich.nix | 9 | ./prometheus ./email ./vpn ./borg.nix ./etebase ./immich.nix |
10 | ./paperless.nix ./hledger.nix ./audiobookshelf.nix | 10 | ./paperless.nix ./hledger.nix ./audiobookshelf.nix ./kimai.nix |
11 | ]; | 11 | ]; |
12 | 12 | ||
13 | config = { | 13 | config = { |
@@ -22,7 +22,6 @@ with lib; | |||
22 | device = "/dev/vda"; | 22 | device = "/dev/vda"; |
23 | }; | 23 | }; |
24 | 24 | ||
25 | |||
26 | tmp.useTmpfs = true; | 25 | tmp.useTmpfs = true; |
27 | 26 | ||
28 | zfs.devNodes = "/dev"; # /dev/vda2 does not show up in /dev/disk/by-id | 27 | zfs.devNodes = "/dev"; # /dev/vda2 does not show up in /dev/disk/by-id |
diff --git a/hosts/surtr/dns/default.nix b/hosts/surtr/dns/default.nix index 7aa3fb00..8aca2b97 100644 --- a/hosts/surtr/dns/default.nix +++ b/hosts/surtr/dns/default.nix | |||
@@ -157,7 +157,7 @@ in { | |||
157 | ${concatMapStringsSep "\n" mkZone [ | 157 | ${concatMapStringsSep "\n" mkZone [ |
158 | { domain = "yggdrasil.li"; | 158 | { domain = "yggdrasil.li"; |
159 | addACLs = { "yggdrasil.li" = ["ymir_acme_acl"]; }; | 159 | addACLs = { "yggdrasil.li" = ["ymir_acme_acl"]; }; |
160 | acmeDomains = ["surtr.yggdrasil.li" "yggdrasil.li" "etesync.yggdrasil.li" "immich.yggdrasil.li" "app.etesync.yggdrasil.li" "paperless.yggdrasil.li" "hledger.yggdrasil.li" "audiobookshelf.yggdrasil.li"]; | 160 | acmeDomains = ["surtr.yggdrasil.li" "yggdrasil.li" "etesync.yggdrasil.li" "immich.yggdrasil.li" "app.etesync.yggdrasil.li" "paperless.yggdrasil.li" "hledger.yggdrasil.li" "audiobookshelf.yggdrasil.li" "kimai.yggdrasil.li"]; |
161 | } | 161 | } |
162 | { domain = "nights.email"; | 162 | { domain = "nights.email"; |
163 | addACLs = { "nights.email" = ["ymir_acme_acl"]; }; | 163 | addACLs = { "nights.email" = ["ymir_acme_acl"]; }; |
diff --git a/hosts/surtr/dns/keys/kimai.yggdrasil.li_acme b/hosts/surtr/dns/keys/kimai.yggdrasil.li_acme new file mode 100644 index 00000000..bdfb135a --- /dev/null +++ b/hosts/surtr/dns/keys/kimai.yggdrasil.li_acme | |||
@@ -0,0 +1,19 @@ | |||
1 | { | ||
2 | "data": "ENC[AES256_GCM,data:sKFt4pH0Xn7Qm6JFMg/2N7Ht7jtMJukfN+U3dQaoYXPbhRJ+heEtDpXV/WP4AlfbfpIOgTPW3mcmQCwKFNhS00vEsQA4728FfXZzDDmZCa3hwg51wDbL7XUOr0OePgzi86lt0Q193K6CkGqEAa1vFIb//ElEfBYIwdATbmcoAsM3mHhz58X7c1qf8LNuB93o/1N2xXXZI3NWOhOjlviTc2DAhffXDwlMJSYUhldnwtDKmLM1mooJzLgm2p9w7gRD7WPqEqZFq9uFDK69P9uX5T9hFHg=,iv:rAE4sYxxLou4tyD4RWTp3LjQP0cya95coy1MvwfEK/U=,tag:u4SSk8SZFlj0ks7d6tDocw==,type:str]", | ||
3 | "sops": { | ||
4 | "age": [ | ||
5 | { | ||
6 | "recipient": "age1rmmhetcmllq0ahl5qznlr0eya2zdxwl9h6y5wnl97d2wtyx5t99sm2u866", | ||
7 | "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSB2KzdNUWhEcDB6QmtUTnVh\nNS9Nc2I4UjAzekxhRXo1UmY3SklPejV1TURJCm9NY2lVOERoMDFKTU56Mmh1NHEr\naGV4M1RoVldHV0xyc3Z0MnVqakpjMFUKLS0tIEYxSk9OUm9kMkdtcG5POWRGQVkx\nY1FEaXYwMGo0L0Z0aTVTZDA5aUFDWEUKJ+e/7lR/rNPNVnIy+wkiKiAYMxWp4L7q\nwnSTx451vSnxv9j3JWB43Y7XQC08cisWDj06ULw8FnEbKYOvTYj9mQ==\n-----END AGE ENCRYPTED FILE-----\n" | ||
8 | }, | ||
9 | { | ||
10 | "recipient": "age19a7j77w267z04zls7m28a8hj4a0g5af6ltye2d5wypg33c3l89csd4r9zq", | ||
11 | "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBwOTU3dUEzaXM5T1VkbDRO\nMm14OG1mUkk2bDRhdnBsMHBkc3kvUzlyNlQwCktFSHJhMnhoQ2J6bC9vUHNLWTRC\nRFpYeHo3N2xjWUhjQnRwQ2Nrc1pRUmsKLS0tIDdPeFBVdkxDd1JWSmcxQ0tLMTBD\ncHU3VExZOUhYUlJvbGNoK3FMK2VIbGMKFk94P9aBY04CPIi983f3Aalgh4fnU+/K\n2mxawSMf9jz8704N5XJfmr2hwNy8hqLIn8bjsEMAPTfE1YBGga4w0g==\n-----END AGE ENCRYPTED FILE-----\n" | ||
12 | } | ||
13 | ], | ||
14 | "lastmodified": "2025-05-24T09:42:23Z", | ||
15 | "mac": "ENC[AES256_GCM,data:diCeJGvBmM0Ng722eKoFwDe7pqZrdLPSLn5j9LfdaFI64BAbSbA5bAq4NFXqdJ1vttarD2A5rEafYoXUxP8228x2GhNyWUGW5AWgBjVPUc59gjs4wYKR5HlkVMIadhTwNheEyoEjrxX40GNBgCG7X3ocOtOYKbKECp433gdAPDg=,iv:d+yJMWj2RyFnveo2ZNrpNeV+amXM+H7vdC0A2F7mwjA=,tag:yjibG2iusdprp0ORghYWhw==,type:str]", | ||
16 | "unencrypted_suffix": "_unencrypted", | ||
17 | "version": "3.10.2" | ||
18 | } | ||
19 | } | ||
diff --git a/hosts/surtr/dns/zones/email.nights.soa b/hosts/surtr/dns/zones/email.nights.soa index 913a88d4..34209a99 100644 --- a/hosts/surtr/dns/zones/email.nights.soa +++ b/hosts/surtr/dns/zones/email.nights.soa | |||
@@ -1,7 +1,7 @@ | |||
1 | $ORIGIN nights.email. | 1 | $ORIGIN nights.email. |
2 | $TTL 3600 | 2 | $TTL 3600 |
3 | @ IN SOA ns.yggdrasil.li. hostmaster.yggdrasil.li ( | 3 | @ IN SOA ns.yggdrasil.li. hostmaster.yggdrasil.li ( |
4 | 2023013000 ; serial | 4 | 2025060700 ; serial |
5 | 10800 ; refresh | 5 | 10800 ; refresh |
6 | 3600 ; retry | 6 | 3600 ; retry |
7 | 604800 ; expire | 7 | 604800 ; expire |
@@ -27,11 +27,7 @@ $TTL 3600 | |||
27 | 27 | ||
28 | _acme-challenge IN NS ns.yggdrasil.li. | 28 | _acme-challenge IN NS ns.yggdrasil.li. |
29 | 29 | ||
30 | ymir._domainkey IN TXT ( | 30 | ymir._domainkey IN CNAME ymir._domainkey.yggdrasil.li. |
31 | "v=DKIM1;k=rsa;p=MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAq3cCKlk+VPhyAanLZTM0BCzUT/+fmxHioZcFk0uJk1akBYj7BRofR7eVNcLKpm3rwYMQgE+9vJH9p8SV6tws9EcWc8SMCqqGZlREYM7PmLDiTSK/vjCzkygfgFCb0EBNsY2A/fpP4rTeoxrbcBSvMkq97iY5rwyw4wXZVZXLiDaCj23s8POoxTk1ClqUJZJQ5x2" | ||
32 | "qzrC0RfN5kLZ9A7Gq2jB09vNxpXHYqABA0bJv88JiZM7hfkp9IafJZ+yCVMaBcJs4DAxnTjNAuFD9gm+qSFVY8+yeXqL6Qjo5PbruhyZRBW8RgRYT8t5n07XRglMGKKGMwOGLanrltcyXqB+GsDZBD36RAAwjFadnxdpDyRv4SgRP7ff2tKRrORYpmpN+mKdqw5j3J/nP6bXV1oAkyh9XQkPEIDi81WT87EZziTElDzVp6A2qFOxqucAovoRk24" | ||
33 | "7vlsns1FApFRsp9mja0UZNObyKD1M6tP9Ep7lS76tFGMk+WDvXRJH5LEsyCpu7sSyl1r/O0M4K+KldRCqLlZd7rf8F5P8T0dn1azk05g7F4p0N/y9GNdzXbPZ9u0eZdI7SEdh8ZoOZp7NVZiBFfbWLSS5ZtyA2kbBa4i7GJ/cuAbEKOmqAkeQPiu96TGIcyjkXjS6mTPI+9UmKZYZC+OM8XdJ02y5KRoonCc19ZS8CAwEAAQ==" | ||
34 | ) | ||
35 | 31 | ||
36 | _xmpp-client._tcp IN SRV 5 0 5222 ymir.yggdrasil.li. | 32 | _xmpp-client._tcp IN SRV 5 0 5222 ymir.yggdrasil.li. |
37 | _xmpp-server._tcp IN SRV 5 0 5269 ymir.yggdrasil.li. | 33 | _xmpp-server._tcp IN SRV 5 0 5269 ymir.yggdrasil.li. |
diff --git a/hosts/surtr/dns/zones/li.141.soa b/hosts/surtr/dns/zones/li.141.soa index ab117f09..78d137bb 100644 --- a/hosts/surtr/dns/zones/li.141.soa +++ b/hosts/surtr/dns/zones/li.141.soa | |||
@@ -1,7 +1,7 @@ | |||
1 | $ORIGIN 141.li. | 1 | $ORIGIN 141.li. |
2 | $TTL 3600 | 2 | $TTL 3600 |
3 | @ IN SOA ns.yggdrasil.li. hostmaster.yggdrasil.li ( | 3 | @ IN SOA ns.yggdrasil.li. hostmaster.yggdrasil.li ( |
4 | 2025020900 ; serial | 4 | 2025060701 ; serial |
5 | 10800 ; refresh | 5 | 10800 ; refresh |
6 | 3600 ; retry | 6 | 3600 ; retry |
7 | 604800 ; expire | 7 | 604800 ; expire |
@@ -45,11 +45,8 @@ ymir IN AAAA 2a03:4000:6:d004:: | |||
45 | ymir IN MX 0 ymir.yggdrasil.li | 45 | ymir IN MX 0 ymir.yggdrasil.li |
46 | ymir IN TXT "v=spf1 redirect=ymir.yggdrasil.li" | 46 | ymir IN TXT "v=spf1 redirect=ymir.yggdrasil.li" |
47 | 47 | ||
48 | ymir._domainkey IN TXT ( | 48 | ymir._domainkey IN CNAME ymir._domainkey.yggdrasil.li. |
49 | "v=DKIM1;k=rsa;p=MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAq3cCKlk+VPhyAanLZTM0BCzUT/+fmxHioZcFk0uJk1akBYj7BRofR7eVNcLKpm3rwYMQgE+9vJH9p8SV6tws9EcWc8SMCqqGZlREYM7PmLDiTSK/vjCzkygfgFCb0EBNsY2A/fpP4rTeoxrbcBSvMkq97iY5rwyw4wXZVZXLiDaCj23s8POoxTk1ClqUJZJQ5x2" | 49 | surtr._domainkey IN CNAME surtr._domainkey.yggdrasil.li. |
50 | "qzrC0RfN5kLZ9A7Gq2jB09vNxpXHYqABA0bJv88JiZM7hfkp9IafJZ+yCVMaBcJs4DAxnTjNAuFD9gm+qSFVY8+yeXqL6Qjo5PbruhyZRBW8RgRYT8t5n07XRglMGKKGMwOGLanrltcyXqB+GsDZBD36RAAwjFadnxdpDyRv4SgRP7ff2tKRrORYpmpN+mKdqw5j3J/nP6bXV1oAkyh9XQkPEIDi81WT87EZziTElDzVp6A2qFOxqucAovoRk24" | ||
51 | "7vlsns1FApFRsp9mja0UZNObyKD1M6tP9Ep7lS76tFGMk+WDvXRJH5LEsyCpu7sSyl1r/O0M4K+KldRCqLlZd7rf8F5P8T0dn1azk05g7F4p0N/y9GNdzXbPZ9u0eZdI7SEdh8ZoOZp7NVZiBFfbWLSS5ZtyA2kbBa4i7GJ/cuAbEKOmqAkeQPiu96TGIcyjkXjS6mTPI+9UmKZYZC+OM8XdJ02y5KRoonCc19ZS8CAwEAAQ==" | ||
52 | ) | ||
53 | 50 | ||
54 | _xmpp-client._tcp IN SRV 5 0 5222 ymir.yggdrasil.li. | 51 | _xmpp-client._tcp IN SRV 5 0 5222 ymir.yggdrasil.li. |
55 | _xmpp-server._tcp IN SRV 5 0 5269 ymir.yggdrasil.li. | 52 | _xmpp-server._tcp IN SRV 5 0 5269 ymir.yggdrasil.li. |
diff --git a/hosts/surtr/dns/zones/li.kleen.soa b/hosts/surtr/dns/zones/li.kleen.soa index a1c7d35a..5dd3e697 100644 --- a/hosts/surtr/dns/zones/li.kleen.soa +++ b/hosts/surtr/dns/zones/li.kleen.soa | |||
@@ -1,7 +1,7 @@ | |||
1 | $ORIGIN kleen.li. | 1 | $ORIGIN kleen.li. |
2 | $TTL 3600 | 2 | $TTL 3600 |
3 | @ IN SOA ns.yggdrasil.li. hostmaster.yggdrasil.li ( | 3 | @ IN SOA ns.yggdrasil.li. hostmaster.yggdrasil.li ( |
4 | 2023013000 ; serial | 4 | 2025060701 ; serial |
5 | 10800 ; refresh | 5 | 10800 ; refresh |
6 | 3600 ; retry | 6 | 3600 ; retry |
7 | 604800 ; expire | 7 | 604800 ; expire |
@@ -27,11 +27,8 @@ $TTL 3600 | |||
27 | 27 | ||
28 | _acme-challenge IN NS ns.yggdrasil.li. | 28 | _acme-challenge IN NS ns.yggdrasil.li. |
29 | 29 | ||
30 | ymir._domainkey IN TXT ( | 30 | ymir._domainkey IN CNAME ymir._domainkey.yggdrasil.li. |
31 | "v=DKIM1;k=rsa;p=MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAq3cCKlk+VPhyAanLZTM0BCzUT/+fmxHioZcFk0uJk1akBYj7BRofR7eVNcLKpm3rwYMQgE+9vJH9p8SV6tws9EcWc8SMCqqGZlREYM7PmLDiTSK/vjCzkygfgFCb0EBNsY2A/fpP4rTeoxrbcBSvMkq97iY5rwyw4wXZVZXLiDaCj23s8POoxTk1ClqUJZJQ5x2" | 31 | surtr._domainkey IN CNAME surtr._domainkey.yggdrasil.li. |
32 | "qzrC0RfN5kLZ9A7Gq2jB09vNxpXHYqABA0bJv88JiZM7hfkp9IafJZ+yCVMaBcJs4DAxnTjNAuFD9gm+qSFVY8+yeXqL6Qjo5PbruhyZRBW8RgRYT8t5n07XRglMGKKGMwOGLanrltcyXqB+GsDZBD36RAAwjFadnxdpDyRv4SgRP7ff2tKRrORYpmpN+mKdqw5j3J/nP6bXV1oAkyh9XQkPEIDi81WT87EZziTElDzVp6A2qFOxqucAovoRk24" | ||
33 | "7vlsns1FApFRsp9mja0UZNObyKD1M6tP9Ep7lS76tFGMk+WDvXRJH5LEsyCpu7sSyl1r/O0M4K+KldRCqLlZd7rf8F5P8T0dn1azk05g7F4p0N/y9GNdzXbPZ9u0eZdI7SEdh8ZoOZp7NVZiBFfbWLSS5ZtyA2kbBa4i7GJ/cuAbEKOmqAkeQPiu96TGIcyjkXjS6mTPI+9UmKZYZC+OM8XdJ02y5KRoonCc19ZS8CAwEAAQ==" | ||
34 | ) | ||
35 | 32 | ||
36 | _xmpp-client._tcp IN SRV 5 0 5222 ymir.yggdrasil.li. | 33 | _xmpp-client._tcp IN SRV 5 0 5222 ymir.yggdrasil.li. |
37 | _xmpp-server._tcp IN SRV 5 0 5269 ymir.yggdrasil.li. | 34 | _xmpp-server._tcp IN SRV 5 0 5269 ymir.yggdrasil.li. |
diff --git a/hosts/surtr/dns/zones/li.synapse.soa b/hosts/surtr/dns/zones/li.synapse.soa index 086d4a85..247cf025 100644 --- a/hosts/surtr/dns/zones/li.synapse.soa +++ b/hosts/surtr/dns/zones/li.synapse.soa | |||
@@ -1,7 +1,7 @@ | |||
1 | $ORIGIN synapse.li. | 1 | $ORIGIN synapse.li. |
2 | $TTL 3600 | 2 | $TTL 3600 |
3 | @ IN SOA ns.yggdrasil.li. hostmaster.yggdrasil.li ( | 3 | @ IN SOA ns.yggdrasil.li. hostmaster.yggdrasil.li ( |
4 | 2023092100 ; serial | 4 | 2025060701 ; serial |
5 | 10800 ; refresh | 5 | 10800 ; refresh |
6 | 3600 ; retry | 6 | 3600 ; retry |
7 | 604800 ; expire | 7 | 604800 ; expire |
diff --git a/hosts/surtr/dns/zones/li.yggdrasil.soa b/hosts/surtr/dns/zones/li.yggdrasil.soa index 7273827b..500194ae 100644 --- a/hosts/surtr/dns/zones/li.yggdrasil.soa +++ b/hosts/surtr/dns/zones/li.yggdrasil.soa | |||
@@ -1,7 +1,7 @@ | |||
1 | $ORIGIN yggdrasil.li. | 1 | $ORIGIN yggdrasil.li. |
2 | $TTL 3600 | 2 | $TTL 3600 |
3 | @ IN SOA ns.yggdrasil.li. hostmaster.yggdrasil.li ( | 3 | @ IN SOA ns.yggdrasil.li. hostmaster.yggdrasil.li ( |
4 | 2025050900 ; serial | 4 | 2025060700 ; serial |
5 | 10800 ; refresh | 5 | 10800 ; refresh |
6 | 3600 ; retry | 6 | 3600 ; retry |
7 | 604800 ; expire | 7 | 604800 ; expire |
@@ -101,12 +101,22 @@ _acme-challenge.audiobookshelf IN NS ns.yggdrasil.li. | |||
101 | 101 | ||
102 | audiobookshelf IN HTTPS 1 . alpn="h2,h3" ipv4hint="202.61.241.61" ipv6hint="2a03:4000:52:ada::" | 102 | audiobookshelf IN HTTPS 1 . alpn="h2,h3" ipv4hint="202.61.241.61" ipv6hint="2a03:4000:52:ada::" |
103 | 103 | ||
104 | kimai IN A 202.61.241.61 | ||
105 | kimai IN AAAA 2a03:4000:52:ada:: | ||
106 | kimai IN MX 0 surtr.yggdrasil.li | ||
107 | kimai IN TXT "v=spf1 redirect=surtr.yggdrasil.li" | ||
108 | _acme-challenge.kimai IN NS ns.yggdrasil.li. | ||
109 | |||
110 | kimai IN HTTPS 1 . alpn="h2,h3" ipv4hint="202.61.241.61" ipv6hint="2a03:4000:52:ada::" | ||
111 | |||
104 | vidhar IN AAAA 2a03:4000:52:ada:4:1:: | 112 | vidhar IN AAAA 2a03:4000:52:ada:4:1:: |
105 | vidhar IN MX 0 ymir.yggdrasil.li | 113 | vidhar IN MX 0 ymir.yggdrasil.li |
106 | vidhar IN TXT "v=spf1 redirect=yggdrasil.li" | 114 | vidhar IN TXT "v=spf1 redirect=yggdrasil.li" |
107 | 115 | ||
108 | mailout IN A 188.68.51.254 | 116 | mailout IN A 188.68.51.254 |
109 | mailout IN AAAA 2a03:4000:6:d004:: | 117 | mailout IN AAAA 2a03:4000:6:d004:: |
118 | mailout IN A 202.61.241.61 | ||
119 | mailout IN AAAA 2a03:4000:52:ada:: | ||
110 | mailout IN MX 0 ymir.yggdrasil.li | 120 | mailout IN MX 0 ymir.yggdrasil.li |
111 | mailout IN TXT "v=spf1 redirect=yggdrasil.li" | 121 | mailout IN TXT "v=spf1 redirect=yggdrasil.li" |
112 | 122 | ||
diff --git a/hosts/surtr/dns/zones/org.praseodym.soa b/hosts/surtr/dns/zones/org.praseodym.soa index df505b4c..2b97ca19 100644 --- a/hosts/surtr/dns/zones/org.praseodym.soa +++ b/hosts/surtr/dns/zones/org.praseodym.soa | |||
@@ -1,7 +1,7 @@ | |||
1 | $ORIGIN praseodym.org. | 1 | $ORIGIN praseodym.org. |
2 | $TTL 3600 | 2 | $TTL 3600 |
3 | @ IN SOA ns.yggdrasil.li. hostmaster.yggdrasil.li ( | 3 | @ IN SOA ns.yggdrasil.li. hostmaster.yggdrasil.li ( |
4 | 2023013000 ; serial | 4 | 2025060701 ; serial |
5 | 10800 ; refresh | 5 | 10800 ; refresh |
6 | 3600 ; retry | 6 | 3600 ; retry |
7 | 604800 ; expire | 7 | 604800 ; expire |
@@ -32,11 +32,8 @@ surtr IN AAAA 2a03:4000:52:ada:: | |||
32 | surtr IN MX 0 ymir.yggdrasil.li | 32 | surtr IN MX 0 ymir.yggdrasil.li |
33 | surtr IN TXT "v=spf1 redirect=yggdrasil.li" | 33 | surtr IN TXT "v=spf1 redirect=yggdrasil.li" |
34 | 34 | ||
35 | ymir._domainkey IN TXT ( | 35 | ymir._domainkey IN CNAME ymir._domainkey.yggdrasil.li. |
36 | "v=DKIM1;k=rsa;p=MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAq3cCKlk+VPhyAanLZTM0BCzUT/+fmxHioZcFk0uJk1akBYj7BRofR7eVNcLKpm3rwYMQgE+9vJH9p8SV6tws9EcWc8SMCqqGZlREYM7PmLDiTSK/vjCzkygfgFCb0EBNsY2A/fpP4rTeoxrbcBSvMkq97iY5rwyw4wXZVZXLiDaCj23s8POoxTk1ClqUJZJQ5x2" | 36 | surtr._domainkey IN CNAME surtr._domainkey.yggdrasil.li. |
37 | "qzrC0RfN5kLZ9A7Gq2jB09vNxpXHYqABA0bJv88JiZM7hfkp9IafJZ+yCVMaBcJs4DAxnTjNAuFD9gm+qSFVY8+yeXqL6Qjo5PbruhyZRBW8RgRYT8t5n07XRglMGKKGMwOGLanrltcyXqB+GsDZBD36RAAwjFadnxdpDyRv4SgRP7ff2tKRrORYpmpN+mKdqw5j3J/nP6bXV1oAkyh9XQkPEIDi81WT87EZziTElDzVp6A2qFOxqucAovoRk24" | ||
38 | "7vlsns1FApFRsp9mja0UZNObyKD1M6tP9Ep7lS76tFGMk+WDvXRJH5LEsyCpu7sSyl1r/O0M4K+KldRCqLlZd7rf8F5P8T0dn1azk05g7F4p0N/y9GNdzXbPZ9u0eZdI7SEdh8ZoOZp7NVZiBFfbWLSS5ZtyA2kbBa4i7GJ/cuAbEKOmqAkeQPiu96TGIcyjkXjS6mTPI+9UmKZYZC+OM8XdJ02y5KRoonCc19ZS8CAwEAAQ==" | ||
39 | ) | ||
40 | 37 | ||
41 | _xmpp-client._tcp IN SRV 5 0 5222 ymir.yggdrasil.li. | 38 | _xmpp-client._tcp IN SRV 5 0 5222 ymir.yggdrasil.li. |
42 | _xmpp-server._tcp IN SRV 5 0 5269 ymir.yggdrasil.li. | 39 | _xmpp-server._tcp IN SRV 5 0 5269 ymir.yggdrasil.li. |
diff --git a/hosts/surtr/email/ccert-policy-server/ccert_policy_server/__main__.py b/hosts/surtr/email/ccert-policy-server/ccert_policy_server/__main__.py index 00182523..7c931559 100644 --- a/hosts/surtr/email/ccert-policy-server/ccert_policy_server/__main__.py +++ b/hosts/surtr/email/ccert-policy-server/ccert_policy_server/__main__.py | |||
@@ -28,10 +28,12 @@ class PolicyHandler(StreamRequestHandler): | |||
28 | 28 | ||
29 | allowed = False | 29 | allowed = False |
30 | user = None | 30 | user = None |
31 | relay_eligible = False | ||
31 | if self.args['sasl_username']: | 32 | if self.args['sasl_username']: |
32 | user = self.args['sasl_username'] | 33 | user = self.args['sasl_username'] |
33 | if self.args['ccert_subject']: | 34 | if self.args['ccert_subject']: |
34 | user = self.args['ccert_subject'] | 35 | user = self.args['ccert_subject'] |
36 | relay_eligible = True | ||
35 | 37 | ||
36 | if user: | 38 | if user: |
37 | with self.server.db_pool.connection() as conn: | 39 | with self.server.db_pool.connection() as conn: |
@@ -44,10 +46,16 @@ class PolicyHandler(StreamRequestHandler): | |||
44 | 46 | ||
45 | with conn.cursor() as cur: | 47 | with conn.cursor() as cur: |
46 | cur.row_factory = namedtuple_row | 48 | cur.row_factory = namedtuple_row |
47 | cur.execute('SELECT "mailbox"."mailbox" as "user", "local", "extension", "domain" FROM "mailbox" INNER JOIN "mailbox_mapping" ON "mailbox".id = "mailbox_mapping"."mailbox" WHERE "mailbox"."mailbox" = %(user)s AND ("local" = %(local)s OR "local" IS NULL) AND ("extension" = %(extension)s OR "extension" IS NULL) AND "domain" = %(domain)s', params = {'user': user, 'local': local, 'extension': extension if extension is not None else '', 'domain': domain}, prepare=True) | 49 | |
48 | for record in cur: | 50 | if relay_eligible: |
49 | logger.debug('Received result: %s', record) | 51 | cur.execute('SELECT EXISTS(SELECT true FROM "mailbox" INNER JOIN "relay_access" ON "mailbox".id = "relay_access"."mailbox" WHERE "mailbox"."mailbox" = %(user)s AND ("domain" = %(domain)s OR %(domain)s ilike CONCAT(\'%%_.\', "domain"))) as "exists"', params = {'user': user, 'domain': domain}) |
50 | allowed = True | 52 | if (row := cur.fetchone()) is not None: |
53 | allowed = row.exists | ||
54 | |||
55 | if not allowed: | ||
56 | cur.execute('SELECT EXISTS(SELECT true FROM "mailbox" INNER JOIN "mailbox_mapping" ON "mailbox".id = "mailbox_mapping"."mailbox" WHERE "mailbox"."mailbox" = %(user)s AND ("local" = %(local)s OR "local" IS NULL) AND ("extension" = %(extension)s OR "extension" IS NULL) AND "domain" = %(domain)s) as "exists"', params = {'user': user, 'local': local, 'extension': extension if extension is not None else '', 'domain': domain}, prepare=True) | ||
57 | if (row := cur.fetchone()) is not None: | ||
58 | allowed = row.exists | ||
51 | 59 | ||
52 | action = '550 5.7.0 Sender address not authorized for current user' | 60 | action = '550 5.7.0 Sender address not authorized for current user' |
53 | if allowed: | 61 | if allowed: |
diff --git a/hosts/surtr/email/default.nix b/hosts/surtr/email/default.nix index 845f6455..a3e06ca6 100644 --- a/hosts/surtr/email/default.nix +++ b/hosts/surtr/email/default.nix | |||
@@ -1,4 +1,4 @@ | |||
1 | { config, pkgs, lib, flakeInputs, ... }: | 1 | { config, pkgs, lib, flake, flakeInputs, ... }: |
2 | 2 | ||
3 | with lib; | 3 | with lib; |
4 | 4 | ||
@@ -15,7 +15,7 @@ let | |||
15 | 15 | ||
16 | for file in $out/pipe/bin/*; do | 16 | for file in $out/pipe/bin/*; do |
17 | wrapProgram $file \ | 17 | wrapProgram $file \ |
18 | --set PATH "${pkgs.coreutils}/bin:${pkgs.rspamd}/bin" | 18 | --set PATH "${makeBinPath (with pkgs; [coreutils rspamd])}" |
19 | done | 19 | done |
20 | ''; | 20 | ''; |
21 | }; | 21 | }; |
@@ -33,12 +33,28 @@ let | |||
33 | }); | 33 | }); |
34 | }); | 34 | }); |
35 | }; | 35 | }; |
36 | internal-policy-server = | ||
37 | let | ||
38 | workspace = flakeInputs.uv2nix.lib.workspace.loadWorkspace { workspaceRoot = ./internal-policy-server; }; | ||
39 | pythonSet = flake.lib.pythonSet { | ||
40 | inherit pkgs; | ||
41 | python = pkgs.python312; | ||
42 | overlay = workspace.mkPyprojectOverlay { | ||
43 | sourcePreference = "wheel"; | ||
44 | }; | ||
45 | }; | ||
46 | virtualEnv = pythonSet.mkVirtualEnv "internal-policy-server-env" workspace.deps.default; | ||
47 | in virtualEnv.overrideAttrs (oldAttrs: { | ||
48 | meta = (oldAttrs.meta or {}) // { | ||
49 | mainProgram = "internal-policy-server"; | ||
50 | }; | ||
51 | }); | ||
36 | 52 | ||
37 | nftables-nologin-script = pkgs.writeScript "nftables-mail-nologin" '' | 53 | nftables-nologin-script = pkgs.resholve.writeScript "nftables-mail-nologin" { |
38 | #!${pkgs.zsh}/bin/zsh | 54 | inputs = with pkgs; [inetutils nftables gnugrep findutils]; |
39 | 55 | interpreter = lib.getExe pkgs.zsh; | |
56 | } '' | ||
40 | set -e | 57 | set -e |
41 | export PATH="${lib.makeBinPath (with pkgs; [inetutils nftables])}:$PATH" | ||
42 | 58 | ||
43 | typeset -a as_sets mnt_bys route route6 | 59 | typeset -a as_sets mnt_bys route route6 |
44 | as_sets=(${lib.escapeShellArgs config.services.email.nologin.ASSets}) | 60 | as_sets=(${lib.escapeShellArgs config.services.email.nologin.ASSets}) |
@@ -51,7 +67,7 @@ let | |||
51 | elif [[ "''${line}" =~ "^route6:\s+(.+)$" ]]; then | 67 | elif [[ "''${line}" =~ "^route6:\s+(.+)$" ]]; then |
52 | route6+=($match[1]) | 68 | route6+=($match[1]) |
53 | fi | 69 | fi |
54 | done < <(whois -h whois.radb.net "!i''${as_set},1" | egrep -o 'AS[0-9]+' | xargs -- whois -h whois.radb.net -- -i origin) | 70 | done < <(whois -h whois.radb.net "!i''${as_set},1" | grep -Eo 'AS[0-9]+' | xargs whois -h whois.radb.net -- -i origin) |
55 | done | 71 | done |
56 | for mnt_by in $mnt_bys; do | 72 | for mnt_by in $mnt_bys; do |
57 | while IFS=$'\n' read line; do | 73 | while IFS=$'\n' read line; do |
@@ -108,19 +124,20 @@ in { | |||
108 | services.postfix = { | 124 | services.postfix = { |
109 | enable = true; | 125 | enable = true; |
110 | enableSmtp = false; | 126 | enableSmtp = false; |
111 | hostname = "surtr.yggdrasil.li"; | ||
112 | recipientDelimiter = ""; | ||
113 | setSendmail = true; | 127 | setSendmail = true; |
114 | postmasterAlias = ""; rootAlias = ""; extraAliases = ""; | 128 | postmasterAlias = ""; rootAlias = ""; extraAliases = ""; |
115 | destination = []; | 129 | settings.main = { |
116 | sslCert = "/run/credentials/postfix.service/surtr.yggdrasil.li.pem"; | 130 | recpipient_delimiter = ""; |
117 | sslKey = "/run/credentials/postfix.service/surtr.yggdrasil.li.key.pem"; | 131 | mydestination = []; |
118 | networks = []; | 132 | mynetworks = []; |
119 | config = let | 133 | myhostname = "surtr.yggdrasil.li"; |
120 | relay_ccert = "texthash:${pkgs.writeText "relay_ccert" ""}"; | 134 | |
121 | in { | ||
122 | smtpd_tls_security_level = "may"; | 135 | smtpd_tls_security_level = "may"; |
123 | 136 | ||
137 | smtpd_tls_chain_files = [ | ||
138 | "/run/credentials/postfix.service/surtr.yggdrasil.li.full.pem" | ||
139 | ]; | ||
140 | |||
124 | #the dh params | 141 | #the dh params |
125 | smtpd_tls_dh1024_param_file = toString config.security.dhparams.params."postfix-1024".path; | 142 | smtpd_tls_dh1024_param_file = toString config.security.dhparams.params."postfix-1024".path; |
126 | smtpd_tls_dh512_param_file = toString config.security.dhparams.params."postfix-512".path; | 143 | smtpd_tls_dh512_param_file = toString config.security.dhparams.params."postfix-512".path; |
@@ -155,21 +172,14 @@ in { | |||
155 | 172 | ||
156 | smtp_tls_connection_reuse = true; | 173 | smtp_tls_connection_reuse = true; |
157 | 174 | ||
158 | tls_server_sni_maps = ''texthash:${pkgs.writeText "sni" ( | 175 | tls_server_sni_maps = "inline:{${concatMapStringsSep ", " (domain: "{ ${domain} = /run/credentials/postfix.service/${removePrefix "." domain}.full.pem }") (concatMap (domain: [domain "mailin.${domain}" "mailsub.${domain}" ".${domain}"]) emailDomains)}}"; |
159 | concatMapStringsSep "\n\n" (domain: | ||
160 | concatMapStringsSep "\n" (subdomain: "${subdomain} /run/credentials/postfix.service/${removePrefix "." subdomain}.full.pem") | ||
161 | [domain "mailin.${domain}" "mailsub.${domain}" ".${domain}"] | ||
162 | ) emailDomains | ||
163 | )}''; | ||
164 | 176 | ||
165 | smtp_tls_policy_maps = "socketmap:unix:${config.services.postfix-mta-sts-resolver.settings.path}:postfix"; | 177 | smtp_tls_policy_maps = "socketmap:unix:${config.services.postfix-mta-sts-resolver.settings.path}:postfix"; |
166 | 178 | ||
167 | local_recipient_maps = ""; | 179 | local_recipient_maps = ""; |
168 | 180 | ||
169 | # 10 GiB | 181 | message_size_limit = 10 * 1024 * 1024 * 1024; |
170 | message_size_limit = "10737418240"; | 182 | mailbox_size_limit = 10 * 1024 * 1024 * 1024; |
171 | # 10 GiB | ||
172 | mailbox_size_limit = "10737418240"; | ||
173 | 183 | ||
174 | smtpd_delay_reject = true; | 184 | smtpd_delay_reject = true; |
175 | smtpd_helo_required = true; | 185 | smtpd_helo_required = true; |
@@ -184,12 +194,12 @@ in { | |||
184 | dbname = email | 194 | dbname = email |
185 | query = SELECT action FROM virtual_mailbox_access WHERE lookup = '%s' | 195 | query = SELECT action FROM virtual_mailbox_access WHERE lookup = '%s' |
186 | ''}" | 196 | ''}" |
187 | "check_ccert_access ${relay_ccert}" | ||
188 | "reject_non_fqdn_helo_hostname" | 197 | "reject_non_fqdn_helo_hostname" |
189 | "reject_invalid_helo_hostname" | 198 | "reject_invalid_helo_hostname" |
190 | "reject_unauth_destination" | 199 | "reject_unauth_destination" |
191 | "reject_unknown_recipient_domain" | 200 | "reject_unknown_recipient_domain" |
192 | "reject_unverified_recipient" | 201 | "reject_unverified_recipient" |
202 | "check_policy_service unix:/run/postfix-internal-policy.sock" | ||
193 | ]; | 203 | ]; |
194 | unverified_recipient_reject_code = "550"; | 204 | unverified_recipient_reject_code = "550"; |
195 | unverified_recipient_reject_reason = "Recipient address lookup failed"; | 205 | unverified_recipient_reject_reason = "Recipient address lookup failed"; |
@@ -204,7 +214,6 @@ in { | |||
204 | address_verify_sender_ttl = "30045s"; | 214 | address_verify_sender_ttl = "30045s"; |
205 | 215 | ||
206 | smtpd_relay_restrictions = [ | 216 | smtpd_relay_restrictions = [ |
207 | "check_ccert_access ${relay_ccert}" | ||
208 | "reject_unauth_destination" | 217 | "reject_unauth_destination" |
209 | ]; | 218 | ]; |
210 | 219 | ||
@@ -251,13 +260,26 @@ in { | |||
251 | virtual_transport = "dvlmtp:unix:/run/dovecot-lmtp"; | 260 | virtual_transport = "dvlmtp:unix:/run/dovecot-lmtp"; |
252 | smtputf8_enable = false; | 261 | smtputf8_enable = false; |
253 | 262 | ||
254 | authorized_submit_users = "inline:{ root= postfwd= }"; | 263 | authorized_submit_users = "inline:{ root= postfwd= ${config.services.dovecot2.user}= }"; |
264 | authorized_flush_users = "inline:{ root= }"; | ||
265 | authorized_mailq_users = "inline:{ root= }"; | ||
255 | 266 | ||
256 | postscreen_access_list = ""; | 267 | postscreen_access_list = ""; |
257 | postscreen_denylist_action = "drop"; | 268 | postscreen_denylist_action = "drop"; |
258 | postscreen_greet_action = "enforce"; | 269 | postscreen_greet_action = "enforce"; |
270 | |||
271 | sender_bcc_maps = ''pgsql:${pkgs.writeText "sender_bcc_maps.cf" '' | ||
272 | hosts = postgresql:///email | ||
273 | dbname = email | ||
274 | query = SELECT value FROM sender_bcc_maps WHERE key = '%s' | ||
275 | ''}''; | ||
276 | recipient_bcc_maps = ''pgsql:${pkgs.writeText "recipient_bcc_maps.cf" '' | ||
277 | hosts = postgresql:///email | ||
278 | dbname = email | ||
279 | query = SELECT value FROM recipient_bcc_maps WHERE key = '%s' | ||
280 | ''}''; | ||
259 | }; | 281 | }; |
260 | masterConfig = { | 282 | settings.master = { |
261 | "465" = { | 283 | "465" = { |
262 | type = "inet"; | 284 | type = "inet"; |
263 | private = false; | 285 | private = false; |
@@ -280,7 +302,7 @@ in { | |||
280 | hosts = postgresql:///email | 302 | hosts = postgresql:///email |
281 | dbname = email | 303 | dbname = email |
282 | query = SELECT action FROM virtual_mailbox_access WHERE lookup = '%s' OR (lookup = regexp_replace('%s', '\+[^@]*@', '@') AND NOT EXISTS (SELECT 1 FROM virtual_mailbox_access WHERE lookup = '%s')) | 304 | query = SELECT action FROM virtual_mailbox_access WHERE lookup = '%s' OR (lookup = regexp_replace('%s', '\+[^@]*@', '@') AND NOT EXISTS (SELECT 1 FROM virtual_mailbox_access WHERE lookup = '%s')) |
283 | ''},permit_tls_all_clientcerts,reject}'' | 305 | ''},check_policy_service unix:/run/postfix-internal-policy.sock,permit_tls_all_clientcerts,reject}'' |
284 | "-o" "smtpd_relay_restrictions=permit_tls_all_clientcerts,reject" | 306 | "-o" "smtpd_relay_restrictions=permit_tls_all_clientcerts,reject" |
285 | "-o" "{smtpd_data_restrictions = check_policy_service unix:/run/postfwd3/postfwd3.sock}" | 307 | "-o" "{smtpd_data_restrictions = check_policy_service unix:/run/postfwd3/postfwd3.sock}" |
286 | "-o" "unverified_sender_reject_code=550" | 308 | "-o" "unverified_sender_reject_code=550" |
@@ -310,7 +332,7 @@ in { | |||
310 | hosts = postgresql:///email | 332 | hosts = postgresql:///email |
311 | dbname = email | 333 | dbname = email |
312 | query = SELECT action FROM virtual_mailbox_access WHERE lookup = '%s' OR (lookup = regexp_replace('%s', '\+[^@]*@', '@') AND NOT EXISTS (SELECT 1 FROM virtual_mailbox_access WHERE lookup = '%s')) | 334 | query = SELECT action FROM virtual_mailbox_access WHERE lookup = '%s' OR (lookup = regexp_replace('%s', '\+[^@]*@', '@') AND NOT EXISTS (SELECT 1 FROM virtual_mailbox_access WHERE lookup = '%s')) |
313 | ''},permit_sasl_authenticated,reject}'' | 335 | ''},check_policy_service unix:/run/postfix-internal-policy.sock,permit_sasl_authenticated,reject}'' |
314 | "-o" "smtpd_relay_restrictions=permit_sasl_authenticated,reject" | 336 | "-o" "smtpd_relay_restrictions=permit_sasl_authenticated,reject" |
315 | "-o" "{smtpd_data_restrictions = check_policy_service unix:/run/postfwd3/postfwd3.sock}" | 337 | "-o" "{smtpd_data_restrictions = check_policy_service unix:/run/postfwd3/postfwd3.sock}" |
316 | "-o" "unverified_sender_reject_code=550" | 338 | "-o" "unverified_sender_reject_code=550" |
@@ -325,7 +347,10 @@ in { | |||
325 | maxproc = 0; | 347 | maxproc = 0; |
326 | args = [ | 348 | args = [ |
327 | "-o" "header_checks=pcre:${pkgs.writeText "header_checks_submission" '' | 349 | "-o" "header_checks=pcre:${pkgs.writeText "header_checks_submission" '' |
350 | if /^Received: / | ||
351 | !/by surtr\.yggdrasil\.li/ STRIP | ||
328 | /^Received: from [^ ]+ \([^ ]+ [^ ]+\)\s+(.*)$/ REPLACE Received: $1 | 352 | /^Received: from [^ ]+ \([^ ]+ [^ ]+\)\s+(.*)$/ REPLACE Received: $1 |
353 | endif | ||
329 | ''}" | 354 | ''}" |
330 | ]; | 355 | ]; |
331 | }; | 356 | }; |
@@ -373,7 +398,7 @@ in { | |||
373 | enable = true; | 398 | enable = true; |
374 | user = "postfix"; group = "postfix"; | 399 | user = "postfix"; group = "postfix"; |
375 | socket = "local:/run/opendkim/opendkim.sock"; | 400 | socket = "local:/run/opendkim/opendkim.sock"; |
376 | domains = ''csl:${concatStringsSep "," (["surtr.yggdrasil.li"] ++ emailDomains)}''; | 401 | domains = ''csl:${concatStringsSep "," (["surtr.yggdrasil.li" "yggdrasil.li" "141.li" "kleen.li" "synapse.li" "praseodym.org"] ++ emailDomains)}''; |
377 | selector = "surtr"; | 402 | selector = "surtr"; |
378 | configFile = builtins.toFile "opendkim.conf" '' | 403 | configFile = builtins.toFile "opendkim.conf" '' |
379 | Syslog true | 404 | Syslog true |
@@ -477,7 +502,7 @@ in { | |||
477 | }; | 502 | }; |
478 | }; | 503 | }; |
479 | 504 | ||
480 | users.groups.${config.services.rspamd.group}.members = [ config.services.postfix.user "dovecot2" ]; | 505 | users.groups.${config.services.rspamd.group}.members = [ config.services.postfix.user config.services.dovecot2.user ]; |
481 | 506 | ||
482 | services.redis.servers.rspamd.enable = true; | 507 | services.redis.servers.rspamd.enable = true; |
483 | 508 | ||
@@ -487,22 +512,22 @@ in { | |||
487 | services.dovecot2 = { | 512 | services.dovecot2 = { |
488 | enable = true; | 513 | enable = true; |
489 | enablePAM = false; | 514 | enablePAM = false; |
490 | sslServerCert = "/run/credentials/dovecot2.service/surtr.yggdrasil.li.pem"; | 515 | sslServerCert = "/run/credentials/dovecot.service/surtr.yggdrasil.li.pem"; |
491 | sslServerKey = "/run/credentials/dovecot2.service/surtr.yggdrasil.li.key.pem"; | 516 | sslServerKey = "/run/credentials/dovecot.service/surtr.yggdrasil.li.key.pem"; |
492 | sslCACert = toString ./ca/ca.crt; | 517 | sslCACert = toString ./ca/ca.crt; |
493 | mailLocation = "maildir:/var/lib/mail/%u/maildir:UTF-8:INDEX=/var/lib/dovecot/indices/%u"; | 518 | mailLocation = "maildir:/var/lib/mail/%u/maildir:UTF-8:INDEX=/var/lib/dovecot/indices/%u"; |
494 | mailPlugins.globally.enable = [ "fts" "fts_xapian" ]; | 519 | mailPlugins.globally.enable = [ "fts" "fts_xapian" ]; |
495 | protocols = [ "lmtp" "sieve" ]; | 520 | protocols = [ "lmtp" "sieve" ]; |
496 | sieve = { | 521 | sieve = { |
497 | extensions = ["copy" "imapsieve" "variables" "imap4flags" "vacation"]; | 522 | extensions = ["copy" "imapsieve" "variables" "imap4flags" "vacation" "vacation-seconds" "vnd.dovecot.debug"]; |
498 | globalExtensions = ["copy" "imapsieve" "variables" "imap4flags" "vacation"]; | 523 | globalExtensions = ["copy" "imapsieve" "variables" "imap4flags" "vacation" "vacation-seconds" "vnd.dovecot.debug"]; |
499 | }; | 524 | }; |
500 | extraConfig = let | 525 | extraConfig = let |
501 | dovecotSqlConf = pkgs.writeText "dovecot-sql.conf" '' | 526 | dovecotSqlConf = pkgs.writeText "dovecot-sql.conf" '' |
502 | driver = pgsql | 527 | driver = pgsql |
503 | connect = dbname=email | 528 | connect = dbname=email |
504 | password_query = SELECT (CASE WHEN '%k' = 'valid' AND '%m' = 'EXTERNAL' THEN NULL ELSE "password" END) as password, (CASE WHEN '%k' = 'valid' AND '%m' = 'EXTERNAL' THEN true WHEN password IS NULL THEN true ELSE NULL END) as nopassword, "user", quota_rule, 'dovecot2' as uid, 'dovecot2' as gid FROM imap_user WHERE "user" = '%n' | 529 | password_query = SELECT (CASE WHEN '%k' = 'valid' AND '%m' = 'EXTERNAL' THEN NULL ELSE "password" END) as password, (CASE WHEN '%k' = 'valid' AND '%m' = 'EXTERNAL' THEN true WHEN password IS NULL THEN true ELSE NULL END) as nopassword, "user", quota_rule, '${config.services.dovecot2.user}' as uid, '${config.services.dovecot2.group}' as gid FROM imap_user WHERE "user" = '%n' |
505 | user_query = SELECT "user", quota_rule, 'dovecot2' as uid, 'dovecot2' as gid FROM imap_user WHERE "user" = '%n' | 530 | user_query = SELECT "user", quota_rule, '${config.services.dovecot2.user}' as uid, 'dovecot2' as gid FROM imap_user WHERE "user" = '%n' |
506 | iterate_query = SELECT "user" FROM imap_user | 531 | iterate_query = SELECT "user" FROM imap_user |
507 | ''; | 532 | ''; |
508 | in '' | 533 | in '' |
@@ -510,16 +535,16 @@ in { | |||
510 | 535 | ||
511 | mail_plugins = $mail_plugins quota | 536 | mail_plugins = $mail_plugins quota |
512 | 537 | ||
513 | first_valid_uid = ${toString config.users.users.dovecot2.uid} | 538 | first_valid_uid = ${toString config.users.users.${config.services.dovecot2.user}.uid} |
514 | last_valid_uid = ${toString config.users.users.dovecot2.uid} | 539 | last_valid_uid = ${toString config.users.users.${config.services.dovecot2.user}.uid} |
515 | first_valid_gid = ${toString config.users.groups.dovecot2.gid} | 540 | first_valid_gid = ${toString config.users.groups.${config.services.dovecot2.group}.gid} |
516 | last_valid_gid = ${toString config.users.groups.dovecot2.gid} | 541 | last_valid_gid = ${toString config.users.groups.${config.services.dovecot2.group}.gid} |
517 | 542 | ||
518 | ${concatMapStringsSep "\n\n" (domain: | 543 | ${concatMapStringsSep "\n\n" (domain: |
519 | concatMapStringsSep "\n" (subdomain: '' | 544 | concatMapStringsSep "\n" (subdomain: '' |
520 | local_name ${subdomain} { | 545 | local_name ${subdomain} { |
521 | ssl_cert = </run/credentials/dovecot2.service/${subdomain}.pem | 546 | ssl_cert = </run/credentials/dovecot.service/${subdomain}.pem |
522 | ssl_key = </run/credentials/dovecot2.service/${subdomain}.key.pem | 547 | ssl_key = </run/credentials/dovecot.service/${subdomain}.key.pem |
523 | } | 548 | } |
524 | '') ["imap.${domain}" domain] | 549 | '') ["imap.${domain}" domain] |
525 | ) emailDomains} | 550 | ) emailDomains} |
@@ -540,10 +565,10 @@ in { | |||
540 | auth_debug = yes | 565 | auth_debug = yes |
541 | 566 | ||
542 | service auth { | 567 | service auth { |
543 | user = dovecot2 | 568 | user = ${config.services.dovecot2.user} |
544 | } | 569 | } |
545 | service auth-worker { | 570 | service auth-worker { |
546 | user = dovecot2 | 571 | user = ${config.services.dovecot2.user} |
547 | } | 572 | } |
548 | 573 | ||
549 | userdb { | 574 | userdb { |
@@ -564,7 +589,7 @@ in { | |||
564 | args = ${pkgs.writeText "dovecot-sql.conf" '' | 589 | args = ${pkgs.writeText "dovecot-sql.conf" '' |
565 | driver = pgsql | 590 | driver = pgsql |
566 | connect = dbname=email | 591 | connect = dbname=email |
567 | user_query = SELECT DISTINCT ON (extension IS NULL, local IS NULL) "user", quota_rule, 'dovecot2' as uid, 'dovecot2' as gid FROM lmtp_mapping WHERE CASE WHEN extension IS NOT NULL AND local IS NOT NULL THEN ('%n' :: citext) = local || '+' || extension AND domain = ('%d' :: citext) WHEN local IS NOT NULL THEN (local = ('%n' :: citext) OR ('%n' :: citext) ILIKE local || '+%%') AND domain = ('%d' :: citext) WHEN extension IS NOT NULL THEN ('%n' :: citext) ILIKE '%%+' || extension AND domain = ('%d' :: citext) ELSE domain = ('%d' :: citext) END ORDER BY (extension IS NULL) ASC, (local IS NULL) ASC | 592 | user_query = SELECT DISTINCT ON (extension IS NULL, local IS NULL) "user", quota_rule, '${config.services.dovecot2.user}' as uid, '${config.services.dovecot2.group}' as gid FROM lmtp_mapping WHERE CASE WHEN extension IS NOT NULL AND local IS NOT NULL THEN ('%n' :: citext) = local || '+' || extension AND domain = ('%d' :: citext) WHEN local IS NOT NULL THEN (local = ('%n' :: citext) OR ('%n' :: citext) ILIKE local || '+%%') AND domain = ('%d' :: citext) WHEN extension IS NOT NULL THEN ('%n' :: citext) ILIKE '%%+' || extension AND domain = ('%d' :: citext) ELSE domain = ('%d' :: citext) END ORDER BY (extension IS NULL) ASC, (local IS NULL) ASC |
568 | ''} | 593 | ''} |
569 | 594 | ||
570 | skip = never | 595 | skip = never |
@@ -634,7 +659,7 @@ in { | |||
634 | quota_status_success = DUNNO | 659 | quota_status_success = DUNNO |
635 | quota_status_nouser = DUNNO | 660 | quota_status_nouser = DUNNO |
636 | quota_grace = 10%% | 661 | quota_grace = 10%% |
637 | quota_max_mail_size = ${config.services.postfix.config.message_size_limit} | 662 | quota_max_mail_size = ${toString config.services.postfix.settings.main.message_size_limit} |
638 | quota_vsizes = yes | 663 | quota_vsizes = yes |
639 | } | 664 | } |
640 | 665 | ||
@@ -672,7 +697,7 @@ in { | |||
672 | plugin { | 697 | plugin { |
673 | plugin = fts fts_xapian | 698 | plugin = fts fts_xapian |
674 | fts = xapian | 699 | fts = xapian |
675 | fts_xapian = partial=2 full=20 attachments=1 verbose=1 | 700 | fts_xapian = partial=3 full=20 attachments=1 verbose=1 |
676 | 701 | ||
677 | fts_autoindex = yes | 702 | fts_autoindex = yes |
678 | 703 | ||
@@ -687,12 +712,12 @@ in { | |||
687 | 712 | ||
688 | systemd.services.dovecot-fts-xapian-optimize = { | 713 | systemd.services.dovecot-fts-xapian-optimize = { |
689 | description = "Optimize dovecot indices for fts_xapian"; | 714 | description = "Optimize dovecot indices for fts_xapian"; |
690 | requisite = [ "dovecot2.service" ]; | 715 | requisite = [ "dovecot.service" ]; |
691 | after = [ "dovecot2.service" ]; | 716 | after = [ "dovecot.service" ]; |
692 | startAt = "*-*-* 22:00:00 Europe/Berlin"; | 717 | startAt = "*-*-* 22:00:00 Europe/Berlin"; |
693 | serviceConfig = { | 718 | serviceConfig = { |
694 | Type = "oneshot"; | 719 | Type = "oneshot"; |
695 | ExecStart = "${pkgs.dovecot}/bin/doveadm fts optimize -A"; | 720 | ExecStart = "${getExe' pkgs.dovecot "doveadm"} fts optimize -A"; |
696 | PrivateDevices = true; | 721 | PrivateDevices = true; |
697 | PrivateNetwork = true; | 722 | PrivateNetwork = true; |
698 | ProtectKernelTunables = true; | 723 | ProtectKernelTunables = true; |
@@ -753,31 +778,29 @@ in { | |||
753 | 778 | ||
754 | security.acme.rfc2136Domains = { | 779 | security.acme.rfc2136Domains = { |
755 | "surtr.yggdrasil.li" = { | 780 | "surtr.yggdrasil.li" = { |
756 | restartUnits = [ "postfix.service" "dovecot2.service" ]; | 781 | restartUnits = [ "postfix.service" "dovecot.service" ]; |
757 | }; | 782 | }; |
758 | } // listToAttrs (map (domain: nameValuePair "spm.${domain}" { restartUnits = ["nginx.service"]; }) spmDomains) | 783 | } // listToAttrs (map (domain: nameValuePair "spm.${domain}" { restartUnits = ["nginx.service"]; }) spmDomains) |
759 | // listToAttrs (concatMap (domain: [ | 784 | // listToAttrs (concatMap (domain: [ |
760 | (nameValuePair domain { restartUnits = ["postfix.service" "dovecot2.service"]; }) | 785 | (nameValuePair domain { restartUnits = ["postfix.service" "dovecot.service"]; }) |
761 | (nameValuePair "mailin.${domain}" { restartUnits = ["postfix.service"]; }) | 786 | (nameValuePair "mailin.${domain}" { restartUnits = ["postfix.service"]; }) |
762 | (nameValuePair "mailsub.${domain}" { restartUnits = ["postfix.service"]; }) | 787 | (nameValuePair "mailsub.${domain}" { restartUnits = ["postfix.service"]; }) |
763 | (nameValuePair "imap.${domain}" { restartUnits = ["dovecot2.service"]; }) | 788 | (nameValuePair "imap.${domain}" { restartUnits = ["dovecot.service"]; }) |
764 | (nameValuePair "mta-sts.${domain}" { restartUnits = ["nginx.service"]; }) | 789 | (nameValuePair "mta-sts.${domain}" { restartUnits = ["nginx.service"]; }) |
765 | ]) emailDomains); | 790 | ]) emailDomains); |
766 | 791 | ||
767 | systemd.services.postfix = { | 792 | systemd.services.postfix = { |
768 | serviceConfig.LoadCredential = [ | 793 | serviceConfig.LoadCredential = let |
769 | "surtr.yggdrasil.li.key.pem:${config.security.acme.certs."surtr.yggdrasil.li".directory}/key.pem" | 794 | tlsCredential = domain: "${domain}.full.pem:${config.security.acme.certs.${domain}.directory}/full.pem"; |
770 | "surtr.yggdrasil.li.pem:${config.security.acme.certs."surtr.yggdrasil.li".directory}/fullchain.pem" | 795 | in [ |
771 | ] ++ concatMap (domain: | 796 | (tlsCredential "surtr.yggdrasil.li") |
772 | map (subdomain: "${subdomain}.full.pem:${config.security.acme.certs.${subdomain}.directory}/full.pem") | 797 | ] ++ concatMap (domain: map tlsCredential [domain "mailin.${domain}" "mailsub.${domain}"]) emailDomains; |
773 | [domain "mailin.${domain}" "mailsub.${domain}"] | ||
774 | ) emailDomains; | ||
775 | }; | 798 | }; |
776 | 799 | ||
777 | systemd.services.dovecot2 = { | 800 | systemd.services.dovecot = { |
778 | preStart = '' | 801 | preStart = '' |
779 | for f in /etc/dovecot/sieve_flag.d/*.sieve /etc/dovecot/sieve_before.d/*.sieve; do | 802 | for f in /etc/dovecot/sieve_flag.d/*.sieve /etc/dovecot/sieve_before.d/*.sieve; do |
780 | ${pkgs.dovecot_pigeonhole}/bin/sievec $f | 803 | ${getExe' pkgs.dovecot_pigeonhole "sievec"} $f |
781 | done | 804 | done |
782 | ''; | 805 | ''; |
783 | 806 | ||
@@ -844,15 +867,16 @@ in { | |||
844 | charset utf-8; | 867 | charset utf-8; |
845 | source_charset utf-8; | 868 | source_charset utf-8; |
846 | ''; | 869 | ''; |
847 | root = pkgs.runCommand "mta-sts.${domain}" {} '' | 870 | root = pkgs.writeTextFile { |
848 | mkdir -p $out/.well-known | 871 | name = "mta-sts.${domain}"; |
849 | cp ${pkgs.writeText "mta-sts.${domain}.txt" '' | 872 | destination = "/.well-known/mta-sts.txt"; |
873 | text = '' | ||
850 | version: STSv1 | 874 | version: STSv1 |
851 | mode: enforce | 875 | mode: enforce |
852 | max_age: 2419200 | 876 | max_age: 2419200 |
853 | mx: mailin.${domain} | 877 | mx: mailin.${domain} |
854 | ''} $out/.well-known/mta-sts.txt | 878 | ''; |
855 | ''; | 879 | }; |
856 | }; | 880 | }; |
857 | }) emailDomains); | 881 | }) emailDomains); |
858 | }; | 882 | }; |
@@ -869,7 +893,7 @@ in { | |||
869 | systemd.services.spm = { | 893 | systemd.services.spm = { |
870 | serviceConfig = { | 894 | serviceConfig = { |
871 | Type = "notify"; | 895 | Type = "notify"; |
872 | ExecStart = "${pkgs.spm}/bin/spm-server"; | 896 | ExecStart = getExe' pkgs.spm "spm-server"; |
873 | User = "spm"; | 897 | User = "spm"; |
874 | Group = "spm"; | 898 | Group = "spm"; |
875 | 899 | ||
@@ -927,7 +951,7 @@ in { | |||
927 | serviceConfig = { | 951 | serviceConfig = { |
928 | Type = "notify"; | 952 | Type = "notify"; |
929 | 953 | ||
930 | ExecStart = "${ccert-policy-server}/bin/ccert-policy-server"; | 954 | ExecStart = getExe' ccert-policy-server "ccert-policy-server"; |
931 | 955 | ||
932 | Environment = [ | 956 | Environment = [ |
933 | "PGDATABASE=email" | 957 | "PGDATABASE=email" |
@@ -960,6 +984,53 @@ in { | |||
960 | }; | 984 | }; |
961 | users.groups."postfix-ccert-sender-policy" = {}; | 985 | users.groups."postfix-ccert-sender-policy" = {}; |
962 | 986 | ||
987 | systemd.sockets."postfix-internal-policy" = { | ||
988 | requiredBy = ["postfix.service"]; | ||
989 | wants = ["postfix-internal-policy.service"]; | ||
990 | socketConfig = { | ||
991 | ListenStream = "/run/postfix-internal-policy.sock"; | ||
992 | }; | ||
993 | }; | ||
994 | systemd.services."postfix-internal-policy" = { | ||
995 | after = [ "postgresql.service" ]; | ||
996 | bindsTo = [ "postgresql.service" ]; | ||
997 | |||
998 | serviceConfig = { | ||
999 | Type = "notify"; | ||
1000 | |||
1001 | ExecStart = lib.getExe internal-policy-server; | ||
1002 | |||
1003 | Environment = [ | ||
1004 | "PGDATABASE=email" | ||
1005 | ]; | ||
1006 | |||
1007 | DynamicUser = false; | ||
1008 | User = "postfix-internal-policy"; | ||
1009 | Group = "postfix-internal-policy"; | ||
1010 | ProtectSystem = "strict"; | ||
1011 | SystemCallFilter = "@system-service"; | ||
1012 | NoNewPrivileges = true; | ||
1013 | ProtectKernelTunables = true; | ||
1014 | ProtectKernelModules = true; | ||
1015 | ProtectKernelLogs = true; | ||
1016 | ProtectControlGroups = true; | ||
1017 | MemoryDenyWriteExecute = true; | ||
1018 | RestrictSUIDSGID = true; | ||
1019 | KeyringMode = "private"; | ||
1020 | ProtectClock = true; | ||
1021 | RestrictRealtime = true; | ||
1022 | PrivateDevices = true; | ||
1023 | PrivateTmp = true; | ||
1024 | ProtectHostname = true; | ||
1025 | ReadWritePaths = ["/run/postgresql"]; | ||
1026 | }; | ||
1027 | }; | ||
1028 | users.users."postfix-internal-policy" = { | ||
1029 | isSystemUser = true; | ||
1030 | group = "postfix-internal-policy"; | ||
1031 | }; | ||
1032 | users.groups."postfix-internal-policy" = {}; | ||
1033 | |||
963 | services.postfwd = { | 1034 | services.postfwd = { |
964 | enable = true; | 1035 | enable = true; |
965 | cache = false; | 1036 | cache = false; |
diff --git a/hosts/surtr/email/internal-policy-server/.envrc b/hosts/surtr/email/internal-policy-server/.envrc new file mode 100644 index 00000000..2c909235 --- /dev/null +++ b/hosts/surtr/email/internal-policy-server/.envrc | |||
@@ -0,0 +1,4 @@ | |||
1 | use flake | ||
2 | |||
3 | [[ -d ".venv" ]] || ( uv venv && uv sync ) | ||
4 | . .venv/bin/activate | ||
diff --git a/hosts/surtr/email/internal-policy-server/.gitignore b/hosts/surtr/email/internal-policy-server/.gitignore new file mode 100644 index 00000000..4ccfae70 --- /dev/null +++ b/hosts/surtr/email/internal-policy-server/.gitignore | |||
@@ -0,0 +1,2 @@ | |||
1 | .venv | ||
2 | **/__pycache__ | ||
diff --git a/hosts/surtr/email/internal-policy-server/internal_policy_server/__init__.py b/hosts/surtr/email/internal-policy-server/internal_policy_server/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/hosts/surtr/email/internal-policy-server/internal_policy_server/__init__.py | |||
diff --git a/hosts/surtr/email/internal-policy-server/internal_policy_server/__main__.py b/hosts/surtr/email/internal-policy-server/internal_policy_server/__main__.py new file mode 100644 index 00000000..04f1a59a --- /dev/null +++ b/hosts/surtr/email/internal-policy-server/internal_policy_server/__main__.py | |||
@@ -0,0 +1,106 @@ | |||
1 | from systemd.daemon import listen_fds | ||
2 | from sdnotify import SystemdNotifier | ||
3 | from socketserver import StreamRequestHandler, ThreadingMixIn | ||
4 | from systemd_socketserver import SystemdSocketServer | ||
5 | import sys | ||
6 | from threading import Thread | ||
7 | from psycopg_pool import ConnectionPool | ||
8 | from psycopg.rows import namedtuple_row | ||
9 | |||
10 | import logging | ||
11 | |||
12 | |||
13 | class PolicyHandler(StreamRequestHandler): | ||
14 | def handle(self): | ||
15 | logger.debug('Handling new connection...') | ||
16 | |||
17 | self.args = dict() | ||
18 | |||
19 | line = None | ||
20 | while line := self.rfile.readline().removesuffix(b'\n'): | ||
21 | if b'=' not in line: | ||
22 | break | ||
23 | |||
24 | key, val = line.split(sep=b'=', maxsplit=1) | ||
25 | self.args[key.decode()] = val.decode() | ||
26 | |||
27 | logger.info('Connection parameters: %s', self.args) | ||
28 | |||
29 | allowed = False | ||
30 | user = None | ||
31 | if self.args['sasl_username']: | ||
32 | user = self.args['sasl_username'] | ||
33 | if self.args['ccert_subject']: | ||
34 | user = self.args['ccert_subject'] | ||
35 | |||
36 | with self.server.db_pool.connection() as conn: | ||
37 | local, domain = self.args['recipient'].split(sep='@', maxsplit=1) | ||
38 | extension = None | ||
39 | if '+' in local: | ||
40 | local, extension = local.split(sep='+', maxsplit=1) | ||
41 | |||
42 | logger.debug('Parsed recipient address: %s', {'local': local, 'extension': extension, 'domain': domain}) | ||
43 | |||
44 | with conn.cursor() as cur: | ||
45 | cur.row_factory = namedtuple_row | ||
46 | cur.execute('SELECT id, internal FROM "mailbox_mapping" WHERE ("local" = %(local)s OR "local" IS NULL) AND ("extension" = %(extension)s OR "extension" IS NULL) AND "domain" = %(domain)s', params = {'local': local, 'extension': extension if extension is not None else '', 'domain': domain}, prepare = True) | ||
47 | if (row := cur.fetchone()) is not None: | ||
48 | if not row.internal: | ||
49 | logger.debug('Recipient mailbox is not internal') | ||
50 | allowed = True | ||
51 | elif user: | ||
52 | cur.execute('SELECT EXISTS(SELECT true FROM "mailbox_mapping_access" INNER JOIN "mailbox" ON "mailbox".id = "mailbox_mapping_access"."mailbox" WHERE mailbox_mapping = %(mailbox_mapping)s AND "mailbox"."mailbox" = %(user)s) as "exists"', params = { 'mailbox_mapping': row.id, 'user': user }, prepare = True) | ||
53 | if (row := cur.fetchone()) is not None: | ||
54 | allowed = row.exists | ||
55 | else: | ||
56 | logger.debug('Recipient is not local') | ||
57 | allowed = True | ||
58 | |||
59 | action = '550 5.7.0 Recipient mailbox mapping not authorized for current user' | ||
60 | if allowed: | ||
61 | action = 'DUNNO' | ||
62 | |||
63 | logger.info('Reached verdict: %s', {'allowed': allowed, 'action': action}) | ||
64 | self.wfile.write(f'action={action}\n\n'.encode()) | ||
65 | |||
66 | class ThreadedSystemdSocketServer(ThreadingMixIn, SystemdSocketServer): | ||
67 | def __init__(self, fd, RequestHandlerClass): | ||
68 | super().__init__(fd, RequestHandlerClass) | ||
69 | |||
70 | self.db_pool = ConnectionPool(min_size=1) | ||
71 | self.db_pool.wait() | ||
72 | |||
73 | def main(): | ||
74 | global logger | ||
75 | logger = logging.getLogger(__name__) | ||
76 | console_handler = logging.StreamHandler() | ||
77 | console_handler.setFormatter( logging.Formatter('[%(levelname)s](%(name)s): %(message)s') ) | ||
78 | if sys.stderr.isatty(): | ||
79 | console_handler.setFormatter( logging.Formatter('%(asctime)s [%(levelname)s](%(name)s): %(message)s') ) | ||
80 | logger.addHandler(console_handler) | ||
81 | logger.setLevel(logging.DEBUG) | ||
82 | |||
83 | # log uncaught exceptions | ||
84 | def log_exceptions(type, value, tb): | ||
85 | global logger | ||
86 | |||
87 | logger.error(value) | ||
88 | sys.__excepthook__(type, value, tb) # calls default excepthook | ||
89 | |||
90 | sys.excepthook = log_exceptions | ||
91 | |||
92 | fds = listen_fds() | ||
93 | servers = [ThreadedSystemdSocketServer(fd, PolicyHandler) for fd in fds] | ||
94 | |||
95 | if servers: | ||
96 | for server in servers: | ||
97 | Thread(name=f'Server for fd{server.fileno()}', target=server.serve_forever).start() | ||
98 | else: | ||
99 | return 2 | ||
100 | |||
101 | SystemdNotifier().notify('READY=1') | ||
102 | |||
103 | return 0 | ||
104 | |||
105 | if __name__ == '__main__': | ||
106 | sys.exit(main()) | ||
diff --git a/hosts/surtr/email/internal-policy-server/pyproject.toml b/hosts/surtr/email/internal-policy-server/pyproject.toml new file mode 100644 index 00000000..c697cd01 --- /dev/null +++ b/hosts/surtr/email/internal-policy-server/pyproject.toml | |||
@@ -0,0 +1,18 @@ | |||
1 | [project] | ||
2 | name = "internal-policy-server" | ||
3 | version = "0.1.0" | ||
4 | requires-python = ">=3.12" | ||
5 | dependencies = [ | ||
6 | "psycopg>=3.2.9", | ||
7 | "psycopg-binary>=3.2.9", | ||
8 | "psycopg-pool>=3.2.6", | ||
9 | "sdnotify>=0.3.2", | ||
10 | "systemd-socketserver>=1.0", | ||
11 | ] | ||
12 | |||
13 | [project.scripts] | ||
14 | internal-policy-server = "internal_policy_server.__main__:main" | ||
15 | |||
16 | [build-system] | ||
17 | requires = ["hatchling"] | ||
18 | build-backend = "hatchling.build" | ||
diff --git a/hosts/surtr/email/internal-policy-server/uv.lock b/hosts/surtr/email/internal-policy-server/uv.lock new file mode 100644 index 00000000..f7a4e729 --- /dev/null +++ b/hosts/surtr/email/internal-policy-server/uv.lock | |||
@@ -0,0 +1,119 @@ | |||
1 | version = 1 | ||
2 | revision = 2 | ||
3 | requires-python = ">=3.12" | ||
4 | |||
5 | [[package]] | ||
6 | name = "internal-policy-server" | ||
7 | version = "0.1.0" | ||
8 | source = { editable = "." } | ||
9 | dependencies = [ | ||
10 | { name = "psycopg" }, | ||
11 | { name = "psycopg-binary" }, | ||
12 | { name = "psycopg-pool" }, | ||
13 | { name = "sdnotify" }, | ||
14 | { name = "systemd-socketserver" }, | ||
15 | ] | ||
16 | |||
17 | [package.metadata] | ||
18 | requires-dist = [ | ||
19 | { name = "psycopg", specifier = ">=3.2.9" }, | ||
20 | { name = "psycopg-binary", specifier = ">=3.2.9" }, | ||
21 | { name = "psycopg-pool", specifier = ">=3.2.6" }, | ||
22 | { name = "sdnotify", specifier = ">=0.3.2" }, | ||
23 | { name = "systemd-socketserver", specifier = ">=1.0" }, | ||
24 | ] | ||
25 | |||
26 | [[package]] | ||
27 | name = "psycopg" | ||
28 | version = "3.2.9" | ||
29 | source = { registry = "https://pypi.org/simple" } | ||
30 | dependencies = [ | ||
31 | { name = "typing-extensions", marker = "python_full_version < '3.13'" }, | ||
32 | { name = "tzdata", marker = "sys_platform == 'win32'" }, | ||
33 | ] | ||
34 | sdist = { url = "https://files.pythonhosted.org/packages/27/4a/93a6ab570a8d1a4ad171a1f4256e205ce48d828781312c0bbaff36380ecb/psycopg-3.2.9.tar.gz", hash = "sha256:2fbb46fcd17bc81f993f28c47f1ebea38d66ae97cc2dbc3cad73b37cefbff700", size = 158122, upload-time = "2025-05-13T16:11:15.533Z" } | ||
35 | wheels = [ | ||
36 | { url = "https://files.pythonhosted.org/packages/44/b0/a73c195a56eb6b92e937a5ca58521a5c3346fb233345adc80fd3e2f542e2/psycopg-3.2.9-py3-none-any.whl", hash = "sha256:01a8dadccdaac2123c916208c96e06631641c0566b22005493f09663c7a8d3b6", size = 202705, upload-time = "2025-05-13T16:06:26.584Z" }, | ||
37 | ] | ||
38 | |||
39 | [[package]] | ||
40 | name = "psycopg-binary" | ||
41 | version = "3.2.9" | ||
42 | source = { registry = "https://pypi.org/simple" } | ||
43 | wheels = [ | ||
44 | { url = "https://files.pythonhosted.org/packages/29/6f/ec9957e37a606cd7564412e03f41f1b3c3637a5be018d0849914cb06e674/psycopg_binary-3.2.9-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:be7d650a434921a6b1ebe3fff324dbc2364393eb29d7672e638ce3e21076974e", size = 4022205, upload-time = "2025-05-13T16:07:48.195Z" }, | ||
45 | { url = "https://files.pythonhosted.org/packages/6b/ba/497b8bea72b20a862ac95a94386967b745a472d9ddc88bc3f32d5d5f0d43/psycopg_binary-3.2.9-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6a76b4722a529390683c0304501f238b365a46b1e5fb6b7249dbc0ad6fea51a0", size = 4083795, upload-time = "2025-05-13T16:07:50.917Z" }, | ||
46 | { url = "https://files.pythonhosted.org/packages/42/07/af9503e8e8bdad3911fd88e10e6a29240f9feaa99f57d6fac4a18b16f5a0/psycopg_binary-3.2.9-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:96a551e4683f1c307cfc3d9a05fec62c00a7264f320c9962a67a543e3ce0d8ff", size = 4655043, upload-time = "2025-05-13T16:07:54.857Z" }, | ||
47 | { url = "https://files.pythonhosted.org/packages/28/ed/aff8c9850df1648cc6a5cc7a381f11ee78d98a6b807edd4a5ae276ad60ad/psycopg_binary-3.2.9-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:61d0a6ceed8f08c75a395bc28cb648a81cf8dee75ba4650093ad1a24a51c8724", size = 4477972, upload-time = "2025-05-13T16:07:57.925Z" }, | ||
48 | { url = "https://files.pythonhosted.org/packages/5c/bd/8e9d1b77ec1a632818fe2f457c3a65af83c68710c4c162d6866947d08cc5/psycopg_binary-3.2.9-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ad280bbd409bf598683dda82232f5215cfc5f2b1bf0854e409b4d0c44a113b1d", size = 4737516, upload-time = "2025-05-13T16:08:01.616Z" }, | ||
49 | { url = "https://files.pythonhosted.org/packages/46/ec/222238f774cd5a0881f3f3b18fb86daceae89cc410f91ef6a9fb4556f236/psycopg_binary-3.2.9-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:76eddaf7fef1d0994e3d536ad48aa75034663d3a07f6f7e3e601105ae73aeff6", size = 4436160, upload-time = "2025-05-13T16:08:04.278Z" }, | ||
50 | { url = "https://files.pythonhosted.org/packages/37/78/af5af2a1b296eeca54ea7592cd19284739a844974c9747e516707e7b3b39/psycopg_binary-3.2.9-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:52e239cd66c4158e412318fbe028cd94b0ef21b0707f56dcb4bdc250ee58fd40", size = 3753518, upload-time = "2025-05-13T16:08:07.567Z" }, | ||
51 | { url = "https://files.pythonhosted.org/packages/ec/ac/8a3ed39ea069402e9e6e6a2f79d81a71879708b31cc3454283314994b1ae/psycopg_binary-3.2.9-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:08bf9d5eabba160dd4f6ad247cf12f229cc19d2458511cab2eb9647f42fa6795", size = 3313598, upload-time = "2025-05-13T16:08:09.999Z" }, | ||
52 | { url = "https://files.pythonhosted.org/packages/da/43/26549af068347c808fbfe5f07d2fa8cef747cfff7c695136172991d2378b/psycopg_binary-3.2.9-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:1b2cf018168cad87580e67bdde38ff5e51511112f1ce6ce9a8336871f465c19a", size = 3407289, upload-time = "2025-05-13T16:08:12.66Z" }, | ||
53 | { url = "https://files.pythonhosted.org/packages/67/55/ea8d227c77df8e8aec880ded398316735add8fda5eb4ff5cc96fac11e964/psycopg_binary-3.2.9-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:14f64d1ac6942ff089fc7e926440f7a5ced062e2ed0949d7d2d680dc5c00e2d4", size = 3472493, upload-time = "2025-05-13T16:08:15.672Z" }, | ||
54 | { url = "https://files.pythonhosted.org/packages/3c/02/6ff2a5bc53c3cd653d281666728e29121149179c73fddefb1e437024c192/psycopg_binary-3.2.9-cp312-cp312-win_amd64.whl", hash = "sha256:7a838852e5afb6b4126f93eb409516a8c02a49b788f4df8b6469a40c2157fa21", size = 2927400, upload-time = "2025-05-13T16:08:18.652Z" }, | ||
55 | { url = "https://files.pythonhosted.org/packages/28/0b/f61ff4e9f23396aca674ed4d5c9a5b7323738021d5d72d36d8b865b3deaf/psycopg_binary-3.2.9-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:98bbe35b5ad24a782c7bf267596638d78aa0e87abc7837bdac5b2a2ab954179e", size = 4017127, upload-time = "2025-05-13T16:08:21.391Z" }, | ||
56 | { url = "https://files.pythonhosted.org/packages/bc/00/7e181fb1179fbfc24493738b61efd0453d4b70a0c4b12728e2b82db355fd/psycopg_binary-3.2.9-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:72691a1615ebb42da8b636c5ca9f2b71f266be9e172f66209a361c175b7842c5", size = 4080322, upload-time = "2025-05-13T16:08:24.049Z" }, | ||
57 | { url = "https://files.pythonhosted.org/packages/58/fd/94fc267c1d1392c4211e54ccb943be96ea4032e761573cf1047951887494/psycopg_binary-3.2.9-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:25ab464bfba8c401f5536d5aa95f0ca1dd8257b5202eede04019b4415f491351", size = 4655097, upload-time = "2025-05-13T16:08:27.376Z" }, | ||
58 | { url = "https://files.pythonhosted.org/packages/41/17/31b3acf43de0b2ba83eac5878ff0dea5a608ca2a5c5dd48067999503a9de/psycopg_binary-3.2.9-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0e8aeefebe752f46e3c4b769e53f1d4ad71208fe1150975ef7662c22cca80fab", size = 4482114, upload-time = "2025-05-13T16:08:30.781Z" }, | ||
59 | { url = "https://files.pythonhosted.org/packages/85/78/b4d75e5fd5a85e17f2beb977abbba3389d11a4536b116205846b0e1cf744/psycopg_binary-3.2.9-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b7e4e4dd177a8665c9ce86bc9caae2ab3aa9360b7ce7ec01827ea1baea9ff748", size = 4737693, upload-time = "2025-05-13T16:08:34.625Z" }, | ||
60 | { url = "https://files.pythonhosted.org/packages/3b/95/7325a8550e3388b00b5e54f4ced5e7346b531eb4573bf054c3dbbfdc14fe/psycopg_binary-3.2.9-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7fc2915949e5c1ea27a851f7a472a7da7d0a40d679f0a31e42f1022f3c562e87", size = 4437423, upload-time = "2025-05-13T16:08:37.444Z" }, | ||
61 | { url = "https://files.pythonhosted.org/packages/1a/db/cef77d08e59910d483df4ee6da8af51c03bb597f500f1fe818f0f3b925d3/psycopg_binary-3.2.9-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a1fa38a4687b14f517f049477178093c39c2a10fdcced21116f47c017516498f", size = 3758667, upload-time = "2025-05-13T16:08:40.116Z" }, | ||
62 | { url = "https://files.pythonhosted.org/packages/95/3e/252fcbffb47189aa84d723b54682e1bb6d05c8875fa50ce1ada914ae6e28/psycopg_binary-3.2.9-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:5be8292d07a3ab828dc95b5ee6b69ca0a5b2e579a577b39671f4f5b47116dfd2", size = 3320576, upload-time = "2025-05-13T16:08:43.243Z" }, | ||
63 | { url = "https://files.pythonhosted.org/packages/1c/cd/9b5583936515d085a1bec32b45289ceb53b80d9ce1cea0fef4c782dc41a7/psycopg_binary-3.2.9-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:778588ca9897b6c6bab39b0d3034efff4c5438f5e3bd52fda3914175498202f9", size = 3411439, upload-time = "2025-05-13T16:08:47.321Z" }, | ||
64 | { url = "https://files.pythonhosted.org/packages/45/6b/6f1164ea1634c87956cdb6db759e0b8c5827f989ee3cdff0f5c70e8331f2/psycopg_binary-3.2.9-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:f0d5b3af045a187aedbd7ed5fc513bd933a97aaff78e61c3745b330792c4345b", size = 3477477, upload-time = "2025-05-13T16:08:51.166Z" }, | ||
65 | { url = "https://files.pythonhosted.org/packages/7b/1d/bf54cfec79377929da600c16114f0da77a5f1670f45e0c3af9fcd36879bc/psycopg_binary-3.2.9-cp313-cp313-win_amd64.whl", hash = "sha256:2290bc146a1b6a9730350f695e8b670e1d1feb8446597bed0bbe7c3c30e0abcb", size = 2928009, upload-time = "2025-05-13T16:08:53.67Z" }, | ||
66 | ] | ||
67 | |||
68 | [[package]] | ||
69 | name = "psycopg-pool" | ||
70 | version = "3.2.6" | ||
71 | source = { registry = "https://pypi.org/simple" } | ||
72 | dependencies = [ | ||
73 | { name = "typing-extensions" }, | ||
74 | ] | ||
75 | sdist = { url = "https://files.pythonhosted.org/packages/cf/13/1e7850bb2c69a63267c3dbf37387d3f71a00fd0e2fa55c5db14d64ba1af4/psycopg_pool-3.2.6.tar.gz", hash = "sha256:0f92a7817719517212fbfe2fd58b8c35c1850cdd2a80d36b581ba2085d9148e5", size = 29770, upload-time = "2025-02-26T12:03:47.129Z" } | ||
76 | wheels = [ | ||
77 | { url = "https://files.pythonhosted.org/packages/47/fd/4feb52a55c1a4bd748f2acaed1903ab54a723c47f6d0242780f4d97104d4/psycopg_pool-3.2.6-py3-none-any.whl", hash = "sha256:5887318a9f6af906d041a0b1dc1c60f8f0dda8340c2572b74e10907b51ed5da7", size = 38252, upload-time = "2025-02-26T12:03:45.073Z" }, | ||
78 | ] | ||
79 | |||
80 | [[package]] | ||
81 | name = "sdnotify" | ||
82 | version = "0.3.2" | ||
83 | source = { registry = "https://pypi.org/simple" } | ||
84 | sdist = { url = "https://files.pythonhosted.org/packages/ce/d8/9fdc36b2a912bf78106de4b3f0de3891ff8f369e7a6f80be842b8b0b6bd5/sdnotify-0.3.2.tar.gz", hash = "sha256:73977fc746b36cc41184dd43c3fe81323e7b8b06c2bb0826c4f59a20c56bb9f1", size = 2459, upload-time = "2017-08-02T20:03:44.395Z" } | ||
85 | |||
86 | [[package]] | ||
87 | name = "systemd-python" | ||
88 | version = "235" | ||
89 | source = { registry = "https://pypi.org/simple" } | ||
90 | sdist = { url = "https://files.pythonhosted.org/packages/10/9e/ab4458e00367223bda2dd7ccf0849a72235ee3e29b36dce732685d9b7ad9/systemd-python-235.tar.gz", hash = "sha256:4e57f39797fd5d9e2d22b8806a252d7c0106c936039d1e71c8c6b8008e695c0a", size = 61677, upload-time = "2023-02-11T13:42:16.588Z" } | ||
91 | |||
92 | [[package]] | ||
93 | name = "systemd-socketserver" | ||
94 | version = "1.0" | ||
95 | source = { registry = "https://pypi.org/simple" } | ||
96 | dependencies = [ | ||
97 | { name = "systemd-python" }, | ||
98 | ] | ||
99 | wheels = [ | ||
100 | { url = "https://files.pythonhosted.org/packages/d8/4f/b28b7f08880120a26669b080ca74487c8c67e8b54dcb0467a8f0c9f38ed6/systemd_socketserver-1.0-py3-none-any.whl", hash = "sha256:987a8bfbf28d959e7c2966c742ad7bad482f05e121077defcf95bb38267db9a8", size = 3248, upload-time = "2020-04-26T05:26:40.661Z" }, | ||
101 | ] | ||
102 | |||
103 | [[package]] | ||
104 | name = "typing-extensions" | ||
105 | version = "4.13.2" | ||
106 | source = { registry = "https://pypi.org/simple" } | ||
107 | sdist = { url = "https://files.pythonhosted.org/packages/f6/37/23083fcd6e35492953e8d2aaaa68b860eb422b34627b13f2ce3eb6106061/typing_extensions-4.13.2.tar.gz", hash = "sha256:e6c81219bd689f51865d9e372991c540bda33a0379d5573cddb9a3a23f7caaef", size = 106967, upload-time = "2025-04-10T14:19:05.416Z" } | ||
108 | wheels = [ | ||
109 | { url = "https://files.pythonhosted.org/packages/8b/54/b1ae86c0973cc6f0210b53d508ca3641fb6d0c56823f288d108bc7ab3cc8/typing_extensions-4.13.2-py3-none-any.whl", hash = "sha256:a439e7c04b49fec3e5d3e2beaa21755cadbbdc391694e28ccdd36ca4a1408f8c", size = 45806, upload-time = "2025-04-10T14:19:03.967Z" }, | ||
110 | ] | ||
111 | |||
112 | [[package]] | ||
113 | name = "tzdata" | ||
114 | version = "2025.2" | ||
115 | source = { registry = "https://pypi.org/simple" } | ||
116 | sdist = { url = "https://files.pythonhosted.org/packages/95/32/1a225d6164441be760d75c2c42e2780dc0873fe382da3e98a2e1e48361e5/tzdata-2025.2.tar.gz", hash = "sha256:b60a638fcc0daffadf82fe0f57e53d06bdec2f36c4df66280ae79bce6bd6f2b9", size = 196380, upload-time = "2025-03-23T13:54:43.652Z" } | ||
117 | wheels = [ | ||
118 | { url = "https://files.pythonhosted.org/packages/5c/23/c7abc0ca0a1526a0774eca151daeb8de62ec457e77262b66b359c3c7679e/tzdata-2025.2-py2.py3-none-any.whl", hash = "sha256:1a403fada01ff9221ca8044d701868fa132215d84beb92242d9acd2147f667a8", size = 347839, upload-time = "2025-03-23T13:54:41.845Z" }, | ||
119 | ] | ||
diff --git a/hosts/surtr/kimai.nix b/hosts/surtr/kimai.nix new file mode 100644 index 00000000..454b3d80 --- /dev/null +++ b/hosts/surtr/kimai.nix | |||
@@ -0,0 +1,68 @@ | |||
1 | { config, ... }: | ||
2 | |||
3 | { | ||
4 | config = { | ||
5 | security.acme.rfc2136Domains = { | ||
6 | "kimai.yggdrasil.li" = { | ||
7 | restartUnits = ["nginx.service"]; | ||
8 | }; | ||
9 | }; | ||
10 | |||
11 | services.nginx = { | ||
12 | upstreams."kimai" = { | ||
13 | servers = { | ||
14 | "[2a03:4000:52:ada:6::2]:80" = {}; | ||
15 | }; | ||
16 | extraConfig = '' | ||
17 | keepalive 8; | ||
18 | ''; | ||
19 | }; | ||
20 | virtualHosts = { | ||
21 | "kimai.yggdrasil.li" = { | ||
22 | kTLS = true; | ||
23 | http3 = true; | ||
24 | forceSSL = true; | ||
25 | sslCertificate = "/run/credentials/nginx.service/kimai.yggdrasil.li.pem"; | ||
26 | sslCertificateKey = "/run/credentials/nginx.service/kimai.yggdrasil.li.key.pem"; | ||
27 | sslTrustedCertificate = "/run/credentials/nginx.service/kimai.yggdrasil.li.chain.pem"; | ||
28 | extraConfig = '' | ||
29 | charset utf-8; | ||
30 | ''; | ||
31 | |||
32 | locations = { | ||
33 | "/".extraConfig = '' | ||
34 | proxy_pass http://kimai; | ||
35 | |||
36 | proxy_http_version 1.1; | ||
37 | proxy_set_header Upgrade $http_upgrade; | ||
38 | proxy_set_header Connection "upgrade"; | ||
39 | |||
40 | proxy_redirect off; | ||
41 | proxy_set_header Host $host; | ||
42 | proxy_set_header X-Real-IP $remote_addr; | ||
43 | proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; | ||
44 | proxy_set_header X-Forwarded-Host $server_name; | ||
45 | proxy_set_header X-Forwarded-Proto $scheme; | ||
46 | |||
47 | client_max_body_size 0; | ||
48 | proxy_request_buffering off; | ||
49 | proxy_buffering off; | ||
50 | |||
51 | proxy_read_timeout 300; | ||
52 | ''; | ||
53 | }; | ||
54 | }; | ||
55 | }; | ||
56 | }; | ||
57 | |||
58 | systemd.services.nginx = { | ||
59 | serviceConfig = { | ||
60 | LoadCredential = [ | ||
61 | "kimai.yggdrasil.li.key.pem:${config.security.acme.certs."kimai.yggdrasil.li".directory}/key.pem" | ||
62 | "kimai.yggdrasil.li.pem:${config.security.acme.certs."kimai.yggdrasil.li".directory}/fullchain.pem" | ||
63 | "kimai.yggdrasil.li.chain.pem:${config.security.acme.certs."kimai.yggdrasil.li".directory}/chain.pem" | ||
64 | ]; | ||
65 | }; | ||
66 | }; | ||
67 | }; | ||
68 | } | ||
diff --git a/hosts/surtr/postgresql/default.nix b/hosts/surtr/postgresql/default.nix index 059f4088..3786ea7c 100644 --- a/hosts/surtr/postgresql/default.nix +++ b/hosts/surtr/postgresql/default.nix | |||
@@ -280,6 +280,64 @@ in { | |||
280 | CREATE VIEW imap_user ("user", "password", quota_rule) AS SELECT mailbox.mailbox AS "user", "password", quota_rule FROM mailbox_quota_rule INNER JOIN mailbox ON mailbox_quota_rule.mailbox = mailbox.mailbox; | 280 | CREATE VIEW imap_user ("user", "password", quota_rule) AS SELECT mailbox.mailbox AS "user", "password", quota_rule FROM mailbox_quota_rule INNER JOIN mailbox ON mailbox_quota_rule.mailbox = mailbox.mailbox; |
281 | 281 | ||
282 | COMMIT; | 282 | COMMIT; |
283 | |||
284 | BEGIN; | ||
285 | SELECT _v.register_patch('013-internal', ARRAY['000-base'], null); | ||
286 | |||
287 | ALTER TABLE mailbox_mapping ADD COLUMN internal bool NOT NULL DEFAULT false; | ||
288 | CREATE TABLE mailbox_mapping_access ( | ||
289 | id uuid PRIMARY KEY NOT NULL DEFAULT gen_random_uuid(), | ||
290 | mailbox_mapping uuid REFERENCES mailbox_mapping(id), | ||
291 | mailbox uuid REFERENCES mailbox(id) | ||
292 | ); | ||
293 | CREATE USER "postfix-internal-policy"; | ||
294 | GRANT CONNECT ON DATABASE "email" TO "postfix-internal-policy"; | ||
295 | ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT SELECT ON TABLES TO "postfix-internal-policy"; | ||
296 | GRANT SELECT ON ALL TABLES IN SCHEMA public TO "postfix-internal-policy"; | ||
297 | |||
298 | COMMIT; | ||
299 | |||
300 | BEGIN; | ||
301 | SELECT _v.register_patch('014-relay', ARRAY['000-base'], null); | ||
302 | |||
303 | CREATE TABLE relay_access ( | ||
304 | id uuid PRIMARY KEY NOT NULL DEFAULT gen_random_uuid(), | ||
305 | mailbox uuid REFERENCES mailbox(id), | ||
306 | domain citext NOT NULL CONSTRAINT domain_non_empty CHECK (domain <> ''') | ||
307 | ); | ||
308 | |||
309 | COMMIT; | ||
310 | |||
311 | BEGIN; | ||
312 | SELECT _v.register_patch('015-relay-unique', ARRAY['000-base', '014-relay'], null); | ||
313 | |||
314 | CREATE UNIQUE INDEX relay_unique ON relay_access (mailbox, domain); | ||
315 | |||
316 | COMMIT; | ||
317 | |||
318 | BEGIN; | ||
319 | SELECT _v.register_patch('015-sender_bcc', null, null); | ||
320 | |||
321 | CREATE TABLE sender_bcc_maps ( | ||
322 | id uuid PRIMARY KEY NOT NULL DEFAULT gen_random_uuid(), | ||
323 | key text NOT NULL CONSTRAINT key_not_empty CHECK (key <> '''), | ||
324 | value text NOT NULL CONSTRAINT value_not_empty CHECK (value <> '''), | ||
325 | CONSTRAINT key_unique UNIQUE (key) | ||
326 | ); | ||
327 | |||
328 | COMMIT; | ||
329 | |||
330 | BEGIN; | ||
331 | SELECT _v.register_patch('016-recipient_bcc', null, null); | ||
332 | |||
333 | CREATE TABLE recipient_bcc_maps ( | ||
334 | id uuid PRIMARY KEY NOT NULL DEFAULT gen_random_uuid(), | ||
335 | key text NOT NULL CONSTRAINT key_not_empty CHECK (key <> '''), | ||
336 | value text NOT NULL CONSTRAINT value_not_empty CHECK (value <> '''), | ||
337 | CONSTRAINT recipient_bcc_maps_key_unique UNIQUE (key) | ||
338 | ); | ||
339 | |||
340 | COMMIT; | ||
283 | ''} | 341 | ''} |
284 | 342 | ||
285 | psql etebase postgres -eXf ${pkgs.writeText "etebase.sql" '' | 343 | psql etebase postgres -eXf ${pkgs.writeText "etebase.sql" '' |
diff --git a/hosts/surtr/tls/tsig_keys/kimai.yggdrasil.li b/hosts/surtr/tls/tsig_keys/kimai.yggdrasil.li new file mode 100644 index 00000000..b9199975 --- /dev/null +++ b/hosts/surtr/tls/tsig_keys/kimai.yggdrasil.li | |||
@@ -0,0 +1,19 @@ | |||
1 | { | ||
2 | "data": "ENC[AES256_GCM,data:ATcU3Ix7o5d/49rD5H8je1ozTjoghrloMh5DIZ5WE3oYauUAknpGfr9xq92V,iv:vy9YK5Ot7CCjMtgAGVeAUQuaSw4F5kmmZ0GJYV9kCdQ=,tag:F/MXTUM2AI1fGXa9Ewn8yQ==,type:str]", | ||
3 | "sops": { | ||
4 | "age": [ | ||
5 | { | ||
6 | "recipient": "age1rmmhetcmllq0ahl5qznlr0eya2zdxwl9h6y5wnl97d2wtyx5t99sm2u866", | ||
7 | "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBDMEF0cUdydERYVzJCa3pW\nTlo0NUFON0d5RGJFVnVTNVg3cjNEUERQMEdFClEvQW5odlNEd2F1VTFmMWQrL2RB\ncllFZVpIVVJrNTJsSGF4UEdZMnVmQzAKLS0tIFUrQkkzRVZiOFNiTnFCT1pEYVRM\nQm8wV1JkQ3RrR1dkL0FsNkhsY2kxa1kKGnAo/6oibgXexUU31THdLu6X+pRtrkjD\nZnXGPZ2xaESDVUVEYQPVpNrjt9brZGJBI1BasrkEwHAXMbJC236yYQ==\n-----END AGE ENCRYPTED FILE-----\n" | ||
8 | }, | ||
9 | { | ||
10 | "recipient": "age19a7j77w267z04zls7m28a8hj4a0g5af6ltye2d5wypg33c3l89csd4r9zq", | ||
11 | "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSB3MGs1Z2ZqK2pqWHdVYTJH\naTlncHdPa3Zld0JhQW5Ccmc1SStWSnlDR0JrCmpML2d4TGdldUdoZCtaWVpPZVl0\nVm4waWVBS1orRS90ZS96N0Y2M29LY0UKLS0tIEI1Z2VVbVVxRUpOZEN4NnBRRklC\nQXloelZCb04xbmduTlVuL005TlRGMHMKfLB6zA3sj3HgDBC7VGfGVB6I1zJpt0PV\nkCV2yADgvAA2pT9HPg9IWAEpTPysOBiuE2jPNtFvylZYwTDHoumFnQ==\n-----END AGE ENCRYPTED FILE-----\n" | ||
12 | } | ||
13 | ], | ||
14 | "lastmodified": "2025-05-24T09:42:23Z", | ||
15 | "mac": "ENC[AES256_GCM,data:0pk1LpWPmX9td/TwJFxwWp5pTDyW78UtHXMDah+V9Tmgi8hH7ONdysgjwpDwS/c4zGnMA3qtobEL286U3//CTXt2qVsiUGLsnngzs2E6yBg8oGMYlGrch4M355Fl5ZxYsc8QLA6qWcuZ4H3QW8PnoqdJixcHoYLoxG01dzh4Bc0=,iv:zchk4enI1D80BkJLji5RLm7OTk3GeF8nYHuwqBxCXIM=,tag:bgkknPMqkSidi6bDFfv6UQ==,type:str]", | ||
16 | "unencrypted_suffix": "_unencrypted", | ||
17 | "version": "3.10.2" | ||
18 | } | ||
19 | } | ||
diff --git a/hosts/vidhar/audiobookshelf/abs-podcast-autoplaylist-gkleen.toml b/hosts/vidhar/audiobookshelf/abs-podcast-autoplaylist-gkleen.toml index a5319e38..42920069 100644 --- a/hosts/vidhar/audiobookshelf/abs-podcast-autoplaylist-gkleen.toml +++ b/hosts/vidhar/audiobookshelf/abs-podcast-autoplaylist-gkleen.toml | |||
@@ -1,5 +1,5 @@ | |||
1 | { | 1 | { |
2 | "data": "ENC[AES256_GCM,data:60OmHwuLC7RJVNNn8lsCFjIFrtDlmmT3yAm3DYn/K2b8OJB/lzKBhMUCyPpoI2lfMm6y47/DMwXI3ExH3QwfgGRf4i/Tcv7p6FCkjFgDc0RhAM7cXNSnh1gKTff8QYtPoNIzmycFCThNr7iZsPsf2/1npVaVHTnt9nTc+cmDLc+lELlvjSE00JOXch/if7KPwFww9K83XlrFmoRvwybfXR0unJqxK2XLvj+dQuKD4Bhyb88iSgu4dX1yw2uBSZBD16S4Io0DaZ+as5Yw4Kon7WMj3Jd5kz8ZxK+0NCy1CVJHOfJIwgYl0SVPp4DpbAPtJO4R/ciXyDQ/XGpoLtHjxnKXaJlJoSiA7FhuSEk+jB/peLHrYV1obdIRE5Dstly01S5cydKlfQ+A0TSjxFSWBYMEiD89sD09Br3iSJX5FejOoS8d2IQJ5faVzgQl4T5aBKsxCNNwmYrEe8m9HN7o2eer8nTKMln5IxZi3ZWhnjgJfrJ4QTXFndxCb78jo8HroN3+7VhoM136UZkqH1OMrIgAH/XSlW08G8m9MRamKsAWklq9aVflcEsPWTHmYW7rjAapQYf+jyK6BbfHcYmyKM82TFZ5iNB60Pth6EJgb2V8PZiChGvDzQvFYYOO3p9a/J8bVqsnPZBXXYcIBt42ZuRPvyyUTfM+75V1eYE9ZGFML+QlofwNCAg+/Rnl+RRy4z+8xQxd8Dn06geDpHsr4yND72FRUTKLbjxF5xfbzBRcZEXjGkyFdEAF7rB78I8xIqii+n6Yt8uEURmd4geI9KWXRQnwofTz9pklaAnRbER8zy/BJIiIYy8zecUHJn9v/DPnsnksfL6RRmG4tHaRBDbpAag0kVkCrpO/flK6dZOl/wvoVVVqT2O69a9/RpHLSV2f//ZS6L9s6vaYe4pXL0M6QymgA22sNHaws6XggJlTxVOFGRejMGYrKqVWtC+2UNbnel+/J0N1qj4luWfQaf9+1j+fq7vyLSzXYFCiyOLAznpqOhzKu6VWy2IbR0UnCoL5ZbhIba9e2MXM7Czy9Yee4xc=,iv:M0GbtFFl1XUeq+y9H+MiD+9z/ASB9hsd06KhpPzSwEo=,tag:vTLIIf+CeZN6DU25CSP8tw==,type:str]", | 2 | "data": "ENC[AES256_GCM,data:7STcG1J3zLHzlHi2LZgSa+pmKlYU6X1eLvjXcx7gvCuHFkXwa/ldrHdzaBXAMrQ+C+DWM4+cyhdal3ypxXueBuBEvH3P7/KofPP2A519sdZo1vDkXCzYRD3Ow74M+hX5ej6hL1mv21HayrRMPnYfH8HUWk1kd/y69EjpjvDHyCpQuw1WG5M4FDUaTd4Vh51QRCSG/Is29dEkvQowhIvNqnAR4KW9PpfjlbUPHauZ6PQLnlJmI9QCcAGJ8hHtp5T+xctTym82GAlXugTJpHnkqcxnGteu9H7UuiDMQ3aKKGYzZCpWkNHrbD1IRhzPzSjVlN2nIX3Ydp8ENNf61EGOrp5hzmV0MhwRHKfz5rE1FRVuyHcwhwBdJzfhzrL9zXZYzn9Zuf9KWwsF+1+58i9u9hPym63r4c1+RbMjNKzHc1/yhhsRDCrTwvMd/Hc6KfUi3H1wW/r02Va2bOCkYrOkWIQpBdx4ThMgkTaHr94DBMqT8n3Fzhc7+uaUsl6I9gMwTn8QCV3g4U7Mp3+/gnQLOdsCTgKr69x1iPfp7jzzbC29MWhIk+2aig+iWRMVT0n4AWIxooc3cYnfOJNtCdJwKuPUl4xNlZ22NK3XQfxBURSw0pi5KyDO1wgTVRoRJMnXZyin3bZ10OU8QKBqLp22FXpG/+mHrm3Y6yLkqfIP5ry+b86Rb7nWJlxDTwTGSoGi8sJzwNb+H9ioG7LjaSmvr7V+2aSq0+72WopAkIFSBq/yIWX3J/rNZuia6jKMY/8bSLDJz3V/YuzeUJ5feUkOS4KW1J1UkaYd6XxZ9Y8szCS/B7Ocz0YiV8fHmOzZKRn8BTmmXUIoyxO07MmwQ1shYfiVCwXjdjq2f3/CZbXNVOPvhGBYOcutUztftu3H9DRDTKrVae1TnztCVSkBSk1o52uSj0r8t6f4l1r7UG1TviOnVLVh1/6iiJVQey0m8G6ugw22zxfhI0Uea/rB70i+2UuoxrmsOfg+TgnvKBuakEBifp4rx0S5wyz05RYXkBLJTIwi2fVGmulqjZuCQNQnI3cfPI+oGcykob9IQqdo/Dwd118hNPVAwdDHyaiGXGh8eu8N2OCLQxlGZDtkVUveaEasJaZ0WWWaK97FUeoNJfUnB7Y3lNjv5u2rzviZyk4=,iv:jT21FNnHod6btDlBa3UflK3au5VmcsABs5OTMXF6oFA=,tag:Oh8cOL+edT5Wp0I1L5+vwg==,type:str]", |
3 | "sops": { | 3 | "sops": { |
4 | "age": [ | 4 | "age": [ |
5 | { | 5 | { |
@@ -11,8 +11,8 @@ | |||
11 | "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBiRWFqSHNlY1IvMkkwaEto\ncHZHa2p1Y25SakFkS2JYMlRFcFhnZGY1dVRFCkxSWmxvcHZMampQKzdKRHI0ZVMx\nUTFtR0pHbzFaQ0xQUFA2ZERDSWpwS0UKLS0tIFBaSGczY3VWdy9TKzRDZWZ2SElY\nbVQ4dDNhQllmVmViWGs5c3V4TmNscjQKeugevQJFAN/8JrzeAm4hm2JsQGb26BCb\n3dKYnN1kJU7oVHr1aVfXwMpELNYt9poX6WTY2h9lsdHuRlqoFXAA5Q==\n-----END AGE ENCRYPTED FILE-----\n" | 11 | "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBiRWFqSHNlY1IvMkkwaEto\ncHZHa2p1Y25SakFkS2JYMlRFcFhnZGY1dVRFCkxSWmxvcHZMampQKzdKRHI0ZVMx\nUTFtR0pHbzFaQ0xQUFA2ZERDSWpwS0UKLS0tIFBaSGczY3VWdy9TKzRDZWZ2SElY\nbVQ4dDNhQllmVmViWGs5c3V4TmNscjQKeugevQJFAN/8JrzeAm4hm2JsQGb26BCb\n3dKYnN1kJU7oVHr1aVfXwMpELNYt9poX6WTY2h9lsdHuRlqoFXAA5Q==\n-----END AGE ENCRYPTED FILE-----\n" |
12 | } | 12 | } |
13 | ], | 13 | ], |
14 | "lastmodified": "2025-05-10T10:25:15Z", | 14 | "lastmodified": "2025-08-11T07:08:36Z", |
15 | "mac": "ENC[AES256_GCM,data:dhj7e+vF3uiR6I22PR5tdNdM8EyrWmGGTIqjj8H7IdNIsZBHzjeHlBDFOwN7z/JMO0BVwIi4DmhApg2BSPGsQZGDQZ28UTCC8TDtd1zmfGtSP8R8AFHADYdLK/desMtHg6BZTnLv5tpba34WWdflMNOQpwgWPZsIk/DkLaoXdvk=,iv:qkoAZngTz2sfWdxDs+h8Mb2IrkF8gqnQoR5iRoeKjbY=,tag:zXrkBJmPM4ItJxMnX8IDxQ==,type:str]", | 15 | "mac": "ENC[AES256_GCM,data:ZL/dOz+NC8sr8vPBsux+gFOWxUhQqMSmG1az7udhB0ckmOXtnrPBzMM1gs+5pwXLvfLux0m4xzT87+o87axIECnCq35FSuMjtEBK24OUJXsLG/q/tDv5dfRBy/976dM5W7YkBVX/uc03p8CLKf5w4XYNeRKnSwjLvWGd9runDOU=,iv:9ZIeJ5aDVVPHi3/oHqWkWtEfeivV/nFFyQ1lJWJwMu8=,tag:TfkHaopMa+Z0zk38A6/NTA==,type:str]", |
16 | "unencrypted_suffix": "_unencrypted", | 16 | "unencrypted_suffix": "_unencrypted", |
17 | "version": "3.10.2" | 17 | "version": "3.10.2" |
18 | } | 18 | } |
diff --git a/hosts/vidhar/default.nix b/hosts/vidhar/default.nix index c9470ee9..547572c6 100644 --- a/hosts/vidhar/default.nix +++ b/hosts/vidhar/default.nix | |||
@@ -4,7 +4,7 @@ with lib; | |||
4 | 4 | ||
5 | { | 5 | { |
6 | imports = with flake.nixosModules.systemProfiles; [ | 6 | imports = with flake.nixosModules.systemProfiles; [ |
7 | ./zfs.nix ./network ./samba.nix ./dns ./prometheus ./borg ./pgbackrest ./postgresql.nix ./immich.nix ./paperless ./hledger ./audiobookshelf | 7 | ./zfs.nix ./network ./samba.nix ./dns ./prometheus ./borg ./pgbackrest ./postgresql.nix ./immich.nix ./paperless ./hledger ./audiobookshelf ./kimai |
8 | tmpfs-root zfs | 8 | tmpfs-root zfs |
9 | initrd-all-crypto-modules default-locale openssh rebuild-machines | 9 | initrd-all-crypto-modules default-locale openssh rebuild-machines |
10 | build-server | 10 | build-server |
@@ -136,7 +136,7 @@ with lib; | |||
136 | wantedBy = ["basic.target"]; | 136 | wantedBy = ["basic.target"]; |
137 | serviceConfig = { | 137 | serviceConfig = { |
138 | ExecStart = pkgs.writeShellScript "limit-pstate-start" '' | 138 | ExecStart = pkgs.writeShellScript "limit-pstate-start" '' |
139 | echo 50 > /sys/devices/system/cpu/intel_pstate/max_perf_pct | 139 | echo 40 > /sys/devices/system/cpu/intel_pstate/max_perf_pct |
140 | ''; | 140 | ''; |
141 | RemainAfterExit = true; | 141 | RemainAfterExit = true; |
142 | ExecStop = pkgs.writeShellScript "limit-pstate-stop" '' | 142 | ExecStop = pkgs.writeShellScript "limit-pstate-stop" '' |
diff --git a/hosts/vidhar/kimai/default.nix b/hosts/vidhar/kimai/default.nix new file mode 100644 index 00000000..0258697b --- /dev/null +++ b/hosts/vidhar/kimai/default.nix | |||
@@ -0,0 +1,89 @@ | |||
1 | { flake, config, ... }: | ||
2 | |||
3 | { | ||
4 | config = { | ||
5 | boot.enableContainers = true; | ||
6 | boot.kernel.sysctl = { | ||
7 | "net.netfilter.nf_log_all_netns" = true; | ||
8 | }; | ||
9 | |||
10 | containers."kimai" = { | ||
11 | autoStart = true; | ||
12 | ephemeral = true; | ||
13 | bindMounts = { | ||
14 | "/var/lib/kimai" = { | ||
15 | hostPath = "/var/lib/kimai/state"; | ||
16 | isReadOnly = false; | ||
17 | }; | ||
18 | "/var/lib/mysql" = { | ||
19 | hostPath = "/var/lib/kimai/mysql"; | ||
20 | isReadOnly = false; | ||
21 | }; | ||
22 | }; | ||
23 | privateNetwork = true; | ||
24 | # forwardPorts = [ | ||
25 | # { containerPort = 80; | ||
26 | # hostPort = 28983; | ||
27 | # } | ||
28 | # ]; | ||
29 | hostAddress = "192.168.52.113"; | ||
30 | localAddress = "192.168.52.114"; | ||
31 | hostAddress6 = "2a03:4000:52:ada:6::1"; | ||
32 | localAddress6 = "2a03:4000:52:ada:6::2"; | ||
33 | config = let hostConfig = config; in { config, pkgs, lib, ... }: { | ||
34 | system.stateVersion = lib.mkIf hostConfig.containers."kimai".ephemeral config.system.nixos.release; | ||
35 | system.configurationRevision = lib.mkIf (flake ? rev) flake.rev; | ||
36 | nixpkgs.pkgs = hostConfig.nixpkgs.pkgs; | ||
37 | |||
38 | services.kimai.sites."kimai.yggdrasil.li" = { | ||
39 | database.socket = "/run/mysqld/mysqld.sock"; | ||
40 | }; | ||
41 | |||
42 | networking = { | ||
43 | useDHCP = false; | ||
44 | useNetworkd = true; | ||
45 | useHostResolvConf = false; | ||
46 | firewall.enable = false; | ||
47 | nftables = { | ||
48 | enable = true; | ||
49 | rulesetFile = ./ruleset.nft; | ||
50 | }; | ||
51 | }; | ||
52 | |||
53 | services.resolved.fallbackDns = [ | ||
54 | "9.9.9.10#dns10.quad9.net" | ||
55 | "149.112.112.10#dns10.quad9.net" | ||
56 | "2620:fe::10#dns10.quad9.net" | ||
57 | "2620:fe::fe:10#dns10.quad9.net" | ||
58 | ]; | ||
59 | |||
60 | systemd.network = { | ||
61 | networks.upstream = { | ||
62 | name = "eth0"; | ||
63 | matchConfig = { | ||
64 | Name = "eth0"; | ||
65 | }; | ||
66 | linkConfig = { | ||
67 | RequiredForOnline = true; | ||
68 | }; | ||
69 | networkConfig = { | ||
70 | Address = [ "192.168.52.114/32" "2a03:4000:52:ada:6::2/128" ]; | ||
71 | LLMNR = false; | ||
72 | MulticastDNS = false; | ||
73 | }; | ||
74 | routes = [ | ||
75 | { Destination = "192.168.52.113/32"; } | ||
76 | { Destination = "2a03:4000:52:ada:6::1/128"; } | ||
77 | { Destination = "0.0.0.0/0"; | ||
78 | Gateway = "192.168.52.113"; | ||
79 | } | ||
80 | { Destination = "::/0"; | ||
81 | Gateway = "2a03:4000:52:ada:6::1"; | ||
82 | } | ||
83 | ]; | ||
84 | }; | ||
85 | }; | ||
86 | }; | ||
87 | }; | ||
88 | }; | ||
89 | } | ||
diff --git a/hosts/vidhar/kimai/ruleset.nft b/hosts/vidhar/kimai/ruleset.nft new file mode 100644 index 00000000..ad4db6d5 --- /dev/null +++ b/hosts/vidhar/kimai/ruleset.nft | |||
@@ -0,0 +1,149 @@ | |||
1 | define icmp_protos = {ipv6-icmp, icmp, igmp} | ||
2 | |||
3 | table arp filter { | ||
4 | limit lim_arp { | ||
5 | rate over 50 mbytes/second burst 50 mbytes | ||
6 | } | ||
7 | |||
8 | counter arp-rx {} | ||
9 | counter arp-tx {} | ||
10 | |||
11 | counter arp-ratelimit-rx {} | ||
12 | counter arp-ratelimit-tx {} | ||
13 | |||
14 | chain input { | ||
15 | type filter hook input priority filter | ||
16 | policy accept | ||
17 | |||
18 | limit name lim_arp counter name arp-ratelimit-rx drop | ||
19 | |||
20 | counter name arp-rx | ||
21 | } | ||
22 | |||
23 | chain output { | ||
24 | type filter hook output priority filter | ||
25 | policy accept | ||
26 | |||
27 | limit name lim_arp counter name arp-ratelimit-tx drop | ||
28 | |||
29 | counter name arp-tx | ||
30 | } | ||
31 | } | ||
32 | |||
33 | table inet filter { | ||
34 | limit lim_reject { | ||
35 | rate over 1000/second burst 1000 packets | ||
36 | } | ||
37 | |||
38 | limit lim_icmp { | ||
39 | rate over 50 mbytes/second burst 50 mbytes | ||
40 | } | ||
41 | |||
42 | counter invalid-fw {} | ||
43 | counter fw-lo {} | ||
44 | |||
45 | counter reject-ratelimit-fw {} | ||
46 | counter reject-fw {} | ||
47 | counter reject-tcp-fw {} | ||
48 | counter reject-icmp-fw {} | ||
49 | |||
50 | counter drop-fw {} | ||
51 | |||
52 | counter invalid-rx {} | ||
53 | |||
54 | counter rx-lo {} | ||
55 | counter invalid-local4-rx {} | ||
56 | counter invalid-local6-rx {} | ||
57 | |||
58 | counter icmp-ratelimit-rx {} | ||
59 | counter icmp-rx {} | ||
60 | |||
61 | counter kimai-rx {} | ||
62 | |||
63 | counter established-rx {} | ||
64 | |||
65 | counter reject-ratelimit-rx {} | ||
66 | counter reject-rx {} | ||
67 | counter reject-tcp-rx {} | ||
68 | counter reject-icmp-rx {} | ||
69 | |||
70 | counter drop-rx {} | ||
71 | |||
72 | counter tx-lo {} | ||
73 | |||
74 | counter icmp-ratelimit-tx {} | ||
75 | counter icmp-tx {} | ||
76 | |||
77 | counter kimai-tx {} | ||
78 | |||
79 | counter tx {} | ||
80 | |||
81 | chain forward { | ||
82 | type filter hook forward priority filter | ||
83 | policy drop | ||
84 | |||
85 | |||
86 | ct state invalid log level debug prefix "kimai: drop invalid forward: " counter name invalid-fw drop | ||
87 | |||
88 | |||
89 | iifname lo counter name fw-lo accept | ||
90 | |||
91 | |||
92 | limit name lim_reject log level debug prefix "kimai: drop forward: " counter name reject-ratelimit-fw drop | ||
93 | log level debug prefix "kimai: reject forward: " counter name reject-fw | ||
94 | meta l4proto tcp ct state new counter name reject-tcp-fw reject with tcp reset | ||
95 | ct state new counter name reject-icmp-fw reject | ||
96 | |||
97 | |||
98 | counter name drop-fw | ||
99 | } | ||
100 | |||
101 | chain input { | ||
102 | type filter hook input priority filter | ||
103 | policy drop | ||
104 | |||
105 | |||
106 | ct state invalid log level debug prefix "kimai: drop invalid input: " counter name invalid-rx drop | ||
107 | |||
108 | |||
109 | iifname lo counter name rx-lo accept | ||
110 | iif != lo ip daddr 127.0.0.1/8 counter name invalid-local4-rx reject | ||
111 | iif != lo ip6 daddr ::1/128 counter name invalid-local6-rx reject | ||
112 | |||
113 | |||
114 | meta l4proto $icmp_protos limit name lim_icmp counter name icmp-ratelimit-rx drop | ||
115 | meta l4proto $icmp_protos counter name icmp-rx accept | ||
116 | |||
117 | |||
118 | tcp dport 80 counter name kimai-rx accept | ||
119 | |||
120 | |||
121 | ct state { established, related } counter name established-rx accept | ||
122 | |||
123 | |||
124 | limit name lim_reject log level debug prefix "kimai: drop input: " counter name reject-ratelimit-rx drop | ||
125 | log level debug prefix "kimai: reject input: " counter name reject-rx | ||
126 | meta l4proto tcp ct state new counter name reject-tcp-rx reject with tcp reset | ||
127 | ct state new counter name reject-icmp-rx reject | ||
128 | |||
129 | |||
130 | counter name drop-rx | ||
131 | } | ||
132 | |||
133 | chain output { | ||
134 | type filter hook output priority filter | ||
135 | policy accept | ||
136 | |||
137 | |||
138 | oifname lo counter name tx-lo accept | ||
139 | |||
140 | meta l4proto $icmp_protos limit name lim_icmp counter name icmp-ratelimit-tx drop | ||
141 | meta l4proto $icmp_protos counter name icmp-tx accept | ||
142 | |||
143 | |||
144 | tcp sport 80 counter name kimai-tx | ||
145 | |||
146 | |||
147 | counter name tx | ||
148 | } | ||
149 | } | ||
diff --git a/hosts/vidhar/network/ruleset.nft b/hosts/vidhar/network/ruleset.nft index 6b0ac9fc..7897fb3d 100644 --- a/hosts/vidhar/network/ruleset.nft +++ b/hosts/vidhar/network/ruleset.nft | |||
@@ -60,6 +60,7 @@ table inet filter { | |||
60 | counter fw-lo {} | 60 | counter fw-lo {} |
61 | counter fw-lan {} | 61 | counter fw-lan {} |
62 | counter fw-gpon {} | 62 | counter fw-gpon {} |
63 | counter fw-kimai {} | ||
63 | 64 | ||
64 | counter fw-cups {} | 65 | counter fw-cups {} |
65 | 66 | ||
@@ -95,6 +96,7 @@ table inet filter { | |||
95 | counter paperless-rx {} | 96 | counter paperless-rx {} |
96 | counter hledger-rx {} | 97 | counter hledger-rx {} |
97 | counter audiobookshelf-rx {} | 98 | counter audiobookshelf-rx {} |
99 | counter kimai-rx {} | ||
98 | 100 | ||
99 | counter established-rx {} | 101 | counter established-rx {} |
100 | 102 | ||
@@ -127,6 +129,7 @@ table inet filter { | |||
127 | counter paperless-tx {} | 129 | counter paperless-tx {} |
128 | counter hledger-tx {} | 130 | counter hledger-tx {} |
129 | counter audiobookshelf-tx {} | 131 | counter audiobookshelf-tx {} |
132 | counter kimai-tx {} | ||
130 | 133 | ||
131 | counter tx {} | 134 | counter tx {} |
132 | 135 | ||
@@ -150,8 +153,13 @@ table inet filter { | |||
150 | 153 | ||
151 | oifname { lan, gpon, bifrost } meta l4proto $icmp_protos jump forward_icmp_accept | 154 | oifname { lan, gpon, bifrost } meta l4proto $icmp_protos jump forward_icmp_accept |
152 | iifname lan oifname { gpon, bifrost } counter name fw-lan accept | 155 | iifname lan oifname { gpon, bifrost } counter name fw-lan accept |
156 | iifname ve-kimai oifname gpon counter name fw-kimai accept | ||
153 | 157 | ||
154 | iifname gpon oifname lan ct state { established, related } counter name fw-gpon accept | 158 | iifname gpon oifname lan ct state { established, related } counter name fw-gpon accept |
159 | iifname gpon oifname ve-kimai ct state { established, related } counter name fw-kimai accept | ||
160 | |||
161 | iifname bifrost oifname ve-kimai tcp dport 80 ip6 saddr $bifrost_surtr ip6 daddr 2a03:4000:52:ada:6::2 counter name kimai-rx accept | ||
162 | iifname ve-kimai oifname bifrost tcp sport 80 ip6 saddr 2a03:4000:52:ada:6::2 ip6 daddr $bifrost_surtr counter name kimai-tx accept | ||
155 | 163 | ||
156 | 164 | ||
157 | limit name lim_reject log level debug prefix "drop forward: " counter name reject-ratelimit-fw drop | 165 | limit name lim_reject log level debug prefix "drop forward: " counter name reject-ratelimit-fw drop |
@@ -266,7 +274,7 @@ table inet filter { | |||
266 | 274 | ||
267 | table inet nat { | 275 | table inet nat { |
268 | counter gpon-nat {} | 276 | counter gpon-nat {} |
269 | # counter container-nat {} | 277 | counter kimai-nat {} |
270 | 278 | ||
271 | chain postrouting { | 279 | chain postrouting { |
272 | type nat hook postrouting priority srcnat | 280 | type nat hook postrouting priority srcnat |
@@ -274,7 +282,7 @@ table inet nat { | |||
274 | 282 | ||
275 | 283 | ||
276 | meta nfproto ipv4 oifname gpon counter name gpon-nat masquerade | 284 | meta nfproto ipv4 oifname gpon counter name gpon-nat masquerade |
277 | # iifname ve-* oifname gpon counter name container-nat masquerade | 285 | iifname ve-kimai oifname gpon counter name kimai-nat masquerade |
278 | } | 286 | } |
279 | } | 287 | } |
280 | 288 | ||
diff --git a/hosts/vidhar/prometheus/default.nix b/hosts/vidhar/prometheus/default.nix index b1d90d47..094f9f7a 100644 --- a/hosts/vidhar/prometheus/default.nix +++ b/hosts/vidhar/prometheus/default.nix | |||
@@ -27,6 +27,7 @@ in { | |||
27 | 27 | ||
28 | extraFlags = [ | 28 | extraFlags = [ |
29 | "--web.enable-remote-write-receiver" | 29 | "--web.enable-remote-write-receiver" |
30 | "--storage.tsdb.retention.size=35GB" | ||
30 | ]; | 31 | ]; |
31 | 32 | ||
32 | exporters = { | 33 | exporters = { |