Add sops, and liam mailserver config

This commit is contained in:
Shelvacu
2024-02-23 14:16:13 -08:00
parent c531c43b9a
commit d301ca0bb1
16 changed files with 722 additions and 126 deletions

1
.gitignore vendored
View File

@@ -1 +1,2 @@
/result
.nixos-test-history

21
.sops.yaml Normal file
View File

@@ -0,0 +1,21 @@
shel_keys: &shel_keys
- &pixel-termux age1y4zp4ddq6xyffd8fgmn2jkl78qfh4m94gcls2cu6vvjnwwznx5uqywjekm
- &t460s age1g9sh8u6s344569d3cg8h30g9h7thld5pexcwzc4549jc84jvceqqjt9cfh
- &pixel-nix age1t5s3txyj403rfecdhq5q2z3cnavy6m543gzyhkl2nu5t8fz0zctqtvm2tj
- &compute-deck-user age1dzdf4rgep3ctk3dnrmrqtdgrchaa8nszfc4dp29gqwsst3z6jyrq57vfsj
machine_host_keys:
- &trip age10lv32k2guszr5y69sez3z5xj92wzmdxvfejd6hm8xr0pmclw2cvq0hk6pe
- &compute-deck-host ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGt43GmXCxkl5QjgPQ/QimW11lKfXmV4GFWvlxQSf4TQ
- &liam age1hkve3khk7fthyrwxjqdf4r37lrqpmnkz6mke7psuphvu2ykynqaq9g6ja5
creation_rules:
- path_regex: secrets/[^/]+$
key_groups:
- age: *shel_keys
- path_regex: ^secrets/liam/
key_groups:
- age:
- *pixel-termux
- *t460s
- *pixel-nix
- *compute-deck-user
- *liam

View File

