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 | | 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 🐚 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 🪟 WM | [Hyprland](./users/patrick/wayland/hyprland.nix) | Tiling window manager
🖼️ Styling | [Stylix](./modules/graphical/default.nix) | globally consistent styling 🖼️ Styling | [Stylix](./users/patrick/theme.nix) | globally consistent styling
📝 Editor | [NeoVim](./users/common/programs/nvim/default.nix) | Extensively configured neovim 📝 Editor | [NeoVim](./users/patrick/programs/nvim/default.nix) | Extensively configured neovim
🎮 Gaming | [Bottles](./users/common/programs/bottles.nix) & [Steam](./modules/optional/steam.nix) | Pew, Pew and such 🎮 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 🌐 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 💻 Terminal | [Kitty](./users/patrick/programs/kitty.nix) | fast terminal
🎵 Music | [Spotify](./users/common/programs/spicetify.nix) | Fancy looking spotify using spicetify 🎵 Music | [Spotify](./users/patrick/programs/spicetify.nix) | Fancy looking spotify using spicetify
📫 Mail | [Thunderbird](./users/common/programs/thunderbird.nix) | Best email client there is 📫 Mail | [Thunderbird](./users/common/programs/thunderbird.nix) | Best email client there is
🎛️ StreamDeck | [StreamDeck](./users/patrick/streamdeck.nix) | More hotkeys = more better
## Service Configuration ## Service Configuration
These are services I've set up 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 [impermanence](https://github.com/nix-community/impermanence) | stateless filesystem
[lanzaboote](https://github.com/nix-community/lanzaboote) | Secure Boot [lanzaboote](https://github.com/nix-community/lanzaboote) | Secure Boot
[stylix](https://github.com/danth/stylix) | theming [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) - This might take multiple minutes(~10)
- Alternatively boot an official nixos image connect with password - Alternatively boot an official nixos image connect with password
3. Copy ISO to usb using dd 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 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 4. Export all zpools and reboot into system
6. Retrieve hostkeys using `ssh-keyscan <host> | grep -o 'ssh-ed25519.*' > host/<target>/secrets/host.pub` 6. Retrieve hostkeys using `ssh-keyscan <host> | grep -o 'ssh-ed25519.*' > host/<target>/secrets/host.pub`
5. Deploy system 5. Deploy system

View file

@ -10,6 +10,7 @@
./net.nix ./net.nix
./nftables.nix ./nftables.nix
./nix.nix ./nix.nix
./secrets.nix
./ssh.nix ./ssh.nix
./system.nix ./system.nix
./users.nix ./users.nix
@ -28,6 +29,7 @@
inputs.agenix.nixosModules.default inputs.agenix.nixosModules.default
inputs.disko.nixosModules.disko inputs.disko.nixosModules.disko
inputs.home-manager.nixosModules.default inputs.home-manager.nixosModules.default
inputs.idmail.nixosModules.default
inputs.impermanence.nixosModules.impermanence inputs.impermanence.nixosModules.impermanence
inputs.lanzaboote.nixosModules.lanzaboote inputs.lanzaboote.nixosModules.lanzaboote
inputs.nix-topology.nixosModules.default 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; signal = uidGid 228;
netbird-main = uidGid 229; netbird-main = uidGid 229;
paperless = uidGid 315; paperless = uidGid 315;
stalwart-mail = uidGid 316;
build = uidGid 317;
systemd-oom = uidGid 300; systemd-oom = uidGid 300;
systemd-coredump = uidGid 301; systemd-coredump = uidGid 301;
patrick = uidGid 1000; 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 = { inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
nixp-meta.url = "git+https://forge.lel.lol/patrick/nixp-meta.git"; 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-octoprint.url = "github:patrickdag/nixpkgs/octoprint-update";
nixpkgs-wayland = { nixpkgs-wayland = {

View file

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

View file

@ -38,7 +38,8 @@ let
ipOf = ipOf =
hostName: hostName:
if hostName == "octoprint" then if hostName == "octoprint" then
nodes.testienix.config.wireguard.elisabeth.ipv4 #nodes.testienix.config.wireguard.elisabeth.ipv4
"0.0.0.0"
else else
nodes."elisabeth-${hostName}".config.wireguard.elisabeth.ipv4; nodes."elisabeth-${hostName}".config.wireguard.elisabeth.ipv4;
in 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 let
inherit (lib) inherit (lib)
any
mkOption mkOption
types types
flip flip
@ -64,42 +65,48 @@ in
} }
]; ];
imports = [ imports =
{ let
environment.systemPackages = [ pkgs.cifs-utils ]; existingCfg = flip any (attrNames config.home-manager.users) (
fileSystems = mkMerge ( user: (config.home-manager.users.${user}.home.smb != [ ])
flip concatMap (attrNames config.home-manager.users) (
user:
let
parentPath = "/home/${user}/smb";
cfg = config.home-manager.users.${user}.home.smb;
inherit (config.users.users.${user}) uid;
inherit (config.users.groups.${user}) gid;
in
flip map cfg (cfg: {
"${parentPath}/${cfg.localPath}" =
let
options =
baseOptions
++ [
"uid=${toString uid}"
"gid=${toString gid}"
"file_mode=0600"
"dir_mode=0700"
"credentials=${cfg.credentials}"
"x-systemd.automount"
"_netdev"
]
++ (optional (!cfg.automatic) "noauto");
in
{
inherit options;
device = "//${cfg.address}/${cfg.remotePath}";
fsType = "cifs";
};
})
)
); );
} in
]; [
{
environment.systemPackages = lib.optional existingCfg pkgs.cifs-utils;
fileSystems = mkMerge (
flip concatMap (attrNames config.home-manager.users) (
user:
let
parentPath = "/home/${user}/smb";
cfg = config.home-manager.users.${user}.home.smb;
inherit (config.users.users.${user}) uid;
inherit (config.users.groups.${user}) gid;
in
flip map cfg (cfg: {
"${parentPath}/${cfg.localPath}" =
let
options =
baseOptions
++ [
"uid=${toString uid}"
"gid=${toString gid}"
"file_mode=0600"
"dir_mode=0700"
"credentials=${cfg.credentials}"
"x-systemd.automount"
"_netdev"
]
++ (optional (!cfg.automatic) "noauto");
in
{
inherit options;
device = "//${cfg.address}/${cfg.remotePath}";
fsType = "cifs";
};
})
)
);
}
];
} }

Binary file not shown.

View file

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

View file

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