Compare commits

...

2 commits

Author SHA1 Message Date
Patrick f918dfda8c
feat: stalwart config 2024-11-27 14:26:48 +01:00
Patrick 41710c109b
feat: new hetzner server 2024-11-26 21:39:09 +01:00
35 changed files with 1640 additions and 302 deletions

View file

@ -17,15 +17,14 @@ This showcases my end user setup, which I dailydrive on all my hosts.
| | Programm | Description
---|---|---
🐚 Shell | [ZSH](./users/common/shells/zsh/default.nix) & [Starship](./users/common/shells/starfish.nix) | ZSH with FZF autocomplete, starship prompt, sqlite history and histdb-skim for fancy reverse search
🪟 WM | [Sway](./users/common/graphical/wayland/sway.nix) & [i3](./users/common/graphical/Xorg/i3.nix) | Tiling window managers with similar behaviour for wayland and xorg
🖼️ Styling | [Stylix](./modules/graphical/default.nix) | globally consistent styling
📝 Editor | [NeoVim](./users/common/programs/nvim/default.nix) | Extensively configured neovim
🎮 Gaming | [Bottles](./users/common/programs/bottles.nix) & [Steam](./modules/optional/steam.nix) | Pew, Pew and such
🪟 WM | [Hyprland](./users/patrick/wayland/hyprland.nix) | Tiling window manager
🖼️ Styling | [Stylix](./users/patrick/theme.nix) | globally consistent styling
📝 Editor | [NeoVim](./users/patrick/programs/nvim/default.nix) | Extensively configured neovim
🎮 Gaming | [Bottles](./users/patrick/programs/bottles.nix) & [Steam](./users/patrick/programs/steam.nix) | Pew, Pew and such
🌐 Browser | [Firefox](./users/patrick/firefox.nix) | Heavily configured Firefox to still my privacy and security needs
💻 Terminal | [Kitty](./users/common/programs/kitty.nix) | fast terminal
🎵 Music | [Spotify](./users/common/programs/spicetify.nix) | Fancy looking spotify using spicetify
💻 Terminal | [Kitty](./users/patrick/programs/kitty.nix) | fast terminal
🎵 Music | [Spotify](./users/patrick/programs/spicetify.nix) | Fancy looking spotify using spicetify
📫 Mail | [Thunderbird](./users/common/programs/thunderbird.nix) | Best email client there is
🎛️ StreamDeck | [StreamDeck](./users/patrick/streamdeck.nix) | More hotkeys = more better
## Service Configuration
These are services I've set up
@ -64,7 +63,7 @@ These are notable external flakes which this config depend upon
[impermanence](https://github.com/nix-community/impermanence) | stateless filesystem
[lanzaboote](https://github.com/nix-community/lanzaboote) | Secure Boot
[stylix](https://github.com/danth/stylix) | theming
[spicetify](https://github.com/the-argus/spicetify-nix) | spotify looking fancy
[spicetify](https://github.com/Gerg-l/spicetify-nix) | spotify looking fancy
@ -82,9 +81,9 @@ These are notable external flakes which this config depend upon
- This might take multiple minutes(~10)
- Alternatively boot an official nixos image connect with password
3. Copy ISO to usb using dd
3. After booting copy the installer to the live system using `nix copy --to <target> .#nodes.<target-system>.config.system.build.installFromLive`
3. After booting copy the installer to the live system using `nix copy --to <target> .#minimalConfigurations.<target-system>.config.system.build.installFromLive`
4. Run the installer script from the nix store of the live system
- you can get the path using `nix path-info .#nodes.<target-system>.config.system.build.installFromLive`
- you can get the path using `nix path-info .#minimalConfigurations.<target-system>.config.system.build.installFromLive`
4. Export all zpools and reboot into system
6. Retrieve hostkeys using `ssh-keyscan <host> | grep -o 'ssh-ed25519.*' > host/<target>/secrets/host.pub`
5. Deploy system

View file

@ -10,6 +10,7 @@
./net.nix
./nftables.nix
./nix.nix
./secrets.nix
./ssh.nix
./system.nix
./users.nix
@ -28,6 +29,7 @@
inputs.agenix.nixosModules.default
inputs.disko.nixosModules.disko
inputs.home-manager.nixosModules.default
inputs.idmail.nixosModules.default
inputs.impermanence.nixosModules.impermanence
inputs.lanzaboote.nixosModules.lanzaboote
inputs.nix-topology.nixosModules.default

21
config/basic/secrets.nix Normal file
View file

@ -0,0 +1,21 @@
{
age.generators.argon2id =
{
pkgs,
lib,
decrypt,
deps,
...
}:
let
dep = builtins.head deps;
in
''
echo " -> Deriving argon2id hash from "${lib.escapeShellArg dep.host}":"${lib.escapeShellArg dep.name}"" >&2
${decrypt} ${lib.escapeShellArg dep.file} \
| tr -d '\n' \
| ${pkgs.libargon2}/bin/argon2 "$(${pkgs.openssl}/bin/openssl rand -base64 16)" -id -e \
|| die "Failure while generating argon2id hash"
'';
}

View file

@ -40,6 +40,8 @@
signal = uidGid 228;
netbird-main = uidGid 229;
paperless = uidGid 315;
stalwart-mail = uidGid 316;
build = uidGid 317;
systemd-oom = uidGid 300;
systemd-coredump = uidGid 301;
patrick = uidGid 1000;

View file

@ -0,0 +1,92 @@
{
inputs,
config,
...
}:
let
domain = config.secrets.secrets.global.domains.mail_public;
idmailDomain = "alias.${domain}";
priv_domain = config.secrets.secrets.global.domains.mail_private;
mkRandomSecret = {
generator.script = "alnum";
mode = "000";
intermediary = true;
};
mkArgon2id = secret: {
generator.dependencies = [ config.age.secrets.${secret} ];
generator.script = "argon2id";
mode = "440";
group = "stalwart-mail";
};
in
{
environment.persistence."/persist".directories = [
{
directory = config.services.idmail.dataDir;
user = "stalwart-mail";
group = "stalwart-mail";
mode = "4770";
}
];
age.secrets = {
idmail-user-pw_admin = mkRandomSecret;
idmail-user-hash_admin = mkArgon2id "idmail-user-pw_admin";
idmail-mailbox-pw_catch-all = mkRandomSecret;
idmail-mailbox-hash_catch-all = mkArgon2id "idmail-mailbox-pw_catch-all";
};
services.idmail = {
package = inputs.idmail.packages."aarch64-linux".default;
enable = true;
# Stalwart will change permissions due to SQLite implementation.
# Therefore, run as stalwart-mail since we don't allow reading
# stalwarts folder anyway (sandboxing is on).
user = "stalwart-mail";
provision = {
enable = true;
users.admin = {
admin = true;
password_hash = "%{file:${config.age.secrets.idmail-user-hash_admin.path}}%";
};
domains = {
"${domain}" = {
owner = "admin";
catch_all = "catch-all@${domain}";
public = true;
};
"${priv_domain}" = {
owner = "admin";
catch_all = "catch-all@${domain}";
public = false;
};
};
mailboxes."catch-all@${domain}" = {
password_hash = "%{file:${config.age.secrets.idmail-mailbox-hash_catch-all.path}}%";
owner = "admin";
};
# XXX: create mailboxes for git@ vaultwarden@ and simultaneously alias them to the catch all for a send only mail.
};
};
systemd.services.idmail.serviceConfig.RestartSec = "60"; # Retry every minute
services.nginx = {
upstreams.idmail = {
servers."127.0.0.1:3000" = { };
extraConfig = ''
zone idmail 64k;
keepalive 2;
'';
};
virtualHosts.${idmailDomain} = {
forceSSL = true;
useACMEWildcardHost = true;
locations."/" = {
proxyPass = "http://idmail";
proxyWebsockets = true;
};
};
};
}

View file

@ -0,0 +1,662 @@
{
config,
lib,
pkgs,
...
}:
let
priv_domain = config.secrets.secrets.global.domains.mail_private;
domain = config.secrets.secrets.global.domains.mail_public;
mailDomains = [
priv_domain
domain
];
mailBackupDir = "/var/cache/mail-backup";
dataDir = "/var/lib/stalwart-mail";
in
{
age.secrets.resticpasswd = {
generator.script = "alnum";
};
age.secrets.stalwartHetznerSshKey = {
generator.script = "ssh-ed25519";
};
services.restic.backups = {
main = {
user = "root";
timerConfig = {
OnCalendar = "06:00";
Persistent = true;
RandomizedDelaySec = "3h";
};
initialize = true;
passwordFile = config.age.secrets.resticpasswd.path;
hetznerStorageBox = {
enable = true;
inherit (config.secrets.secrets.global.hetzner) mainUser;
inherit (config.secrets.secrets.global.hetzner.users.stalwart-mail) subUid path;
sshAgeSecret = "stalwartHetznerSshKey";
};
paths = [
mailBackupDir
];
#pruneOpts = [
# "--keep-daily 10"
# "--keep-weekly 7"
# "--keep-monthly 12"
# "--keep-yearly 75"
#];
};
};
systemd.services.backup-mail = {
description = "Mail backup";
environment = {
STALWART_DATA = dataDir;
IDMAIL_DATA = config.services.idmail.dataDir;
BACKUP_DIR = mailBackupDir;
};
serviceConfig = {
SyslogIdentifier = "backup-mail";
Type = "oneshot";
User = "stalwart-mail";
Group = "stalwart-mail";
ExecStart = lib.getExe (
pkgs.writeShellApplication {
name = "backup-mail";
runtimeInputs = [ pkgs.sqlite ];
text = ''
sqlite3 "$STALWART_DATA/database.sqlite3" ".backup '$BACKUP_DIR/database.sqlite3'"
sqlite3 "$IDMAIL_DATA/database.sqlite3" ".backup '$BACKUP_DIR/idmail.db'"
cp -r "$STALWART_DATA/dkim" "$BACKUP_DIR/"
'';
}
);
ReadWritePaths = [
dataDir
config.services.idmail.dataDir
mailBackupDir
];
Restart = "no";
};
requiredBy = [ "restic-backups-main.service" ];
before = [ "restic-backups-main.service" ];
};
age.secrets.stalwart-admin-pw = {
generator.script = "alnum";
mode = "000";
intermediary = true;
};
age.secrets.stalwart-admin-hash = {
generator.dependencies = [ config.age.secrets.stalwart-admin-pw ];
generator.script = "argon2id";
mode = "440";
group = "stalwart-mail";
};
users.groups.acme.members = [ "stalwart-mail" ];
networking.firewall.allowedTCPPorts = [
25 # smtp
465 # submission tls
# 587 # submission starttls
993 # imap tls
# 143 # imap starttls
4190 # manage sieve
];
environment.persistence."/persist".directories = [
{
directory = dataDir;
user = "stalwart-mail";
group = "stalwart-mail";
mode = "0700";
}
];
# Needed so we don't run out of tmpfs space for large backups.
# Technically this could be cleared each boot but whatever.
environment.persistence."/state".directories = [
{
directory = mailBackupDir;
user = "stalwart-mail";
group = "stalwart-mail";
mode = "0700";
}
];
services.nginx = {
upstreams.stalwart = {
servers."127.0.0.1:8080" = { };
extraConfig = ''
zone stalwart 64k;
keepalive 2;
'';
};
virtualHosts =
{
${domain} = {
forceSSL = true;
useACMEWildcardHost = true;
extraConfig = ''
client_max_body_size 512M;
'';
locations."/" = {
proxyPass = "http://stalwart";
proxyWebsockets = true;
};
};
}
// lib.genAttrs
[
"autoconfig.${domain}"
"autodiscover.${domain}"
"mta-sts.${domain}"
]
(_: {
forceSSL = true;
useACMEWildcardHost = true;
locations."/".proxyPass = "http://stalwart";
});
};
systemd.services.stalwart-mail =
let
cfg = config.services.stalwart-mail;
configFormat = pkgs.formats.toml { };
configFile = configFormat.generate "stalwart-mail.toml" cfg.settings;
in
{
preStart = lib.mkAfter (
''
cat ${configFile} > /run/stalwart-mail/config.toml
cat ${config.age.secrets.stalwart-admin-hash.path} \
| tr -d '\n' > /run/stalwart-mail/admin-hash
mkdir -p /var/lib/stalwart-mail/dkim
''
# Generate DKIM keys if necessary
+ lib.concatLines (
lib.forEach mailDomains (domain: ''
if [[ ! -e /var/lib/stalwart-mail/dkim/rsa-${domain}.key ]]; then
echo "Generating DKIM key for ${domain} (rsa)"
${lib.getExe pkgs.openssl} genrsa -traditional -out /var/lib/stalwart-mail/dkim/rsa-${domain}.key 2048
fi
if [[ ! -e /var/lib/stalwart-mail/dkim/ed25519-${domain}.key ]]; then
echo "Generating DKIM key for ${domain} (ed25519)"
${lib.getExe pkgs.openssl} genpkey -algorithm ed25519 -out /var/lib/stalwart-mail/dkim/ed25519-${domain}.key
fi
'')
)
);
serviceConfig = {
RuntimeDirectory = "stalwart-mail";
ReadWritePaths = [ config.services.idmail.dataDir ];
ExecStart = lib.mkForce [
""
"${cfg.package}/bin/stalwart-mail --config=/run/stalwart-mail/config.toml"
];
RestartSec = "60"; # Retry every minute
};
};
services.stalwart-mail = {
enable = true;
settings =
let
case = field: check: value: data: {
"if" = field;
${check} = value;
"then" = data;
};
ifthen = field: data: {
"if" = field;
"then" = data;
};
otherwise = value: { "else" = value; };
is-smtp = case "listener" "eq" "smtp";
is-authenticated = data: {
"if" = "!is_empty(authenticated_as)";
"then" = data;
};
in
lib.mkForce {
config.local-keys = [
"store.*"
"directory.*"
"tracer.*"
"server.*"
"!server.blocked-ip.*"
"!server.allowed-ip.*"
"authentication.fallback-admin.*"
"cluster.node-id"
"storage.data"
"storage.blob"
"storage.lookup"
"storage.fts"
"storage.directory"
"lookup.default.hostname"
"certificate.*"
"auth.dkim.*"
"signature.*"
];
authentication.fallback-admin = {
user = "admin";
secret = "%{file:/run/stalwart-mail/admin-hash}%";
};
tracer.stdout = {
# Do not use the built-in journal tracer, as it shows much less auxiliary
# information for the same loglevel
type = "stdout";
level = "info";
ansi = false; # no colour markers to journald
enable = true;
};
store.db = {
type = "sqlite";
path = "${dataDir}/database.sqlite3";
};
store.idmail = {
type = "sqlite";
path = "${config.services.idmail.dataDir}/idmail.db";
query =
let
# Remove comments from SQL and make it single-line
toSingleLineSql =
sql:
lib.concatStringsSep " " (
lib.forEach (lib.flatten (lib.split "\n" sql)) (
line: lib.optionalString (builtins.match "^[[:space:]]*--.*" line == null) line
)
);
in
{
# "SELECT name, type, secret, description, quota FROM accounts WHERE name = ?1 AND active = true";
name = toSingleLineSql ''
SELECT
m.address AS name,
'individual' AS type,
m.password_hash AS secret,
m.address AS description,
0 AS quota
FROM mailboxes AS m
JOIN domains AS d ON m.domain = d.domain
JOIN users AS u ON m.owner = u.username
WHERE m.address = ?1
AND m.active = true
AND d.active = true
AND u.active = true
'';
# "SELECT member_of FROM group_members WHERE name = ?1";
members = "";
# "SELECT name FROM emails WHERE address = ?1";
recipients = toSingleLineSql ''
-- It is important that we return only one value here, but in theory three UNIONed
-- queries are guaranteed to be distinct. This is because a mailbox address
-- and alias address can never be the same, their cross-table uniqueness is guaranteed on insert.
-- The catch-all union can also only return something if @domain.tld is given as a parameter,
-- which is invalid for aliases and mailboxes.
--
-- Nonetheless, it may be beneficial to allow an alias to override an existing mailbox,
-- so we can have send-only mailboxes which have their incoming mail redirected somewhere else.
-- Therefore, we make sure to order the query by (aliases -> mailboxes -> catch all) and only return the
-- highest priority one.
SELECT name FROM (
-- Select the target of a matching alias (if any)
-- but make sure that all related parts are active.
SELECT a.target AS name, 1 AS rowOrder
FROM aliases AS a
JOIN domains AS d ON a.domain = d.domain
JOIN (
-- To check whether the owner is active we need to make a subquery
-- because the owner could be a user or mailbox
SELECT username
FROM users
WHERE active = true
UNION
SELECT m.address AS username
FROM mailboxes AS m
JOIN users AS u ON m.owner = u.username
WHERE m.active = true
AND u.active = true
) AS u ON a.owner = u.username
WHERE a.address = ?1
AND a.active = true
AND d.active = true
-- Select the primary mailbox address if it matches and
-- all related parts are active.
UNION
SELECT m.address AS name, 2 AS rowOrder
FROM mailboxes AS m
JOIN domains AS d ON m.domain = d.domain
JOIN users AS u ON m.owner = u.username
WHERE m.address = ?1
AND m.active = true
AND d.active = true
AND u.active = true
-- Finally, select any catch_all address that would catch this.
-- Again make sure everything is active.
UNION
SELECT d.catch_all AS name, 3 AS rowOrder
FROM domains AS d
JOIN mailboxes AS m ON d.catch_all = m.address
JOIN users AS u ON m.owner = u.username
WHERE ?1 = ('@' || d.domain)
AND d.active = true
AND m.active = true
AND u.active = true
ORDER BY rowOrder, name ASC
LIMIT 1
)
'';
# "SELECT address FROM emails WHERE name = ?1 AND type != 'list' ORDER BY type DESC, address ASC";
emails = toSingleLineSql ''
-- Return first the primary address, then any aliases.
SELECT address FROM (
-- Select primary address, if active
SELECT m.address AS address, 1 AS rowOrder
FROM mailboxes AS m
JOIN domains AS d ON m.domain = d.domain
JOIN users AS u ON m.owner = u.username
WHERE m.address = ?1
AND m.active = true
AND d.active = true
AND u.active = true
-- Select any active aliases
UNION
SELECT a.address AS address, 2 AS rowOrder
FROM aliases AS a
JOIN domains AS d ON a.domain = d.domain
JOIN (
-- To check whether the owner is active we need to make a subquery
-- because the owner could be a user or mailbox
SELECT username
FROM users
WHERE active = true
UNION
SELECT m.address AS username
FROM mailboxes AS m
JOIN users AS u ON m.owner = u.username
WHERE m.active = true
AND u.active = true
) AS u ON a.owner = u.username
WHERE a.target = ?1
AND a.active = true
AND d.active = true
-- Select the catch-all marker, if we are the target.
UNION
-- Order 2 is correct, it counts as an alias
SELECT ('@' || d.domain) AS address, 2 AS rowOrder
FROM domains AS d
JOIN mailboxes AS m ON d.catch_all = m.address
JOIN users AS u ON m.owner = u.username
WHERE d.catch_all = ?1
AND d.active = true
AND m.active = true
AND u.active = true
ORDER BY rowOrder, address ASC
)
'';
# "SELECT address FROM emails WHERE address LIKE '%' || ?1 || '%' AND type = 'primary' ORDER BY address LIMIT 5";
verify = toSingleLineSql ''
SELECT m.address AS address
FROM mailboxes AS m
JOIN domains AS d ON m.domain = d.domain
JOIN users AS u ON m.owner = u.username
WHERE m.address LIKE '%' || ?1 || '%'
AND m.active = true
AND d.active = true
AND u.active = true
UNION
SELECT a.address AS address
FROM aliases AS a
JOIN domains AS d ON a.domain = d.domain
JOIN (
-- To check whether the owner is active we need to make a subquery
-- because the owner could be a user or mailbox
SELECT username
FROM users
WHERE active = true
UNION
SELECT m.address AS username
FROM mailboxes AS m
JOIN users AS u ON m.owner = u.username
WHERE m.active = true
AND u.active = true
) AS u ON a.owner = u.username
WHERE a.address LIKE '%' || ?1 || '%'
AND a.active = true
AND d.active = true
ORDER BY address
LIMIT 5
'';
# "SELECT p.address FROM emails AS p JOIN emails AS l ON p.name = l.name WHERE p.type = 'primary' AND l.address = ?1 AND l.type = 'list' ORDER BY p.address LIMIT 50";
# XXX: We don't actually expand, but return the same address if it exists since we don't support mailing lists
expand = toSingleLineSql ''
SELECT m.address AS address
FROM mailboxes AS m
JOIN domains AS d ON m.domain = d.domain
JOIN users AS u ON m.owner = u.username
WHERE m.address = ?1
AND m.active = true
AND d.active = true
AND u.active = true
UNION
SELECT a.address AS address
FROM aliases AS a
JOIN domains AS d ON a.domain = d.domain
JOIN (
-- To check whether the owner is active we need to make a subquery
-- because the owner could be a user or mailbox
SELECT username
FROM users
WHERE active = true
UNION
SELECT m.address AS username
FROM mailboxes AS m
JOIN users AS u ON m.owner = u.username
WHERE m.active = true
AND u.active = true
) AS u ON a.owner = u.username
WHERE a.address = ?1
AND a.active = true
AND d.active = true
ORDER BY address
LIMIT 50
'';
# "SELECT 1 FROM emails WHERE address LIKE '%@' || ?1 LIMIT 1";
domains = toSingleLineSql ''
SELECT domain
FROM domains
WHERE domain = ?1
'';
};
};
storage = {
data = "db";
fts = "db";
lookup = "db";
blob = "db";
directory = "idmail";
};
directory.idmail = {
type = "sql";
store = "idmail";
columns = {
name = "name";
description = "description";
secret = "secret";
email = "email";
#quota = "quota";
class = "type";
};
};
resolver = {
type = "system";
public-suffix = [
"file://${pkgs.publicsuffix-list}/share/publicsuffix/public_suffix_list.dat"
];
};
config.resource.spam-filter = "file://${config.services.stalwart-mail.package}/etc/stalwart/spamfilter.toml";
config.resource.webadmin = "file://${config.services.stalwart-mail.package.webadmin}/webadmin.zip";
webadmin.path = "/var/cache/stalwart-mail";
certificate.default = {
cert = "%{file:${config.security.acme.certs.${domain}.directory}/fullchain.pem}%";
private-key = "%{file:${config.security.acme.certs.${domain}.directory}/key.pem}%";
default = true;
};
lookup.default.hostname = domain;
server = {
tls = {
certificate = "default";
ignore-client-order = true;
};
socket = {
nodelay = true;
reuse-addr = true;
};
listener = {
smtp = {
protocol = "smtp";
bind = "[::]:25";
};
submissions = {
protocol = "smtp";
bind = "[::]:465";
tls.implicit = true;
};
imaps = {
protocol = "imap";
bind = "[::]:993";
tls.implicit = true;
};
http = {
# jmap, web interface
protocol = "http";
bind = "[::]:8080";
url = "https://${domain}";
use-x-forwarded = true;
};
sieve = {
protocol = "managesieve";
bind = "[::]:4190";
tls.implicit = true;
};
};
};
imap = {
request.max-size = 52428800;
auth = {
max-failures = 3;
allow-plain-text = false;
};
timeout = {
authentication = "30m";
anonymous = "1m";
idle = "30m";
};
rate-limit = {
requests = "20000/1m";
concurrent = 32;
};
};
auth.dkim.sign = [
(ifthen "is_local_domain('*', sender_domain)" "['rsa-' + sender_domain, 'ed25519-' + sender_domain]")
(otherwise false)
];
signature = lib.mergeAttrsList (
lib.forEach mailDomains (domain: {
"ed25519-${domain}" = {
private-key = "%{file:/var/lib/stalwart-mail/dkim/ed25519-${domain}.key}%";
inherit domain;
selector = "ed_default";
headers = [
"From"
"To"
"Date"
"Subject"
"Message-ID"
];
algorithm = "ed25519-sha256";
canonicalization = "relaxed/relaxed";
set-body-length = false;
report = true;
};
"rsa-${domain}" = {
private-key = "%{file:/var/lib/stalwart-mail/dkim/rsa-${domain}.key}%";
inherit domain;
selector = "rsa_default";
headers = [
"From"
"To"
"Date"
"Subject"
"Message-ID"
];
algorithm = "rsa-sha256";
canonicalization = "relaxed/relaxed";
set-body-length = false;
report = true;
};
})
);
session.extensions = {
pipelining = true;
chunking = true;
requiretls = true;
no-soliciting = "";
dsn = false;
expn = [
(is-authenticated true)
(otherwise false)
];
vrfy = [
(is-authenticated true)
(otherwise false)
];
future-release = [
(is-authenticated "30d")
(otherwise false)
];
deliver-by = [
(is-authenticated "365d")
(otherwise false)
];
mt-priority = [
(is-authenticated "mixer")
(otherwise false)
];
};
session.ehlo = {
require = true;
reject-non-fqdn = [
(is-smtp true)
(otherwise false)
];
};
session.rcpt = {
catch-all = true;
relay = [
(is-authenticated true)
(otherwise false)
];
max-recipients = 25;
};
};
};
}

File diff suppressed because it is too large Load diff

View file

@ -4,6 +4,10 @@
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
nixp-meta.url = "git+https://forge.lel.lol/patrick/nixp-meta.git";
idmail = {
url = "github:oddlama/idmail/";
inputs.nixpkgs.follows = "nixpkgs";
};
nixpkgs-octoprint.url = "github:patrickdag/nixpkgs/octoprint-update";
nixpkgs-wayland = {

View file

@ -37,10 +37,10 @@
services.xserver.xkb = {
layout = "de";
};
services.logkeys = {
enable = true;
device = "/dev/input/event15";
};
# services.logkeys = {
# enable = true;
# device = "/dev/input/event15";
# };
boot.binfmt.emulatedSystems = [
"aarch64-linux"

View file

@ -38,7 +38,8 @@ let
ipOf =
hostName:
if hostName == "octoprint" then
nodes.testienix.config.wireguard.elisabeth.ipv4
#nodes.testienix.config.wireguard.elisabeth.ipv4
"0.0.0.0"
else
nodes."elisabeth-${hostName}".config.wireguard.elisabeth.ipv4;
in

27
hosts/mailnix/default.nix Normal file
View file

@ -0,0 +1,27 @@
{
imports = [
../../config/basic
../../config/support/initrd-ssh.nix
../../config/support/zfs.nix
../../config/services/idmail.nix
../../config/services/stalwart.nix
./net.nix
./fs.nix
];
boot = {
initrd.availableKernelModules = [
"virtio_pci"
"virtio_net"
"virtio_scsi"
"virtio_blk"
"virtio_gpu"
];
kernelParams = [ "console=tty" ];
loader = {
systemd-boot.enable = true;
efi.canTouchEfiVariables = true;
};
};
nixpkgs.hostPlatform = "aarch64-linux";
}

32
hosts/mailnix/fs.nix Normal file
View file

@ -0,0 +1,32 @@
{ config, lib, ... }:
{
disko.devices = {
disk = {
drive = rec {
type = "disk";
device = "/dev/disk/by-id/${config.secrets.secrets.local.disko.drive}";
content = with lib.disko.gpt; {
type = "gpt";
partitions = {
boot = (partEfi "256M") // {
device = "${device}-part1";
};
rpool = (partLuksZfs "drive" "rpool" "100%") // {
device = "${device}-part2";
};
};
};
};
};
zpool = with lib.disko.zfs; {
rpool = mkZpool { datasets = impermanenceZfsDatasets; };
};
};
fileSystems."/state".neededForBoot = true;
fileSystems."/persist".neededForBoot = true;
boot.initrd.systemd.services."zfs-import-panzer".after = [ "cryptsetup.target" ];
boot.initrd.systemd.services."zfs-import-rpool".after = [ "cryptsetup.target" ];
}

73
hosts/mailnix/net.nix Normal file
View file

@ -0,0 +1,73 @@
{ config, lib, ... }:
{
networking.hostId = config.secrets.secrets.local.networking.hostId;
networking.domain = config.secrets.secrets.global.domains.mail_public;
boot.initrd.systemd.network = {
enable = true;
networks = {
inherit (config.systemd.network.networks) "lan01";
};
};
systemd.network.networks = {
"lan01" =
let
icfg = config.secrets.secrets.local.networking.interfaces.lan01;
in
{
address = [
icfg.hostCidrv4
(lib.net.cidr.hostCidr 1 icfg.hostCidrv6)
];
gateway = [ "fe80::1" ];
routes = [
{ Destination = "172.31.1.1"; }
{
Gateway = "172.31.1.1";
GatewayOnLink = true;
}
];
matchConfig.MACAddress = icfg.mac;
networkConfig.IPv6PrivacyExtensions = "yes";
linkConfig.RequiredForOnline = "routable";
};
};
age.secrets.cloudflare_token_acme = {
rekeyFile = ./secrets/cloudflare_api_token.age;
mode = "440";
group = "acme";
};
security.acme = {
acceptTerms = true;
defaults = {
email = config.secrets.secrets.global.devEmail;
dnsProvider = "cloudflare";
dnsPropagationCheck = true;
reloadServices = [ "nginx" ];
credentialFiles = {
"CF_DNS_API_TOKEN_FILE" = config.age.secrets.cloudflare_token_acme.path;
"CF_ZONE_API_TOKEN_FILE" = config.age.secrets.cloudflare_token_acme.path;
};
};
};
networking.nftables.firewall.zones.untrusted.interfaces = [ "lan01" ];
security.acme.certs = {
"${config.secrets.secrets.global.domains.mail_public}" = {
domain = config.secrets.secrets.global.domains.mail_public;
extraDomainNames = [ "*.${config.secrets.secrets.global.domains.mail_public}" ];
};
"${config.secrets.secrets.global.domains.mail_private}" = {
domain = config.secrets.secrets.global.domains.mail_private;
extraDomainNames = [ "*.${config.secrets.secrets.global.domains.mail_private}" ];
};
};
environment.persistence."/state".directories = [
{
directory = "/var/lib/acme";
user = "acme";
group = "acme";
mode = "0755";
}
];
}

View file

@ -0,0 +1,15 @@
age-encryption.org/v1
-> X25519 uhnRibm92XSz2UcJWT43CrsZfOrSzUyqVFU8nWiYEXs
QNxh6YGDCgSSoCWLthZlou7F7i9OJpunB+/6J4ogk2k
-> piv-p256 XTQkUA AzTDTMXLU5jTp54ysvnVIDo5lIb5ED1zkP8659tTH2JJ
VLO6rtfY5poFGVH/eeD+T/xrlNdPGnlLQ6mK1HytT8A
-> piv-p256 ZFgiIw AnwL/t0GNZI3/y7KlatHLebToW1pJLfOasODGQ7ogriz
Wl7xm6+a1qmqLeTZszpO0XG96BcDRO5l8wvpc0atW0Y
-> piv-p256 5vmPtQ AzC3t9sPdKF/IPkJSqhldnx3Mnkc84DCD13l8tYqZIWd
GaNzRxPoSOy/kEuLzbXpiRDo5F2hZT8KriXpgqZkQ5Y
-> piv-p256 ZFgiIw ApFdJVoW4zoWq38fE27TR/OFEDs4Wub1g3q6RiF+fDTR
IypnQqeluntk31gez5I6eYtlKiY/8sy+dXNkpWhdwPs
-> wX-grease
neAQttCOcpQWsfSpI38jdOjODJYK8uOhqjWsZOLWlHZaRUQtoyXI
--- r44AgWizs6H92oY6hKMs67ARXqr8Je0Z0cIJr9xidBg
°ß¦Ñ¨â<>Ÿî̪øÙ¤Ph\œdv úí¥]’ÀÓšÆÜŠÚ˜ùÄE<C384>ʃ´¯æewIé‡t.¬²WÃÂ6ZFi

View file

@ -0,0 +1,17 @@
age-encryption.org/v1
-> X25519 7NpA9hDsF1TwTVRvAKHpovHSUCr0Gg11mzsubZemyDs
3eE/PWJizZWIDMr3Dt6012F6db/nlmhpM8y06eLO48s
-> piv-p256 ZFgiIw Alb1ynSHX7YiSZkhDbId9MGoQeRacJJ9Mv4a64WVxXdE
gTRZ2XC+K6bZ9my7B28oXJGfQ2fvHFZHDTGWfjzQMdA
-> piv-p256 XTQkUA AhrEyyEHX3BxETBIWDmbK5pfzmfcFCmTWX2psLAzGhYS
XFR2JtZJikRDATiZ8eflzShfvrrUMLp00s2+0N54tiI
-> piv-p256 ZFgiIw A/MHWK7H85OTk0JLH8y0t7QHcG4xRNwYwEuWPuVBLojT
Vbwyekt8SwUfJzfyualAekCf/MGW+Igs/ZALTydd9Qc
-> piv-p256 5vmPtQ Av/BlH1sZh++RL4fh2NS2HN7yipM9nLfT90OiRh9Flbj
kzi2VBRvIxbBbze4iBahMGROtnSOKznXzCNS0PR9TG0
-> N"-grease p, Pb+NMCL ^
BeBdV3XQgNJVO309KZx1hphkECZLRrCqPmEoR3pEzB4I3L8Q6ur+4ALEy2mLjmSp
Mir7Hdy3Pg
--- Klvxozur3RYybVYWbakGVXiTymaTfOoFXcwnj7hsEAY
믰zéR¼õÍÅx-@ß`Ö«? ka´L53<35>öܘ°}B<>Ê¡ü7 -6B»t¤hë¦öá$<24>h·‡ò&ÐBƒ.™7O„ä„Þ.ßëk±g·m
¸<EFBFBD>kѱW<EFBFBD>‰4Ûâ1GyÔâ1%È<ù°ê›?z<>7êÿéfûXcgMaÝnn!

View file

@ -0,0 +1,18 @@
age-encryption.org/v1
-> X25519 Ud9UzEUeDmMIb90vOTWVkdDvIcebEwSzI4Ii8M5jAUI
4rloQ7OzT0voyVboOaWLvOxvrlYxtcOY91dt1lq6wtg
-> piv-p256 ZFgiIw Aro3d4Lv0WTRa1OiE1f0hROViqhes5elbt5a+uKCS0y7
UZFViBihW5si4+JbzN1OyzWDuWiFwWfoVls+EH+EUmk
-> piv-p256 XTQkUA A0mE5ni66UlnsafkVu3MK0N6aTX2UtV+jADROmg4M1aN
cYqc/9CCT1PC3inzqfQvK59MCHHNEtIhpvOvqL7E2nA
-> piv-p256 ZFgiIw AnFFxNY3lsY4fsze7Hm4vAmK7zZKGA4qEfSUH5aIkQ4j
1OwdPteTYQCWrt4IkRhflolMXJ+FUMm91n3p7icqnsc
-> piv-p256 5vmPtQ Amg+62BwmCb9ZQmZ74PzT0/FheaK2OzfyGgbHYcyo5Cl
OnlF+hKq6p91i3Jk+iwYQ2ByRTgmZX57mIAIpMRoCD8
-> >aAO.fE-grease ' 7nl% c#t R]j<n
pC7HsBXeonXLPKBlbzkYZepNa2/RDKAwF9UvfnYPbw6ouLI6wuwmYO1moo2ERk4c
D7yBUPkIdFKD
--- 9oExlogv7s/uU+7/UeLOrs0v26TpK6fW1E7Y4hT4umc
G¥É¶ÒdMç*óùThrm¾÷®ÜPÙW{
 kŸÌ¦Z<áñ†h¡þ,¬nxnúE‹€
Uoè¶QßÙï§S&õÇ·ÿ™ÓéÁ>w•

View file

@ -0,0 +1,16 @@
age-encryption.org/v1
-> X25519 wbI8kJZQwT22oltbFeQSWX/Kdb+QPGy/yq5FWi4tURE
QkCRyLTSekF7e+9tFhlr36Rn0vmEZ4Ad7nmYFyDrUIE
-> piv-p256 ZFgiIw AnlFacrcAVmYBv6lWsStu/GyihNjZxqBiRArNYuD6UOk
L8kywowjuGRQ7YshqlitmhvD5DlTKtnhdh0ufeybyEw
-> piv-p256 XTQkUA Anc9FjiKfMpMqjgLBF3gyTdTpdOkmQisBWNSQLPkn7qa
Nc6+wZvNadzIo1pI0Q1xV0LEuWZ5nLMC6e3PwtpviX8
-> piv-p256 ZFgiIw Aqym6w/aiUpbfE44JfxlV0eAS/naenJCHZNWqzwypHLN
ppB2QXwm0i64LaQnyG9NxjT2MRaI7v6Xj8L3KxB7s8E
-> piv-p256 5vmPtQ ArYm0jb3ONfijVVVWJHVUXfPm+hOJAIVn2+RUAXmIW0e
/rijgC2Iok8VcAbBTeWgQ7KB2u9T7TA03WfE9Ju3UP4
-> M^g-grease P Jl/S]j
DY4S+rVP2pA38O2f+JWEC5ODoxEXnRdO37COHof6cB85a7a64+FWT1QcAI+P+CEx
Y6FiZ2sYbqwePti5qjUxjg
--- KfDaVg+3cbtTkC/1i2dIORAEjPLrwS5ot4szuOMqMZ0
u2eN}ÍÅ5éW¡<C2A1>¯Ø¨>|×øéP)æ¿„XF'l¢˜€ó°˜<C2B0>O×ÞQZµ!ò[>Y÷þùÙ–—¢Ö£Æ¹cÏ@íW;q ?'ç¦Mæ

View file

@ -0,0 +1,18 @@
age-encryption.org/v1
-> X25519 ddFv+EKlJUwePVA7CiwFOA/mECyJ9pC328u5r9Bjhz4
5c05Nlc1ADOpUq5MugDuHJqSz4OW4Yupl0UBl9DxyDk
-> piv-p256 ZFgiIw AgiBCvmbqRaShnyh+NuDFFESQ9Q5sZZ/YjYXelOzCYyV
/6/igWsiKPwTlydwiAR1ZECyURFkBiJWCppdXP5GDRk
-> piv-p256 XTQkUA An4etg/KtGFdnw74QM+QW9vRkrAxEZmMjhexLKENVnV3
m8UleuJcvy/OZhlZrOEguL+0hWo5n2Ykgboq8BqFrdU
-> piv-p256 ZFgiIw A1vxQkA8CeZGrXNcvBZo57iL82PiTPm0hP3KODzWnU/z
USt8rTNK7l9VUUyAiSnDiLVQgLZiFZQgcy04lWdk+nM
-> piv-p256 5vmPtQ AnAB3M/AWePGjmIUdoH0rSHg+gDnxg62RPy9qgHAgIIN
7PPd5p4sXrbDvZBITS3zMnG7qNmlj61hdHPlVo5cJDQ
-> qtLMz'K-grease ?Rtv +~4H. vh
ZBK1Zs8LKTiGvOSxH/dacE3yc1ouqSylHM5Ahv+HmR89RQX/JR4y3Gtec+G2W0Ty
Dh9z2wVbCDlJTTt+N+9sLvV/b5+wETpwhPmiSWbh92yvNYH1yLQ
--- jynsWcgTRZR51+fu9nqHP5yTxxz1BovM1s2YY+0uL2I
ìS€Ó¿Þ4}°NTÀ (Ú뮃+ ¬e9•_³qÛã2OÚÂ)=òw¸»/¼/D|šv`ÓàŒ6æXI³/™®””ˆîw^ÆÇ+hñ¨ˆÃÀ<11>6núrÒå˜q†²ü¢<>@Îf—2 ÇlîÞ/¤Ý“z—7,ôrFÇPÈ6ÎÓ€‰e1Nè4‡F»SÒ©¹<C2A9>ª8»Ì·¯g…]iϳ}n)ÒM×D¬ªH%ë^28(…«8\Ç`AñCƒÐ½/û•<÷%ûØ8°ú©ÀÚÔ\eÑäÿ=¹ýâ(*6§a ßV\G³ôuIÕøÓÓ |´gÝ—™Õ#<23>²v&@ÞæóÑ… ‡æÉHâ´IhþXÀìñ÷îšÓÈY9­ƒÐøÎÛM5%‡ËeÆ6?2º¿<C2BF>—"„¥
Y<EFBFBD>æíM<EFBFBD>#ŽñãL*6*!ùJMœ& Gó%C3ô4s<34>ÇÙ<C399>©mÑÛA?—€³9|þMȺ¿OSrÍÍ+ó3†ÇQeþãRLK—Âue?{=‡ýíîö­š™€-±¼[⾊ˆüôëm
Û•øœ>¦QT7‰œ™ô1z

View file

@ -0,0 +1,16 @@
age-encryption.org/v1
-> X25519 PdEHSeb3vou1ceHtkrlTbsu5BGWZ2onVCXPCwmW8znk
q8UKSDCiI+oZp+iODHddauFYFbLdc82tEo+Bsu2bgbo
-> piv-p256 ZFgiIw AnZuTRltFip1RHFY1dr+uTJPGbAYFzWpU/HEiZYuMIgz
r0nxJt1eZsXsnCnQ0Ls+kYqyz/PJCUjef9uvziqMqls
-> piv-p256 XTQkUA Au50Oa5SpTyUFjF4W6ETiofTruRqQItE94SmHRPzR4Y2
T9m1cYYtJr8TQuZYquoJUM+uDeim8llDiMVk3N+kDqk
-> piv-p256 ZFgiIw A/6WS2AnElPTKjwYT6K7CWnL8bolB6HNlQnuqjQ8lKt+
/0StgIwLSpVT7NyOJLxsPJz9TtfAOZU+qWls8gYkkFE
-> piv-p256 5vmPtQ A92v/hxaXEVRNqrsNhFuKCn5TllPrJCGk1e726IDBVo+
+yCS8ZD3uO4UWwMhk9xqWSWZ3UGgmBkIAqAtBGKF8Nw
-> a^_IFyLy-grease
smwxe0ZqF7Qc1wsp0rYM20J5FjFiTQV2UpYfUUgt3edM0+iMmBzHG9EPxKjGNmt9
yogZ0dRKId6mKtaNJeLHUDaCMhIsYAcrhNVGDvG9JOPdhRx9Og0
--- sG4CDChcMPfQS4gtEDGd+bH/WKNXi5ohWX4NTNkaAi0
⹎¹Öod‡6îí?áõK<C3B5>ç¾|¤ë$ý Ød(Ó@) Ó·îÑ<1B>ùèø#ëE©™qÈá(¼!jYš`ôhlL<>ñµÃ!_§õ¥®

View file

@ -0,0 +1,15 @@
age-encryption.org/v1
-> X25519 GPymk3LLzkZtbBTHtb5BryUrBoDLImS86IoNS78OqlE
YrRCbTE595ZhRw6VxiBS9lTWB9yP4kijFqSFFdIiUpQ
-> piv-p256 ZFgiIw AsaTyNrw7YuguAOnLv5BFyU2lW61yY++gJmgNq2M+0wq
VGtlEXVaKpzomsLzjEiBtFE3q0emFLHsiWdahPS/WJU
-> piv-p256 XTQkUA A+Jsj+fWxo26HKlA5TOM2nB5WggS6TVRyfhKzNFQxpI2
RwQp5jlvHByeXPPsov5wMEuZ2pED/iFpVBVXVrKshH4
-> piv-p256 ZFgiIw A6HBCYbgWEEBsBQpJfiRwu672I9QOI2JF9eSeCztlBKJ
LOcgLvCIGWvs9Vhc1VuvGlYWKbnkJdngVhBDbdoMSLs
-> piv-p256 5vmPtQ A1VVL35NHnMdTROSGAKYG6V32v2D7KVo9eHuRPqejzas
WvdUexTb/Di4mv5owD/3ug2nn8Le/TMgJ+hZYbuED6c
-> M$iT~z2-grease SDOB\mE" Zxfxg kZ\' LB@$4
--- 2KhnAceJmwDjVhuEx3saTPzXbDOAjFcpp4DH2lgqsZE
Q¿ÞIu{¦¶ôþº¿ÎʼEvé†Ý7ês°¸Þ^éeLÁ%A‰kiÆé¹çhµ_ïû$•÷Ôr"z0AI¼ÝqÖ*¾ ½áS<C3A1>cL

View file

@ -0,0 +1,16 @@
age-encryption.org/v1
-> X25519 rLdYm0p5eFCwaK8u7dz/Qco//mCdnylMwhLo6nX28R4
0XcnRiSWtCyxn1YISgdt/zVIFKPBPbKOueh+L1f62Fc
-> piv-p256 ZFgiIw A6fWtzhy3ylrbXZG4xjSGRh3Qrk7ZwMS7Fawt0XZvESm
wAMOQQvRnMCJ5DriuLHRsc9zJe5UazJBVvNNy97jJos
-> piv-p256 XTQkUA ArhoKZeRdRGXbHOcLiPcT1AruJEE7hckq7QiGKLfcm9d
YXh4slVMY/U+DfCBW6V/4Uf60Zb8RPyd0PrAHHB8xDE
-> piv-p256 ZFgiIw AsjeXhDC4x+6TPG902gZSlW9qFC0JVoznTVmnpQgip9f
ZxDSKVSBiGCGVE1w+8yZwEJx59DkdFy/6Iq1tbHQ41I
-> piv-p256 5vmPtQ AmlUti+62DpPs4k9HN+ZdKry9pwPjS1HAtnTq9xm1zT1
zTmFw+xHDQSLkDyVXC8MtlxD5cw/tQ1yK5zlYoDKv8Y
-> w0-grease /mVZ/4hd jq'R
fvJoC6ucvHgsXQysHHQhXQQ3TMUhFIPpSHwOURHSHn/+9qFVd02Ey0DWl9LujA
--- 5VjQP6nmIwBXtA/0+zL+EQt9eZHtyp6oD6u5IPgW1s8
0ös¤(Âî(ÍÙ‰ñ·íXÎÞ5UJ ³pÁŽ·‡ùÏ}:ÔyˆAr÷¨_ã6÷
XDæ¬Fá9ÖŸ?1¿‡ä]ŒÐ4{½EhÆ<68>±ê”¿ë&~SÝAèÖ­ S² dVJËeÏSüˆ0/öWG½€ºèp^ßfÉŒ÷ú”‰<E2809D>Bê"^ìøÿÀÓ¶ÀÔ<C394>¯N×:ÒçEiBÞ·lK4I1<49>ï'ðˆK2à>^îìàݽ÷L)x¶§ë\«qè.Ýè#´¸.Yþ¢rÌ8sc‰NRQ66ºŠª xÍZ<C38D>)<29>¡Ø¹Bv)ÒíÊž<C38A>¥³4½¯" $õK dØÏ Mp*<>Ρ³åž«OØâsÁÅñ¼úä3CE#;¸!.Á.4ITäÄ÷{¢€f<E282AC>Tˆl”Ѻ ¥Ö{¶éÚëàÕ±™bxHn7^×Ë<C397>_%¡5Òfq¾ãPL‡lŒ`×Á0‰?-!ä½íuEJ™[•%bŽENWíÖùC,øE¹1´1a1ü€{EƸPcÇõã<C3B5>XÎTsB¸ ³m]RH"#v§aßóñXA(ŒŒóqœ­àcÀ`ÕŸ¹&<26>”OaÓÖù„p%º`‡½

View file

@ -0,0 +1 @@
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGFqG1mU7UX0uNATdPaodHdSm9YYDV4grqmf266D0ajO

Binary file not shown.

View file

@ -1,26 +0,0 @@
{
inputs,
lib,
minimal,
...
}:
{
imports = [
inputs.nixos-hardware.nixosModules.common-pc
inputs.nixos-hardware.nixosModules.common-pc-ssd
../../config/basic
../../config/support/initrd-ssh.nix
../../config/support/physical.nix
../../config/support/zfs.nix
./net.nix
./fs.nix
] ++ lib.lists.optionals (!minimal) [ ../../config/services/octoprint.nix ];
services.xserver.xkb = {
layout = "de";
};
services.thermald.enable = lib.mkForce false;
nixpkgs.hostPlatform = "x86_64-linux";
}

View file

@ -1,83 +0,0 @@
{ config, lib, ... }:
{
disko.devices = {
disk = {
internal-hdd = {
type = "disk";
device = "/dev/disk/by-id/${config.secrets.secrets.local.disko.internal-hdd}";
content = with lib.disko.gpt; {
type = "gpt";
partitions = {
boot = partEfi "1G";
swap = partSwap "16G";
rpool = lib.attrsets.recursiveUpdate (partLuksZfs "rpool" "rpool" "100%") {
content.extraFormatArgs = [ "--pbkdf pbkdf2" ];
};
};
};
};
};
zpool = with lib.disko.zfs; {
rpool = mkZpool { datasets = impermanenceZfsDatasets; };
};
};
services.zrepl = {
enable = true;
settings = {
global = {
logging = [
{
type = "syslog";
level = "info";
format = "human";
}
];
# TODO Monitoring
};
jobs = [
#{
# type = "push";
# name = "push-to-remote";
#}
{
type = "snap";
name = "mach-schnipp-schusss";
filesystems = {
"rpool/local/state<" = true;
"rpool/safe<" = true;
};
snapshotting = {
type = "periodic";
prefix = "zrepl-";
interval = "10m";
timestamp_format = "iso-8601";
};
pruning = {
keep = [
{
type = "regex";
regex = "^zrepl-.*$";
negate = true;
}
{
type = "grid";
grid = lib.concatStringsSep " | " [
"1x1d(keep=all)"
"142x1h(keep=2)"
"90x1d(keep=2)"
"500x7d"
];
regex = "^zrepl-.*$";
}
];
};
}
];
};
};
fileSystems."/state".neededForBoot = true;
fileSystems."/persist".neededForBoot = true;
}

View file

@ -1,18 +0,0 @@
{ config, ... }:
{
networking = {
inherit (config.secrets.secrets.local.networking) hostId;
};
networking.nftables.firewall.zones.untrusted.interfaces = [ "lan01" ];
systemd.network.networks = {
"lan01" = {
address = [ "192.168.178.32/24" ];
gateway = [ "192.168.178.1" ];
matchConfig.MACAddress = config.secrets.secrets.local.networking.interfaces.lan01.mac;
networkConfig = {
IPv6PrivacyExtensions = "yes";
MulticastDNS = true;
};
};
};
}

View file

@ -1 +0,0 @@
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIMTiFpqpCiJaFOcSjFrJWk7YPBiZLwoJRbyy1JgZWFmN

View file

@ -6,6 +6,7 @@
}:
let
inherit (lib)
any
mkOption
types
flip
@ -64,9 +65,15 @@ in
}
];
imports = [
imports =
let
existingCfg = flip any (attrNames config.home-manager.users) (
user: (config.home-manager.users.${user}.home.smb != [ ])
);
in
[
{
environment.systemPackages = [ pkgs.cifs-utils ];
environment.systemPackages = lib.optional existingCfg pkgs.cifs-utils;
fileSystems = mkMerge (
flip concatMap (attrNames config.home-manager.users) (
user:

Binary file not shown.

View file

@ -40,6 +40,10 @@
hostname = config.secrets.secrets.global.user.hetzner_ip;
user = "root";
};
"mailnix" = {
hostname = config.secrets.secrets.global.user.mailnix_ip;
user = "root";
};
"desktopnix" = {
hostname = "desktopnix.local";

View file

@ -1,6 +1,8 @@
{
pkgs,
config,
lib,
minimal,
...
}:
{
@ -10,12 +12,10 @@
# Patrick
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIDZixkix0KfKuq7Q19whS5FQQg51/AJGB5BiNF/7h/LM"
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIHxD4GOrwrBTG4/qQhm5hoSB2CP7W9g1LPWP11oLGOjQ"
# Simon old yubikey
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIFmees72GMKG/rsQQRhs2I/lQnJa0uW5KmZlNBeriCh0"
];
hashedPassword = config.secrets.secrets.global.users.root.passwordHash;
};
imports = [
imports = lib.optionals (!minimal) [
../patrick/alias.nix
../patrick/theme.nix