@@ -1,111 +1,146 @@
{ config, pkgs, lib, inputs,... }:
{
time.timeZone = lib.mkDefault "America/Los_Angeles";
i18n.defaultLocale = lib.mkDefault "en_US.UTF-8";
console = {
font = lib.mkDefault "Lat2-Terminus16";
keyMap = lib.mkDefault "us";
{ config, pkgs, lib, inputs, utils, ... }: {
options.vacu.isDroid = lib.mkOption {
default = false;
type = lib.types.bool;
};
environment.systemPackages = (import ./common-packages.nix pkgs) ++ [
inputs.nix-search-cli.packages.${pkgs.system}.default
] ++ (if config.services.xserver.enable then [ pkgs.xorg.xev ] else []);
users.users.shelvacu = {
openssh.authorizedKeys.keys = [
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIC4LYvUe9dsQb9OaTDFI4QKPtMmOHOGLwWsXsEmcJW86 u0_a132@localhost"
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIHcYwYy9/0Gu/GsqS72Nkz6OkId+zevqXA/aTIcvqflp shelvacu@DESKTOP-DG7QKO2"
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIFsErA6M9LSHj2hPlLuHD8Lpei7WjMup1JxI1vxA6B8W nix-on-droid@localhost"
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIKoy1TrmfhBGWtVedgOM1FB1oD2UdodN3LkBnnLx6Tug shelvacu@compute-deck"
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAICVeSzDkGTueZijB0xUa08e06ovAEwwZK/D+Cc7bo91g shelvacu@triple-dezert"
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOtwtao/TXbiuQOYJbousRPVesVcb/2nP0PCFUec0Nv8 root@triple-dezert"
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIAxAFFxQMXAgi+0cmGaNE/eAkVfEl91wafUqFIuAkI5I root@compute-deck"
];
isNormalUser = true;
extraGroups = [ "wheel" ];
options.vacu.acmeCertDependencies = lib.mkOption {
default = {};
example = ''
vacu.acmeCertDependencies."mail.example.com" = [ "postfix.service" ];
'';
type = lib.types.attrsOf (lib.types.listOf utils.systemdUtils.lib.unitNameType);
};
config = let
for-systemd-services = lib.concatMapAttrs
(cert: units:
{
"acme-selfsigned-${cert}" = {
wantedBy = units;
before = units;
};
}
)
config.vacu.acmeCertDependencies;
for-security-acme-certs = lib.concatMapAttrs
(cert: units:
{
${cert}.reloadServices = units;
}
)
config.vacu.acmeCertDependencies;
in {
systemd.services = for-systemd-services;
security.acme.certs = for-security-acme-certs;
time.timeZone = lib.mkDefault "America/Los_Angeles";
nix.settings.trusted-users = [ "shelvacu" ];
security.sudo.wheelNeedsPassword = lib.mkDefault false;
programs.screen.screenrc = ''
defscrollback 10000
termcapinfo xterm* ti@:te@
'';
programs.tmux.enable = true;
programs.tmux.extraConfig = "setw mouse";
programs.tmux.clock24 = true;
nix.settings = {
experimental-features = [ "nix-command" "flakes" ];
# substituters = [ "https://nixcache.shelvacu.com" "https://cache.nixos.org/"];
# trusted-substituters = [ "https://nixcache.shelvacu.com" ];
# trusted-public-keys = [ "nixcache.shelvacu.com:73u5ZGBpPRoVZfgNJQKYYBt9K9Io/jPwgUfuOLsJbsM=" ];
};
nixpkgs.config.allowUnfree = lib.mkDefault true;
programs.mosh.enable = lib.mkDefault true;
programs.ssh.extraConfig = ''
Host deckvacu
User deck
Host rsb
User user
HostName finaltask.xyz
Port 2222
Host awoo
HostName 45.142.157.71
Host trip
HostName trip.shelvacu.com
Port 6922
'';
programs.ssh.knownHosts = {
#public hosts
"github.com".publicKey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOMqqnkVzrm0SdG6UOoqKLsabgH5C9okWi0dh2l9GKJl";
"gitlab.com".publicKey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIAfuCHKVTjquxvt6CM6tdG4SLp1Btn/nOeHHE5UOzRdf";
#colin's stuff
"uninsane.org" = {
extraHostNames = [ "git.uninsane.org" ];
publicKey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOfdSmFkrVT6DhpgvFeQKm3Fh9VKZ9DbLYOPOJWYQ0E8";
};
"desko" = {
publicKey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIFw9NoRaYrM6LbDd3aFBc4yyBlxGQn8HjeHd/dZ3CfHk";
i18n.defaultLocale = lib.mkDefault "en_US.UTF-8";
console = {
font = lib.mkDefault "Lat2-Terminus16";
keyMap = lib.mkDefault "us";
};
#personal hosts
trip = {
extraHostNames = [ "triple-dezert" "trip.shelvacu.com" "[trip.shelvacu.com]:6922" ];
publicKey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGUQux9V0mSF5IauoO1z311NXR7ymEbwRMzT+OaaNQr+";
environment.systemPackages = (import ./common-packages.nix pkgs) ++ [
inputs.nix-search-cli.packages.${pkgs.system}.default
] ++ (if config.services.xserver.enable then [ pkgs.xorg.xev ] else []);
users.users.shelvacu = {
openssh.authorizedKeys.keys = [
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIC4LYvUe9dsQb9OaTDFI4QKPtMmOHOGLwWsXsEmcJW86 u0_a132@localhost"
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIHcYwYy9/0Gu/GsqS72Nkz6OkId+zevqXA/aTIcvqflp shelvacu@DESKTOP-DG7QKO2"
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIFsErA6M9LSHj2hPlLuHD8Lpei7WjMup1JxI1vxA6B8W nix-on-droid@localhost"
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIKoy1TrmfhBGWtVedgOM1FB1oD2UdodN3LkBnnLx6Tug shelvacu@compute-deck"
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAICVeSzDkGTueZijB0xUa08e06ovAEwwZK/D+Cc7bo91g shelvacu@triple-dezert"
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOtwtao/TXbiuQOYJbousRPVesVcb/2nP0PCFUec0Nv8 root@triple-dezert"
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIAxAFFxQMXAgi+0cmGaNE/eAkVfEl91wafUqFIuAkI5I root@compute-deck"
];
isNormalUser = true;
extraGroups = [ "wheel" ];
};
servacu = {
extraHostNames = [ "mail.dis8.net" "servacu.shelvacu.com" ];
publicKey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIE+E6na7np0HnBV2X7owno+Fg+bNNRSHLxO6n1JzdUTV";
nix.settings.trusted-users = [ "shelvacu" ];
security.sudo.wheelNeedsPassword = lib.mkDefault false;
programs.screen.screenrc = ''
defscrollback 10000
termcapinfo xterm* ti@:te@
'';
programs.tmux.enable = true;
programs.tmux.extraConfig = "setw mouse";
programs.tmux.clock24 = true;
nix.settings = {
experimental-features = [ "nix-command" "flakes" ];
# substituters = [ "https://nixcache.shelvacu.com" "https://cache.nixos.org/"];
# trusted-substituters = [ "https://nixcache.shelvacu.com" ];
# trusted-public-keys = [ "nixcache.shelvacu.com:73u5ZGBpPRoVZfgNJQKYYBt9K9Io/jPwgUfuOLsJbsM=" ];
};
finaltask = {
extraHostNames = [ "rsb" "finaltask.xyz" "[finaltask.xyz]:2222" ];
publicKey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPTx8WBNNKBVRV98HgDChpd59SHbreJ87SXU+zOKan6y";
};
compute-deck = {
publicKey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGt43GmXCxkl5QjgPQ/QimW11lKfXmV4GFWvlxQSf4TQ";
};
"2esrever" = {
extraHostNames = [ "10.4.5.218" "10.244.46.71" ];
publicKey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIH0LnPrJxAdffZ//uRe3NBiIfFCBNMLqKVylkyU0llvT";
};
awoo = {
extraHostNames = [ "45.142.157.71" ];
publicKey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOQaDjjfSK8jnk9aFIiYH9LZO4nLY/oeAc7BKIPUXMh1";
};
deckvacu = {
publicKey = "ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBEa8qpFkIlLLJkH8rmEAn6/MZ9ilCGmEQWC3CeFae7r1kOqfwRk0nq0oyOGJ50uIh+PpwEh3rbgq6mLfpRfsFmM=";
nixpkgs.config.allowUnfree = lib.mkDefault true;
programs.mosh.enable = lib.mkDefault true;
programs.ssh.extraConfig = ''
Host deckvacu
User deck
Host rsb
User user
HostName finaltask.xyz
Port 2222
Host awoo
HostName 45.142.157.71
Host trip
HostName trip.shelvacu.com
Port 6922
'';
programs.ssh.knownHosts = {
#public hosts
"github.com".publicKey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOMqqnkVzrm0SdG6UOoqKLsabgH5C9okWi0dh2l9GKJl";
"gitlab.com".publicKey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIAfuCHKVTjquxvt6CM6tdG4SLp1Btn/nOeHHE5UOzRdf";
#colin's stuff
"uninsane.org" = {
extraHostNames = [ "git.uninsane.org" ];
publicKey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOfdSmFkrVT6DhpgvFeQKm3Fh9VKZ9DbLYOPOJWYQ0E8";
};
"desko" = {
publicKey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIFw9NoRaYrM6LbDd3aFBc4yyBlxGQn8HjeHd/dZ3CfHk";
};
#personal hosts
trip = {
extraHostNames = [ "triple-dezert" "trip.shelvacu.com" "[trip.shelvacu.com]:6922" ];
publicKey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGUQux9V0mSF5IauoO1z311NXR7ymEbwRMzT+OaaNQr+";
};
servacu = {
extraHostNames = [ "mail.dis8.net" "servacu.shelvacu.com" ];
publicKey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIE+E6na7np0HnBV2X7owno+Fg+bNNRSHLxO6n1JzdUTV";
};
finaltask = {
extraHostNames = [ "rsb" "finaltask.xyz" "[finaltask.xyz]:2222" ];
publicKey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPTx8WBNNKBVRV98HgDChpd59SHbreJ87SXU+zOKan6y";
};
compute-deck = {
publicKey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGt43GmXCxkl5QjgPQ/QimW11lKfXmV4GFWvlxQSf4TQ";
};
"2esrever" = {
extraHostNames = [ "10.4.5.218" "10.244.46.71" ];
publicKey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIH0LnPrJxAdffZ//uRe3NBiIfFCBNMLqKVylkyU0llvT";
};
awoo = {
extraHostNames = [ "45.142.157.71" ];
publicKey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOQaDjjfSK8jnk9aFIiYH9LZO4nLY/oeAc7BKIPUXMh1";
};
deckvacu = {
publicKey = "ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBEa8qpFkIlLLJkH8rmEAn6/MZ9ilCGmEQWC3CeFae7r1kOqfwRk0nq0oyOGJ50uIh+PpwEh3rbgq6mLfpRfsFmM=";
};
liam = {
extraHostNames = [ "liam.dis8.net" ];
publicKey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIHOqJYVHOIFmEA5uRbbirIupWvyBLAFwic/8EZQRdN/c";
};
};
};
}
}

View File

@@ -28,4 +28,6 @@ pkgs: (with pkgs; [
tree
rclone
iputils
ssh-to-age
sops
])

70
flake.lock generated
View File

@@ -7,11 +7,11 @@
]
},
"locked": {
"lastModified": 1708221730,
"narHash": "sha256-vyx6tsnDGX4bNegiF1kREHMRDH7hy+q1m56V8JYHWLI=",
"lastModified": 1709286488,
"narHash": "sha256-RDpTZ72zLu05djvXRzK76Ysqp9zSdh84ax/edEaJucs=",
"owner": "nix-community",
"repo": "disko",
"rev": "d8a4377cd8eec23668ea3fae07efee9d5782cb91",
"rev": "bde7dd352c07d43bd5b8245e6c39074a391fdd46",
"type": "github"
},
"original": {
@@ -123,11 +123,11 @@
]
},
"locked": {
"lastModified": 1708294481,
"narHash": "sha256-DZtxmeb4OR7iCaKUUuq05ADV2rX8WReZEF7Tq//W0+Y=",
"lastModified": 1709204054,
"narHash": "sha256-U1idK0JHs1XOfSI1APYuXi4AEADf+B+ZU4Wifc0pBHk=",
"owner": "nix-community",
"repo": "home-manager",
"rev": "a54e05bc12d88ff2df941d0dc1183cb5235fa438",
"rev": "2f3367769a93b226c467551315e9e270c3f78b15",
"type": "github"
},
"original": {
@@ -145,11 +145,11 @@
]
},
"locked": {
"lastModified": 1708148641,
"narHash": "sha256-UliMMpx84PPA+0mcyBj2yuKNawOX7L4u/wO8j9QGHVk=",
"lastModified": 1709295149,
"narHash": "sha256-+blV8vKyvh3gYnUFYTOu2yuWxEEBqwS7hfLm6qdpoe4=",
"owner": "Jovian-Experiments",
"repo": "Jovian-NixOS",
"rev": "f318a856ff3177fdea6044d6c77dd49992330a31",
"rev": "0ef51034dcc8b65b8be72eedd0d5db7d426ea054",
"type": "github"
},
"original": {
@@ -253,11 +253,11 @@
},
"nixpkgs": {
"locked": {
"lastModified": 1708210246,
"narHash": "sha256-Q8L9XwrBK53fbuuIFMbjKvoV7ixfLFKLw4yV+SD28Y8=",
"lastModified": 1709315803,
"narHash": "sha256-/hHKlXR/w2Q1CgNfMPlbu68/0kGXG6py08hzhWuA5jI=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "69405156cffbdf2be50153f13cbdf9a0bea38e49",
"rev": "01c6ad6d0b29988f30b8526b64775e02aba126d0",
"type": "github"
},
"original": {
@@ -282,13 +282,29 @@
"type": "github"
}
},
"nixpkgs-unstable": {
"nixpkgs-stable": {
"locked": {
"lastModified": 1708232726,
"narHash": "sha256-DYuEHWQSBwaJkS2rjLUsKvGgDK8QIVojC3klAUw6uyk=",
"lastModified": 1708819810,
"narHash": "sha256-1KosU+ZFXf31GPeCBNxobZWMgHsSOJcrSFA6F2jhzdE=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "35c5863c29ce81199ded8a3384f4979b7793f5dc",
"rev": "89a2a12e6c8c6a56c72eb3589982c8e2f89c70ea",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "release-23.11",
"repo": "nixpkgs",
"type": "github"
}
},
"nixpkgs-unstable": {
"locked": {
"lastModified": 1709357594,
"narHash": "sha256-C6BNtZewmFbBuPgqAUa/o3pZ4nYZJkQfFB1nhQbBFEc=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "b8698cd8d62c42cf3e2b3a95224c57173b73e494",
"type": "github"
},
"original": {
@@ -375,9 +391,31 @@
"nixpkgs": "nixpkgs",
"nixpkgs-unstable": "nixpkgs-unstable",
"padtype": "padtype",
"sops-nix": "sops-nix",
"vscode-server": "vscode-server"
}
},
"sops-nix": {
"inputs": {
"nixpkgs": [
"nixpkgs"
],
"nixpkgs-stable": "nixpkgs-stable"
},
"locked": {
"lastModified": 1708987867,
"narHash": "sha256-k2lDaDWNTU5sBVHanYzjDKVDmk29RHIgdbbXu5sdzBA=",
"owner": "Mic92",
"repo": "sops-nix",
"rev": "a1c8de14f60924fafe13aea66b46157f0150f4cf",
"type": "github"
},
"original": {
"owner": "Mic92",
"repo": "sops-nix",
"type": "github"
}
},
"systems": {
"locked": {
"lastModified": 1681028828,

View File

@@ -32,6 +32,10 @@
url = "gitlab:shelvacu/padtype";
inputs.nixpkgs.follows = "nixpkgs";
};
sops-nix = {
url = "github:Mic92/sops-nix";
inputs.nixpkgs.follows = "nixpkgs";
};
};
outputs = { self, nixpkgs, nix-on-droid, ... }@inputs: {
@@ -49,15 +53,36 @@
specialArgs = { inherit inputs; };
};
nixosConfigurations.liam = nixpkgs.lib.nixosSystem {
system = "x86_64-linux";
modules = [ ./liam ];
specialArgs = { inherit inputs; };
};
nixOnDroidConfigurations.default = nix-on-droid.lib.nixOnDroidConfiguration {
modules = [ ./nix-on-droid.nix ];
};
legacyPackages.x86_64-linux = rec {
padtype = (nixpkgs.legacyPackages.x86_64-linux.extend (import ./packages)).pkgsStatic.padtype;
default = padtype;
};
diskoConfigurations.compute-deck = import ./compute-deck/partitioning.nix;
checks = nixpkgs.lib.genAttrs [ "x86_64-linux" ] (system:
let
pkgs = nixpkgs.legacyPackages.${system};
config = {
node.pkgs = pkgs;
node.pkgsReadOnly = false;
node.specialArgs.selfPackages = self.packages.${system};
node.specialArgs.inputs = inputs;
};
in
{
liam = nixpkgs.lib.nixos.runTest {
hostPkgs = pkgs;
imports = [ config ./tests/liam.nix ];
};
}
);
packages.x86_64-linux.digitalOceanImage = import ./generic-digitalocean-nixos.nix { inherit inputs; };
};
}

View File

@@ -0,0 +1,10 @@
{ inputs, system ? "x86_64-linux" }:
let
pkgs = inputs.nixpkgs.legacyPackages.${system};
config = { config, ... }: {
imports = [ "${inputs.nixpkgs}/nixos/modules/virtualisation/digital-ocean-image.nix" ];
system.stateVersion = config.system.nixos.release;
};
in
(pkgs.nixos config).digitalOceanImage

28
liam/default.nix Normal file
View File

@@ -0,0 +1,28 @@
{ modulesPath, config, lib, pkgs, inputs, ... }: {
imports = [
(modulesPath + "/installer/scan/not-detected.nix")
(modulesPath + "/virtualisation/digital-ocean-config.nix")
../common-config.nix
./nginx.nix
./sops.nix
./dovecot.nix
./mail.nix
];
networking.hostName = "liam";
networking.domain = "dis8.net";
services.openssh = {
enable = true;
# require public key authentication for better security
settings.PasswordAuthentication = false;
settings.KbdInteractiveAuthentication = false;
settings.PermitRootLogin = "prohibit-password";
};
virtualisation.digitalOcean.setSshKeys = false;
users.users.root.openssh.authorizedKeys.keys = config.users.users.shelvacu.openssh.authorizedKeys.keys;
system.stateVersion = "23.11";
}

74
liam/dovecot.nix Normal file
View File

@@ -0,0 +1,74 @@
{ config, pkgs, lib, ... }:
{
networking.firewall.allowedTCPPorts = [ 993 ];
systemd.tmpfiles.settings."whatever"."/var/lib/mail".d = {
user = config.services.dovecot2.mailUser;
group = config.services.dovecot2.mailGroup;
mode = "0700";
};
vacu.acmeCertDependencies."liam.dis8.net" = [ "dovecot2.service" ];
services.dovecot2 = {
enable = true;
sslServerKey = config.security.acme.certs."liam.dis8.net".directory + "/key.pem";
sslServerCert = config.security.acme.certs."liam.dis8.net".directory + "/full.pem";
enablePAM = false;
protocols = lib.mkForce [ "imap" "lmtp" "sieve" ];
modules = [ pkgs.dovecot_pigeonhole ];
mailUser = "vmail";
mailGroup = "vmail";
createMailUser = true;
mailLocation = "mdbox:~/mail";
extraConfig = ''
mail_home = /var/lib/mail/%n
service auth {
unix_listener /var/lib/postfix/queue/private/dovecot-auth {
group = ${config.services.postfix.group}
mode = 0660
user = ${config.services.postfix.user}
}
}
service lmtp {
unix_listener /var/lib/postfix/queue/private/dovecot-lmtp {
group = ${config.services.postfix.group}
mode = 0660
user = ${config.services.postfix.user}
}
}
protocol lmtp {
postmaster_address = postmaster@shelvacu.com
mail_plugins = $mail_plugins sieve
}
protocol imaps {
disable_plaintext_auth = yes
ssl = required
ssl_min_protocol = TLSv1.3
}
service imap-login {
inet_listener imap {
# this disables non-SSL IMAP, including STARTTLS
port = 0
}
inet_listener imaps {
port = 993
ssl = yes
}
}
userdb {
driver = passwd-file
args = username_format=%n /run/secrets/dovecot-passwd
override_fields = uid=${config.services.dovecot2.mailUser} gid=${config.services.dovecot2.mailGroup} user=%n
}
passdb {
driver = passwd-file
args = username_format=%n /run/secrets/dovecot-passwd
override_fields = user=%n
}
'';
};
}

93
liam/mail.nix Normal file
View File

@@ -0,0 +1,93 @@
{ config, lib, pkgs, ... }:
let
fqdn = config.networking.fqdn;
shel_domains = [
"shelvacu.com"
"dis8.net"
"mail.dis8.net"
"jean-luc.org"
"in.jean-luc.org"
"vacu.store"
];
julie_domains = [
"violingifts.com"
"theviolincase.com"
"shop.theviolincase.com"
];
domains = shel_domains ++ julie_domains;
dovecot_transport = "lmtp:unix:private/dovecot-lmtp";
in {
networking.firewall.allowedTCPPorts = [ 25 465 ];
vacu.acmeCertDependencies."liam.dis8.net" = [ "postfix.service" ];
services.postfix = {
enable = true;
hostname = fqdn;
# this goes into virtual_alias_maps
# "Note: for historical reasons, virtual_alias_maps apply to recipients in all domain classes, not only the virtual alias domain class."
virtual = ''
julie@shelvacu.com julie
mom@shelvacu.com julie
'' + (lib.concatMapStringsSep "\n" (d: "@${d} shelvacu") shel_domains) + "\n"
+ (lib.concatMapStringsSep "\n" (d: "@${d} julie") julie_domains);
transport = ''
shelvacu@${fqdn} ${dovecot_transport}
julie@${fqdn} ${dovecot_transport}
'';
sslKey = config.security.acme.certs."liam.dis8.net".directory + "/key.pem";
sslCert = config.security.acme.certs."liam.dis8.net".directory + "/full.pem";
postmasterAlias = "shelvacu";
rootAlias = "shelvacu";
enableSubmission = false;
enableSubmissions = true;
mapFiles.header_checks = pkgs.writeText "header-checks" (lib.concatMapStringsSep "\n" (d: "/^(from|x-original-from|return-path|mail-?from):.*@${lib.escape [ "." ] d}\\s*>?\\s*$/ REJECT") domains);
mapFiles.sender_access = pkgs.writeText "sender-access" (lib.concatMapStringsSep "\n" (d: "${d} REJECT") domains);
# verbatim appended to main.cf
extraConfig = ''
virtual_alias_domains =
${lib.concatStringsSep ",\n " domains}
header_checks = pcre:/etc/postfix/header_checks
smtpd_sender_restrictions = check_sender_access hash:/etc/postfix/sender_access
#we should never use these transport methods unless thru transport map
# RFC3463:
# 5.X.X = permanent error
# X.3.X = mail system failure
# X.3.5 = System incorrectly configured
# I would've never thought there'd be a standard way to specifically say "you found an error in my config"
local_transport = error:5.3.5 how did this even hapenn??
virtual_transport = error:5.3.5 how did this even happenn??
# X.7.1 = Delivery not authorized, message refused
relay_transport = error:5.7.1 relay is so very disabled
lmtp_destination_recipient_limit = 1
# not actually 1024 bits, this applies to all DHE >= 1024 bits
smtpd_tls_dh1024_param_file = ${lib.optionalString config.services.dovecot2.enableDHE config.security.dhparams.params.dovecot2.path}
'';
submissionsOptions = {
smtpd_tls_key_file = config.security.acme.certs."liam.dis8.net".directory + "/key.pem";
smtpd_tls_cert_file = config.security.acme.certs."liam.dis8.net".directory + "/full.pem";
smtpd_tls_security_level = "encrypt";
smtpd_sasl_auth_enable = "yes";
smtpd_tls_auth_only = "yes";
smtpd_reject_unlisted_recipient = "no";
smtpd_client_restrictions = "permit_sasl_authenticated,reject";
milter_macro_daemon_name = "ORIGINATING";
smtpd_sasl_security_options = "noanonymous";
smtpd_sasl_type = "dovecot";
smtpd_sasl_path = "private/dovecot-auth";
message_size_limit = "100000000";
smtpd_sender_login_maps = "hash:/etc/postfix/virtual";
smtpd_sender_restrictions = "reject_authenticated_sender_login_mismatch";
smtpd_tls_mandatory_protocols = "!SSLv2,!SSLv3,!TLSv1,!TLSv1.1,!TLSv1.2";
smtpd_tls_protocols = "!SSLv2,!SSLv3,!TLSv1,!TLSv1.1,!TLSv1.2";
};
};
}

34
liam/nginx.nix Normal file
View File

@@ -0,0 +1,34 @@
{ config, ... }:
let
domains = [
"smtp.shelvacu.com"
"imap.shelvacu.com"
"mail.shelvacu.com"
"autoconfig.shelvacu.com"
"mail.dis8.net"
"liam.dis8.net"
];
in
{
networking.firewall.allowedTCPPorts = [ 80 443 ];
security.acme.acceptTerms = true;
security.acme.defaults.webroot = "/var/lib/acme/acme-challenge";
security.acme.defaults.email = "shelvacu@gmail.com";
services.nginx = {
enable = true;
recommendedZstdSettings = true;
recommendedGzipSettings = true;
recommendedBrotliSettings = true;
recommendedTlsSettings = true;
recommendedOptimisation = true;
virtualHosts."liam.dis8.net" = {
serverAliases = domains;
listen = [
{ addr = "0.0.0.0"; port = 443; ssl = true; }
];
forceSSL = true;
enableACME = true;
default = true;
};
};
}

12
liam/sops.nix Normal file
View File

@@ -0,0 +1,12 @@
{ inputs, ... }:
{
imports = [
inputs.sops-nix.nixosModules.sops
];
sops.defaultSopsFile = ../secrets/liam/main.yaml;
sops.age.sshKeyPaths = [ "/etc/ssh/ssh_host_ed25519_key" ];
sops.secrets.dovecot-passwd = {
restartUnits = [ "dovecot.service" ];
};
}

57
secrets/liam/main.yaml Normal file
View File

@@ -0,0 +1,57 @@
dovecot-passwd: ENC[AES256_GCM,data:4anZ03sjG3puSxDvQPfL4pu5vygvYNh5xVl5II76Y6XAhCDakgZkFCz7FgOk5B94KShNddl0iikRjSi6Fw5z4351j5nq29oq5mBR568I2MvB38ts++coxv+cai+L39yRHpTsNfFVcEq9J2y9/WldXfV5r9AinEYjyxM0hptWTCw0zC8txHLMCvqMPO+QmYsEJZu+u7QhwCcFhkrhf0zXmlu365V1tF79RVUY7QACUPgznFyDmYC/tdrZ3g0a8SlOAzIHXkdk7vy3eFR/GyNiLpNyM5cuOriNyPFbNHCxx5JEBH8fdc/CJpptPCasny3k4Js0vwy4wA==,iv:oBqnWI9XHaNhW4fyiVWG4Auv1QFGFPP7XUjNnO1l9yU=,tag:uYSjkCLlGQGee+RuvWGAlw==,type:str]
sops:
kms: []
gcp_kms: []
azure_kv: []
hc_vault: []
age:
- recipient: age1y4zp4ddq6xyffd8fgmn2jkl78qfh4m94gcls2cu6vvjnwwznx5uqywjekm
enc: |
-----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSA3S0FqZWxDYmxHYU5FZVQz
V2FZMFFSVXJubVRaNDZORDJPSXhHMnludmpRCjJrendscEdqU0p6K1R6eE9FUGtj
RVB4Z3dlNHlBSHRhZ0ZMODdDRkN6ZFEKLS0tIFlzUStVWmhlYWExV1JscHE0KzhG
Vm1uUmhQRzAvL1YzTWVVbllRUlE2Z0EKwg6SBat+CG8E7/j7K0sakqGSyJYNzXqt
b0DMsGq9GnHE1Ph6gGVVWO+pos/FGuunSDyL0lcXk9xJE02FErnw+w==
-----END AGE ENCRYPTED FILE-----
- recipient: age1g9sh8u6s344569d3cg8h30g9h7thld5pexcwzc4549jc84jvceqqjt9cfh
enc: |
-----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSAwNVl3ZTNGWGdMT280MnhQ
R2RHTGRWVFpjMWltMDVIWk1YSUc2eEhjbWwwCnNiTjA4dUZuOU1tNTZtd240VXpU
c0FKY3VoR1dYUVo1MDZjMEJ5MmhjeEEKLS0tIGhuT3k2VlFpTWpJdFJYM0JhZWtS
dzNFb0FDcERGTFVUOTgxN3czTmRUME0KihoqiXkph3sNWTwn6tFi29z9jnht6JRT
zOMNiaWjMHQ7GiR+Yv1JMWrEvKRrEjNaFXt89z0Ebx4llTtyH8W2fw==
-----END AGE ENCRYPTED FILE-----
- recipient: age1t5s3txyj403rfecdhq5q2z3cnavy6m543gzyhkl2nu5t8fz0zctqtvm2tj
enc: |
-----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBzWE91QUFmTzdEUjJ3TTFX
Y2o0Yy9BZjdkc2VVcis4a3FlcDVScDF5eGwwCkZocDFIN3B5dHdNTDNaVXI2WHBF
dDVXMDdvOXVBM3V1NW01YngzclJ1RXMKLS0tIDV5M2JURHkvWWFlbGtUNEhxZ2ZE
RVlDMDgvNVFOamlFR1BZMUtrMzJ4N1UK6r7QbX3nEBu+S8e7oqCk3ys6hqXHkyW4
z4hWz1rr/23JpGR2ENRS+DpHRCRo4KKRhUx2hLc6C2XijNgD4YsUCA==
-----END AGE ENCRYPTED FILE-----
- recipient: age1dzdf4rgep3ctk3dnrmrqtdgrchaa8nszfc4dp29gqwsst3z6jyrq57vfsj
enc: |
-----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBqelVSdDFFcVZxODBiVkEv
QUhYUzM5SDZLVWQ4YlB2UGorZWlidUhIa1N3Ck96TXFGTXBtSVFLdFY1b3BKK3g5
ejZFTkZOTDdqdHFsWmRKNEcyaUZZWW8KLS0tIDJtL2JaRE5XaHNvYW9HMFYrbTFP
NUFlUTVvQVdiTlBZOVZqSjA1ODNhUHcK8hnqUuHjUgjF8nbZgY4BTkk58BbRCYWV
NOPw/jUdEZBRoTJqoEdOLAtW/x1h7Xo+mpVuDW0K7h07LiaU7FL8xQ==
-----END AGE ENCRYPTED FILE-----
- recipient: age1hkve3khk7fthyrwxjqdf4r37lrqpmnkz6mke7psuphvu2ykynqaq9g6ja5
enc: |
-----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSB3UDNVSG9Fb01YSWJTdXRD
UFB1dWhpRUFhWmMwTzdHeTNRdlg2YXd1ZnlnCndBRXBpMTJWdFRsMVNYeDBBY2g2
ZEZKTEw4dHpHSlFNT1BsSXQvaCs4MDQKLS0tIDg3YUlJYU1nUjRTTGtIeTJBVEhR
SjZLWG4xNmxoSmtaTFZweEd3TDJ2QUkKcI4MdgglGFJT58ugHebiE6YQUehEomnH
qPZdH0SZAtJxBPqt78wJqvndR5INt5HBmLtXMDLLEk8o43lqfIkK5Q==
-----END AGE ENCRYPTED FILE-----
lastmodified: "2024-03-04T03:24:29Z"
mac: ENC[AES256_GCM,data:cplfdUFsl0dB/V0kJwvSnd33EcGQoepVGHJPXX8H/AsSiSQjRaPlfRCuVRYzr1NBpFpG4rg/GE+gs84gJccWPrpDyugkHdJfoAiXXaWvjgCt9fhWoZTNh2zgZFxKiCChYGXkn1/JjQuSR9X+ILM+xR+S6l1J5dB3IvP2Y0xQKmQ=,iv:diFxVQEYjRWQmmD5zXrX2n6oYvj7x2zySSdrmiwUfQQ=,tag:AWEis+niiVi+5tt+0uwVVw==,type:str]
pgp: []
unencrypted_suffix: _unencrypted
version: 3.8.1

77
tests/liam.nix Normal file
View File

@@ -0,0 +1,77 @@
{ ... }: {
name = "liam-receives-mail";
nodes.liam = { lib, ... }: {
imports = [ ../liam ];
systemd.services."acme-liam.dis8.net".enable = lib.mkForce false;
systemd.timers."acme-liam.dis8.net".enable = lib.mkForce false;
systemd.services."acme-selfsigned-liam.dis8.net".wantedBy = [ "postfix.service" "dovecot2.service" ];
systemd.services."acme-selfsigned-liam.dis8.net".before = [ "postfix.service" "dovecot2.service" ];
sops = lib.mkForce {};
services.do-agent.enable = false;
virtualisation.digitalOcean = {
seedEntropy = false;
setSshKeys = false;
rebuildFromUserData = false;
setRootPassword = false;
};
systemd.tmpfiles.settings."10-whatever"."/run/secrets/dovecot-passwd".f = {
group = "root";
user = "root";
mode = "0600";
argument = "shelvacu:{plain}shelvacu::::::\\njulie:{plain}julie::::::";
};
# uncomment to significantly speed up the test
# services.dovecot2.enableDHE = lib.mkForce false;
security.acme.defaults.email = lib.mkForce "me@example.org";
security.acme.defaults.server = lib.mkForce "https://example.com"; # self-signed only
};
nodes.checker = { pkgs, lib, nodes, ... }: {
environment.systemPackages = [
(pkgs.writeScriptBin "mailtest" ''
#!${pkgs.python3.interpreter}
import sys
sys.argv.insert(1, "${nodes.liam.networking.primaryIPAddress}")
${builtins.readFile ./mailtest.py}
'')
];
};
testScript = ''
start_all()
liam.wait_for_unit("postfix.service")
liam.wait_for_unit("dovecot2.service")
checks = """
--rcptto shelvacu@shelvacu.com --username shelvacu --smtp-starttls
--rcptto shelvacu@shelvacu.com --username shelvacu
--rcptto julie@shelvacu.com --username julie
--rcptto foobar@shelvacu.com --username shelvacu
--rcptto sales@theviolincase.com --username julie
--rcptto superwow@shop.theviolincase.com --username julie
--rcptto roboman@vacu.store --username shelvacu
--mailfrom bob@vacu.store --expect-recipient-refused
--mailfrom shelvacu@shelvacu.com --expect-recipient-refused
--mailfrom julie@shelvacu.com --expect-recipient-refused
--mailfrom @vacu.store --expect-recipient-refused
--submission --expect-recipient-refused --mailfrom julie@shelvacu.com --username shelvacu
--submission --expect-recipient-refused --mailfrom fubar@theviolincase.com --username shelvacu
--submission --expect-recipient-refused --mailfrom fubar@vacu.store --username julie
--submission --mailfrom shelvacu@shelvacu.com --rcptto foo@example.com --username shelvacu --password shelvacu --expect-sent
--submission --mailfrom shelvacu@shelvacu.com --rcptto foo@example.com --username shelvacu@shelvacu.com --password shelvacu --expect-sent
--submission --mailfrom foo@vacu.store --rcptto foo@example.com --username shelvacu --password shelvacu --expect-sent
--submission --mailfrom foo@vacu.store --rcptto foo@example.com --username shelvacu@shelvacu.com --password shelvacu --expect-sent
--submission --mailfrom foo@violingifts.com --rcptto foo@example.com --username julie --password julie --expect-sent
--submission --mailfrom foo@violingifts.com --rcptto foo@example.com --username julie@shelvacu.com --password julie --expect-sent
"""
for check in checks.split("\n"):
check = check.strip()
if check == "" or check[0] == "#":
continue
checker.succeed("mailtest " + check.strip())
'';
}

92
tests/mailtest.py Normal file
View File

@@ -0,0 +1,92 @@
import smtplib
import imaplib
import time
import ssl
import argparse
import uuid
parser = argparse.ArgumentParser()
parser.add_argument('host', type = str)
parser.add_argument('--mailfrom', default = 'foo@example.com')
parser.add_argument('--rcptto', default = 'awesome@vacu.store')
parser.add_argument('--submission', default = False, action='store_true')
parser.add_argument('--smtp-starttls', default = None, action='store_true')
parser.add_argument('--imap-insecure', default = False, action = 'store_true')
parser.add_argument('--username')
parser.add_argument('--password')
parser.add_argument('--expect-recipient-refused',
dest = 'expect',
action = 'store_const',
const = 'recipient_refused',
default = 'received'
)
parser.add_argument('--expect-sent', dest = 'expect', action = 'store_const', const = 'sent')
parser.add_argument('--expect-imap-error', dest = 'expect', action = 'store_const', const = 'imap_error')
args = parser.parse_args()
print(f"got args {args!r}")
# smtp_starttls = args.smtp_starttls
# if smtp_starttls is None:
# smtp_starttls = args.submission
username = args.username
password = args.password
if password is None:
password = username
if (username is None or password is None) and (args.submission or args.expect == 'received'):
assert False, "Bad args"
msg_magic = str(uuid.uuid4())
def mk_ctx():
ctx = ssl.create_default_context()
ctx.check_hostname = False
ctx.verify_mode = ssl.CERT_NONE
return ctx
try:
smtp = None
if args.submission:
smtp = smtplib.SMTP_SSL(args.host, port=465, context = mk_ctx())
else:
smtp = smtplib.SMTP(args.host, port=25)
smtp.ehlo()
if args.smtp_starttls:
smtp.starttls(context = mk_ctx())
smtp.ehlo()
if args.submission:
smtp.login(username, password)
smtp.sendmail(args.mailfrom, args.rcptto, 'Subject: Test\n\n' + msg_magic)
smtp.close()
except smtplib.SMTPRecipientsRefused:
assert args.expect == 'recipient_refused', "Server rejected message as recipients refused"
else:
assert (not args.expect == 'recipient_refused'), "Server was supposed to reject this message, but it didn't"
if args.expect == 'received' or args.expect == 'imap_error':
time.sleep(3)
try:
with imaplib.IMAP4_SSL(args.host, ssl_context = mk_ctx()) as imap:
# if not args.imap_insecure:
# imap.starttls(ssl_context = mk_ctx())
imap.login(username, password)
imap.select()
status, refs = imap.search(None, 'ALL')
assert status == 'OK', f"imap SEARCH command failed {status!r} {refs!r}"
any_okay = False
for ref in refs[0].split(b' '):
print(f"fetching {ref!r}")
status, msgs = imap.fetch(ref, 'BODY[TEXT]')
assert status == 'OK', f"imap FETCH command failed {status!r} {refs!r}"
print(f"got msgs {msgs!r}")
found_msg = msgs[0][1].strip()
print(f"Found {found_msg}, looking for {msg_magic}")
any_okay = any_okay or found_msg == msg_magic.encode('ascii')
assert any_okay, "Could not find the message in the mailbox"
except imaplib.IMAP4.error as e:
assert args.expect == 'imap_error', f"IMAP error: {e}"
else:
assert not args.expect == 'imap_error', "Expected an IMAP error, but didn't get one"

View File

@@ -1,6 +1,3 @@
# Do not modify this file! It was generated by nixos-generate-config
# and may be overwritten by future invocations. Please make changes
# to /etc/nixos/configuration.nix instead.
{ config, lib, pkgs, modulesPath, ... }:
{