start mailserver work
This commit is contained in:
parent
b8b409e632
commit
69df8d1a43
|
@ -1,15 +1,24 @@
|
||||||
{ lib, pkgs, tree, ... }:
|
{ lib, pkgs, nixpkgs, config, tree, ... }:
|
||||||
let
|
let
|
||||||
wifiInterface = "shenanigans0";
|
wifiInterface = "shenanigans0";
|
||||||
wifiMac = "00:0F:55:A8:2B:8E";
|
wifiMac = "00:0F:55:A8:2B:8E";
|
||||||
|
|
||||||
|
usbethInterface = "shenanigans1";
|
||||||
|
usbethMac = "d0:37:45:88:9a:49";
|
||||||
|
|
||||||
ssid = "Shenanigans";
|
ssid = "Shenanigans";
|
||||||
password = "password123";
|
password = "password123";
|
||||||
in {
|
in {
|
||||||
# Set interface name to ${wifiInterface}
|
boot.extraModulePackages = with config.boot.kernelPackages; [ rtl8812au ];
|
||||||
|
nixpkgs.config.allowBroken = true;
|
||||||
|
|
||||||
services.udev.extraRules = ''
|
services.udev.extraRules = ''
|
||||||
KERNEL=="wlan*", ATTR{address}=="${
|
KERNEL=="wlan*", ATTR{address}=="${
|
||||||
lib.toLower wifiMac
|
lib.toLower wifiMac
|
||||||
}", NAME="${wifiInterface}"
|
}", NAME="${wifiInterface}"
|
||||||
|
KERNEL=="eth*", ACTION=="add", ATTR{address}=="${
|
||||||
|
lib.toLower usbethMac
|
||||||
|
}", NAME="${usbethInterface}"
|
||||||
'';
|
'';
|
||||||
|
|
||||||
networking.interfaces."${wifiInterface}".ipv4.addresses = [{
|
networking.interfaces."${wifiInterface}".ipv4.addresses = [{
|
||||||
|
@ -17,27 +26,36 @@ in {
|
||||||
prefixLength = 24;
|
prefixLength = 24;
|
||||||
}];
|
}];
|
||||||
|
|
||||||
|
networking.interfaces."${usbethInterface}".ipv4.addresses = [{
|
||||||
|
address = "192.168.2.1";
|
||||||
|
prefixLength = 24;
|
||||||
|
}];
|
||||||
|
|
||||||
networking.networkmanager.unmanaged = [
|
networking.networkmanager.unmanaged = [
|
||||||
# Wifi
|
# Wifi
|
||||||
"interface-name:${wifiInterface}"
|
"interface-name:${wifiInterface}"
|
||||||
"mac:${wifiMac}"
|
"mac:${wifiMac}"
|
||||||
|
"interface-name:${usbethInterface}"
|
||||||
|
"mac:${usbethMac}"
|
||||||
];
|
];
|
||||||
|
|
||||||
systemd.services.wifi-relay = let inherit (pkgs) iptables gnugrep;
|
systemd.services.wifi-relay = let inherit (pkgs) iptables gnugrep;
|
||||||
in {
|
in {
|
||||||
description = "iptables rules for wifi-relay";
|
description = "iptables rules for wifi-relay";
|
||||||
after = [ "dhcpd4.service" ];
|
after = [ "dhcpd4.service" ];
|
||||||
wantedBy = [ "multi-user.target" ];
|
wantedBy = [ "multi-user.target" ];
|
||||||
script = ''
|
script = ''
|
||||||
${iptables}/bin/iptables -w -t nat -I POSTROUTING -s 192.168.2.0/24 ! -o ${wifiInterface} -j MASQUERADE
|
${iptables}/bin/iptables -w -t nat -I POSTROUTING -s 192.168.2.0/24 ! -o ${wifiInterface} -j MASQUERADE
|
||||||
${iptables}/bin/iptables -w -I FORWARD -i ${wifiInterface} -s 192.168.2.0/24 -j ACCEPT
|
${iptables}/bin/iptables -w -I FORWARD -i ${wifiInterface} -s 192.168.2.0/24 -j ACCEPT
|
||||||
${iptables}/bin/iptables -t nat -A PREROUTING -i ${wifiInterface} -p tcp --dport 80 -j REDIRECT --to-port 8080
|
${iptables}/bin/iptables -w -t nat -I POSTROUTING -s 192.168.2.0/24 ! -o ${usbethInterface} -j MASQUERADE
|
||||||
${iptables}/bin/iptables -t nat -A PREROUTING -i ${wifiInterface} -p tcp --dport 443 -j REDIRECT --to-port 8080
|
${iptables}/bin/iptables -w -I FORWARD -i ${usbethInterface} -s 192.168.2.0/24 -j ACCEPT
|
||||||
'';
|
#${iptables}/bin/iptables -t nat -A PREROUTING -i ${wifiInterface} -p tcp --dport 80 -j REDIRECT --to-port 8080
|
||||||
};
|
#${iptables}/bin/iptables -t nat -A PREROUTING -i ${wifiInterface} -p tcp --dport 443 -j REDIRECT --to-port 8080
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
networking.firewall = {
|
networking.firewall = {
|
||||||
trustedInterfaces = [ wifiInterface ];
|
trustedInterfaces = [ wifiInterface usbethInterface ];
|
||||||
checkReversePath = lib.mkForce false;
|
checkReversePath = lib.mkForce false;
|
||||||
allowedTCPPorts = [ 53 80 443 ];
|
allowedTCPPorts = [ 53 80 443 ];
|
||||||
};
|
};
|
||||||
|
@ -54,14 +72,14 @@ in {
|
||||||
|
|
||||||
services.dhcpd4 = {
|
services.dhcpd4 = {
|
||||||
enable = true;
|
enable = true;
|
||||||
interfaces = [ "${wifiInterface}" ];
|
interfaces = [ "${usbethInterface}" ];
|
||||||
extraConfig = ''
|
extraConfig = ''
|
||||||
option subnet-mask 255.255.255.0;
|
|
||||||
option broadcast-address 192.168.2.255;
|
|
||||||
option routers 192.168.2.1;
|
|
||||||
option domain-name-servers 192.168.2.1;
|
|
||||||
subnet 192.168.2.0 netmask 255.255.255.0 {
|
subnet 192.168.2.0 netmask 255.255.255.0 {
|
||||||
range 192.168.2.100 192.168.2.200;
|
range 192.168.2.100 192.168.2.200;
|
||||||
|
option subnet-mask 255.255.255.0;
|
||||||
|
option broadcast-address 192.168.2.255;
|
||||||
|
option routers 192.168.2.1;
|
||||||
|
option domain-name-servers 192.168.2.1;
|
||||||
}
|
}
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
|
|
111
flake.lock
111
flake.lock
|
@ -3,9 +3,7 @@
|
||||||
"deploy-rs": {
|
"deploy-rs": {
|
||||||
"inputs": {
|
"inputs": {
|
||||||
"flake-compat": "flake-compat",
|
"flake-compat": "flake-compat",
|
||||||
"nixpkgs": [
|
"nixpkgs": "nixpkgs",
|
||||||
"nixpkgs-unstable"
|
|
||||||
],
|
|
||||||
"utils": "utils"
|
"utils": "utils"
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
|
@ -54,18 +52,38 @@
|
||||||
"type": "github"
|
"type": "github"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"flake-compat_3": {
|
||||||
|
"flake": false,
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1650374568,
|
||||||
|
"narHash": "sha256-Z+s0J8/r907g149rllvwhb4pKi8Wam5ij0st8PwAh+E=",
|
||||||
|
"owner": "edolstra",
|
||||||
|
"repo": "flake-compat",
|
||||||
|
"rev": "b4a34015c698c7793d592d66adbab377907a2be8",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "edolstra",
|
||||||
|
"repo": "flake-compat",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
"home-manager-unstable": {
|
"home-manager-unstable": {
|
||||||
"inputs": {
|
"inputs": {
|
||||||
|
"flake-compat": "flake-compat_2",
|
||||||
"nixpkgs": [
|
"nixpkgs": [
|
||||||
"nixpkgs-unstable"
|
"nixpkgs-unstable"
|
||||||
]
|
],
|
||||||
|
"nmd": "nmd",
|
||||||
|
"nmt": "nmt",
|
||||||
|
"utils": "utils_2"
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1653943687,
|
"lastModified": 1655858799,
|
||||||
"narHash": "sha256-xXW9t24HLf89+n/92kOqRRfOBE3KDna+9rAOefs5WSQ=",
|
"narHash": "sha256-Ws6BKlVuEVO29Ab3OEUfVLbWTECv/5Ax3yOMq/UeY0E=",
|
||||||
"owner": "nix-community",
|
"owner": "nix-community",
|
||||||
"repo": "home-manager",
|
"repo": "home-manager",
|
||||||
"rev": "8f3e26705178cc8c1d982d37d881fc0d5b5b1837",
|
"rev": "06bb67ab24bd6e6c6d2bc97ecbcddd6c8b07ac18",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
|
@ -76,11 +94,11 @@
|
||||||
},
|
},
|
||||||
"musicutil": {
|
"musicutil": {
|
||||||
"inputs": {
|
"inputs": {
|
||||||
"flake-compat": "flake-compat_2",
|
"flake-compat": "flake-compat_3",
|
||||||
"nixpkgs": [
|
"nixpkgs": [
|
||||||
"nixpkgs-unstable"
|
"nixpkgs-unstable"
|
||||||
],
|
],
|
||||||
"utils": "utils_2"
|
"utils": "utils_3"
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1650728466,
|
"lastModified": 1650728466,
|
||||||
|
@ -117,13 +135,29 @@
|
||||||
"type": "github"
|
"type": "github"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"nixpkgs": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1648219316,
|
||||||
|
"narHash": "sha256-Ctij+dOi0ZZIfX5eMhgwugfvB+WZSrvVNAyAuANOsnQ=",
|
||||||
|
"owner": "NixOS",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"rev": "30d3d79b7d3607d56546dd2a6b49e156ba0ec634",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "NixOS",
|
||||||
|
"ref": "nixpkgs-unstable",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
"nixpkgs-stable": {
|
"nixpkgs-stable": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1653996475,
|
"lastModified": 1655770856,
|
||||||
"narHash": "sha256-r/UA7h3Dfgf4dlOCkakpqejf1Tagfb+6T+9OdT0qBgU=",
|
"narHash": "sha256-GZRIyHjuCbOl0UA8ClKmyRxZkCQEh/rsvU0otH037BU=",
|
||||||
"owner": "nixos",
|
"owner": "nixos",
|
||||||
"repo": "nixpkgs",
|
"repo": "nixpkgs",
|
||||||
"rev": "ec6eaba9dfcfdd11547d75a193e91e26701bf7e3",
|
"rev": "63198c9ccefdbd337cef0d85db0ea2689f4ce418",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
|
@ -135,11 +169,11 @@
|
||||||
},
|
},
|
||||||
"nixpkgs-unstable": {
|
"nixpkgs-unstable": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1653931853,
|
"lastModified": 1655624069,
|
||||||
"narHash": "sha256-O3wncIouj9x7gBPntzHeK/Hkmm9M1SGlYq7JI7saTAE=",
|
"narHash": "sha256-7g1zwTdp35GMTERnSzZMWJ7PG3QdDE8VOX3WsnOkAtM=",
|
||||||
"owner": "nixos",
|
"owner": "nixos",
|
||||||
"repo": "nixpkgs",
|
"repo": "nixpkgs",
|
||||||
"rev": "f1c167688a6f81f4a51ab542e5f476c8c595e457",
|
"rev": "0d68d7c857fe301d49cdcd56130e0beea4ecd5aa",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
|
@ -149,6 +183,38 @@
|
||||||
"type": "github"
|
"type": "github"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"nmd": {
|
||||||
|
"flake": false,
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1653339422,
|
||||||
|
"narHash": "sha256-RNLq09vfj21TyYuUCeD6BNTNC6Ew8bLhQULZytN4Xx8=",
|
||||||
|
"owner": "rycee",
|
||||||
|
"repo": "nmd",
|
||||||
|
"rev": "91dee681dd1c478d6040a00835d73c0f4a4c5c29",
|
||||||
|
"type": "gitlab"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "rycee",
|
||||||
|
"repo": "nmd",
|
||||||
|
"type": "gitlab"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"nmt": {
|
||||||
|
"flake": false,
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1648075362,
|
||||||
|
"narHash": "sha256-u36WgzoA84dMVsGXzml4wZ5ckGgfnvS0ryzo/3zn/Pc=",
|
||||||
|
"owner": "rycee",
|
||||||
|
"repo": "nmt",
|
||||||
|
"rev": "d83601002c99b78c89ea80e5e6ba21addcfe12ae",
|
||||||
|
"type": "gitlab"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "rycee",
|
||||||
|
"repo": "nmt",
|
||||||
|
"type": "gitlab"
|
||||||
|
}
|
||||||
|
},
|
||||||
"root": {
|
"root": {
|
||||||
"inputs": {
|
"inputs": {
|
||||||
"deploy-rs": "deploy-rs",
|
"deploy-rs": "deploy-rs",
|
||||||
|
@ -175,6 +241,21 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"utils_2": {
|
"utils_2": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1653893745,
|
||||||
|
"narHash": "sha256-0jntwV3Z8//YwuOjzhV2sgJJPt+HY6KhU7VZUL0fKZQ=",
|
||||||
|
"owner": "numtide",
|
||||||
|
"repo": "flake-utils",
|
||||||
|
"rev": "1ed9fb1935d260de5fe1c2f7ee0ebaae17ed2fa1",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "numtide",
|
||||||
|
"repo": "flake-utils",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"utils_3": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1649676176,
|
"lastModified": 1649676176,
|
||||||
"narHash": "sha256-OWKJratjt2RW151VUlJPRALb7OU2S5s+f0vLj4o1bHM=",
|
"narHash": "sha256-OWKJratjt2RW151VUlJPRALb7OU2S5s+f0vLj4o1bHM=",
|
||||||
|
|
|
@ -12,7 +12,7 @@
|
||||||
nixpkgs-stable.url = "github:nixos/nixpkgs/nixos-21.11";
|
nixpkgs-stable.url = "github:nixos/nixpkgs/nixos-21.11";
|
||||||
|
|
||||||
deploy-rs.url = "github:serokell/deploy-rs";
|
deploy-rs.url = "github:serokell/deploy-rs";
|
||||||
deploy-rs.inputs.nixpkgs.follows = "nixpkgs-unstable";
|
#deploy-rs.inputs.nixpkgs.follows = "nixpkgs-unstable";
|
||||||
|
|
||||||
musicutil.url = "gitlab:ChaotiCryptidz/musicutil";
|
musicutil.url = "gitlab:ChaotiCryptidz/musicutil";
|
||||||
musicutil.inputs.nixpkgs.follows = "nixpkgs-unstable";
|
musicutil.inputs.nixpkgs.follows = "nixpkgs-unstable";
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
{ pkgs, ... }: { home.packages = with pkgs; [ neofetch inxi htop ]; }
|
{ pkgs, ... }: { home.packages = with pkgs; [ neofetch inxi htop usbutils iotop ]; }
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
hosts.hetzner-vm.services.quassel
|
hosts.hetzner-vm.services.quassel
|
||||||
hosts.hetzner-vm.services.mpd
|
hosts.hetzner-vm.services.mpd
|
||||||
hosts.hetzner-vm.services.storage-sftp
|
hosts.hetzner-vm.services.storage-sftp
|
||||||
|
hosts.hetzner-vm.services.mail
|
||||||
#hosts.hetzner-vm.services.misskey
|
#hosts.hetzner-vm.services.misskey
|
||||||
|
|
||||||
(modulesPath + "/profiles/qemu-guest.nix")
|
(modulesPath + "/profiles/qemu-guest.nix")
|
||||||
|
@ -25,9 +26,11 @@
|
||||||
|
|
||||||
home-manager.users.root = {
|
home-manager.users.root = {
|
||||||
imports = with tree; [ home.base home.dev.small ];
|
imports = with tree; [ home.base home.dev.small ];
|
||||||
|
home.stateVersion = "22.05";
|
||||||
};
|
};
|
||||||
home-manager.users.chaos = {
|
home-manager.users.chaos = {
|
||||||
imports = with tree; [ home.base home.dev.small ];
|
imports = with tree; [ home.base home.dev.small ];
|
||||||
|
home.stateVersion = "22.05";
|
||||||
};
|
};
|
||||||
|
|
||||||
nix.settings.auto-optimise-store = true;
|
nix.settings.auto-optimise-store = true;
|
||||||
|
@ -53,7 +56,7 @@
|
||||||
enable = true;
|
enable = true;
|
||||||
networks.eth0 = {
|
networks.eth0 = {
|
||||||
name = "eth0";
|
name = "eth0";
|
||||||
address = [ "2a01:4f9:c010:8beb::/64" ];
|
address = [ "2a01:4f9:c010:8beb::1/64" ];
|
||||||
gateway = [ "fe80::1" ];
|
gateway = [ "fe80::1" ];
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
9
hosts/hetzner-vm/services/mail.nix
Normal file
9
hosts/hetzner-vm/services/mail.nix
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
{...}: {
|
||||||
|
imports = [
|
||||||
|
./mailserver/postfix.nix
|
||||||
|
./mailserver/vmail.nix
|
||||||
|
./mailserver/ssl.nix
|
||||||
|
./mailserver/dovecot.nix
|
||||||
|
./mailserver/firewall.nix
|
||||||
|
];
|
||||||
|
}
|
36
hosts/hetzner-vm/services/mailserver/config.nix
Normal file
36
hosts/hetzner-vm/services/mailserver/config.nix
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
{ }: rec {
|
||||||
|
fqdn = "mail.owo.monster";
|
||||||
|
domains = [
|
||||||
|
"owo.monster"
|
||||||
|
#"kitteh.pw"
|
||||||
|
];
|
||||||
|
|
||||||
|
debug_mode = true;
|
||||||
|
|
||||||
|
ssl_config = {
|
||||||
|
cert = "/var/lib/acme/${fqdn}/fullchain.pem";
|
||||||
|
key = "/var/lib/acme/${fqdn}/key.pem";
|
||||||
|
#cert = "/tmp/cert-${fqdn}.pem";
|
||||||
|
#key = "/tmp/key-${fqdn}.pem";
|
||||||
|
};
|
||||||
|
|
||||||
|
# generate password files with:
|
||||||
|
# nix run nixpkgs.apacheHttpd -c htpasswd -nbB "" "password" | cut -d: -f2
|
||||||
|
|
||||||
|
accounts = {
|
||||||
|
"chaoticryptidz@owo.monster" = {
|
||||||
|
passwordFile = "/secrets/chaos-mail-password";
|
||||||
|
aliases = [
|
||||||
|
"all@owo.monster"
|
||||||
|
];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
sieve_directory = "/var/sieve";
|
||||||
|
|
||||||
|
vmail_config = {
|
||||||
|
user_group_name = "vmail";
|
||||||
|
user_group_id = 5000;
|
||||||
|
directory = "/home/vmail";
|
||||||
|
};
|
||||||
|
}
|
186
hosts/hetzner-vm/services/mailserver/dovecot.nix
Normal file
186
hosts/hetzner-vm/services/mailserver/dovecot.nix
Normal file
|
@ -0,0 +1,186 @@
|
||||||
|
{ config, pkgs, lib, ... }:
|
||||||
|
let
|
||||||
|
mail_config = (import ./config.nix { });
|
||||||
|
|
||||||
|
passwdDir = "/run/dovecot2";
|
||||||
|
passwdFile = "${passwdDir}/passwd";
|
||||||
|
|
||||||
|
bool2int = x: if x then "1" else "0";
|
||||||
|
|
||||||
|
# maildir in format "/${domain}/${user}"
|
||||||
|
dovecotMaildir = "maildir:${mail_config.vmail_config.directory}/%d/%n";
|
||||||
|
|
||||||
|
postfixCfg = config.services.postfix;
|
||||||
|
dovecot2Cfg = config.services.dovecot2;
|
||||||
|
|
||||||
|
stateDir = "/var/lib/dovecot";
|
||||||
|
|
||||||
|
passwordFiles =
|
||||||
|
lib.mapAttrs (name: value: value.passwordFile) mail_config.accounts;
|
||||||
|
|
||||||
|
genPasswdScript = pkgs.writeScript "generate-password-file" ''
|
||||||
|
#!${pkgs.stdenv.shell}
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
if (! test -d "${passwdDir}"); then
|
||||||
|
mkdir "${passwdDir}"
|
||||||
|
chmod 755 "${passwdDir}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
for f in ${
|
||||||
|
builtins.toString
|
||||||
|
(lib.mapAttrsToList (name: value: passwordFiles."${name}")
|
||||||
|
mail_config.accounts)
|
||||||
|
}; do
|
||||||
|
if [ ! -f "$f" ]; then
|
||||||
|
echo "Expected password hash file $f does not exist!"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
cat <<EOF > ${passwdFile}
|
||||||
|
${lib.concatStringsSep "\n" (lib.mapAttrsToList (name: value:
|
||||||
|
"${name}:${"$(head -n 1 ${passwordFiles."${name}"})"}:${
|
||||||
|
builtins.toString mail_config.vmail_config.user_group_id
|
||||||
|
}:${
|
||||||
|
builtins.toString mail_config.vmail_config.user_group_id
|
||||||
|
}::${mail_config.vmail_config.directory}:/run/current-system/sw/bin/nologin:")
|
||||||
|
mail_config.accounts)}
|
||||||
|
EOF
|
||||||
|
|
||||||
|
chmod 600 ${passwdFile}
|
||||||
|
'';
|
||||||
|
in {
|
||||||
|
services.dovecot2 = {
|
||||||
|
enable = true;
|
||||||
|
enableImap = true;
|
||||||
|
enablePop3 = false;
|
||||||
|
enablePAM = false;
|
||||||
|
enableQuota = true;
|
||||||
|
mailGroup = mail_config.vmail_config.user_group_name;
|
||||||
|
mailUser = mail_config.vmail_config.user_group_name;
|
||||||
|
mailLocation = dovecotMaildir;
|
||||||
|
sslServerCert = mail_config.ssl_config.cert;
|
||||||
|
sslServerKey = mail_config.ssl_config.key;
|
||||||
|
enableLmtp = true;
|
||||||
|
modules = [ pkgs.dovecot_pigeonhole ];
|
||||||
|
protocols = [ "sieve" ];
|
||||||
|
|
||||||
|
sieveScripts = {
|
||||||
|
after = builtins.toFile "spam.sieve" ''
|
||||||
|
require "fileinto";
|
||||||
|
|
||||||
|
if header :is "X-Spam" "Yes" {
|
||||||
|
fileinto "Junk";
|
||||||
|
stop;
|
||||||
|
}
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
mailboxes = {
|
||||||
|
Trash = {
|
||||||
|
auto = "no";
|
||||||
|
specialUse = "Trash";
|
||||||
|
};
|
||||||
|
Junk = {
|
||||||
|
auto = "subscribe";
|
||||||
|
specialUse = "Junk";
|
||||||
|
};
|
||||||
|
Drafts = {
|
||||||
|
auto = "subscribe";
|
||||||
|
specialUse = "Drafts";
|
||||||
|
};
|
||||||
|
Sent = {
|
||||||
|
auto = "subscribe";
|
||||||
|
specialUse = "Sent";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
extraConfig = ''
|
||||||
|
${lib.optionalString mail_config.debug_mode ''
|
||||||
|
mail_debug = yes
|
||||||
|
auth_debug = yes
|
||||||
|
verbose_ssl = yes
|
||||||
|
''}
|
||||||
|
|
||||||
|
service imap-login {
|
||||||
|
inet_listener imap {
|
||||||
|
port = 143
|
||||||
|
}
|
||||||
|
inet_listener imaps {
|
||||||
|
port = 993
|
||||||
|
ssl = yes
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protocol imap {
|
||||||
|
mail_max_userip_connections = 100
|
||||||
|
mail_plugins = $mail_plugins imap_sieve
|
||||||
|
}
|
||||||
|
|
||||||
|
mail_access_groups = "${mail_config.vmail_config.user_group_name}"
|
||||||
|
ssl = required
|
||||||
|
ssl_min_protocol = TLSv1.2
|
||||||
|
ssl_prefer_server_ciphers = yes
|
||||||
|
|
||||||
|
service lmtp {
|
||||||
|
unix_listener dovecot-lmtp {
|
||||||
|
group = ${postfixCfg.group}
|
||||||
|
mode = 0600
|
||||||
|
user = ${postfixCfg.user}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
recipient_delimiter = "+"
|
||||||
|
lmtp_save_to_detail_mailbox = "no"
|
||||||
|
|
||||||
|
protocol lmtp {
|
||||||
|
mail_plugins = $mail_plugins sieve
|
||||||
|
}
|
||||||
|
|
||||||
|
passdb {
|
||||||
|
driver = passwd-file
|
||||||
|
args = ${passwdFile}
|
||||||
|
}
|
||||||
|
|
||||||
|
userdb {
|
||||||
|
driver = passwd-file
|
||||||
|
args = ${passwdFile}
|
||||||
|
}
|
||||||
|
|
||||||
|
service auth {
|
||||||
|
unix_listener auth {
|
||||||
|
mode = 0660
|
||||||
|
user = ${postfixCfg.user}
|
||||||
|
group = ${postfixCfg.group}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
auth_mechanisms = plain login
|
||||||
|
|
||||||
|
namespace inbox {
|
||||||
|
separator = "."
|
||||||
|
inbox = yes
|
||||||
|
}
|
||||||
|
|
||||||
|
plugin {
|
||||||
|
sieve = file:${mail_config.sieve_directory}/%u/scripts;active=${mail_config.sieve_directory}/%u/active.sieve
|
||||||
|
sieve_default = file:${mail_config.sieve_directory}/%u/default.sieve
|
||||||
|
sieve_default_name = default
|
||||||
|
|
||||||
|
sieve_global_extensions = +vnd.dovecot.environment
|
||||||
|
}
|
||||||
|
lda_mailbox_autosubscribe = yes
|
||||||
|
lda_mailbox_autocreate = yes
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
systemd.services.dovecot2 = {
|
||||||
|
preStart = ''
|
||||||
|
${genPasswdScript}
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
systemd.services.postfix.restartTriggers = [ genPasswdScript ];
|
||||||
|
}
|
18
hosts/hetzner-vm/services/mailserver/firewall.nix
Normal file
18
hosts/hetzner-vm/services/mailserver/firewall.nix
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
{ ... }: {
|
||||||
|
networking.firewall = {
|
||||||
|
allowedTCPPorts = [
|
||||||
|
# SMTP
|
||||||
|
25
|
||||||
|
# Submission
|
||||||
|
587
|
||||||
|
# Submission w/ SSL
|
||||||
|
465
|
||||||
|
# IMAP
|
||||||
|
143
|
||||||
|
# IMAP w/ SSL
|
||||||
|
993
|
||||||
|
# Sieve
|
||||||
|
4190
|
||||||
|
];
|
||||||
|
};
|
||||||
|
}
|
193
hosts/hetzner-vm/services/mailserver/postfix.nix
Normal file
193
hosts/hetzner-vm/services/mailserver/postfix.nix
Normal file
|
@ -0,0 +1,193 @@
|
||||||
|
{ config, pkgs, lib, ... }:
|
||||||
|
|
||||||
|
let
|
||||||
|
mail_config = (import ./config.nix { });
|
||||||
|
submissionHeaderCleanupRules =
|
||||||
|
pkgs.writeText "submission_header_cleanup_rules" (''
|
||||||
|
/^Received:/ IGNORE
|
||||||
|
/^X-Originating-IP:/ IGNORE
|
||||||
|
/^X-Mailer:/ IGNORE
|
||||||
|
/^User-Agent:/ IGNORE
|
||||||
|
/^X-Enigmail:/ IGNORE
|
||||||
|
/^Message-ID:\s+<(.*?)@.*?>/ REPLACE Message-ID: <$1@${mail_config.fqdn}>
|
||||||
|
'');
|
||||||
|
|
||||||
|
inetSocket = addr: port: "inet:[${toString port}@${addr}]";
|
||||||
|
unixSocket = sock: "unix:${sock}";
|
||||||
|
|
||||||
|
|
||||||
|
# Merge several lookup tables. A lookup table is a attribute set where
|
||||||
|
# - the key is an address (user@example.com) or a domain (@example.com)
|
||||||
|
# - the value is a list of addresses
|
||||||
|
mergeLookupTables = tables: lib.zipAttrsWith (n: v: lib.flatten v) tables;
|
||||||
|
|
||||||
|
# valiases_postfix :: Map String [String]
|
||||||
|
valiases_postfix = mergeLookupTables (lib.flatten (lib.mapAttrsToList
|
||||||
|
(name: value:
|
||||||
|
let to = name;
|
||||||
|
in map (from: {"${from}" = to;}) (value.aliases ++ lib.singleton name))
|
||||||
|
mail_config.accounts));
|
||||||
|
|
||||||
|
# all_valiases_postfix :: Map String [String]
|
||||||
|
all_valiases_postfix = mergeLookupTables [valiases_postfix];
|
||||||
|
|
||||||
|
# attrsToLookupTable :: Map String (Either String [ String ]) -> Map String [String]
|
||||||
|
attrsToLookupTable = aliases: let
|
||||||
|
lookupTables = lib.mapAttrsToList (from: to: {"${from}" = to;}) aliases;
|
||||||
|
in mergeLookupTables lookupTables;
|
||||||
|
|
||||||
|
# lookupTableToString :: Map String [String] -> String
|
||||||
|
lookupTableToString = attrs: let
|
||||||
|
valueToString = value: lib.concatStringsSep ", " value;
|
||||||
|
in lib.concatStringsSep "\n" (lib.mapAttrsToList (name: value: "${name} ${valueToString value}") attrs);
|
||||||
|
|
||||||
|
# valiases_file :: Path
|
||||||
|
valiases_file = let
|
||||||
|
content = lookupTableToString (mergeLookupTables [all_valiases_postfix]);
|
||||||
|
in builtins.toFile "valias" content;
|
||||||
|
|
||||||
|
# vhosts_file :: Path
|
||||||
|
vhosts_file = builtins.toFile "vhosts" (lib.concatStringsSep "\n" mail_config.domains);
|
||||||
|
vaccounts_file = builtins.toFile "vaccounts" (lookupTableToString all_valiases_postfix);
|
||||||
|
|
||||||
|
smtpdMilters = [
|
||||||
|
# "unix:/run/opendkim/opendkim.sock"
|
||||||
|
];
|
||||||
|
|
||||||
|
mappedFile = name: "hash:/var/lib/postfix/conf/${name}";
|
||||||
|
|
||||||
|
submissionOptions = {
|
||||||
|
smtpd_tls_security_level = "encrypt";
|
||||||
|
smtpd_sasl_auth_enable = "yes";
|
||||||
|
smtpd_sasl_type = "dovecot";
|
||||||
|
smtpd_sasl_path = "/run/dovecot2/auth";
|
||||||
|
smtpd_sasl_security_options = "noanonymous";
|
||||||
|
smtpd_sasl_local_domain = "$myhostname";
|
||||||
|
smtpd_client_restrictions = "permit_sasl_authenticated,reject";
|
||||||
|
smtpd_sender_login_maps = "hash:/etc/postfix/vaccounts";
|
||||||
|
smtpd_sender_restrictions = "reject_sender_login_mismatch";
|
||||||
|
smtpd_recipient_restrictions =
|
||||||
|
"reject_non_fqdn_recipient,reject_unknown_recipient_domain,permit_sasl_authenticated,reject";
|
||||||
|
cleanup_service_name = "submission-header-cleanup";
|
||||||
|
};
|
||||||
|
in {
|
||||||
|
|
||||||
|
services.postfix = {
|
||||||
|
enable = true;
|
||||||
|
hostname = "${mail_config.fqdn}";
|
||||||
|
networksStyle = "host";
|
||||||
|
mapFiles."valias" = valiases_file;
|
||||||
|
mapFiles."vaccounts" = vaccounts_file;
|
||||||
|
sslCert = mail_config.ssl_config.cert;
|
||||||
|
sslKey = mail_config.ssl_config.key;
|
||||||
|
enableSubmission = true;
|
||||||
|
enableSubmissions = true;
|
||||||
|
#virtual = lookupTableToString
|
||||||
|
# (mergeLookupTables [ all_valiases_postfix catchAllPostfix forwards ]);
|
||||||
|
|
||||||
|
config = {
|
||||||
|
# Extra Config
|
||||||
|
mydestination = "";
|
||||||
|
recipient_delimiter = "+";
|
||||||
|
smtpd_banner = "${mail_config.fqdn} ESMTP NO UCE";
|
||||||
|
disable_vrfy_command = true;
|
||||||
|
message_size_limit = "20971520";
|
||||||
|
|
||||||
|
virtual_uid_maps =
|
||||||
|
"static:${toString mail_config.vmail_config.user_group_id}";
|
||||||
|
virtual_gid_maps =
|
||||||
|
"static:${toString mail_config.vmail_config.user_group_id}";
|
||||||
|
virtual_mailbox_base = "${mail_config.vmail_config.directory}";
|
||||||
|
virtual_mailbox_domains = vhosts_file;
|
||||||
|
virtual_mailbox_maps = mappedFile "valias";
|
||||||
|
virtual_transport = "lmtp:unix:/run/dovecot2/dovecot-lmtp";
|
||||||
|
lmtp_destination_recipient_limit = "1";
|
||||||
|
|
||||||
|
smtpd_sasl_type = "dovecot";
|
||||||
|
smtpd_sasl_path = "/run/dovecot2/auth";
|
||||||
|
smtpd_sasl_auth_enable = true;
|
||||||
|
smtpd_relay_restrictions = [
|
||||||
|
"permit_mynetworks"
|
||||||
|
"permit_sasl_authenticated"
|
||||||
|
"reject_unauth_destination"
|
||||||
|
];
|
||||||
|
|
||||||
|
#policy-spf_time_limit = "3600s";
|
||||||
|
|
||||||
|
# reject selected senders
|
||||||
|
#smtpd_sender_restrictions =
|
||||||
|
# [ "check_sender_access ${mappedFile "reject_senders"}" ];
|
||||||
|
|
||||||
|
# quota and spf checking
|
||||||
|
#smtpd_recipient_restrictions = [
|
||||||
|
#"check_recipient_access ${mappedFile "denied_recipients"}"
|
||||||
|
#"check_recipient_access ${mappedFile "reject_recipients"}"
|
||||||
|
#"check_policy_service unix:private/policy-spf"
|
||||||
|
#];
|
||||||
|
|
||||||
|
# TLS settings, inspired by https://github.com/jeaye/nix-files
|
||||||
|
# Submission by mail clients is handled in submissionOptions
|
||||||
|
smtpd_tls_security_level = "may";
|
||||||
|
|
||||||
|
# strong might suffice and is computationally less expensive
|
||||||
|
smtpd_tls_eecdh_grade = "ultra";
|
||||||
|
|
||||||
|
# Disable obselete protocols
|
||||||
|
smtpd_tls_protocols = "TLSv1.3, TLSv1.2, TLSv1.1, !TLSv1, !SSLv2, !SSLv3";
|
||||||
|
smtp_tls_protocols = "TLSv1.3, TLSv1.2, TLSv1.1, !TLSv1, !SSLv2, !SSLv3";
|
||||||
|
smtpd_tls_mandatory_protocols =
|
||||||
|
"TLSv1.3, TLSv1.2, TLSv1.1, !TLSv1, !SSLv2, !SSLv3";
|
||||||
|
smtp_tls_mandatory_protocols =
|
||||||
|
"TLSv1.3, TLSv1.2, TLSv1.1, !TLSv1, !SSLv2, !SSLv3";
|
||||||
|
|
||||||
|
smtp_tls_ciphers = "high";
|
||||||
|
smtpd_tls_ciphers = "high";
|
||||||
|
smtp_tls_mandatory_ciphers = "high";
|
||||||
|
smtpd_tls_mandatory_ciphers = "high";
|
||||||
|
|
||||||
|
# Disable deprecated ciphers
|
||||||
|
smtpd_tls_mandatory_exclude_ciphers =
|
||||||
|
"MD5, DES, ADH, RC4, PSD, SRP, 3DES, eNULL, aNULL";
|
||||||
|
smtpd_tls_exclude_ciphers =
|
||||||
|
"MD5, DES, ADH, RC4, PSD, SRP, 3DES, eNULL, aNULL";
|
||||||
|
smtp_tls_mandatory_exclude_ciphers =
|
||||||
|
"MD5, DES, ADH, RC4, PSD, SRP, 3DES, eNULL, aNULL";
|
||||||
|
smtp_tls_exclude_ciphers =
|
||||||
|
"MD5, DES, ADH, RC4, PSD, SRP, 3DES, eNULL, aNULL";
|
||||||
|
|
||||||
|
tls_preempt_cipherlist = true;
|
||||||
|
|
||||||
|
smtpd_tls_auth_only = true;
|
||||||
|
smtpd_tls_loglevel = "1";
|
||||||
|
|
||||||
|
# Configure a non blocking source of randomness
|
||||||
|
tls_random_source = "dev:/dev/urandom";
|
||||||
|
|
||||||
|
smtpd_milters = smtpdMilters;
|
||||||
|
#non_smtpd_milters = [ "unix:/run/opendkim/opendkim.sock" ];
|
||||||
|
milter_protocol = "6";
|
||||||
|
milter_mail_macros =
|
||||||
|
"i {mail_addr} {client_addr} {client_name} {auth_type} {auth_authen} {auth_author} {mail_addr} {mail_host} {mail_mailer}";
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
submissionOptions = submissionOptions;
|
||||||
|
submissionsOptions = submissionOptions;
|
||||||
|
|
||||||
|
masterConfig = {
|
||||||
|
"lmtp" = {
|
||||||
|
# Add headers when delivering, see http://www.postfix.org/smtp.8.html
|
||||||
|
# D => Delivered-To, O => X-Original-To, R => Return-Path
|
||||||
|
args = [ "flags=O" ];
|
||||||
|
};
|
||||||
|
"submission-header-cleanup" = {
|
||||||
|
type = "unix";
|
||||||
|
private = false;
|
||||||
|
chroot = false;
|
||||||
|
maxproc = 0;
|
||||||
|
command = "cleanup";
|
||||||
|
args = [ "-o" "header_checks=pcre:${submissionHeaderCleanupRules}" ];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
20
hosts/hetzner-vm/services/mailserver/ssl.nix
Normal file
20
hosts/hetzner-vm/services/mailserver/ssl.nix
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
{ pkgs, ... }:
|
||||||
|
let mail_config = (import ./config.nix { });
|
||||||
|
acmeRoot = "/var/lib/acme/acme-challenge";
|
||||||
|
|
||||||
|
in {
|
||||||
|
services.nginx = {
|
||||||
|
enable = true;
|
||||||
|
virtualHosts."${mail_config.fqdn}" = {
|
||||||
|
serverName = mail_config.fqdn;
|
||||||
|
serverAliases = mail_config.domains;
|
||||||
|
forceSSL = false;
|
||||||
|
enableACME = true;
|
||||||
|
acmeRoot=acmeRoot;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
security.acme.certs."${mail_config.fqdn}" = {
|
||||||
|
reloadServices = [ "postfix.service" "dovecot2.service" ];
|
||||||
|
};
|
||||||
|
}
|
15
hosts/hetzner-vm/services/mailserver/vmail.nix
Normal file
15
hosts/hetzner-vm/services/mailserver/vmail.nix
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
{ config, pkgs, lib, ... }: let
|
||||||
|
config = (import ./config.nix {});
|
||||||
|
|
||||||
|
v = config.vmail_config;
|
||||||
|
in {
|
||||||
|
users.users."${v.user_group_name}" = {
|
||||||
|
name = "${v.user_group_name}";
|
||||||
|
isSystemUser = true;
|
||||||
|
uid = v.user_group_id;
|
||||||
|
home = v.directory;
|
||||||
|
createHome = true;
|
||||||
|
group = "${v.user_group_name}";
|
||||||
|
};
|
||||||
|
users.groups."${v.user_group_name}" = { gid = v.user_group_id; };
|
||||||
|
}
|
|
@ -15,19 +15,5 @@
|
||||||
};
|
};
|
||||||
|
|
||||||
services.nginx.logError = "/var/log/nginx/debug.log debug";
|
services.nginx.logError = "/var/log/nginx/debug.log debug";
|
||||||
|
|
||||||
services.nginx.upstreams.chaos-github-vaultui = {
|
|
||||||
servers = { "chaoticryptidz.gitlab.io" = {}; };
|
|
||||||
};
|
|
||||||
|
|
||||||
services.nginx.virtualHosts."vaultui.owo.monster" = {
|
|
||||||
forceSSL = true;
|
|
||||||
enableACME = true;
|
|
||||||
locations = {
|
|
||||||
"~ ^/(.*)" = {
|
|
||||||
proxyPass = "http://chaos-github-vaultui/VaultUI/$1";
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
#networking.firewall.allowedTCPPorts = [ 8200 ];
|
#networking.firewall.allowedTCPPorts = [ 8200 ];
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,7 +5,7 @@ in {
|
||||||
users.root
|
users.root
|
||||||
users.chaos
|
users.chaos
|
||||||
profiles.tailscale
|
profiles.tailscale
|
||||||
profiles.dnscrypt
|
#profiles.dnscrypt
|
||||||
#profiles.printing
|
#profiles.printing
|
||||||
profiles.sshd
|
profiles.sshd
|
||||||
|
|
||||||
|
@ -36,13 +36,16 @@ in {
|
||||||
# For cross compiling and deploying to raspberry
|
# For cross compiling and deploying to raspberry
|
||||||
profiles.cross.arm64
|
profiles.cross.arm64
|
||||||
|
|
||||||
#profiles.force_dns
|
profiles.force_dns
|
||||||
#extras.shenanigans-hotspot
|
#extras.shenanigans-hotspot
|
||||||
];
|
];
|
||||||
|
|
||||||
services.mullvad-vpn.enable = true;
|
services.mullvad-vpn.enable = true;
|
||||||
|
|
||||||
home-manager.users.root = { imports = with tree; [ home.base ]; };
|
home-manager.users.root = {
|
||||||
|
imports = with tree; [ home.base ];
|
||||||
|
home.stateVersion = "22.05";
|
||||||
|
};
|
||||||
home-manager.users.chaos = {
|
home-manager.users.chaos = {
|
||||||
programs.ssh.matchBlocks."*".identityFile = "${usb_data.ssh_priv_path}";
|
programs.ssh.matchBlocks."*".identityFile = "${usb_data.ssh_priv_path}";
|
||||||
programs.git.extraConfig = {
|
programs.git.extraConfig = {
|
||||||
|
@ -85,6 +88,7 @@ in {
|
||||||
home.programming.languages.go
|
home.programming.languages.go
|
||||||
home.programming.languages.nix
|
home.programming.languages.nix
|
||||||
];
|
];
|
||||||
|
home.stateVersion = "22.05";
|
||||||
};
|
};
|
||||||
|
|
||||||
hardware.opengl.extraPackages = with pkgs; [
|
hardware.opengl.extraPackages = with pkgs; [
|
||||||
|
@ -124,6 +128,7 @@ in {
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
|
networking.enableIPv6 = true;
|
||||||
systemd.services.NetworkManager-wait-online.enable = false;
|
systemd.services.NetworkManager-wait-online.enable = false;
|
||||||
|
|
||||||
# let vscode, vivaldi, etc work.
|
# let vscode, vivaldi, etc work.
|
||||||
|
|
|
@ -8,6 +8,19 @@ final: prev: {
|
||||||
lsquic = final.callPackage ./invidious-latest/lsquic.nix { };
|
lsquic = final.callPackage ./invidious-latest/lsquic.nix { };
|
||||||
};
|
};
|
||||||
|
|
||||||
|
linuxPackages = prev.linuxPackages.extend (lpFinal: lpPrev: {
|
||||||
|
rtl8812au = lpPrev.linuxPackages.rtl8812au.overrideAttrs (oldAttrs: {
|
||||||
|
version = "8821au-20210708";
|
||||||
|
src = lpPrev.pkgs.fetchFromGitHub {
|
||||||
|
owner = "morrownr";
|
||||||
|
repo = "8821au-20210708";
|
||||||
|
rev = "6a0f4752cab464e1d895c9d8091d36e720f4ed18";
|
||||||
|
|
||||||
|
sha256 = prev.pkgs.lib.fakeSha256;
|
||||||
|
};
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
roc-toolkit-patched = final.callPackage ./roc-toolkit-patched { };
|
roc-toolkit-patched = final.callPackage ./roc-toolkit-patched { };
|
||||||
roc-send-pcm = final.callPackage ./roc-send-pcm { };
|
roc-send-pcm = final.callPackage ./roc-send-pcm { };
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,2 +1,4 @@
|
||||||
{ config, ... }: { services.localtime.enable = true; }
|
{ config, ... }: {
|
||||||
|
#services.localtimed.enable = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue