feat: globals

feat: more vlan config
This commit is contained in:
Patrick 2024-12-20 20:40:27 +01:00
parent 8332bc45ba
commit 053365c277
Signed by: patrick
GPG key ID: 451F95EFB8BECD0F
196 changed files with 1624 additions and 1448 deletions

View file

@ -2,13 +2,14 @@
config, config,
lib, lib,
pkgs, pkgs,
globals,
... ...
}: }:
{ {
boot = lib.mkIf (!config.boot.isContainer) { boot = lib.mkIf (!config.boot.isContainer) {
initrd.systemd = { initrd.systemd = {
enable = true; enable = true;
emergencyAccess = config.secrets.secrets.global.users.root.passwordHash; emergencyAccess = globals.users.root.hashedPassword;
extraBin.ip = "${pkgs.iproute2}/bin/ip"; extraBin.ip = "${pkgs.iproute2}/bin/ip";
extraBin.cryptsetup = "${pkgs.cryptsetup}/bin/cryptsetup"; extraBin.cryptsetup = "${pkgs.cryptsetup}/bin/cryptsetup";
users.root.shell = "${pkgs.bashInteractive}/bin/bash"; users.root.shell = "${pkgs.bashInteractive}/bin/bash";

View file

@ -20,6 +20,7 @@
../../modules/deterministic-ids.nix ../../modules/deterministic-ids.nix
../../modules/distributed-config.nix ../../modules/distributed-config.nix
../../modules/globals.nix
../../modules/meta.nix ../../modules/meta.nix
../../modules/iwd.nix ../../modules/iwd.nix
../../modules/secrets.nix ../../modules/secrets.nix

View file

@ -77,8 +77,5 @@
let let
local = config.node.secretsDir + "/secrets.nix.age"; local = config.node.secretsDir + "/secrets.nix.age";
in in
{ lib.optionalAttrs (config.node.name != null && lib.pathExists local) { inherit local; };
global = ../../secrets/secrets.nix.age;
}
// lib.optionalAttrs (config.node.name != null && lib.pathExists local) { inherit local; };
} }

View file

@ -1,7 +1,7 @@
{ {
wireguard.elisabeth = { wireguard.services = {
client.via = "elisabeth"; client.via = "nucnix";
firewallRuleForNode.elisabeth.allowedTCPPorts = [ 3000 ]; firewallRuleForNode.nucnix-nginx.allowedTCPPorts = [ 3000 ];
}; };
imports = [ ../actual.nix ]; imports = [ ../actual.nix ];
services.actual = { services.actual = {

View file

@ -1,8 +1,8 @@
{ config, lib, ... }: { config, ... }:
{ {
wireguard.elisabeth = { wireguard.services = {
client.via = "elisabeth"; client.via = "nucnix";
firewallRuleForNode.elisabeth.allowedTCPPorts = [ config.services.adguardhome.port ]; firewallRuleForNode.nucnix-nginx.allowedTCPPorts = [ config.services.adguardhome.port ];
}; };
services.adguardhome = { services.adguardhome = {
enable = true; enable = true;
@ -30,11 +30,11 @@
]; ];
}; };
user_rules = [ user_rules = [
"||adguardhome.${config.secrets.secrets.global.domains.web}^$dnsrewrite=${lib.net.cidr.host config.secrets.secrets.global.net.ips.elisabeth config.secrets.secrets.global.net.privateSubnetv4}" # "||adguardhome.${config.secrets.secrets.global.domains.web}^$dnsrewrite=${lib.net.cidr.host config.secrets.secrets.global.net.ips.elisabeth config.secrets.secrets.global.net.privateSubnetv4}"
"||nc.${config.secrets.secrets.global.domains.web}^$dnsrewrite=${lib.net.cidr.host config.secrets.secrets.global.net.ips.elisabeth config.secrets.secrets.global.net.privateSubnetv4}" # "||nc.${config.secrets.secrets.global.domains.web}^$dnsrewrite=${lib.net.cidr.host config.secrets.secrets.global.net.ips.elisabeth config.secrets.secrets.global.net.privateSubnetv4}"
"||immich.${config.secrets.secrets.global.domains.web}^$dnsrewrite=${lib.net.cidr.host config.secrets.secrets.global.net.ips.elisabeth config.secrets.secrets.global.net.privateSubnetv4}" # "||immich.${config.secrets.secrets.global.domains.web}^$dnsrewrite=${lib.net.cidr.host config.secrets.secrets.global.net.ips.elisabeth config.secrets.secrets.global.net.privateSubnetv4}"
"||smb.${config.secrets.secrets.global.domains.web}^$dnsrewrite=${lib.net.cidr.host config.secrets.secrets.global.net.ips.elisabeth-samba config.secrets.secrets.global.net.privateSubnetv4}" # "||smb.${config.secrets.secrets.global.domains.web}^$dnsrewrite=${lib.net.cidr.host config.secrets.secrets.global.net.ips.elisabeth-samba config.secrets.secrets.global.net.privateSubnetv4}"
"||fritz.box^$dnsrewrite=${lib.net.cidr.host 1 config.secrets.secrets.global.net.privateSubnetv4}" # "||fritz.box^$dnsrewrite=${lib.net.cidr.host 1 config.secrets.secrets.global.net.privateSubnetv4}"
]; ];
dhcp.enabled = false; dhcp.enabled = false;
ratelimit = 60; ratelimit = 60;

View file

@ -1,4 +1,10 @@
{ config, pkgs, lib, ... }: {
config,
pkgs,
lib,
globals,
...
}:
let let
prestart = pkgs.writeShellScript "blog-pre" '' prestart = pkgs.writeShellScript "blog-pre" ''
if [ ! -d ./.ssh ]; then if [ ! -d ./.ssh ]; then
@ -8,18 +14,20 @@ let
ssh-keygen -t ed25519 -N "" -f .ssh/id_ed25519 ssh-keygen -t ed25519 -N "" -f .ssh/id_ed25519
fi fi
if [ ! -d ./blog ]; then if [ ! -d ./blog ]; then
${ ${lib.getExe pkgs.git} clone --recurse-submodules ssh://git@forge.lel.lol:9922/patrick/blog.git ||\
lib.getExe pkgs.git
} clone --recurse-submodules ssh://git@forge.lel.lol:9922/patrick/blog.git ||\
echo "failed to clone the repository did you forget to add the ssh key?" echo "failed to clone the repository did you forget to add the ssh key?"
fi fi
''; '';
in { in
wireguard.elisabeth = { {
client.via = "elisabeth"; wireguard.services = {
firewallRuleForNode.elisabeth.allowedTCPPorts = [ 80 ]; client.via = "nucnix";
firewallRuleForNode.nucnix-nginx.allowedTCPPorts = [ 80 ];
}; };
environment.systemPackages = [ pkgs.signal-cli pkgs.cargo ]; environment.systemPackages = [
pkgs.signal-cli
pkgs.cargo
];
services.nginx = { services.nginx = {
enable = true; enable = true;
user = "blog"; user = "blog";
@ -31,12 +39,14 @@ in {
"[forge.lel.lol]:9922".publicKey = "[forge.lel.lol]:9922".publicKey =
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOWoGqHwkLVFXJwYcKs3CjQognvlZmROUIgkvvUgNalx"; "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOWoGqHwkLVFXJwYcKs3CjQognvlZmROUIgkvvUgNalx";
}; };
environment.persistence."/persist".directories = [{ environment.persistence."/persist".directories = [
directory = "/var/lib/blog"; {
user = "blog"; directory = "/var/lib/blog";
group = "blog"; user = "blog";
mode = "0700"; group = "blog";
}]; mode = "0700";
}
];
systemd.timers.blog-update = { systemd.timers.blog-update = {
wantedBy = [ "timers.target" ]; wantedBy = [ "timers.target" ];
timerConfig = { timerConfig = {
@ -60,14 +70,17 @@ in {
else else
echo "Commiting newest changes" echo "Commiting newest changes"
git -c user.name="blog-bot" \ git -c user.name="blog-bot" \
-c user.email="blog-bot@${config.secrets.secrets.global.domains.mail_public}" \ -c user.email="blog-bot@${globals.domains.mail_public}" \
commit -m "Automatic commit for blog on $(date -u -I)" commit -m "Automatic commit for blog on $(date -u -I)"
fi fi
git pull --rebase git pull --rebase
git push git push
${lib.getExe pkgs.zola} -r public build ${lib.getExe pkgs.zola} -r public build
''; '';
path = [ pkgs.openssh pkgs.git ]; path = [
pkgs.openssh
pkgs.git
];
serviceConfig = { serviceConfig = {
Requires = "blog"; Requires = "blog";
Type = "oneshot"; Type = "oneshot";

View file

@ -1,4 +1,4 @@
{ config, ... }: { config, globals, ... }:
{ {
age.secrets.cloudflare_token_dns = { age.secrets.cloudflare_token_dns = {
rekeyFile = config.node.secretsDir + "/cloudflare_api_token.age"; rekeyFile = config.node.secretsDir + "/cloudflare_api_token.age";
@ -8,12 +8,12 @@
networking.enableIPv6 = false; networking.enableIPv6 = false;
services.ddclient = { services.ddclient = {
enable = true; enable = true;
zone = config.secrets.secrets.global.domains.web; zone = globals.domains.web;
protocol = "Cloudflare"; protocol = "Cloudflare";
username = "token"; username = "token";
usev4 = "webv4, webv4='https://cloudflare.com/cdn-cgi/trace', webv4-skip='ip='"; usev4 = "webv4, webv4='https://cloudflare.com/cdn-cgi/trace', webv4-skip='ip='";
usev6 = ""; usev6 = "";
passwordFile = config.age.secrets.cloudflare_token_dns.path; passwordFile = config.age.secrets.cloudflare_token_dns.path;
domains = [ config.secrets.secrets.global.domains.web ]; domains = [ globals.domains.web ];
}; };
} }

View file

@ -1,9 +1,14 @@
{ config, nodes, ... }: {
config,
nodes,
globals,
...
}:
{ {
i18n.supportedLocales = [ "all" ]; i18n.supportedLocales = [ "all" ];
wireguard.elisabeth = { wireguard.services = {
client.via = "elisabeth"; client.via = "nucnix";
firewallRuleForNode.elisabeth.allowedTCPPorts = [ 80 ]; firewallRuleForNode.nucnix-nginx.allowedTCPPorts = [ 80 ];
}; };
age.secrets.appKey = { age.secrets.appKey = {
@ -16,12 +21,12 @@
services.firefly-iii = { services.firefly-iii = {
enable = true; enable = true;
enableNginx = true; enableNginx = true;
virtualHost = "money.${config.secrets.secrets.global.domains.web}"; virtualHost = globals.services.firefly.domain;
settings = { settings = {
APP_URL = "https://money.${config.secrets.secrets.global.domains.web}"; APP_URL = "https://${globals.services.firefly.domain}";
TZ = "Europe/Berlin"; TZ = "Europe/Berlin";
TRUSTED_PROXIES = nodes.elisabeth.config.wireguard.elisabeth.ipv4; TRUSTED_PROXIES = nodes.nucnix-nginx.config.wireguard.services.ipv4;
SITE_OWNER = "firefly-admin@${config.secrets.secrets.global.domains.mail_public}"; SITE_OWNER = "firefly-admin@${globals.domains.mail_public}";
APP_KEY_FILE = config.age.secrets.appKey.path; APP_KEY_FILE = config.age.secrets.appKey.path;
AUTHENTICATION_GUARD = "remote_user_guard"; AUTHENTICATION_GUARD = "remote_user_guard";
AUTHENTICATION_GUARD_HEADER = "X-User"; AUTHENTICATION_GUARD_HEADER = "X-User";

View file

@ -1,13 +1,11 @@
{ {
config, config,
globals,
nodes, nodes,
pkgs, pkgs,
lib, lib,
... ...
}: }:
let
forgejoDomain = "forge.${config.secrets.secrets.global.domains.web}";
in
{ {
age.secrets.resticpasswd = { age.secrets.resticpasswd = {
generator.script = "alnum"; generator.script = "alnum";
@ -27,8 +25,8 @@ in
passwordFile = config.age.secrets.resticpasswd.path; passwordFile = config.age.secrets.resticpasswd.path;
hetznerStorageBox = { hetznerStorageBox = {
enable = true; enable = true;
inherit (config.secrets.secrets.global.hetzner) mainUser; inherit (globals.hetzner) mainUser;
inherit (config.secrets.secrets.global.hetzner.users.forgejo) subUid path; inherit (globals.hetzner.users.forgejo) subUid path;
sshAgeSecret = "forgejoHetznerSsh"; sshAgeSecret = "forgejoHetznerSsh";
}; };
paths = [ config.services.forgejo.stateDir ]; paths = [ config.services.forgejo.stateDir ];
@ -52,9 +50,9 @@ in
home = config.services.forgejo.stateDir; home = config.services.forgejo.stateDir;
}; };
wireguard.elisabeth = { wireguard.services = {
client.via = "elisabeth"; client.via = "nucnix";
firewallRuleForNode.elisabeth.allowedTCPPorts = [ firewallRuleForNode.nucnix-nginx.allowedTCPPorts = [
config.services.forgejo.settings.server.HTTP_PORT config.services.forgejo.settings.server.HTTP_PORT
]; ];
}; };
@ -86,7 +84,7 @@ in
group = "stalwart-mail"; group = "stalwart-mail";
mode = "440"; mode = "440";
}; };
services.idmail.provision.mailboxes."forge@${config.secrets.secrets.global.domains.mail_public}" = { services.idmail.provision.mailboxes."forge@${globals.domains.mail_public}" = {
password_hash = "%{file:${nodes.mailnix.config.age.secrets.idmail-forgejo-passwd-hash.path}}%"; password_hash = "%{file:${nodes.mailnix.config.age.secrets.idmail-forgejo-passwd-hash.path}}%";
owner = "admin"; owner = "admin";
}; };
@ -116,9 +114,9 @@ in
# federation.ENABLED = true; # federation.ENABLED = true;
mailer = { mailer = {
ENABLED = true; ENABLED = true;
SMTP_ADDR = "smtp.${config.secrets.secrets.global.domains.mail_public}"; SMTP_ADDR = "smtp.${globals.domains.mail_public}";
FROM = "forge@${config.secrets.secrets.global.domains.mail_public}"; FROM = "forge@${globals.domains.mail_public}";
USER = "forge@${config.secrets.secrets.global.domains.mail_public}"; USER = "forge@${globals.domains.mail_public}";
SEND_AS_PLAIN_TEXT = true; SEND_AS_PLAIN_TEXT = true;
}; };
oauth2_client = { oauth2_client = {
@ -137,8 +135,8 @@ in
server = { server = {
HTTP_ADDR = "0.0.0.0"; HTTP_ADDR = "0.0.0.0";
HTTP_PORT = 3000; HTTP_PORT = 3000;
DOMAIN = forgejoDomain; DOMAIN = globals.services.forgejo.domain;
ROOT_URL = "https://${forgejoDomain}/"; ROOT_URL = "https://${globals.services.forgejo.domain}/";
LANDING_PAGE = "login"; LANDING_PAGE = "login";
SSH_PORT = 9922; SSH_PORT = 9922;
}; };
@ -176,7 +174,7 @@ in
"--key" "--key"
clientId clientId
"--auto-discover-url" "--auto-discover-url"
"https://auth.${config.secrets.secrets.global.domains.web}/oauth2/openid/${clientId}/.well-known/openid-configuration" "https://auth.${globals.domains.web}/oauth2/openid/${clientId}/.well-known/openid-configuration"
"--scopes" "--scopes"
"email" "email"
"--scopes" "--scopes"

View file

@ -1,7 +1,7 @@
{ {
wireguard.elisabeth = { wireguard.services = {
client.via = "elisabeth"; client.via = "nucnix";
firewallRuleForNode.elisabeth.allowedTCPPorts = [ 3000 ]; firewallRuleForNode.nucnix-nginx.allowedTCPPorts = [ 3000 ];
}; };
services.homebox = { services.homebox = {
enable = true; enable = true;

View file

@ -1,12 +1,13 @@
{ {
inputs, inputs,
config, config,
globals,
... ...
}: }:
let let
domain = config.secrets.secrets.global.domains.mail_public; domain = globals.domains.mail_public;
idmailDomain = "alias.${domain}"; idmailDomain = globals.services.idmail.domain;
priv_domain = config.secrets.secrets.global.domains.mail_private; priv_domain = globals.domains.mail_private;
mkRandomSecret = { mkRandomSecret = {
generator.script = "alnum"; generator.script = "alnum";

View file

@ -3,11 +3,12 @@
pkgs, pkgs,
nodes, nodes,
config, config,
globals,
... ...
}: }:
let let
version = "v1.119.1"; version = "v1.119.1";
immichDomain = "immich.${config.secrets.secrets.global.domains.web}"; immichDomain = "immich.${globals.domains.web}";
ipImmichMachineLearning = "10.89.0.10"; ipImmichMachineLearning = "10.89.0.10";
ipImmichPostgres = "10.89.0.12"; ipImmichPostgres = "10.89.0.12";
@ -57,10 +58,10 @@ let
}; };
notifications.smtp = { notifications.smtp = {
enabled = true; enabled = true;
from = "immich@${config.secrets.secrets.global.domains.mail_public}"; from = "immich@${globals.domains.mail_public}";
transport = { transport = {
username = "immich@${config.secrets.secrets.global.domains.mail_public}"; username = "immich@${globals.domains.mail_public}";
host = "smtp.${config.secrets.secrets.global.domains.mail_public}"; host = "smtp.${globals.domains.mail_public}";
port = 465; port = 465;
}; };
}; };
@ -91,7 +92,7 @@ let
clientId = "immich"; clientId = "immich";
# clientSecret will be dynamically added in activation script # clientSecret will be dynamically added in activation script
issuerUrl = "https://auth.${config.secrets.secrets.global.domains.web}/oauth2/openid/${clientId}"; issuerUrl = "https://auth.${globals.domains.web}/oauth2/openid/${clientId}";
scope = "openid email profile"; scope = "openid email profile";
storageLabelClaim = "preferred_username"; storageLabelClaim = "preferred_username";
}; };
@ -163,7 +164,7 @@ in
group = "stalwart-mail"; group = "stalwart-mail";
mode = "440"; mode = "440";
}; };
services.idmail.provision.mailboxes."immich@${config.secrets.secrets.global.domains.mail_public}" = { services.idmail.provision.mailboxes."immich@${globals.domains.mail_public}" = {
password_hash = "%{file:${nodes.mailnix.config.age.secrets.idmail-immich-passwd-hash.path}}%"; password_hash = "%{file:${nodes.mailnix.config.age.secrets.idmail-immich-passwd-hash.path}}%";
owner = "admin"; owner = "admin";
}; };
@ -193,8 +194,8 @@ in
passwordFile = config.age.secrets.resticpasswd.path; passwordFile = config.age.secrets.resticpasswd.path;
hetznerStorageBox = { hetznerStorageBox = {
enable = true; enable = true;
inherit (config.secrets.secrets.global.hetzner) mainUser; inherit (globals.hetzner) mainUser;
inherit (config.secrets.secrets.global.hetzner.users.immich) subUid path; inherit (globals.hetzner.users.immich) subUid path;
sshAgeSecret = "immichHetznerSsh"; sshAgeSecret = "immichHetznerSsh";
}; };
backupPrepareCommand = '' backupPrepareCommand = ''
@ -242,15 +243,15 @@ in
vcpu = 12; vcpu = 12;
}; };
wireguard.elisabeth = { wireguard.services = {
client.via = "elisabeth"; client.via = "nucnix";
firewallRuleForNode.elisabeth.allowedTCPPorts = [ 3000 ]; firewallRuleForNode.nucnix-nginx.allowedTCPPorts = [ 3000 ];
}; };
networking.nftables.chains.forward.into-immich-container = { networking.nftables.chains.forward.into-immich-container = {
after = [ "conntrack" ]; after = [ "conntrack" ];
rules = [ rules = [
"iifname elisabeth ip saddr ${nodes.elisabeth.config.wireguard.elisabeth.ipv4} tcp dport 2283 accept" "iifname elisabeth ip saddr ${nodes.nucnix-nginx.config.wireguard.services.ipv4} tcp dport 2283 accept"
"iifname podman1 oifname lan accept" "iifname podman1 oifname lan accept"
]; ];
}; };

View file

@ -1,8 +1,8 @@
{ config, ... }: { globals, ... }:
{ {
services.invidious = { services.invidious = {
enable = true; enable = true;
domain = "yt.${config.secrets.secrets.global.domains.web}"; inherit (globals.services.invidious) domain;
sig-helper.enable = true; sig-helper.enable = true;
settings = { settings = {
external_port = 443; external_port = 443;
@ -33,8 +33,8 @@
} }
]; ];
wireguard.elisabeth = { wireguard.services = {
client.via = "elisabeth"; client.via = "nucnix";
firewallRuleForNode.elisabeth.allowedTCPPorts = [ 3000 ]; firewallRuleForNode.nucnix-nginx.allowedTCPPorts = [ 3000 ];
}; };
} }

View file

@ -1,11 +1,13 @@
{ config, pkgs, ... }:
let
kanidmdomain = "auth.${config.secrets.secrets.global.domains.web}";
in
{ {
wireguard.elisabeth = { globals,
client.via = "elisabeth"; config,
firewallRuleForNode.elisabeth.allowedTCPPorts = [ 3000 ]; pkgs,
...
}:
{
wireguard.services = {
client.via = "nucnix";
firewallRuleForNode.nucnix-nginx.allowedTCPPorts = [ 3000 ];
}; };
environment.persistence."/persist".directories = [ environment.persistence."/persist".directories = [
{ {
@ -56,8 +58,8 @@ in
package = pkgs.kanidm.withSecretProvisioning; package = pkgs.kanidm.withSecretProvisioning;
enableServer = true; enableServer = true;
serverSettings = { serverSettings = {
domain = kanidmdomain; inherit (globals.services.kanidm) domain;
origin = "https://${kanidmdomain}"; origin = "https://${globals.services.kanidm.domain}";
tls_chain = config.age.secrets.kanidm-cert.path; tls_chain = config.age.secrets.kanidm-cert.path;
tls_key = config.age.secrets.kanidm-key.path; tls_key = config.age.secrets.kanidm-key.path;
bindaddress = "0.0.0.0:3000"; bindaddress = "0.0.0.0:3000";
@ -83,8 +85,8 @@ in
}; };
systems.oauth2.paperless = { systems.oauth2.paperless = {
displayName = "paperless"; displayName = "paperless";
originUrl = "https://ppl.${config.secrets.secrets.global.domains.web}/accounts/oidc/kanidm/login/callback/"; originUrl = "https://${globals.services.paperless.domain}/accounts/oidc/kanidm/login/callback/";
originLanding = "https://ppl.${config.secrets.secrets.global.domains.web}/"; originLanding = "https://${globals.services.paperless.domain}/";
basicSecretFile = config.age.secrets.oauth2-paperless.path; basicSecretFile = config.age.secrets.oauth2-paperless.path;
scopeMaps."paperless.access" = [ scopeMaps."paperless.access" = [
"openid" "openid"
@ -103,8 +105,8 @@ in
}; };
systems.oauth2.nextcloud = { systems.oauth2.nextcloud = {
displayName = "nextcloud"; displayName = "nextcloud";
originUrl = "https://nc.${config.secrets.secrets.global.domains.web}/"; originUrl = "https://${globals.services.nextcloud.domain}/";
originLanding = "https://nc.${config.secrets.secrets.global.domains.web}/"; originLanding = "https://${globals.services.nextcloud.domain}/";
basicSecretFile = config.age.secrets.oauth2-nextcloud.path; basicSecretFile = config.age.secrets.oauth2-nextcloud.path;
allowInsecureClientDisablePkce = true; allowInsecureClientDisablePkce = true;
scopeMaps."nextcloud.access" = [ scopeMaps."nextcloud.access" = [
@ -125,10 +127,10 @@ in
systems.oauth2.immich = { systems.oauth2.immich = {
displayName = "Immich"; displayName = "Immich";
originUrl = [ originUrl = [
"https://immich.${config.secrets.secrets.global.domains.web}/auth/login" "https://${globals.services.immich.domain}/auth/login"
"https://immich.${config.secrets.secrets.global.domains.web}/api/oauth/mobile-redirect" "https://${globals.services.immich.domain}/api/oauth/mobile-redirect"
]; ];
originLanding = "https://immich.${config.secrets.secrets.global.domains.web}/"; originLanding = "https://${globals.services.immich.domain}/";
basicSecretFile = config.age.secrets.oauth2-immich.path; basicSecretFile = config.age.secrets.oauth2-immich.path;
allowInsecureClientDisablePkce = true; allowInsecureClientDisablePkce = true;
enableLegacyCrypto = true; enableLegacyCrypto = true;
@ -149,8 +151,8 @@ in
systems.oauth2.oauth2-proxy = { systems.oauth2.oauth2-proxy = {
displayName = "Oauth2-Proxy"; displayName = "Oauth2-Proxy";
originUrl = "https://oauth2.${config.secrets.secrets.global.domains.web}/oauth2/callback"; originUrl = "https://${globals.services.oauth2-proxy.domain}/oauth2/callback";
originLanding = "https://oauth2.${config.secrets.secrets.global.domains.web}/"; originLanding = "https://${globals.services.oauth2-proxy.domain}/";
basicSecretFile = config.age.secrets.oauth2-proxy.path; basicSecretFile = config.age.secrets.oauth2-proxy.path;
scopeMaps."adguardhome.access" = [ scopeMaps."adguardhome.access" = [
"openid" "openid"
@ -202,8 +204,8 @@ in
}; };
systems.oauth2.forgejo = { systems.oauth2.forgejo = {
displayName = "Forgejo"; displayName = "Forgejo";
originUrl = "https://forge.${config.secrets.secrets.global.domains.web}/user/oauth2/kanidm/callback"; originUrl = "https://${globals.services.forgejo.domain}/user/oauth2/kanidm/callback";
originLanding = "https://forge.${config.secrets.secrets.global.domains.web}/"; originLanding = "https://${globals.services.forgejo.domain}/";
basicSecretFile = config.age.secrets.oauth2-forgejo.path; basicSecretFile = config.age.secrets.oauth2-forgejo.path;
scopeMaps."forgejo.access" = [ scopeMaps."forgejo.access" = [
"openid" "openid"
@ -223,10 +225,10 @@ in
public = true; public = true;
displayName = "Netbird"; displayName = "Netbird";
originUrl = [ originUrl = [
"https://netbird.${config.secrets.secrets.global.domains.web}/peers" "https://${globals.services.netbird.domain}/peers"
"https://netbird.${config.secrets.secrets.global.domains.web}/add-peers" "https://${globals.services.netbird.domain}/add-peers"
]; ];
originLanding = "https://netbird.${config.secrets.secrets.global.domains.web}/"; originLanding = "https://${globals.services.netbird.domain}/";
preferShortUsername = true; preferShortUsername = true;
enableLocalhostRedirects = true; enableLocalhostRedirects = true;
enableLegacyCrypto = true; enableLegacyCrypto = true;

View file

@ -4,11 +4,12 @@
config, config,
pkgs, pkgs,
lib, lib,
globals,
... ...
}: }:
let let
priv_domain = config.secrets.secrets.global.domains.mail_private; priv_domain = globals.domains.mail_private;
domain = config.secrets.secrets.global.domains.mail_public; domain = globals.domains.mail_public;
mailDomains = [ mailDomains = [
priv_domain priv_domain
domain domain
@ -41,8 +42,8 @@ in
passwordFile = config.age.secrets.resticpasswd.path; passwordFile = config.age.secrets.resticpasswd.path;
hetznerStorageBox = { hetznerStorageBox = {
enable = true; enable = true;
inherit (config.secrets.secrets.global.hetzner) mainUser; inherit (globals.hetzner) mainUser;
inherit (config.secrets.secrets.global.hetzner.users.maddy) subUid path; inherit (globals.hetzner.users.maddy) subUid path;
sshAgeSecret = "maddyHetznerSsh"; sshAgeSecret = "maddyHetznerSsh";
}; };
paths = [ paths = [

View file

@ -1,8 +1,13 @@
{ config, lib, ... }:
{ {
wireguard.elisabeth = { config,
client.via = "elisabeth"; lib,
firewallRuleForNode.elisabeth.allowedTCPPorts = [ globals,
...
}:
{
wireguard.services = {
client.via = "nucnix";
firewallRuleForNode.nucnix-nginx.allowedTCPPorts = [
80 # dashboard 80 # dashboard
3000 # management 3000 # management
8012 # signal 8012 # signal
@ -47,20 +52,20 @@
clients.main = { clients.main = {
port = 51820; port = 51820;
environment = { environment = {
NB_MANAGEMENT_URL = "https://netbird.${config.secrets.secrets.global.domains.web}"; NB_MANAGEMENT_URL = "https://${globals.services.netbird.domain}";
NB_ADMIN_URL = "https://netbird.${config.secrets.secrets.global.domains.web}"; NB_ADMIN_URL = "https://${globals.services.netbird.domain}";
NB_HOSTNAME = "home"; NB_HOSTNAME = "home";
}; };
}; };
server = { server = {
enable = true; enable = true;
domain = "netbird.${config.secrets.secrets.global.domains.web}"; inherit (globals.services.netbird) domain;
dashboard = { dashboard = {
enableNginx = true; enableNginx = true;
settings = { settings = {
AUTH_AUTHORITY = "https://auth.${config.secrets.secrets.global.domains.web}/oauth2/openid/netbird"; AUTH_AUTHORITY = "https://${globals.services.kanidm.domain}/oauth2/openid/netbird";
# Fix Kanidm not supporting fragmented URIs # Fix Kanidm not supporting fragmented URIs
AUTH_REDIRECT_URI = "/peers"; AUTH_REDIRECT_URI = "/peers";
AUTH_SILENT_REDIRECT_URI = "/add-peers"; AUTH_SILENT_REDIRECT_URI = "/add-peers";
@ -69,7 +74,7 @@
relay = { relay = {
authSecretFile = config.age.secrets.relaySecret.path; authSecretFile = config.age.secrets.relaySecret.path;
settings.NB_EXPOSED_ADDRESS = "rels://netbird.${config.secrets.secrets.global.domains.web}:443"; settings.NB_EXPOSED_ADDRESS = "rels://${globals.services.netbird.domain}:443";
}; };
coturn = { coturn = {
@ -82,12 +87,12 @@
# DNS server should do the lookup this is not used # DNS server should do the lookup this is not used
dnsDomain = "internal.invalid"; dnsDomain = "internal.invalid";
singleAccountModeDomain = "netbird.patrick"; singleAccountModeDomain = "netbird.patrick";
oidcConfigEndpoint = "https://auth.${config.secrets.secrets.global.domains.web}/oauth2/openid/netbird/.well-known/openid-configuration"; oidcConfigEndpoint = "https://${globals.services.kanidm.domain}/oauth2/openid/netbird/.well-known/openid-configuration";
settings = { settings = {
TURNConfig = { TURNConfig = {
Secret._secret = config.age.secrets.coturnSecret.path; Secret._secret = config.age.secrets.coturnSecret.path;
}; };
Signal.URI = "netbird.${config.secrets.secrets.global.domains.web}:443"; Signal.URI = "${globals.services.netbird.domain}:443";
HttpConfig = { HttpConfig = {
# This is not possible # This is not possible
# failed validating JWT token sent from peer y1ParZkbzVMQGeU/KMycYl75v90i2O6EwgO1YQZnSFs= with error rpc error: code = Internal desc = unable to fetch account with claims, err: user ID is empty # failed validating JWT token sent from peer y1ParZkbzVMQGeU/KMycYl75v90i2O6EwgO1YQZnSFs= with error rpc error: code = Internal desc = unable to fetch account with claims, err: user ID is empty

View file

@ -3,11 +3,9 @@
pkgs, pkgs,
config, config,
nodes, nodes,
globals,
... ...
}: }:
let
hostName = "nc.${config.secrets.secrets.global.domains.web}";
in
{ {
age.secrets.mailnix-passwd = { age.secrets.mailnix-passwd = {
@ -28,7 +26,7 @@ in
group = "stalwart-mail"; group = "stalwart-mail";
mode = "440"; mode = "440";
}; };
services.idmail.provision.mailboxes."nextcloud@${config.secrets.secrets.global.domains.mail_public}" = { services.idmail.provision.mailboxes."nextcloud@${globals.domains.mail_public}" = {
password_hash = "%{file:${nodes.mailnix.config.age.secrets.idmail-nextcloud-passwd-hash.path}}%"; password_hash = "%{file:${nodes.mailnix.config.age.secrets.idmail-nextcloud-passwd-hash.path}}%";
owner = "admin"; owner = "admin";
}; };
@ -57,7 +55,7 @@ in
services.postgresql.package = pkgs.postgresql_16; services.postgresql.package = pkgs.postgresql_16;
services.nextcloud = { services.nextcloud = {
inherit hostName; hostName = globals.services.nextcloud.domain;
enable = true; enable = true;
package = pkgs.nextcloud30; package = pkgs.nextcloud30;
configureRedis = true; configureRedis = true;
@ -79,7 +77,7 @@ in
phpOptions."opcache.interned_strings_buffer" = "32"; phpOptions."opcache.interned_strings_buffer" = "32";
settings = { settings = {
default_phone_region = "DE"; default_phone_region = "DE";
trusted_proxies = [ nodes.elisabeth.config.wireguard.elisabeth.ipv4 ]; trusted_proxies = [ nodes.nucnix-nginx.config.wireguard.services.ipv4 ];
overwriteprotocol = "https"; overwriteprotocol = "https";
maintenance_window_start = 2; maintenance_window_start = 2;
enabledPreviewProviders = [ enabledPreviewProviders = [
@ -97,13 +95,13 @@ in
]; ];
mail_smtpmode = "smtp"; mail_smtpmode = "smtp";
mail_smtphost = "smtp.${config.secrets.secrets.global.domains.mail_public}"; mail_smtphost = "smtp.${globals.domains.mail_public}";
mail_smtpport = 465; mail_smtpport = 465;
mail_from_address = "nextcloud"; mail_from_address = "nextcloud";
mail_smtpsecure = "ssl"; mail_smtpsecure = "ssl";
mail_domain = config.secrets.secrets.global.domains.mail_public; mail_domain = globals.domains.mail_public;
mail_smtpauth = true; mail_smtpauth = true;
mail_smtpname = "nextcloud@${config.secrets.secrets.global.domains.mail_public}"; mail_smtpname = "nextcloud@${globals.domains.mail_public}";
loglevel = 2; loglevel = 2;
}; };
config = { config = {
@ -123,9 +121,9 @@ in
"L+ ${config.services.nextcloud.datadir}/config/mailer.config.php - - - - ${mailer-passwd-conf}" "L+ ${config.services.nextcloud.datadir}/config/mailer.config.php - - - - ${mailer-passwd-conf}"
]; ];
wireguard.elisabeth = { wireguard.services = {
client.via = "elisabeth"; client.via = "nucnix";
firewallRuleForNode.elisabeth.allowedTCPPorts = [ 80 ]; firewallRuleForNode.nucnix-nginx.allowedTCPPorts = [ 80 ];
}; };
networking = { networking = {
# Use systemd-resolved inside the container # Use systemd-resolved inside the container

186
config/services/nginx.nix Normal file
View file

@ -0,0 +1,186 @@
{
config,
nodes,
lib,
globals,
...
}:
let
ipOf = name: nodes.${globals.services.${name}.host}.config.wireguard.services.ipv4;
in
{
wireguard.services = {
client.via = "nucnix";
};
services.netbird.server.proxy =
let
cfg = nodes.elisabeth-netbird.config.services.netbird.server;
in
{
domain = "${globals.services.netbird.domain}";
enable = true;
enableNginx = true;
signalAddress = "${nodes.elisabeth-netbird.config.wireguard.services.ipv4}:${toString cfg.signal.port}";
relayAddress = "${nodes.elisabeth-netbird.config.wireguard.services.ipv4}:${toString cfg.relay.port}";
managementAddress = "${nodes.elisabeth-netbird.config.wireguard.services.ipv4}:${toString cfg.management.port}";
dashboardAddress = "${nodes.elisabeth-netbird.config.wireguard.services.ipv4}:80";
};
services.nginx =
let
blockOf =
hostName:
{
virtualHostExtraConfig ? "",
maxBodySize ? "500M",
port ? 3000,
upstream ? hostName,
protocol ? "http",
...
}:
{
upstreams.${hostName} = {
servers."${ipOf upstream}:${toString port}" = { };
extraConfig = ''
zone ${hostName} 64k ;
keepalive 5 ;
'';
};
virtualHosts.${globals.services.${hostName}.domain} = {
forceSSL = true;
useACMEHost = "web";
locations."/" = {
proxyPass = "${protocol}://${hostName}";
proxyWebsockets = true;
X-Frame-Options = "SAMEORIGIN";
};
extraConfig =
''
client_max_body_size ${maxBodySize} ;
''
+ virtualHostExtraConfig;
};
};
proxyProtect =
hostName:
{
allowedGroup ? true,
...
}@cfg:
lib.mkMerge [
(blockOf hostName cfg)
{
virtualHosts.${globals.services.${hostName}.domain} = {
locations."/".extraConfig = ''
auth_request /oauth2/auth;
error_page 401 = /oauth2/sign_in;
# pass information via X-User and X-Email headers to backend,
# requires running with --set-xauthrequest flag
auth_request_set $user $upstream_http_x_auth_request_preferred_username;
# Set the email to our own domain in case user change their mail
auth_request_set $email "''${upstream_http_x_auth_request_preferred_username}@${globals.domains.web}";
proxy_set_header X-User $user;
proxy_set_header X-Email $email;
# if you enabled --cookie-refresh, this is needed for it to work with auth_request
auth_request_set $auth_cookie $upstream_http_set_cookie;
add_header Set-Cookie $auth_cookie;
'';
locations."/oauth2/" = {
proxyPass = "http://oauth2-proxy";
extraConfig = ''
proxy_set_header X-Scheme $scheme;
proxy_set_header X-Auth-Request-Redirect $scheme://$host$request_uri;
'';
};
locations."= /oauth2/auth" = {
proxyPass =
"http://oauth2-proxy/oauth2/auth"
+ lib.optionalString allowedGroup "?allowed_groups=${hostName}_access";
extraConfig = ''
internal;
proxy_set_header X-Scheme $scheme;
# nginx auth_request includes headers but not body
proxy_set_header Content-Length "";
proxy_pass_request_body off;
'';
};
};
}
];
in
lib.mkMerge [
{
enable = true;
recommendedSetup = true;
virtualHosts."${globals.services.netbird.domain}".useACMEHost = "web";
}
(blockOf "vaultwarden" { maxBodySize = "1G"; })
(blockOf "forgejo" { maxBodySize = "1G"; })
(blockOf "immich" {
maxBodySize = "5G";
virtualHostExtraConfig = ''
proxy_buffering off;
proxy_request_buffering off;
'';
})
(proxyProtect "adguardhome" { })
(proxyProtect "oauth2-proxy" { allowedGroup = false; })
(blockOf "paperless" { maxBodySize = "5G"; })
(proxyProtect "ttrss" { port = 80; })
(proxyProtect "invidious" { })
(blockOf "yourspotify" { port = 80; })
(blockOf "blog" { port = 80; })
(blockOf "homebox" { })
(proxyProtect "ollama" { })
(proxyProtect "firefly" { port = 80; })
(blockOf "apispotify" {
port = 3000;
upstream = "yourspotify";
})
(blockOf "nextcloud" {
maxBodySize = "5G";
port = 80;
})
(blockOf "kanidm" {
protocol = "https";
virtualHostExtraConfig = ''
proxy_ssl_verify off ;
'';
})
];
age.secrets.cloudflare_token_acme = {
rekeyFile = config.node.secretsDir + "/cloudflare_api_token.age";
mode = "440";
group = "acme";
};
security.acme = {
acceptTerms = true;
defaults = {
email = globals.accounts.email."1".address;
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;
};
};
};
security.acme.certs.web = {
domain = globals.domains.web;
extraDomainNames = [ "*.${globals.domains.web}" ];
};
users.groups.acme.members = [ "nginx" ];
environment.persistence."/state".directories = [
{
directory = "/var/lib/acme";
user = "acme";
group = "acme";
mode = "0755";
}
];
}

View file

@ -1,8 +1,13 @@
{ config, nodes, ... }:
{ {
wireguard.elisabeth = { config,
client.via = "elisabeth"; nodes,
firewallRuleForNode.elisabeth.allowedTCPPorts = [ 3000 ]; globals,
...
}:
{
wireguard.services = {
client.via = "nucnix";
firewallRuleForNode.nucnix-nginx.allowedTCPPorts = [ 3000 ];
}; };
age.secrets.oauth2-cookie-secret = { age.secrets.oauth2-cookie-secret = {
@ -13,7 +18,7 @@
services.oauth2-proxy = { services.oauth2-proxy = {
enable = true; enable = true;
cookie.domain = ".${config.secrets.secrets.global.domains.web}"; cookie.domain = ".${globals.domains.web}";
cookie.secure = true; cookie.secure = true;
cookie.expire = "900m"; cookie.expire = "900m";
cookie.secret = null; cookie.secret = null;
@ -22,26 +27,26 @@
reverseProxy = true; reverseProxy = true;
httpAddress = "0.0.0.0:3000"; httpAddress = "0.0.0.0:3000";
redirectURL = "https://oauth2.${config.secrets.secrets.global.domains.web}/oauth2/callback"; redirectURL = "https://oauth2.${globals.domains.web}/oauth2/callback";
setXauthrequest = true; setXauthrequest = true;
extraConfig = { extraConfig = {
code-challenge-method = "S256"; code-challenge-method = "S256";
whitelist-domain = ".${config.secrets.secrets.global.domains.web}"; whitelist-domain = ".${globals.domains.web}";
set-authorization-header = true; set-authorization-header = true;
pass-access-token = true; pass-access-token = true;
skip-jwt-bearer-tokens = true; skip-jwt-bearer-tokens = true;
upstream = "static://202"; upstream = "static://202";
oidc-issuer-url = "https://auth.${config.secrets.secrets.global.domains.web}/oauth2/openid/oauth2-proxy"; oidc-issuer-url = "https://auth.${globals.domains.web}/oauth2/openid/oauth2-proxy";
provider-display-name = "Kanidm"; provider-display-name = "Kanidm";
#client-secret-file = config.age.secrets.oauth2-client-secret.path; #client-secret-file = config.age.secrets.oauth2-client-secret.path;
}; };
provider = "oidc"; provider = "oidc";
scope = "openid email"; scope = "openid email";
loginURL = "https://auth.${config.secrets.secrets.global.domains.web}/ui/oauth2"; loginURL = "https://auth.${globals.domains.web}/ui/oauth2";
redeemURL = "https://auth.${config.secrets.secrets.global.domains.web}/oauth2/token"; redeemURL = "https://auth.${globals.domains.web}/oauth2/token";
validateURL = "https://auth.${config.secrets.secrets.global.domains.web}/oauth2/openid/oauth2-proxy/userinfo"; validateURL = "https://auth.${globals.domains.web}/oauth2/openid/oauth2-proxy/userinfo";
clientID = "oauth2-proxy"; clientID = "oauth2-proxy";
email.domains = [ "*" ]; email.domains = [ "*" ];
}; };

View file

@ -3,9 +3,9 @@
disabledModules = [ "services/misc/octoprint.nix" ]; disabledModules = [ "services/misc/octoprint.nix" ];
imports = [ "${inputs.nixpkgs-octoprint}/nixos/modules/services/misc/octoprint.nix" ]; imports = [ "${inputs.nixpkgs-octoprint}/nixos/modules/services/misc/octoprint.nix" ];
wireguard.elisabeth = { wireguard.services = {
client.via = "elisabeth"; client.via = "nucnix";
firewallRuleForNode.elisabeth.allowedTCPPorts = [ config.services.octoprint.port ]; firewallRuleForNode.nucnix-nginx.allowedTCPPorts = [ config.services.octoprint.port ];
}; };
environment.persistence."/persist".directories = [ environment.persistence."/persist".directories = [
{ {

View file

@ -1,8 +1,8 @@
{ config, ... }: { config, ... }:
{ {
wireguard.elisabeth = { wireguard.services = {
client.via = "elisabeth"; client.via = "nucnix";
firewallRuleForNode.elisabeth.allowedTCPPorts = [ config.services.open-webui.port ]; firewallRuleForNode.nucnix-nginx.allowedTCPPorts = [ config.services.open-webui.port ];
}; };
services.ollama = { services.ollama = {
host = "localhost"; host = "localhost";

View file

@ -1,12 +1,12 @@
{ {
pkgs, pkgs,
nodes, nodes,
globals,
config, config,
lib, lib,
... ...
}: }:
let let
paperlessdomain = "ppl.${config.secrets.secrets.global.domains.web}";
paperlessBackupDir = "/var/cache/backups/paperless"; paperlessBackupDir = "/var/cache/backups/paperless";
in in
{ {
@ -34,8 +34,8 @@ in
passwordFile = config.age.secrets.resticpasswd.path; passwordFile = config.age.secrets.resticpasswd.path;
hetznerStorageBox = { hetznerStorageBox = {
enable = true; enable = true;
inherit (config.secrets.secrets.global.hetzner) mainUser; inherit (globals.hetzner) mainUser;
inherit (config.secrets.secrets.global.hetzner.users.paperless) subUid path; inherit (globals.hetzner.users.paperless) subUid path;
sshAgeSecret = "paperlessHetznerSsh"; sshAgeSecret = "paperlessHetznerSsh";
}; };
paths = [ paperlessBackupDir ]; paths = [ paperlessBackupDir ];
@ -64,9 +64,9 @@ in
before = [ "restic-backups-main.service" ]; before = [ "restic-backups-main.service" ];
}; };
wireguard.elisabeth = { wireguard.services = {
client.via = "elisabeth"; client.via = "nucnix";
firewallRuleForNode.elisabeth.allowedTCPPorts = [ config.services.paperless.port ]; firewallRuleForNode.nucnix-nginx.allowedTCPPorts = [ config.services.paperless.port ];
}; };
age.secrets.paperless-admin-passwd = { age.secrets.paperless-admin-passwd = {
@ -83,10 +83,10 @@ in
consumptionDir = "/paperless/consume"; consumptionDir = "/paperless/consume";
mediaDir = "/paperless/media"; mediaDir = "/paperless/media";
settings = { settings = {
PAPERLESS_URL = "https://${paperlessdomain}"; PAPERLESS_URL = "https://${globals.services.paperless.domain}";
PAPERLESS_ALLOWED_HOSTS = paperlessdomain; PAPERLESS_ALLOWED_HOSTS = globals.services.paperless.domain;
PAPERLESS_CORS_ALLOWED_HOSTS = "https://${paperlessdomain}"; PAPERLESS_CORS_ALLOWED_HOSTS = "https://${globals.services.paperless.domain}";
PAPERLESS_TRUSTED_PROXIES = nodes.elisabeth.config.wireguard.elisabeth.ipv4; PAPERLESS_TRUSTED_PROXIES = nodes.nucnix-nginx.config.wireguard.services.ipv4;
PAPERLESS_APPS = "allauth.socialaccount.providers.openid_connect"; PAPERLESS_APPS = "allauth.socialaccount.providers.openid_connect";
@ -98,7 +98,7 @@ in
provider_id = "kanidm"; provider_id = "kanidm";
name = "Kanidm"; name = "Kanidm";
client_id = "paperless"; client_id = "paperless";
settings.server_url = "https://auth.${config.secrets.secrets.global.domains.web}/oauth2/openid/${client_id}/.well-known/openid-configuration"; settings.server_url = "https://${globals.services.kanidm.domain}/oauth2/openid/${client_id}/.well-known/openid-configuration";
} }
]; ];
}; };

View file

@ -1,143 +0,0 @@
{
config,
nodes,
lib,
pkgs,
...
}:
let
prestart = pkgs.writeShellScript "pr-tracker-pre" ''
if [ ! -d ./nixpkgs ]; then
${lib.getExe pkgs.git} clone https://github.com/NixOS/nixpkgs.git
fi
'';
in
{
wireguard.elisabeth = {
client.via = "elisabeth";
firewallRuleForNode.elisabeth.allowedTCPPorts = [ 3000 ];
};
networking.firewall.allowedTCPPorts = [ 3000 ];
environment.persistence."/persist".directories = [
{
directory = "/var/lib/pr-tracker";
user = "pr-tracker";
group = "pr-tracker";
mode = "0700";
}
];
age.secrets.maddyPasswd = {
generator.script = "alnum";
owner = "pr-tracker";
};
age.secrets.prTrackerEnv = {
rekeyFile = config.node.secretsDir + "/env.age";
owner = "pr-tracker";
};
age.secrets.prTrackerWhiteList = {
rekeyFile = config.node.secretsDir + "/white-list.age";
owner = "pr-tracker";
};
nodes.maddy = {
age.secrets.pr-trackerPasswd = {
inherit (config.age.secrets.maddyPasswd) rekeyFile;
inherit (nodes.maddy.config.services.maddy) group;
mode = "640";
};
services.maddy.ensureCredentials = {
"pr-tracker@${config.secrets.secrets.global.domains.mail_public}".passwordFile =
nodes.maddy.config.age.secrets.pr-trackerPasswd.path;
};
};
systemd.sockets.pr-tracker = {
listenStreams = [ "0.0.0.0:3000" ];
wantedBy = [ "sockets.target" ];
};
systemd.services.pr-tracker = {
path = [ pkgs.git ];
serviceConfig = {
User = "pr-tracker";
Group = "pr-tracker";
StateDirectory = "pr-tracker";
WorkingDirectory = "/var/lib/pr-tracker";
LimitNOFILE = "1048576";
PrivateTmp = true;
PrivateDevices = true;
StateDirectoryMode = "0700";
Restart = "always";
ExecStartPre = prestart;
ExecStart = ''
${lib.getExe pkgs.pr-tracker} --url "https://pr-tracker.${config.secrets.secrets.global.domains.web}"\
--user-agent "Patricks pr-tracker" \
--path nixpkgs --remote origin \
--email-white-list ${config.age.secrets.prTrackerWhiteList.path} \
--email-address pr-tracker@${config.secrets.secrets.global.domains.mail_public} \
--email-server smtp.${config.secrets.secrets.global.domains.mail_public} \
'';
EnvironmentFile = config.age.secrets.prTrackerEnv.path;
# Hardening
CapabilityBoundingSet = "";
LockPersonality = true;
MemoryDenyWriteExecute = true;
PrivateUsers = true;
ProtectClock = true;
ProtectControlGroups = true;
ProtectHome = true;
ProtectHostname = true;
ProtectKernelLogs = true;
ProtectKernelModules = true;
ProtectKernelTunables = true;
ProtectProc = "invisible";
ProcSubset = "pid";
ProtectSystem = "strict";
RestrictAddressFamilies = [
"AF_INET"
"AF_INET6"
"AF_NETLINK"
];
RestrictNamespaces = true;
RestrictRealtime = true;
SystemCallArchitectures = "native";
SystemCallFilter = [
"@system-service"
"@pkey"
];
UMask = "0077";
};
};
systemd.timers.pr-tracker-update = {
wantedBy = [ "timers.target" ];
timerConfig = {
OnBootSec = "30m";
OnUnitActiveSec = "30m";
};
};
users.groups.pr-tracker = { };
users.users.pr-tracker = {
isSystemUser = true;
group = "pr-tracker";
home = "/var/lib/pr-tracker";
};
systemd.services.pr-tracker-update = {
script = ''
${lib.getExe pkgs.git} -C nixpkgs fetch
${lib.getExe pkgs.curl} http://localhost:3000/update
'';
serviceConfig = {
Requires = "pr-tracker";
Type = "oneshot";
User = "pr-tracker";
Group = "pr-tracker";
StateDirectory = "pr-tracker";
WorkingDirectory = "/var/lib/pr-tracker";
LimitNOFILE = "1048576";
PrivateTmp = true;
PrivateDevices = true;
StateDirectoryMode = "0700";
ExecStartPre = prestart;
EnvironmentFile = config.age.secrets.prTrackerEnv.path;
};
};
}

View file

@ -1,4 +1,9 @@
{ config, lib, ... }: {
config,
lib,
globals,
...
}:
let let
shares = lib.removeAttrs config.services.samba.settings [ "global" ]; shares = lib.removeAttrs config.services.samba.settings [ "global" ];
in in
@ -26,8 +31,8 @@ in
passwordFile = config.age.secrets.resticpasswd.path; passwordFile = config.age.secrets.resticpasswd.path;
hetznerStorageBox = { hetznerStorageBox = {
enable = true; enable = true;
inherit (config.secrets.secrets.global.hetzner) mainUser; inherit (globals.hetzner) mainUser;
inherit (config.secrets.secrets.global.hetzner.users.smb) subUid path; inherit (globals.hetzner.users.smb) subUid path;
sshAgeSecret = "resticHetznerSsh"; sshAgeSecret = "resticHetznerSsh";
}; };
paths = [ "/bunker" ]; paths = [ "/bunker" ];

View file

@ -1,8 +1,8 @@
{ config, pkgs, ... }: { config, pkgs, ... }:
{ {
wireguard.elisabeth = { wireguard.services = {
client.via = "elisabeth"; client.via = "nucnix";
firewallRuleForNode.elisabeth.allowedTCPPorts = [ 80 ]; firewallRuleForNode.nucnix-nginx.allowedTCPPorts = [ 80 ];
}; };
services.freshrss = { services.freshrss = {
enable = true; enable = true;

View file

@ -2,11 +2,9 @@
config, config,
lib, lib,
nodes, nodes,
globals,
... ...
}: }:
let
vaultwardenDomain = "pw.${config.secrets.secrets.global.domains.web}";
in
{ {
age.secrets.vaultwarden-env = { age.secrets.vaultwarden-env = {
rekeyFile = config.node.secretsDir + "/vaultwarden-env.age"; rekeyFile = config.node.secretsDir + "/vaultwarden-env.age";
@ -41,8 +39,8 @@ in
passwordFile = config.age.secrets.resticpasswd.path; passwordFile = config.age.secrets.resticpasswd.path;
hetznerStorageBox = { hetznerStorageBox = {
enable = true; enable = true;
inherit (config.secrets.secrets.global.hetzner) mainUser; inherit (globals.hetzner) mainUser;
inherit (config.secrets.secrets.global.hetzner.users.vaultwarden) subUid path; inherit (globals.hetzner.users.vaultwarden) subUid path;
sshAgeSecret = "vaultwardenHetznerSsh"; sshAgeSecret = "vaultwardenHetznerSsh";
}; };
paths = [ config.services.vaultwarden.backupDir ]; paths = [ config.services.vaultwarden.backupDir ];
@ -70,7 +68,7 @@ in
group = "stalwart-mail"; group = "stalwart-mail";
mode = "440"; mode = "440";
}; };
services.idmail.provision.mailboxes."vaultwarden@${config.secrets.secrets.global.domains.mail_public}" = { services.idmail.provision.mailboxes."vaultwarden@${globals.domains.mail_public}" = {
password_hash = "%{file:${nodes.mailnix.config.age.secrets.idmail-vaultwarden-passwd-hash.path}}%"; password_hash = "%{file:${nodes.mailnix.config.age.secrets.idmail-vaultwarden-passwd-hash.path}}%";
owner = "admin"; owner = "admin";
}; };
@ -101,21 +99,23 @@ in
passwordIterations = 1000000; passwordIterations = 1000000;
invitationsAllowed = true; invitationsAllowed = true;
invitationOrgName = "Vaultwarden"; invitationOrgName = "Vaultwarden";
domain = "https://${vaultwardenDomain}"; domain = "https://${globals.services.vaultwarden.domain}";
smtpHost = "smtp.${config.secrets.secrets.global.domains.mail_public}"; smtpHost = "smtp.${globals.domains.mail_public}";
smtpFrom = "vaultwarden@${config.secrets.secrets.global.domains.mail_public}"; smtpFrom = "vaultwarden@${globals.domains.mail_public}";
smtpPort = 465; smtpPort = 465;
smtpSecurity = "force_tls"; smtpSecurity = "force_tls";
smtpUsername = "vaultwarden@${config.secrets.secrets.global.domains.mail_public}"; smtpUsername = "vaultwarden@${globals.domains.mail_public}";
smtpEmbedImages = true; smtpEmbedImages = true;
}; };
environmentFile = config.age.secrets.vaultwarden-env.path; environmentFile = config.age.secrets.vaultwarden-env.path;
}; };
wireguard.elisabeth = { wireguard.services = {
client.via = "elisabeth"; client.via = "nucnix";
firewallRuleForNode.elisabeth.allowedTCPPorts = [ config.services.vaultwarden.config.rocketPort ]; firewallRuleForNode.nucnix-nginx.allowedTCPPorts = [
config.services.vaultwarden.config.rocketPort
];
}; };
# Replace uses of old name # Replace uses of old name

View file

@ -1,8 +1,8 @@
{ config, pkgs, ... }: { config, pkgs, ... }:
{ {
wireguard.elisabeth = { wireguard.services = {
client.via = "elisabeth"; client.via = "nucnix";
firewallRuleForNode.elisabeth.allowedTCPPorts = [ firewallRuleForNode.nucnix-nginx.allowedTCPPorts = [
3000 3000
80 80
]; ];

View file

@ -1637,11 +1637,11 @@
"treefmt-nix": "treefmt-nix_3" "treefmt-nix": "treefmt-nix_3"
}, },
"locked": { "locked": {
"lastModified": 1734374811, "lastModified": 1734695484,
"narHash": "sha256-+an6TysKwyWWeC7MeWGoHcULR9gc7TeXyszMAzvwRRo=", "narHash": "sha256-wmUjUxaXpItyGzafb96oVuJu/0qM6VEBKehIQ2cC1dg=",
"ref": "refs/heads/main", "ref": "refs/heads/main",
"rev": "85a6a4df38b05ed2d70e530d43de9820b3231e4a", "rev": "99cbcc03d9ce737e53fbdab3213ce136fbca8bbe",
"revCount": 25, "revCount": 26,
"type": "git", "type": "git",
"url": "https://forge.lel.lol/patrick/nixp-meta.git" "url": "https://forge.lel.lol/patrick/nixp-meta.git"
}, },

View file

@ -116,6 +116,7 @@
imports = [ imports = [
./nix/agenix-rekey.nix ./nix/agenix-rekey.nix
./nix/devshell.nix ./nix/devshell.nix
./nix/globals.nix
./nix/hosts.nix ./nix/hosts.nix
./nix/pkgs.nix ./nix/pkgs.nix
./nix/patch.nix ./nix/patch.nix

123
globals.nix Normal file
View file

@ -0,0 +1,123 @@
{
config,
lib,
inputs,
...
}:
let
inherit (config) globals;
# Try to access the extra builtin we loaded via nix-plugins.
# Throw an error if that doesn't exist.
rageImportEncrypted =
assert lib.assertMsg (builtins ? extraBuiltins.rageImportEncrypted)
"The extra builtin 'rageImportEncrypted' is not available, so repo.secrets cannot be decrypted. Did you forget to add nix-plugins and point it to `./nix/extra-builtins.nix` ?";
builtins.extraBuiltins.rageImportEncrypted;
in
{
imports = [
(rageImportEncrypted inputs.self.secretsConfig.masterIdentities ./secrets/global.nix.age)
];
globals = {
net.vlans = {
home = rec {
id = 10;
cidrv4 = "10.99.${toString id}.0/24";
cidrv6 = "fd${toString id}::/64";
};
services = rec {
id = 20;
cidrv4 = "10.99.${toString id}.0/24";
cidrv6 = "fd${toString id}::/64";
};
devices = rec {
id = 30;
cidrv4 = "10.99.${toString id}.0/24";
cidrv6 = "fd${toString id}::/64";
};
iot = rec {
id = 40;
cidrv4 = "10.99.${toString id}.0/24";
cidrv6 = "fd${toString id}::/64";
};
guests = rec {
id = 50;
cidrv4 = "10.99.${toString id}.0/24";
cidrv6 = "fd${toString id}::/64";
};
};
services = {
adguardhome = {
domain = "adguardhome.${globals.domains.web}";
host = "nucnix-adguardhome";
};
forgejo = {
domain = "forge.${globals.domains.web}";
host = "elisabeth-forgejo";
};
immich = {
domain = "immich.${globals.domains.web}";
host = "elisabeth-immich";
};
nextcloud = {
domain = "nc.${globals.domains.web}";
host = "elisabeth-nextcloud";
};
ollama = {
domain = "ai.${globals.domains.web}";
host = "elisabeth-ollama";
};
paperless = {
domain = "ppl.${globals.domains.web}";
host = "elisabeth-paperless";
};
ttrss = {
domain = "rss.${globals.domains.web}";
host = "elisabeth-ttrss";
};
vaultwarden = {
domain = "pw.${globals.domains.web}";
host = "elisabeth-vaultwarden";
};
yourspotify = {
domain = "sptfy.${globals.domains.web}";
host = "elisabeth-yourspotify";
};
apispotify = {
domain = "apisptfy.${globals.domains.web}";
host = "elisabeth-apispotify";
};
kanidm = {
domain = "auth.${globals.domains.web}";
host = "elisabeth-kanidm";
};
oauth2-proxy = {
domain = "oauth2.${globals.domains.web}";
host = "elisabeth-oauth2-proxy";
};
actual = {
domain = "actual.${globals.domains.web}";
host = "elisabeth-actual";
};
firefly = {
domain = "money.${globals.domains.web}";
host = "elisabeth-firefly";
};
homebox = {
domain = "homebox.${globals.domains.web}";
host = "elisabeth-homebox";
};
invidious = {
domain = "yt.${globals.domains.web}";
host = "elisabeth-invidious";
};
blog = {
domain = "blog.${globals.domains.web}";
host = "elisabeth-blog";
};
netbird = {
domain = "netbird.${globals.domains.web}";
host = "elisabeth-netbird";
};
};
};
}

View file

@ -1,4 +1,9 @@
{ config, lib, ... }: {
config,
lib,
# globals,
...
}:
{ {
disko.devices = { disko.devices = {
disk = { disk = {
@ -127,7 +132,8 @@
}; };
wireguard.scrtiny-patrick.server = { wireguard.scrtiny-patrick.server = {
host = config.secrets.secrets.global.domains.web; #host = globals.domains.web;
host = "3.3.3.3";
port = 51831; port = 51831;
reservedAddresses = [ reservedAddresses = [
"10.44.0.0/16" "10.44.0.0/16"

View file

@ -4,177 +4,9 @@
inputs, inputs,
lib, lib,
minimal, minimal,
nodes,
... ...
}: }:
let
domainOf =
hostName:
let
domains = {
adguardhome = "adguardhome";
forgejo = "forge";
immich = "immich";
nextcloud = "nc";
ollama = "ai";
paperless = "ppl";
ttrss = "rss";
vaultwarden = "pw";
yourspotify = "sptfy";
apispotify = "apisptfy";
kanidm = "auth";
oauth2-proxy = "oauth2";
actual = "actual";
firefly = "money";
homebox = "homebox";
invidious = "yt";
blog = "blog";
};
in
"${domains.${hostName}}.${config.secrets.secrets.global.domains.web}";
# TODO hard coded elisabeth nicht so schön
ipOf = hostName: nodes."elisabeth-${hostName}".config.wireguard.elisabeth.ipv4;
in
{ {
services.netbird.server.proxy =
let
cfg = nodes.elisabeth-netbird.config.services.netbird.server;
in
{
domain = "netbird.${config.secrets.secrets.global.domains.web}";
enable = true;
enableNginx = true;
signalAddress = "${nodes.elisabeth-netbird.config.wireguard.elisabeth.ipv4}:${toString cfg.signal.port}";
relayAddress = "${nodes.elisabeth-netbird.config.wireguard.elisabeth.ipv4}:${toString cfg.relay.port}";
managementAddress = "${nodes.elisabeth-netbird.config.wireguard.elisabeth.ipv4}:${toString cfg.management.port}";
dashboardAddress = "${nodes.elisabeth-netbird.config.wireguard.elisabeth.ipv4}:80";
};
services.nginx =
let
blockOf =
hostName:
{
virtualHostExtraConfig ? "",
maxBodySize ? "500M",
port ? 3000,
upstream ? hostName,
protocol ? "http",
...
}:
{
upstreams.${hostName} = {
servers."${ipOf upstream}:${toString port}" = { };
extraConfig = ''
zone ${hostName} 64k ;
keepalive 5 ;
'';
};
virtualHosts.${domainOf hostName} = {
forceSSL = true;
useACMEHost = "web";
locations."/" = {
proxyPass = "${protocol}://${hostName}";
proxyWebsockets = true;
X-Frame-Options = "SAMEORIGIN";
};
extraConfig =
''
client_max_body_size ${maxBodySize} ;
''
+ virtualHostExtraConfig;
};
};
proxyProtect =
hostName:
{
allowedGroup ? true,
...
}@cfg:
lib.mkMerge [
(blockOf hostName cfg)
{
virtualHosts.${domainOf hostName} = {
locations."/".extraConfig = ''
auth_request /oauth2/auth;
error_page 401 = /oauth2/sign_in;
# pass information via X-User and X-Email headers to backend,
# requires running with --set-xauthrequest flag
auth_request_set $user $upstream_http_x_auth_request_preferred_username;
# Set the email to our own domain in case user change their mail
auth_request_set $email "''${upstream_http_x_auth_request_preferred_username}@${config.secrets.secrets.global.domains.web}";
proxy_set_header X-User $user;
proxy_set_header X-Email $email;
# if you enabled --cookie-refresh, this is needed for it to work with auth_request
auth_request_set $auth_cookie $upstream_http_set_cookie;
add_header Set-Cookie $auth_cookie;
'';
locations."/oauth2/" = {
proxyPass = "http://oauth2-proxy";
extraConfig = ''
proxy_set_header X-Scheme $scheme;
proxy_set_header X-Auth-Request-Redirect $scheme://$host$request_uri;
'';
};
locations."= /oauth2/auth" = {
proxyPass =
"http://oauth2-proxy/oauth2/auth"
+ lib.optionalString allowedGroup "?allowed_groups=${hostName}_access";
extraConfig = ''
internal;
proxy_set_header X-Scheme $scheme;
# nginx auth_request includes headers but not body
proxy_set_header Content-Length "";
proxy_pass_request_body off;
'';
};
};
}
];
in
lib.mkMerge [
{
enable = true;
recommendedSetup = true;
virtualHosts."netbird.${config.secrets.secrets.global.domains.web}".useACMEHost = "web";
}
(blockOf "vaultwarden" { maxBodySize = "1G"; })
(blockOf "forgejo" { maxBodySize = "1G"; })
(blockOf "immich" {
maxBodySize = "5G";
virtualHostExtraConfig = ''
proxy_buffering off;
proxy_request_buffering off;
'';
})
(proxyProtect "adguardhome" { })
(proxyProtect "oauth2-proxy" { allowedGroup = false; })
(blockOf "paperless" { maxBodySize = "5G"; })
(proxyProtect "ttrss" { port = 80; })
(proxyProtect "invidious" { })
(blockOf "yourspotify" { port = 80; })
(blockOf "blog" { port = 80; })
(blockOf "homebox" { })
(proxyProtect "ollama" { })
(proxyProtect "firefly" { port = 80; })
(blockOf "apispotify" {
port = 3000;
upstream = "yourspotify";
})
(blockOf "nextcloud" {
maxBodySize = "5G";
port = 80;
})
(blockOf "kanidm" {
protocol = "https";
virtualHostExtraConfig = ''
proxy_ssl_verify off ;
'';
})
];
guests = guests =
let let
@ -219,11 +51,9 @@ in
../../config/services/${guestName}.nix ../../config/services/${guestName}.nix
{ {
node.secretsDir = config.node.secretsDir + "/${guestName}"; node.secretsDir = config.node.secretsDir + "/${guestName}";
networking.nftables.firewall.zones.untrusted.interfaces = networking.nftables.firewall.zones.untrusted.interfaces = lib.mkIf (
if lib.length config.guests.${guestName}.networking.links < 2 then lib.length config.guests.${guestName}.networking.links == 1
config.guests.${guestName}.networking.links ) config.guests.${guestName}.networking.links;
else
[ ];
} }
]; ];
}; };
@ -233,11 +63,11 @@ in
backend = "microvm"; backend = "microvm";
microvm = { microvm = {
system = "x86_64-linux"; system = "x86_64-linux";
interfaces."lan" = { }; interfaces.lan = { };
baseMac = config.secrets.secrets.local.networking.interfaces.lan01.mac; baseMac = config.secrets.secrets.local.networking.interfaces.lan01.mac;
}; };
extraSpecialArgs = { extraSpecialArgs = {
inherit (inputs.self) nodes; inherit (inputs.self) nodes globals;
inherit (inputs.self.pkgs.x86_64-linux) lib; inherit (inputs.self.pkgs.x86_64-linux) lib;
inherit inputs minimal stateVersion; inherit inputs minimal stateVersion;
}; };
@ -247,15 +77,11 @@ in
mkContainer = guestName: cfg: { mkContainer = guestName: cfg: {
${guestName} = mkGuest guestName cfg // { ${guestName} = mkGuest guestName cfg // {
backend = "container"; backend = "container";
container.macvlans = [ "lan" ]; container.macvlans = [ "lan-services" ];
extraSpecialArgs = { extraSpecialArgs = {
inherit inherit (inputs.self) nodes globals;
lib inherit (inputs.self.pkgs.x86_64-linux) lib;
nodes inherit inputs minimal stateVersion;
inputs
minimal
stateVersion
;
}; };
}; };
}; };

View file

@ -1,96 +1,151 @@
{ config, lib, ... }: {
config,
lib,
globals,
...
}:
let
inherit (lib)
flip
mapAttrsToList
mkMerge
genAttrs
attrNames
;
in
{ {
networking = { networking = {
inherit (config.secrets.secrets.local.networking) hostId; inherit (config.secrets.secrets.local.networking) hostId;
}; };
systemd.network.networks = { networking.nftables.firewall.zones = genAttrs (attrNames globals.net.vlans) (name: {
"10-lan01" = { interfaces = [ "lan-${name}" ];
address = [ });
(lib.net.cidr.hostCidr config.secrets.secrets.global.net.ips.${config.node.name} systemd.network.netdevs = mkMerge (
config.secrets.secrets.global.net.privateSubnetv4 flip mapAttrsToList globals.net.vlans (
) name:
]; {
gateway = [ (lib.net.cidr.host 1 config.secrets.secrets.global.net.privateSubnetv4) ]; id,
#matchConfig.MACAddress = config.secrets.secrets.local.networking.interfaces.lan01.mac; ...
matchConfig.Name = "lan"; }:
dhcpV6Config.UseDNS = false; {
dhcpV4Config.UseDNS = false; "40-vlan-${name}" = {
ipv6AcceptRAConfig.UseDNS = false; netdevConfig = {
networkConfig = { Name = "vlan-${name}";
MulticastDNS = true; Kind = "vlan";
};
vlanConfig.Id = id;
};
"50-macvlan-${name}" = {
netdevConfig = {
Name = "lan-${name}";
Kind = "macvlan";
};
extraConfig = ''
[MACVLAN]
Mode=bridge
'';
};
}
)
);
systemd.network.networks = mkMerge (
[
{
"40-vlans" = {
matchConfig.Name = "lan01";
networkConfig.LinkLocalAddressing = "no";
};
}
]
++ (flip mapAttrsToList globals.net.vlans (
name:
{
cidrv4,
cidrv6,
...
}:
{
"40-vlans".vlan = [ "vlan-${name}" ];
"10-vlan-${name}" = {
matchConfig.Name = "vlan-${name}";
# This interface should only be used from attached macvtaps.
# So don't acquire a link local address and only wait for
# this interface to gain a carrier.
networkConfig.LinkLocalAddressing = "no";
linkConfig.RequiredForOnline = "carrier";
extraConfig = ''
[Network]
MACVLAN=lan-${name}
'';
};
"20-lan-${name}" = {
address = [
(lib.net.cidr.hostCidr 1 cidrv4)
];
matchConfig.Name = "lan-${name}";
networkConfig = {
MulticastDNS = true;
IPv6PrivacyExtensions = "yes";
IPv4Forwarding = "yes";
IPv6SendRA = true;
IPv6AcceptRA = false;
DHCPPrefixDelegation = true;
};
ipv6Prefixes = [
{ Prefix = cidrv6; }
];
};
}
))
);
networking.nftables.firewall = {
snippets.nnf-ssh.enable = lib.mkForce false;
rules = {
ssh = {
from = [
"home"
];
to = [ "local" ];
allowedTCPPorts = [ 22 ];
}; };
}; };
}; };
boot.initrd.systemd.network = {
enable = true; boot.initrd = {
networks = {
# redo the network cause the livesystem has macvlans availableKernelModules = [
"10-lan01" = { "8021q"
address = [ ];
(lib.net.cidr.hostCidr config.secrets.secrets.global.net.ips.${config.node.name} systemd.network = {
config.secrets.secrets.global.net.privateSubnetv4 enable = true;
) networks = {
]; # redo the network cause the livesystem has macvlans
gateway = [ (lib.net.cidr.host 1 config.secrets.secrets.global.net.privateSubnetv4) ]; "10-lanhome" = {
matchConfig.MACAddress = config.secrets.secrets.local.networking.interfaces.lan01.mac; address = [
dhcpV6Config.UseDNS = false; # (lib.net.cidr.hostCidr 1 globals.net.vlans.home.cidrv4)
dhcpV4Config.UseDNS = false; ];
ipv6AcceptRAConfig.UseDNS = false; matchConfig.Name = "vlan-home";
networkConfig = { networkConfig = {
IPv6PrivacyExtensions = "yes"; IPv6PrivacyExtensions = "yes";
MulticastDNS = true; };
};
"40-vlans" = {
matchConfig.MACAddress = config.secrets.secrets.local.networking.interfaces.lan01.mac;
vlan = [
"vlan-home"
];
};
};
netdevs = {
"10-vlan-home" = {
netdevConfig = {
Name = "vlan-home";
Kind = "vlan";
};
# vlanConfig.Id = globals.net.vlans.home.id;
}; };
}; };
}; };
}; };
networking.nftables.firewall.zones.untrusted.interfaces = [ "lan" ];
wireguard.elisabeth.server = {
host =
lib.net.cidr.host config.secrets.secrets.global.net.ips.${config.node.name}
config.secrets.secrets.global.net.privateSubnetv4;
reservedAddresses = [
"10.42.0.0/20"
"fd00:1764::/112"
];
openFirewall = true;
};
# To be able to ping containers from the host, it is necessary
# to create a macvlan on the host on the VLAN 1 network.
networking.macvlans.lan = {
interface = "lan01";
mode = "bridge";
};
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;
};
};
};
security.acme.certs.web = {
domain = config.secrets.secrets.global.domains.web;
extraDomainNames = [ "*.${config.secrets.secrets.global.domains.web}" ];
};
users.groups.acme.members = [ "nginx" ];
environment.persistence."/state".directories = [
{
directory = "/var/lib/acme";
user = "acme";
group = "acme";
mode = "0755";
}
];
} }

View file

@ -1,20 +0,0 @@
age-encryption.org/v1
-> X25519 WretELIMVw/omsoHEMGR7PsFsfiUEfyUmlKMzmrw+wA
IW+zJKWSMfZiKs1LQwuAtej7ZDEvDt5oY+wfWpZoB1c
-> piv-p256 XTQkUA A5MNklHowU6rYbcJBT/+dW0v9Gex5IJ1sC5ksuRsfu1k
VPN/pCvMXi6Uc1uk6yuySK/e8bSjJ66zm4W62leQpBk
-> piv-p256 ZFgiIw Ah5jjfu6nrqXrW7YqfIEKWF3PrLOmEEM5LhRvi5EJVmE
MaVt5imJLBgM3NEw7tc18g9jMwPRl9c5RgCFzDIl8hk
-> piv-p256 5vmPtQ AqViuuU1xW/ngBTWFMjZax9SaQyZ/COo0fHNOwq/8Hkb
MDD3bD8PMS3AWPougqz/BXGGZGGnFPafZ0dc7Xqa0VM
-> piv-p256 ZFgiIw AuTg62739Zom64yEb4FZfA5lyeW9YP9h+3iDQJcQZSuM
TtwsPfCJi6bYH8tpPSdf9ZQlpXUC6t/AT1wM2aCXcNM
-> "-grease
n7GU3iZJjAz/ul8nNXzXYtrR
--- mvuAEeT2IOYZKF9u/htBSJSAxKuzLjx4hR65yyHzPK4
fœ<šJ{ïAzß(Ôz(Z|òxÏ0þhÍiaJd*T,o‡Ì«“A?¯ôò,ùÒ>²?»òe§.*Ð'Lû„ Æ7Õ®®B±÷ˆ®Â=<3D>ÈQ&<>c?*Wã¢#"ðÛù$Ee~?íû1‡í²Aý…ÿ±°aŸñÓ1Elƶx<C2B6>ÄñƼñ\<5C>Ëfµ'ê¼Xb"KÞñØÜf oÄhy|I¨5¦hÐÏÍÕî%?CKÒ <0C>R‰…È5÷ŽC! T7¯ÏÒððñ½'‰zÝIøSΰÖ&­AÖ&l´²®¤†ïþv$JYŸíR<C3AD> DVòO"üô­±Ùåў¥µ LSˆ|€¾øˆ\St,éÅŽÇ€w°Ïɬ<C389>,]>¼Qï8uLä~L&ûhÎ ÑÅ`špÙXÐ)Çål²
ã`ãÓ¸L/Rþ åÖ<ºZ3qW
î®\n]$çlÂÊ>Á<>Døj˜a5ÔÂ@=ñÒ5À|‰‰@Rõü- a½ÊÖµë<C2B5><C3AB>s•³^žŠÆ´—`ašì«b^ƒ0qræˆÙ™F¿%ÖCåë¼Ykª¯à&%<25><>ʪ7V™þUj2¯Ü.¥õ<C2A5>KÞ'«c!†ôÈ°+AÝ è[ ¸<>ö¥r©—¼&òjÉ[<06>†G<E280A0>t2D}ÿ»‚³-ÙHî Þàú€Å¾4ø0奣ýÊå&WÖ,“;}¢ØÀ5ðš¤÷
‰Zmìô^á[=çL Ëa(
4­=NŽ8ø¹=ð<>ƒ¢ü=åKQl“ð^ôm¶¢Aæ´?¸ì*°¾$í+<56>Ç—%UZ"á2²™Bá7ëÈbÅ>ÆMÛÊé(ø§û·ÿbÇAV^Ù¾a1bæg£ûA³ù,:±¢‚Å­íÖöRÙ.RöZ6M„ãšö! BÏ·þQ¶³El
”‰‰ïÓ¹~qö=ê³ üJ<C3BC>R{Œ#¾Ø×fªwQC>®cOºf<>°q¬ªÊä.eY@®Ú_붹»<cï.G˜â®ßÓù9E¬~´ë£¬xPÿ

View file

@ -4,114 +4,9 @@
inputs, inputs,
lib, lib,
minimal, minimal,
nodes,
... ...
}: }:
let
domainOf =
hostName:
let
domains = {
};
in
"${domains.${hostName}}.${config.secrets.secrets.global.domains.web}";
# TODO hard coded elisabeth nicht so schön
ipOf = hostName: nodes."elisabeth-${hostName}".config.wireguard.elisabeth.ipv4;
in
{ {
services.nginx =
let
blockOf =
hostName:
{
virtualHostExtraConfig ? "",
maxBodySize ? "500M",
port ? 3000,
upstream ? hostName,
protocol ? "http",
...
}:
{
upstreams.${hostName} = {
servers."${ipOf upstream}:${toString port}" = { };
extraConfig = ''
zone ${hostName} 64k ;
keepalive 5 ;
'';
};
virtualHosts.${domainOf hostName} = {
forceSSL = true;
useACMEHost = "web";
locations."/" = {
proxyPass = "${protocol}://${hostName}";
proxyWebsockets = true;
X-Frame-Options = "SAMEORIGIN";
};
extraConfig =
''
client_max_body_size ${maxBodySize} ;
''
+ virtualHostExtraConfig;
};
};
proxyProtect =
hostName:
{
allowedGroup ? true,
...
}@cfg:
lib.mkMerge [
(blockOf hostName cfg)
{
virtualHosts.${domainOf hostName} = {
locations."/".extraConfig = ''
auth_request /oauth2/auth;
error_page 401 = /oauth2/sign_in;
# pass information via X-User and X-Email headers to backend,
# requires running with --set-xauthrequest flag
auth_request_set $user $upstream_http_x_auth_request_preferred_username;
# Set the email to our own domain in case user change their mail
auth_request_set $email "''${upstream_http_x_auth_request_preferred_username}@${config.secrets.secrets.global.domains.web}";
proxy_set_header X-User $user;
proxy_set_header X-Email $email;
# if you enabled --cookie-refresh, this is needed for it to work with auth_request
auth_request_set $auth_cookie $upstream_http_set_cookie;
add_header Set-Cookie $auth_cookie;
'';
locations."/oauth2/" = {
proxyPass = "http://oauth2-proxy";
extraConfig = ''
proxy_set_header X-Scheme $scheme;
proxy_set_header X-Auth-Request-Redirect $scheme://$host$request_uri;
'';
};
locations."= /oauth2/auth" = {
proxyPass =
"http://oauth2-proxy/oauth2/auth"
+ lib.optionalString allowedGroup "?allowed_groups=${hostName}_access";
extraConfig = ''
internal;
proxy_set_header X-Scheme $scheme;
# nginx auth_request includes headers but not body
proxy_set_header Content-Length "";
proxy_pass_request_body off;
'';
};
};
}
];
in
lib.mkMerge [
{
enable = false;
recommendedSetup = true;
}
];
guests = guests =
let let
mkGuest = guestName: _: { mkGuest = guestName: _: {
@ -129,11 +24,9 @@ in
../../config/services/${guestName}.nix ../../config/services/${guestName}.nix
{ {
node.secretsDir = config.node.secretsDir + "/${guestName}"; node.secretsDir = config.node.secretsDir + "/${guestName}";
networking.nftables.firewall.zones.untrusted.interfaces = networking.nftables.firewall.zones.untrusted.interfaces = lib.mkIf (
if lib.length config.guests.${guestName}.networking.links < 2 then lib.length config.guests.${guestName}.networking.links == 1
config.guests.${guestName}.networking.links ) config.guests.${guestName}.networking.links;
else
[ ];
} }
]; ];
}; };
@ -143,11 +36,11 @@ in
backend = "microvm"; backend = "microvm";
microvm = { microvm = {
system = "x86_64-linux"; system = "x86_64-linux";
macvtap = "lan"; interfaces.lan = { };
baseMac = config.secrets.secrets.local.networking.interfaces.lan01.mac; baseMac = config.secrets.secrets.local.networking.interfaces.lan01.mac;
}; };
extraSpecialArgs = { extraSpecialArgs = {
inherit (inputs.self) nodes; inherit (inputs.self) nodes globals;
inherit (inputs.self.pkgs.x86_64-linux) lib; inherit (inputs.self.pkgs.x86_64-linux) lib;
inherit inputs minimal stateVersion; inherit inputs minimal stateVersion;
}; };
@ -165,16 +58,14 @@ in
backend = "container"; backend = "container";
container.macvlans = macvlans; container.macvlans = macvlans;
extraSpecialArgs = { extraSpecialArgs = {
inherit inherit (inputs.self) nodes globals;
lib inherit (inputs.self.pkgs.x86_64-linux) lib;
nodes inherit inputs minimal stateVersion;
inputs
minimal
stateVersion
;
}; };
}; };
}; };
in in
{ } // mkContainer "adguardhome" { macvlans = [ "lan-services" ]; }; { }
// mkContainer "adguardhome" { macvlans = [ "lan-services" ]; }
// mkContainer "nginx" { macvlans = [ "lan-services" ]; };
} }

View file

@ -1,4 +1,4 @@
{ config, ... }: { globals, ... }:
{ {
@ -22,7 +22,7 @@
wifi6.enable = true; wifi6.enable = true;
wifi7.enable = true; wifi7.enable = true;
networks.wlan1 = { networks.wlan1 = {
inherit (config.secrets.secrets.global.hostapd) ssid; inherit (globals.hostapd) ssid;
apIsolate = true; apIsolate = true;
authentication = { authentication = {
saePasswords = [ saePasswords = [

View file

@ -1,78 +1,130 @@
{ config, lib, ... }: {
config,
lib,
globals,
...
}:
let let
vlans = { inherit (lib)
home = 10; flip
services = 20; mapAttrsToList
devices = 30; mkMerge
iot = 40; genAttrs
guests = 50; attrNames
}; ;
inherit (lib) flip mapAttrsToList;
in in
{ {
imports = imports = [
./hostapd.nix
./kea.nix
];
networking.nftables.firewall.zones = mkMerge [
{ fritz.interfaces = [ "vlan-fritz" ]; }
(genAttrs (attrNames globals.net.vlans) (name: {
interfaces = [ "lan-${name}" ];
}))
];
systemd.network.netdevs = mkMerge (
[ [
./hostapd.nix {
./kea.nix "40-vlan-fritz" = {
] netdevConfig = {
++ (flip mapAttrsToList vlans ( Name = "vlan-fritz";
name: id: { Kind = "vlan";
networking.nftables.firewall.zones.${name}.interfaces = [ "lan-${name}" ];
systemd.network = {
netdevs = {
"40-vlan-${name}" = {
netdevConfig = {
Name = "vlan-${name}";
Kind = "vlan";
};
vlanConfig.Id = id;
};
"50-mlan-${name}" = {
netdevConfig = {
Name = "lan-${name}";
Kind = "macvlan";
};
extraConfig = ''
[MACVLAN]
Mode=bridge
'';
};
};
networks = {
"10-vlan-${name}" = {
matchConfig.Name = "vlan-${name}";
# This interface should only be used from attached macvtaps.
# So don't acquire a link local address and only wait for
# this interface to gain a carrier.
networkConfig.LinkLocalAddressing = "no";
linkConfig.RequiredForOnline = "carrier";
extraConfig = ''
[Network]
MACVLAN=lan-${name}
'';
};
"20-lan-${name}" = {
address = [
(lib.net.cidr.hostCidr 1 "10.99.${toString id}.0/24")
];
matchConfig.Name = "lan-${name}";
networkConfig = {
MulticastDNS = true;
IPv6PrivacyExtensions = "yes";
IPv4Forwarding = "yes";
IPv6SendRA = true;
IPv6AcceptRA = false;
DHCPPrefixDelegation = true;
};
ipv6Prefixes = [
{ Prefix = "fd${toString id}::/64"; }
];
};
}; };
vlanConfig.Id = 2;
}; };
} }
)); ]
++ (flip mapAttrsToList globals.net.vlans (
name:
{
id,
...
}:
{
"40-vlan-${name}" = {
netdevConfig = {
Name = "vlan-${name}";
Kind = "vlan";
};
vlanConfig.Id = id;
};
"50-macvlan-${name}" = {
netdevConfig = {
Name = "lan-${name}";
Kind = "macvlan";
};
extraConfig = ''
[MACVLAN]
Mode=bridge
'';
};
}
))
);
systemd.network.networks = mkMerge (
[
{
"10-lan-fritz" = {
address = [
(lib.net.cidr.hostCidr 2 "10.99.2.0/24")
];
gateway = [ (lib.net.cidr.host 1 "10.99.2.0/24") ];
matchConfig.Name = "vlan-fritz";
networkConfig = {
IPv6PrivacyExtensions = "yes";
};
};
"40-vlans" = {
matchConfig.Name = "lan01";
networkConfig.LinkLocalAddressing = "no";
vlan = [ "lan-fritz" ];
};
}
]
++ (flip mapAttrsToList globals.net.vlans (
name:
{
cidrv4,
cidrv6,
...
}:
{
"40-vlans".vlan = [ "vlan-${name}" ];
"10-vlan-${name}" = {
matchConfig.Name = "vlan-${name}";
# This interface should only be used from attached macvtaps.
# So don't acquire a link local address and only wait for
# this interface to gain a carrier.
networkConfig.LinkLocalAddressing = "no";
linkConfig.RequiredForOnline = "carrier";
extraConfig = ''
[Network]
MACVLAN=lan-${name}
'';
};
"20-lan-${name}" = {
address = [
(lib.net.cidr.hostCidr 1 cidrv4)
];
matchConfig.Name = "lan-${name}";
networkConfig = {
MulticastDNS = true;
IPv6PrivacyExtensions = "yes";
IPv4Forwarding = "yes";
IPv6SendRA = true;
IPv6AcceptRA = false;
DHCPPrefixDelegation = true;
};
ipv6Prefixes = [
{ Prefix = cidrv6; }
];
};
}
))
);
networking.nftables.firewall = { networking.nftables.firewall = {
snippets.nnf-ssh.enable = lib.mkForce false; snippets.nnf-ssh.enable = lib.mkForce false;
rules = { rules = {
@ -96,46 +148,24 @@ in
verdict = "accept"; verdict = "accept";
masquerade = true; masquerade = true;
}; };
wireguard = {
from = [ "services" ];
to = [ "local" ];
allowedUDPPorts = [ config.wireguard.services.server.port ];
};
}; };
}; };
networking.nftables.firewall.zones.fritz.interfaces = [ "vlan-fritz" ]; wireguard.services.server = {
host = lib.net.cidr.host 1 "10.99.20.0/24";
reservedAddresses = [
"10.42.0.0/20"
"fd00:1764::/112"
];
openFirewall = true;
};
networking = { networking = {
inherit (config.secrets.secrets.local.networking) hostId; inherit (config.secrets.secrets.local.networking) hostId;
}; };
systemd.network = {
netdevs."40-vlan-fritz" = {
netdevConfig = {
Name = "vlan-fritz";
Kind = "vlan";
};
vlanConfig.Id = 2;
};
networks = {
"10-lan-fritz" = {
address = [
(lib.net.cidr.hostCidr 2 "10.99.2.0/24")
];
gateway = [ (lib.net.cidr.host 1 "10.99.2.0/24") ];
matchConfig.Name = "vlan-fritz";
networkConfig = {
IPv6PrivacyExtensions = "yes";
};
};
};
networks."40-vlans" = {
matchConfig.Name = "lan01";
networkConfig.LinkLocalAddressing = "no";
vlan = [
"vlan-fritz"
"vlan-home"
"vlan-services"
"vlan-devices"
"vlan-iot"
"vlan-guests"
];
};
};
boot.initrd = { boot.initrd = {
@ -148,7 +178,7 @@ in
# redo the network cause the livesystem has macvlans # redo the network cause the livesystem has macvlans
"10-lanhome" = { "10-lanhome" = {
address = [ address = [
(lib.net.cidr.hostCidr 1 "10.99.10.0/24") (lib.net.cidr.hostCidr 1 globals.net.vlans.home.cidrv4)
]; ];
matchConfig.Name = "vlan-home"; matchConfig.Name = "vlan-home";
networkConfig = { networkConfig = {
@ -180,7 +210,7 @@ in
Name = "vlan-home"; Name = "vlan-home";
Kind = "vlan"; Kind = "vlan";
}; };
vlanConfig.Id = 10; vlanConfig.Id = globals.net.vlans.home.id;
}; };
"10-vlan-fritz" = { "10-vlan-fritz" = {
netdevConfig = { netdevConfig = {

Binary file not shown.

150
modules/globals.nix Normal file
View file

@ -0,0 +1,150 @@
{
lib,
options,
...
}:
let
inherit (lib)
mkOption
types
;
in
{
options = {
globals = mkOption {
default = { };
type = types.submodule {
options = {
accounts.email = mkOption {
# Not really, should be the same type as the home-manager accounts.email option
# Just don't wann copy the whole definition
type = types.attrs;
default = { };
};
hostapd.ssid = mkOption {
type = types.nullOr types.str;
default = null;
description = "The ssid to use with hostapd";
};
domains = mkOption {
type = types.attrsOf types.str;
default = { };
};
hosts = mkOption {
type = types.attrsOf (
types.submodule {
options = {
ip = mkOption {
type = types.nullOr types.net.ipv4;
default = null;
description = "The public IP of this host";
};
};
}
);
};
users = mkOption {
type = types.attrsOf (
types.submodule {
options = {
hashedPassword = mkOption {
type = types.nullOr types.str;
default = null;
description = "The public IP of this host";
};
};
}
);
};
hetzner = mkOption {
default = { };
description = "Storage box configurations.";
type = types.submodule {
options = {
mainUser = mkOption {
type = types.str;
description = "Main username for the storagebox";
};
users = mkOption {
default = { };
description = "Subuser configurations.";
type = types.attrsOf (
types.submodule {
options = {
subUid = mkOption {
type = types.int;
description = "The subuser id";
};
path = mkOption {
type = types.str;
description = "The home path for this subuser (i.e. backup destination)";
};
};
}
);
};
};
};
};
net.vlans = mkOption {
default = { };
type = types.attrsOf (
types.submodule (vlanNetSubmod: {
options = {
id = mkOption {
type = types.ints.between 1 4094;
description = "The VLAN id";
};
cidrv4 = mkOption {
type = types.nullOr types.net.cidrv4;
default = null;
description = "The CIDRv4 of this vlan";
};
cidrv6 = mkOption {
type = types.nullOr types.net.cidrv6;
default = null;
description = "The CIDRv6 of this vlan";
};
name = mkOption {
description = "The name of this VLAN";
default = vlanNetSubmod.config._module.args.name;
type = types.str;
};
};
})
);
};
services = mkOption {
type = types.attrsOf (
types.submodule {
options = {
domain = mkOption {
type = types.str;
description = "The domain under which this service can be reached";
};
host = mkOption {
type = types.str;
description = "The node-name on which this service runs";
};
};
}
);
};
};
};
};
_globalsDefs = mkOption {
type = types.unspecified;
default = options.globals.definitions;
readOnly = true;
internal = true;
};
};
}

53
nix/globals.nix Normal file
View file

@ -0,0 +1,53 @@
{ inputs, ... }:
{
flake =
{
config,
lib,
...
}:
{
globals =
let
globalsSystem = lib.evalModules {
prefix = [ "globals" ];
specialArgs = {
inherit (inputs.self.pkgs.x86_64-linux) lib;
inherit inputs;
inherit (config) nodes;
};
modules = [
../modules/globals.nix
../globals.nix
(
{ lib, ... }:
{
globals = lib.mkMerge (
lib.concatLists (
lib.flip lib.mapAttrsToList config.nodes (
name: cfg:
builtins.addErrorContext "while aggregating globals from nixosConfigurations.${name} into flake-level globals:" cfg.config._globalsDefs
)
)
);
}
)
];
};
in
{
# Make sure the keys of this attrset are trivially evaluatable to avoid infinite recursion,
# therefore we inherit relevant attributes from the config.
inherit (globalsSystem.config.globals)
accounts
hosts
hostapd
domains
services
hetzner
net
users
;
};
};
}

View file

@ -25,7 +25,7 @@
specialArgs = { specialArgs = {
# Use the correct instance lib that has our overlays # Use the correct instance lib that has our overlays
inherit (pkgs) lib; inherit (pkgs) lib;
inherit (config) nodes; inherit (config) nodes globals;
inherit minimal stateVersion; inherit minimal stateVersion;
inputs = inputs // { inputs = inputs // {
nixpkgs = self.nixpkgs-patched; nixpkgs = self.nixpkgs-patched;
@ -87,7 +87,7 @@
nodes = config.nixosConfigurations // config.guestConfigurations; nodes = config.nixosConfigurations // config.guestConfigurations;
wireguardEvalCache = config.pkgs.x86_64-linux.lib.wireguard.createEvalCache inputs [ wireguardEvalCache = config.pkgs.x86_64-linux.lib.wireguard.createEvalCache inputs [
"scrtiny-patrick" "scrtiny-patrick"
"elisabeth" "services"
]; ];
}; };
} }

BIN
secrets/global.nix.age Normal file

Binary file not shown.

Binary file not shown.

View file

@ -1,16 +0,0 @@
age-encryption.org/v1
-> X25519 Mv11pZInyrNKXp9yT3maeq+nLpYWEKGSTog8bpa/KWw
ybH+dojanR8n4Ubq1H9D7CE5ipz9y3nqUnqw/6h9VNY
-> piv-p256 XTQkUA A3oYQXSUKuRPADT5kQEcZdgnkWuquWC2IMTYY7PHxU2g
dHajYp4/VOsBjdhQD1+UmX47F0v6q54zAFtJk82H1Os
-> piv-p256 ZFgiIw As8XHst+QSiFmM+jsDEPunagwwGsy9XG5ECAH3p4nUzp
qRxV2IOLGyMvsGIIKEj5wsjPzv8VB3s8UsXZ5tSJwxE
-> piv-p256 5vmPtQ At3pi/3ckCTfglnBNUOo3Iw182iBhm4/BdpEo6j51FZi
hJlqdt9g3g/BnvoXzjpjJgaRaNQlNgebF1SvGxLFTkw
-> piv-p256 ZFgiIw A3idLYAMWytoYJMcEl3wMbmWYxkFKMgQyBBp6KT/+OsY
29hfrgCAF+wRMQD4f+cItT63oOp0lx05FqpCKZTNyXs
-> 9O-grease < `3z5 sj+v
Qp3zpkMRcdwm62T+5GuIsMOd8dP1UetRc2x+z95NyQGM4lgNwjV2yoGPFNo8igPR
Hd7p4XkjjEcYtS9jv8m+pZbIi2KRdVCMLRC8f+Av7Y2ONQI
--- ViopD9rjKx8zdT8FHjYlB+N0MUsQT9imiTv8dlzF6RU
z”ëç<C3AB>¹“ Š~{†r¶Ë<C2B6>ƒ<03><>÷Ʀo<>Ã]¸-µñ¢<>¦!;$Ùûd“J<|IÀ<>óÎÁíás*Ó·ö×v¿ Å

View file

@ -1 +0,0 @@
n3HlzW2vkFj565rNTLcZHgJbBip9MXe4s1rctRWi1TQ=

View file

@ -1,16 +0,0 @@
age-encryption.org/v1
-> X25519 OJ8Lc0YjxJytlBJ14MMt6cuzyNeXkdOnh+mFymRz52U
sqSNr/vm5OZvaoiLTMxCcncIKtsGaZFfgHUXxFEfyiA
-> piv-p256 XTQkUA AhTYzUCOiOKq4EEU/bjl/eAkeDwo8o4YNVGKWw5Fuhux
ryBAAFjmFQM+4BLL66/Uvbb9Rtwb+neZS//aXYtHucY
-> piv-p256 ZFgiIw AtoEavPlKH74ztkeKOqRyPrzWQ7HLgE4yRrPxhGhRBX5
K1X0z4320HfFUDfNlYVJ73y6dp8ZtUXm31A86lud1cI
-> piv-p256 5vmPtQ AkNdVLt9VK/jBtew/8P70REU+qLxfsa8/4hsHaUD89cI
0odU8kcEA2hLHi5j8MW9twXX8zskKLudJPwyFT4/h0Q
-> piv-p256 ZFgiIw Axrpxh2W6qRG46jz+DLqIf74ZaSregbkUpKGlf/YFxcx
0pPiAtjPImcD+tnw4iKqiUPMW3q/edcX9z9/ZhEo67A
-> L1Uvx5wl-grease |&LSN XV(8oXE S*[P j6
JxdNfsiy1wJneYw90pf7Nlu7maEmuoC+KEXNpEB65P9TO16LfEobXUd5jwd+qjKG
GbvBchGQbYb5lFuVFbcgQDaI2Smadf4/IZZIfQ
--- UXIgkYtiD7ga9iZQAypc3agc0j8i1lbtdvNUphx2VZo
`~ÖpÓâKåb çFQ“S<E2809C>ò­"<22>¼ïêþYª2 ;r#UÀuÌÀOqx”{.ßäÃߣe[ØãÊvE™<E284A2>¨…E øLÕ­IMÑ"ÑmC

View file

@ -1 +0,0 @@
np/SufIR7ds1sqhdyEOf3bBXmvauVFnvcprB2osMAQE=

View file

@ -1,17 +0,0 @@
age-encryption.org/v1
-> X25519 eVtVzXtvsEgbNOdIy4VDn1FbpMAoSZ89cHEoFF+zDls
0naCdMLWG6MRREA/+OC+xbjxDnPXXfcwdvhGY9bmF3I
-> piv-p256 ZFgiIw A0b4W+z9JJLdoeLsceIWTgfq9AGhGCYzghM8A/xxi73q
9z6A/Xk39YcMlY6vflm/HEvMjjrfC8hcp9SVIZ601Xs
-> piv-p256 XTQkUA As6ZR0tijPVbIGJJQE7ebHDJVuMdvEF7uSecCAFZBr8q
f6KhqssOYi6Lm7xpNaQEtHKZ6qyd3/lRLDI7Id0+1I8
-> piv-p256 ZFgiIw AuJ8buC0fCg9gT9DpLSAfVFpYue6nKwq1Q4RLZU0eIfy
+UP/GGc/qW8wznHYVsW7xFuK4/pLgvesoODaafsZDhs
-> piv-p256 5vmPtQ AyrqpWUElWE9Ai+DeV1lUq+nHAqaZFZkMTPPIu0DiesF
S5T0MFAArqnNXtwrYGzAi5rK+BkWn/Gs8U6vtqijIwc
-> 0Skk=zN=-grease E: ]pN}4
zV/hyZHiaSckbVGuS+oNFItTxcLKTyL8G4G+Btzcym1Afm0CCrXL5fc/ss7tQ7Mx
ac7JEfvv9cCnAezvog
--- rvFVqJgSmwEk/Qy4x/LoIlAuJ6JtWxFvInGyO7lv96k
4ËñNÕࣅ
/ýLÒ©—C³I*³Ë¨¢2ã•‘¸.!Ûøšï.Ñ1þ »¡•Øèö 39ˆ)]t•mIý-&5t

View file

@ -1 +0,0 @@
DVpnYaoXKKk37IbTyG08bTWogBAD9N/s2PVodeHFaXo=

View file

@ -1 +0,0 @@
9xRD/oWYa5zpt7Om257Aj7U9IV6zKo4OqDBUxVzHo2A=

View file

@ -1,15 +0,0 @@
age-encryption.org/v1
-> X25519 eycLfsdMAUw2tJj5x33PGrfpUpivh/HTPa68TmTPmGQ
sTqEotydAfRHRRjI1JzO04OKBoHyVy0yk1wbdE4Psjc
-> piv-p256 XTQkUA AhCVTIgeo2WfoMZOvjZpf+YrQtruXlc5zt4u7giH6iOM
XYE/PHqHLWdTTYeBa12wIEMYp4dWa1uUkIRVB1SZ32U
-> piv-p256 ZFgiIw Akz/mZ2lQ/ZdzCX5R9rbM75WrMuJNGUYQ/jmsAzD8S25
a57G5Ceu7PcT0RK3gxbUmkqQoD6x3yjciqOU4JR69OM
-> piv-p256 5vmPtQ AzJjFtgTTuJxJRj2vJGJyOEnlYSa1teV4HPliIpffFHx
mLYOWr6SuCu5kgMUnTMDmXDpUZO6gnwm3V3qXRMxKDU
-> piv-p256 ZFgiIw A/OaBb5aN3DKxTAK4n2WtYvKGLZmRb4YCzlih9re4PcF
b45rIFE73gyGiRimMTREoMVSxWPbho8kwM0NzPGeNV4
-> TjQN9Fe6-grease 90VQ v=D
p4sbV1E
--- Wv+ihDw2UuzFYlPz6bQN/9kpXygD1+IWXzhM3g/q/ZQ
âW+Gd$<24>x”âzň5…÷”˕óaÁĺ5zĘüĽ´~řąú¨7jě!hóöťŠF€¦dPöą0MŇkž/p»ŇŕÝřĘ

View file

@ -1 +0,0 @@
7MnECQQR91RRR4S2M7iW0h8wDn4Ewhj7R2Z+y8AAg2A=

View file

@ -1 +0,0 @@
QZ8sx7wJ0pMAfxyA1hDgcemyI26/Vfaf7TICofiXPhM=

View file

@ -1,16 +0,0 @@
age-encryption.org/v1
-> X25519 QmW1YFkf2wn5zgzh1wLmb+dLk0+1/D6FWUEKg7mxciw
OR7j2nCl9FxcKBxMsJN0i3jrv5UQOxDGnwfmye7DYxo
-> piv-p256 XTQkUA AnTdNy1t0SAaeHIG91KQmlMVpAKqmalwfktAg91FL4aB
Z+jBzSM0JmJFzcqMe3N7r0HdFGeOnDVGh4ROYTYVP08
-> piv-p256 ZFgiIw Alks31//hpPgAS3ADktyVTQdT/Ab4Yu8FajsmWBijhqD
PzmjkWcHT8sEeKvIZLWNaUkFhR92YQ0Vs0SkG1c+lpQ
-> piv-p256 5vmPtQ A0t2/mWwCHc/UpwYvkObwJZ1gTqMYyjhljelgQCXNM+m
5q3i0ClG03ASXtlqBHMbhCFYSPem3d8y3lkFeEUW0eI
-> piv-p256 ZFgiIw AxL98VRYkHkM+uDSBWTI8bjdgvboJQ3o5l0M6ICq9IbF
N+Sb5dU3rksUVD4QFNu6U0jgs8Mo71CGWn4GiUb5CAU
-> e=H-grease T :(0"zbb` 7"
TkofyvqI9KJyWtPh3r4GLt0zpT5CJxo720xjJihdUjHeOLp4oVbhV1z2J2dsfJdG
vuZ3EBDXzhYYtLfVyQZltSKRSOw+5za9b7MEdKaulAMPeRo
--- qoqvdfP6fW3lXoN6DP2Qvl1NFXB4S3iipvV8gUiu/CY
³‰ ÚÇ<C39A>/h°K?8†ÓCC¼WíQš{Ù¹i<C2B9>{,ι³¡¡CEÃGö<47>£\G8jžP¶{<7B>‰6é·‡BüåLb«O÷®¹nR(å($°

View file

@ -1 +0,0 @@
V/8fGOARvXPqD+bZmn1n6E+/6R5bhP7kO15eKJctqTE=

View file

@ -1 +0,0 @@
34nMC0dvuS70Rn+685ExtKqQcEHdJvUzVvTcTZNwoVM=

View file

@ -1,16 +0,0 @@
age-encryption.org/v1
-> X25519 fFaEXRFuLeEW1V7DL243Zno37t1iA/ZoNatPCeh2LAc
bJ0y06//wH8ik5U1bfFifh+pmeOR0YpkZQoGscjMWSw
-> piv-p256 XTQkUA Aidtub6Z2JRPQDYO7Kz6bt+dQ2pmoNmbWxtViDt6F4GK
2sJMJfb4s/7KLjbjscvj7PktYrq+Y63GtAq8FQHiq9M
-> piv-p256 ZFgiIw Aw59iVn6zdxOepPlOge2b7As/G4+xWlVFYaVKkQOGwnw
m5PFMiGMV84Z6RY33ThrInsEKJTz92XFywunORtcw7c
-> piv-p256 5vmPtQ AuWWwbt+X8944l9dQdrop5cU7Yba4d6iNtgDcaOecfsH
l8/suY98Y0OLbYwhuLU6TYr7p9ZgTa5MvH/RvNwkWKQ
-> piv-p256 ZFgiIw A0QKpC1NyUusFefjUhHLQ+/0+nNWl928B1bZuXluWAQl
OcC8nBvW5KvozJSGX9gIyO8sh3DBxo9tOMQUhqjxKSk
-> v6t-grease
XjsK/Era/aby9lXJis4lXJrRGLUyyiwjo+jCOUwazvB5ZegR+2hXI8zjd78CgvXX
Iw
--- oYdppQraw32pbZ3RTXwoIv7A18Ul4wGCECPeZuxxvtI
È&'“©Ø¶ÃíãÜ.“[7~Ž‡rò™=‡sOu2u;¬hTzº·<C2BA>¬O}Œ¶ÔF,e+ÄzäT.ºþŒ·½·÷.+Œv<C592>ÔU¦¿áóDßå Ž

View file

@ -1 +0,0 @@
/89yv+rT1lqLAtDoIynHCEgHcrv6lwfoPTp7/4GP4ks=

View file

@ -1,16 +0,0 @@
age-encryption.org/v1
-> X25519 zghXrLqQhlVqAAbMi1k8gvG5IjG9boIJCyEx63DwwGo
/ae8dzj7mPxZdpciA+lLiR6H/WCrIvTkUfaXGP+RZiY
-> piv-p256 XTQkUA A4PgmdpN1WmH++JUTIdADZBqDrCQ2N8HP9FzQ7DtyJuU
CIzSKNP8YYYfMycueE564094XeKJ9mNEceAuUEnvFFI
-> piv-p256 ZFgiIw AgxtRiqyF4Fo6Us/l8vXhWl2tQakCQGwd1Dogf/Wqnyv
Gi9O1lFR2hhfkXoC7cmlpT+iHx0DxeDFmuU9i+Gc4Ms
-> piv-p256 5vmPtQ AsbmH20Pc58VF7tBnoE5iqzlrsahCDTHkvuyAQ4W5SPy
BcJr9QsIDanypSNZ0UWrt0VnJK99LM0FOmCQWc+2rPY
-> piv-p256 ZFgiIw A5CT86jvz263c1GoDrtGVBXZx9EZeQwCL2d/tCXGqay3
8kHhsuD77fPPLPe8JYTuHNcCtp0VJcdrTg220BVdyGc
-> v-"@h-grease sJN %C \ ?mh0`=L
FarOmtacPX3pzMNzucQdNxI8MpVZdumJghhEPiukRJxp5+3InvEp7lvBhtZv49i3
QPoKNFjUweN6aXA9Vs1cpSQ
--- k49nSQRFr22Pc4QtH0WlYQ2/yMpBXSJasmQ97ZcxLkU
^sÛø¬hÈÊ<C388>LÜvÕçøí©++'•å8¶{ZŠïÀÑCÆðŽ}ö¯ZúPQ0U$¸±²¨þ•:YÀ—õ”ïËÊ1­NO l'

View file

@ -1 +0,0 @@
yv8nqlqgBxDIf6oYrn01FRKoKnqZPfdenWIFHxfSLiA=

View file

@ -1,16 +0,0 @@
age-encryption.org/v1
-> X25519 iEBzJEtJTSmO3Sh0BMklgsGOBgVaiCYESkyvEjNRqg4
f8QwJYgLHLCrILE3QjeTiRL3B7o/YyzJry43O2m4v1w
-> piv-p256 XTQkUA A9pZLJ7fdKXK8/vAvk0dxotvScae5Y4nNXNDCwIPRm5M
AIsWjvaRKXLsKrPnncf70FmLBzZCoCApDutow7YBYNA
-> piv-p256 ZFgiIw AgeKhANA0G02k3DHnLD6m3fr6JKEDboK5mxScP8azmnT
lLW7QTJRhTlfg1rWl5tmHlkSL3jtU3Q6XcNlCW839Wg
-> piv-p256 5vmPtQ AkkCLbo5aWnOow68CsrVModJBDJmaberAIothw92Uj6W
iwVUFQkCOHg5e+EwuKZq21hkCk/8ZgyT2FrqD1vvMbs
-> piv-p256 ZFgiIw A5ldqhV8Y7KIzQ7iKleWUqirmt9/YC5kqmP7mR+b779K
I2OwnqfBAZOHQ8R3kiz20PUJA7PJlaUsh5Q9+W2XDyY
-> m*X-grease
tpDjVLTPOYTlDyBgstO+1xHdCTwc8iW0rOKpgqNF1iZH+e76Q7fUqt7OSSshyFqf
EZzGvqkemxXNLccD8VJXeeU5zLA4LqBEmNiK36zPzEMoJO8xEJ7SsmTtufY
--- RYsqETvw8iUKHCkw8z5mKPtEUds3e5WRn7o+llL33u0
ŒãwøypjÍl}سÖ/âÎúü×Ví±úÁ4óúù"m@þyZ‡xè­ýuý<75>ë,pn=Á¾ „,:­¤`É„¤/Á0¡Ó>3

View file

@ -1 +0,0 @@
qV+5b1yOMnHBE5hgKbJSDWnmvb15yt9XF37Le00C8wE=

View file

@ -1,16 +0,0 @@
age-encryption.org/v1
-> X25519 g1vqWzZctykJkoxT61vjFHJUqeNOKSg0bf3wCYB7MDc
D4JZnpctpgjZaIn5WK3/hpY9R5XhQHWnHFJ+Uh48ZIs
-> piv-p256 XTQkUA A64jIlHXaqPB+WBfZFOrOihV6EF7Y2yt5BxVtGydv/E/
JJ/yeiDQSl2NINj2sSN/TUzdNhgmrtI6NcPcp45tLSk
-> piv-p256 ZFgiIw AmP5WWw0UADOv+ilwqnbRYtq4sQUPPIAANFNjf33yqKr
B5FRnKIDqLPAlFzrpZXjoiH6BhE51GRocvGhRrGFEv0
-> piv-p256 5vmPtQ AoFOKSUyPwm3C/KmrC7z38CFMXr/Ct9mzvil7bHjg4jV
hFV3hQGKo8zPOybLDnRYlyeNTX5kFb8AOwBrgZ0JTb0
-> piv-p256 ZFgiIw A8SpfqzsNh/My9UQSiBNFKH8p29bLNs3NfbBkAQbbr+0
xeittOKnGG63GDguhqN2fYMg0LiLkqU3b7XStbDrrNY
-> y-grease x#'<Q
2zNxND7dipOrQT2lnYqzR3Nfl3ovQ7RJXiuUbsGuDp9tyPCxZ6I2DgclkMmt9VYu
qNm5aDYTLJpNfPhJniEtkBi+Yw
--- BA6/EyC1xL8JXy/dwpqrTyHT2SRbsyvlAHq32w8yIDE
û<EFBFBD>EëåD!¿dJUEŽÁ<C5BD>Zw5H#£i7<69>}­1»ÙãhÈ3÷«P1Õ<31>Q<EFBFBD>]gñ2à!YÈLmw×E¯ÒNò™Žd*Ì«È›

View file

@ -1 +0,0 @@
+OFa4ZrDnLty45PNocVJ7uhzU8JAaZiqZxCP9dENu1c=

View file

@ -1,16 +0,0 @@
age-encryption.org/v1
-> X25519 Fpotjtu7lksK7LzYZTkTP7OXF2etf6k/jAs3qT63pyg
Az3CTRHiYmqI9mVSvt61WgbQa1Sw7tTI/GwuwGNm2Rk
-> piv-p256 ZFgiIw AwwKW8KYhA3dsUgANUxvffEiFLOadwllahNrchfzQTfq
AO08XTSUINWT5eY1EgPqHHSY/y0gsgszz3psNnGSauA
-> piv-p256 XTQkUA AuxujxLf1wM1siHqnkbayQ6C4KZbsAzdUO/8dsiTRohe
1AUfKkOngKRI4jPG820VihSIP5ms9jH8MvHlEBiwVAE
-> piv-p256 ZFgiIw AqLEvSEzM5D4K/W67DVz7icte3mw5+FqFtBiv4Ba2xua
mbrEOcAnkiXq1Phh1SlnTjDuhLma+4hqv8FMceymOzQ
-> piv-p256 5vmPtQ AzENFlgqOyGbU/FXskgenHamZs/H+78mS9PWsYoXXqae
pyx2IlIw+p+7dAUg5Ohj1cKxW/9S51LjR2A47aNgH0c
-> AJ/nN^^b-grease P%To4qn; llf1 (\|f~06
ROV54+I9IMrCY2DvOXDRsY4otebllTMp6ddWYA
--- PGvDf7ZhjEQzcNDXVlDw4Qehrs/lg7hi22vu/2lo0N8
ˆ¤rëyÔ<79>±³{<7B>f#ßç`M}Ñ”<iö÷<C3B6>d=ÇÔp¾bÍnÿÂåEËé<C38B>-
¹$thòˆû:;.9ñ

View file

@ -1 +0,0 @@
wODUgMHl+qSCB8O1purynIY/AaPyIJ4kCFCEHmRedEk=

View file

@ -1,16 +0,0 @@
age-encryption.org/v1
-> X25519 m8FrH/TJL5v2er4GSGnDNLJGaTiRaiXYtxk7pGMMJnY
o6eINCtC6MdZUy3t5K7jWbWyp66YIILG8ndYxmRp974
-> piv-p256 XTQkUA Al8tF63UnANIwwup8gZEEcFb4DdF+6LDbe24InqpVfjb
dPAkYSsEe2vqmXx7k84bK0PYxiI8UKFHZzHswnSSQjs
-> piv-p256 ZFgiIw AqUv2b0Mg00xIF9QoCa2u6YBrMJAMJQ5q5TkJlT94pyL
q6LsNNkptP6KHorvFTeVfbhQVWeKRcgl7dnaY23hDGM
-> piv-p256 5vmPtQ AqIVMtD5c/hClFfSEjjEC/YEhuB1yk1Lgmse9yCkfdkA
V9/tCgauksldhaCRp8WZ9WfOSFPq4NOZptk+mp5dZI8
-> piv-p256 ZFgiIw A3LfSXJschjsAQHGwmkaHDeezim1DjR4T8n9hSpGj0I5
rHpCP8fa0VxPYV6qAKYQLg6Jreyq++HDV/nUQJzTVzw
-> ]-grease ?+jZ e jc:Xwo$
O92bCAaMkQpSsOKzFztoIy94sjgyZs4RfFoBz9Zcwb+P3IaHUpTGvW8wyYOGNcm8
2FLljf/kFZtHxtV8W7GtVnFDj0uwrMnClCnen329/46Ou6pHDcJ+/Q
--- swSl+llzwbh5ymR1l6iRQlTM0j+70PAw0v8xhZA/jlY
¸ÒÌ6WñÛÙ™ì†5BÔÙ IVSs$¯¥çC¿”ûl:Ʋ+»8+¶2‰µ<î³ÒT-R¶»¥âÀ®÷Iÿs>¬ZRˆÄ¥¾"㤽o

View file

@ -1 +0,0 @@
k0IBTHKntu0plDUIApo0ZOa3XlAh2Wea09nih4Ahij8=

View file

@ -1,16 +0,0 @@
age-encryption.org/v1
-> X25519 sJWb1AB1ani7iSARBKiza76F4BZ/1RT+nYo+h3SCvDM
G9r4LID6JVa+CbM+goWlorWNAutTfCWCRXkMKe68GnQ
-> piv-p256 ZFgiIw AimY8gt/sR16sX1pmQ7KsWjklSprUl5xQT51DJ2CBrmo
35Gchuo7PlxnVg7nCmPX2l+Hwpqkn11Deh/gINotDK4
-> piv-p256 XTQkUA A4Y83D0/vdl4f2gr8g09YO5xTM2en6/zdXTA4tlXTzse
pt0/k460n/rw0pGQVmbBvWkmscra5wL7Q4pUfC1aqJs
-> piv-p256 ZFgiIw A7kGeBnc71Bei30JFsrUPlhOYRfP/WwrtNYxyZ94blmd
tQcInK3OPdN5uYugFZc6JNMgMMrBHrNrfPLgK1GQuOU
-> piv-p256 5vmPtQ A2cBNFJA8IFoZcUGhwpTCrrh9v+ffe6UhbJkhYvfv310
zf161XjBEKWYDLwaWw+wGuCGJJFD6NatL3BgSQACB38
-> --grease \tv Z&IiJD *{Xl~2`' FOEGQ+s
hnw8ilMQCmjeH1dsP0p0Y6fY0X7l5goCmTR07RFMnXRH2Y7FQzSe5Ipg16+V9Rmj
1+RZABaebmFQFAJwtfFmeLXzsFVn0sMtflMR/wmunn+RuZ0XfzHzM0QOU2g
--- rdxJZDoceAdq9YF8GoDLcHz5UInJlcXCrOgr3/XxI/Q
è×Ч"ðV÷\ÆÒ¯<C392>œ/¤ßSÙñó×Üw¿qH(„¡€­HÐØö<C398>(=÷Åæ­ùŒœîaPiäÇ”ûØÜ_:K°·ˆSŸ1tØ¥Æ

View file

@ -1 +0,0 @@
HKftlC7tQXYToYo0VLHqvdnZxQfNtJ8u0QDN3mLgqiA=

View file

@ -1,16 +0,0 @@
age-encryption.org/v1
-> X25519 jCMM6Kfzndo9RElgyE/ufEMYrpwsowEpuYQ+U7NypCo
MBpF5pwy9moVqDHGudv0OxxG6UtdbKfvdphs89h3mi0
-> piv-p256 XTQkUA Asa11BAxSalte9zAy9P2TCw+OlzgPHHmVZJ0idqMUTOq
I7Uc1mXKZZCJ2sJ0vFvXzo0a173AwtO5IBQZ4LTfjuI
-> piv-p256 ZFgiIw AxkNUN4odgmfqbKIddw7LtY5SEDB0oxMOg+/vo3ooiMZ
rX4mq9JYyp6secsjIclReA4hDdSumaEeVava7TtO36M
-> piv-p256 5vmPtQ Au5aRQkGYLFwjjZGs/z/HDpVIwAMLK+O2FHK4tI+gxNw
HQYY3BJvG912yNOhne/e5Bosoa0N9i/d3Arsi1otmsQ
-> piv-p256 ZFgiIw AhGklGMPM/rAaye57Fz2PO1CIMBNjRPyP1sgsBsFhdUL
ITdXsq7gZ/13qqTsvfh+8FReiBmIpRwI+vDL+UBQKGY
-> ^}`pou-grease Wfm6eR *q.w\ ifZ #dT9
vd8IjtgnVmIKwldS7/Ii71SzniVtW9G6tCCiSmPM3tZE1EaYy0Z/6KuKPyz+tWst
Y+i4j7okriIH645tQXaI0oHcx4VZFn+JyRdX7mYNldwoNW3OKA
--- bAVe+xtXMtXfbGWz8TC+Wvbpmb8d5YVtUtdYqIG6Qfo
CÝöKvÛ3<EFBFBD>±mJÂÙÅÙ[l~0šr)+2Ì?éœÑ¦þ\U"“ôóMVÎQââ'BÎÄþ*Xï@¦­<10>Ä$DІ<C390>ìXy´dJ·fü<66>¶•9

View file

@ -1 +0,0 @@
9kyNM6XKz6HRLBECG/xRwplVZ7o6SEIxTPDuTvcPxw0=

View file

@ -1 +0,0 @@
SX7PZcM1u/eJZM/ghvBDS7am6HZzlsxhK537HWp62VQ=

View file

@ -1,16 +0,0 @@
age-encryption.org/v1
-> X25519 8Xu4B4tsiFMALzzDr8aIj1srctAEZ1QKYzT9wTs0DwU
0URbUZ1UlEdZpo8vT/LiJHW5RERO61S7RrJYviO6OYQ
-> piv-p256 XTQkUA A2ZwHHpSy6EzWxhfouDwh+PO//N1isE6TPUwAVPaAW2c
ljLdsmhEgsDRlz0y5Waea2FEm0k1L4W8igzYHz+/amk
-> piv-p256 ZFgiIw AohzN9q6Jo0LVuuYmxzhfizqlRPnuAlYIKx6dvMYvcq/
lSHu87hQJNVNHDTnMc9Se693+yELopkk6hFmUclLiuc
-> piv-p256 5vmPtQ AsBXiyuQmIaO2+Z2GTyT/rdhai2ahEkYkcO+dYsibZX4
DE5cSckHALqUdEYBe8Tpioo/DnD+DBpV/0pWZwvd2eI
-> piv-p256 ZFgiIw AgSNI31rf5CH8Gy+3ulIla3MgNkLfaHO/wKtfu4XTG/Y
n10QiolManskviiW3ogFtTpbzr1Mcs7/nFCxO6IQvdg
-> &\+nN-grease
xHRCwm5QRd8kTNpD9BNQflDjSoMEES64Y2yIHfbaEhJlLEp3MR+m2RzayFNxOfpr
zRjUwvQfjlhkS4bXLmYf5HHtBApMMX4
--- Ucy5PhVNSDJP+v6m5QDaZcomuvr5Z4XveQSTJwCAMsM
³çÜHaý<12> ÿ-´íÀý—5t¸õ†—Tv3‰¦DêÛ³Ø^?ݸ‰¬‚ãò\huýÊ9ù.`EÙ¬ªÓÂùè¦Áè cÒVBHÔG†Ìž©G

View file

@ -1 +0,0 @@
zipMs/ic3IPILamMOvnGWZU+PYdyA1i9UzC9UxRMXXc=

View file

@ -1 +0,0 @@
01wz/sO0PIlwtKTfR2z8pQKzFt4kO5CSq57f32y2F0Q=

View file

@ -1 +0,0 @@
F3tFnEGn58ahB2p4hI4xFRfwyK7SU3+Dx598DcLAQlA=

View file

@ -1 +0,0 @@
eIq8a4zS+xAcuilz8dw2znMm8xzMmYm3jg7wvAX5UV8=

View file

@ -1,16 +0,0 @@
age-encryption.org/v1
-> X25519 kZNXioiEjSwPSSCQfLIUHJ/Po3Kjyzexkm9JOT02CUU
ZDtL78nQ9iM5br5keKL/HuuLO31giHa40m5YhoNkeN8
-> piv-p256 ZFgiIw AgNxOYUDaV1QTaV+IyMF09gouj/UtJA+y8H9h/MrgVM1
luS8js59wJ3cfsEloakGvoMmMWNkkq3Rc/AAvbxCA0I
-> piv-p256 XTQkUA A97+iI4FmLSEqnBpW7MJDFocMQBnv1yl5sNPLsZzapzP
CEmzOhcv6V6OGC3fpP8pUomlIRZxj05TQyYdDEtVwbI
-> piv-p256 ZFgiIw Az7qLW7ASn9x4PQt5yswl6BWULosp8j9D1sIlYe+E+XW
g80n2hOdx1m8pw8jeCFPHOH34bOApNVxapgiQj13USc
-> piv-p256 5vmPtQ Aj9Px4PctikcatMGBt7PTghHWLGtUXu0dbWUBROppbnX
5uYv5eEaZ6nfaId6JUaQSjbwTwZ+uyv7wSppAFZFnAM
-> )K-#NG;f-grease w] Do
Jlm3URc6Elpr7TIlK8e5K6Xu1Xy1f/mpG6XgdWgPRbNNOf0dXddDRuFT3g6lf6tK
FVmTXrLndZmPq33DD0WP6MwtBWmDCeh59/3DpjmvSxppM6Q
--- Pj8J65gm8i3w3GErpi1PXNdeQs/8cGkG2vetkgOifis
<06>ÿ·mBÑò¸¸F àºîG“…‰pRbúÎeƒ:8þiG8<47>Uyl +V`01ççà4<C3A0>ƒ´±ŒŽº¹Êèá+¢ñBâîó£&

View file

@ -1 +0,0 @@
2l6LxDMuuo+vr3aAraMbaVrCMHbWNNIujpwjDD/UPWA=

View file

@ -1 +0,0 @@
YFUko5BLbPFUxgMBOdRmuaP3W8MyKqcbKfGs+kJsaHQ=

View file

@ -1,15 +0,0 @@
age-encryption.org/v1
-> X25519 EYthUGeAWjYiRRcvPvVuWppnAnVEKvbBgkegeGFGzGQ
STGglgLwWiYP0Plr69RVxlIGVh0ZohPCaUy0Tl2rnbw
-> piv-p256 XTQkUA A1Tk7Xmx3KAxWakrxXyjLHzuAvwc0Y7p582tV/i45s/0
nhkvRnz7+lr0df84MMoHQJbpUoj+0UrdTw/XISq8taU
-> piv-p256 ZFgiIw A4rpsK3V2kcIQ2DRRL3Vj9nZUgANguzqvtHuLAVsCVlP
3V0M6j9CU/LWRkYaDI+3qvynu3s8UU91pjCaMEG8sTc
-> piv-p256 5vmPtQ AsD/VOJLQcHSoOVtJ8zdHxSnOv2JX/MsAGP0fB3SPvBq
yy4YY33Tzflj3rQg9xVAfJe47NNeX3GLBn4iZa0+aVM
-> piv-p256 ZFgiIw ApTVTCfJLHfVGA1Qbi44CisjSX4j/tJINa8xRDnEGYAN
4Z9/mK57H6JH7fsAlQTcEX/JjdzDiA+XgsA8tvcqM7U
-> Vffv6Z%t-grease Kc1"0ol xYS0
SOTywmAk8Z0fVaBEgVlPJMVWYNrN
--- GsqSM5RXgbGD3xulF6piH/NxH7AcVRVJT6rHQUqV/sY
xA€Żˇ,©Afí°ôáYíüU„R×!·$9ŻQăöUcNj¤<6A>Ţn%îHĺŇýnźÂĹáuéîČťÜVAŃéâtébJ

View file

@ -1,15 +0,0 @@
age-encryption.org/v1
-> X25519 8xUsd8+0vzcdMZ+9/Q7c4uHrINfL/YnGb2oi5TPPUwk
GrQDEqwQpunmU/Fwa0o2YV1VEMwb7F3uuUqPC2b9kNg
-> piv-p256 ZFgiIw AtzJHfEspGUDVtaXot1EE/u3Z5cTVL+PeBN2f5ZWbL6M
fOV0Hp6+cZB3NbypVXQtPULDonweA/62/G5gnunWVG4
-> piv-p256 XTQkUA AmPo/XlWsLPW+JYoTGCLTxWccJuh4EcKafN+D+URuGoF
3rHV1yeANXzWRpWb/0EA1IjCOitoTsLGN4dU1raTr0k
-> piv-p256 ZFgiIw AuduWmro6APJsPTCZrtRpkwECkOfsDL109rvrE9UxkkV
cnJb8UKLM1Oy9nZr+HQp3p6OhT/+9Htc3GoAqADa2nI
-> piv-p256 5vmPtQ A3ge8G2tligkbgdXvrngnObz6/kk3R5HN1Gl31Diz5hc
1d0ebykK0ccq7R4UegjAL+dl0EX6dves6Qsg4n7I0sA
-> :/-grease 4$+ P= _VV%:"P|
4Ny9m7mh1lEg
--- CsASon+mZ54A0BLZmBl9NaSa9n6M9mYbpY6igzdGF+U
“]lS"qùv¢½Öö˜P2­Õb¥“ç]•áÍÞ nóƒ7ÆÏ¥C@dD-y+å=ÊÇ<C38A>äY§ †Dë` v  ÆpñÙ~‹ìãþž

View file

@ -1,15 +0,0 @@
age-encryption.org/v1
-> X25519 xJo5HhKHIrFP7wbV//wpaFoboByUMZOXreZt5Xdd5BU
Ru6CBmrq1v/rbPhoPXYhCHq0yMGCCUiTgs6ZM5kVNKU
-> piv-p256 XTQkUA AivD+Matq7mjlIusMtx6+lHk5ryKZcg56EEwhGN71x2M
6qgJoPZWiuylup3zgJjYm1zLoG9YYL+as8UtGFhpnCg
-> piv-p256 ZFgiIw AqW0keuSK7y8oSO3JYe3/l+pAh+Wxqbu0XFNJ+qBH1Xc
vfCxAefSzfOo/1+ihhRS8Ilh7nsKwwyEf1LLRPfaKiQ
-> piv-p256 5vmPtQ Aqz7EelM5PCayYWA4IBPOjcPQp+qRU0TcTQIJM6cOBu9
t1bkON97ATB7CCcCFCOZVAr3PvZ0dFR9rnURWLD7dkk
-> piv-p256 ZFgiIw AvVb0aN4gHr586PdYixoAPBpF061efDQBshijna2MwQH
7TJos5wgP7QfSDLmjKvWEQCt1svv8/psA9os7FcG7Aw
-> mIBupI-grease jy yXj i/P I
M9tNIG4pdjXCQm9gWUWNr7bE0YBOzA
--- kKw6CxBObbcfeukkS/spDO+s4zcMkriNfDrfXoD04uw
 #¥sÏ®&ºJwÁ´Àüm~ÄÄS<C384>¼Ä&<04>X-Æ¥u—[WŒn¦tB[<01>죣ecÆHr×É)hÃg')Öð.M—Ыó U—Îkš

View file

@ -1,17 +0,0 @@
age-encryption.org/v1
-> X25519 qsgCpy8yqEIlPRdfE+Lxs5gOIYX9zIcllgXtOT0bQV8
GN/imU+Sf+2hT5zzOmYI9TgbLX4QgncJ1SHBjKaYlSA
-> piv-p256 XTQkUA AoLpzcqYmEDQTqFx+W3IBRGp03iJjaRrDRI8wfGbq/1X
QO08SCWFfwpSTUaQCnIKaGGWIgXh0i7w/p62X56ZMEc
-> piv-p256 ZFgiIw AlBi1aYyOCfnmlfVAdDVfvbN6NzEr/ypLeoH90cEwa5G
HZJ9bubfkFIEJbygeuvRm7UeTLppXG4knQFkKL678mM
-> piv-p256 5vmPtQ A3JtC4PRXJTHIuJzHoygX/5X4ok7cIfFF4wIQ2oghhpm
g4dV5vVrjbDt3ysLfBs74sy7yu1ol9PGPYF6uWnIu6k
-> piv-p256 ZFgiIw AuxXXZDLX6G9CTNow/ppXhTJ0GrNBO3RB7p9VC3BeY+0
QyfdagRgpUghg5U+mTYxxhVKrIIDEcAAzqwSSjwEbrk
-> 4-grease }E2
0IdsRluyK0F88hpuyJ8yVMFkcBJ6L9z5JBs8lovL26wWtxUg6knJD2vVopGiKCiD
Vol1dGBhU9085pt0C68av0GXXvPzxrsO+SDTz8c
--- m8uTaLg5F3GK5noq8WaqyfWN4bwotHUgnWvOMgzzAII
ñ­Ýûc„àFÉ%
ÍLD9QŽvÜ °W;!ÐÄê˵aÆ…aÀŠ o¨²Ì<C2B2>§±ÏE}Z/»s¦Û8š7º¿ÐD<C390>ã§<C3A3>±D<C2B1>7

View file

@ -1,17 +0,0 @@
age-encryption.org/v1
-> X25519 h2wNST4+qSw4uCVCUqSoprjByli3t11plBHp9y7dRGA
DCCsXoA+stUFmu0aNcNJSClOFTF9pNjgN6hsZjHkOrA
-> piv-p256 XTQkUA AvmTYpnMbBf4FiesxT0+RahR55nXJbmCsPh9jSXCk28K
AUOUpit2AsUMCh3KRqwMMSLJlSUlGBeoJZWyey3S41Q
-> piv-p256 ZFgiIw Ax8nhmzow+Pshj2paySHEdKc+V+BBP55FpwNa/HOumWu
1vnybx4PiWiep4LKISh9+DQzDcv46iTf0BytjwsVPqo
-> piv-p256 5vmPtQ A5l+gaNbTzurlEnGVdjdYBrXjF5R+xdxBANv3V9W74Tq
AmWUmtqPpGCG2G9xEswFwnCLNWS0iP9wdaS7UhMIA68
-> piv-p256 ZFgiIw Aq2tikCz8rv/r8PcY/3PKws74HTRdKC5WP1Ht/0ifeC+
kSiDUso530lPlYN2P0JIVG1LgEbL2TkRK9v8YQpUQ7A
-> =3mcTXky-grease |'ZI-R @E>y{ m){w =.h
yyiAGQon2cSKl+YqqZzrHRtsAnSVkg88UlO9Oj6nAdMc7/X+kNmoV0roz471Qcst
5WRDl9zm+ZUTS5bCqDdLThdKlxe2BFc4vp5WWd/QBVrlGuKPza8
--- JfX5HKp3fQCfBufji0c+DBERd4JPBp1v/HG5vXkRUzY
+{<7B>|Æ\X,<2C>50†¶tº+½KcÎ<>²ôàp¼àN²³d
ÇW:MÈ°Í•¼ÜJŽã”­*ìnË™a­9xþ-]

View file

@ -1,15 +0,0 @@
age-encryption.org/v1
-> X25519 DgYfF0fRhZ8YZ/OhLAkh2yTKJ8wJGn3NIWlZKPSouT8
t0ru+RkJaYwu1182O+7mXUPY//1MfMpWfAZHt8EB0Qg
-> piv-p256 XTQkUA AhsO8VrcSN3C0OvXnQZgknZmPQXkJ/AZLgoEJi8SEb02
45FaY0/8fSFDe7ICj26UaZU2b7FJ6LwYjA8PAG0te7k
-> piv-p256 ZFgiIw AyajmWcvtlbiql9fmKjAqOFrGXwxE+dKlO450qEzY6gj
ybg/Vq7X6iqFEvNAUeSwBL9MYEZk4PB1rj7m980JQZI
-> piv-p256 5vmPtQ ArpWoKRL+CQf70RgopH6D3atHb8F29h7wjuJcsTSgyQn
JuvfAbnXSwP3Jl1nX1y2pxsoIMuoh3vPr09vO42GgRs
-> piv-p256 ZFgiIw AwrP0evFqosflrXzbYJNx4fdJS9dF1107gPf3NEAoDJl
4TRZzpprOcjoXKMpWCXsgwMiKQHlKPmcFGxEQfq0fTM
-> HYEBa=-grease 5a{m+}I
vCELeWobKeGEIHMdXjqKDVyjrsgrKdp74Z8adOYuFF+01bSwou0bx5NE4PypoY8
--- Jp0EMbTh9Fm57m+RQGZZ1TQx2si06y00JrDP8a2quCo
% Pq~K<>!w`<60>/¦øÖI¬áMR$Zz·ô·©vNWDµç¦¶Å4`p1ª•S já*¤Ýxî`è¦<C3A8>"%*µk:

View file

@ -1,16 +0,0 @@
age-encryption.org/v1
-> X25519 rqjulMMqQvFeDApkCZo4KQvgVbmZ/TLOpy3fe9CQCTc
j3JoyoBWSZtVDka4qqquips0HmZakBuToEjNe+ZEccQ
-> piv-p256 ZFgiIw A4e5w+3n+gkOMBeSI5VklW1kJ3846byVint8b7HGer4Z
jY/O+b0JwsNxpSvEtrWB1IaeVACDagAaqfLmoy9VGrw
-> piv-p256 XTQkUA A8WfkKXTvoJ4M4gX/t3xaK8wy2pZbLO9dBHrlUqKJHjr
I6WsWbqg+DIrOR7cJCk5cHz4gz0d44RhcNSqUU/9VSA
-> piv-p256 ZFgiIw Axn28eRfih6xjAKMw9ZFXHN4jKs013d2IhmLTAwl1Ixq
RldIXTSGdfjC5o4xzOttzyX89zAsuJGitSeoyts62mo
-> piv-p256 5vmPtQ A7sqh4eBJsdzALHPVdbk2WJ5YH0M8iSBX/wP8DtI7Mpm
tq6yVRXYXKwQD3qbvvBdF4AuFehgvgS7lq2DkI5hI6Y
-> s?-grease 38 Pego6HDg _|QaxRe
rexAgfgN8bC3JvURMFuCxfHxnIQ88B2hvka0BmvM7XJSWA8gAGLxjhOr0sw6iygG
6R+lshVeDfexCFxX4KWENEVzb9f4JWCqcGA
--- NtjNfHsaetHNRBHHwX0ncFGEb5hewYNhg8/WmJCLg80
<EFBFBD>ÎJÀZ¡»uó­Ö#ù|4|«/kd¥å óçêínÄÝ^@ß…3{—õ85Gtû‚å¼þò0mÐ.!Ç×¹ÿ€ÇXú½!¤ÃQ

View file

@ -1,16 +0,0 @@
age-encryption.org/v1
-> X25519 OOfIE0asKIsd83l3FlOAUzVTZ2nyzgVVZ+3eFmVQTSM
Xp86tkjnZahB3SOb+/5/Q74MsCRwj0E5cWe3XyNnJlE
-> piv-p256 XTQkUA A+LfTwtC6M9B5IuzZx9zcSZ6/hblgphmAIwA6CRxk6AW
mYyS2Ot8h2eJbrJ/afIcfOX59aQCThE26KTibA71MQE
-> piv-p256 ZFgiIw A5I8g7TKBSDLsM0FpV6U/JVpabKuuCHHR9HdPqkuZqqa
CkvfGh6xS9GvSKhh/FNW4nKJgQMTUGbuqZtMbJvVyPg
-> piv-p256 5vmPtQ AlNBDeN5ihouDbb7mjNn7f4GDTRR0hf2M67LhCwMRR+x
vffnqgDMvm3OVlBKUvLR+aG5t9vBBJ8ygKTyk314G/Y
-> piv-p256 ZFgiIw ArEwrMQWaBWaOOYzUfB1zTCRQu/AjNiyN58UBSGaNhq/
ZwryYVzJR3RYGYMZPWmvWkvD5dyGwF1FIsDPSvCTmOg
-> .-grease
OozUcy+eh4uVbpuy/agtDWTCaZeccGlqym5s6L7KE+LqYmNhy61RwRC5NZqBPbsT
7H6EepsguVZzijQBhvPhJOK/a82g
--- 14GCAxnHT3eXYAvqtbaW6qHO2IAANgmVPl6Wlfox6wM
慾・刎^<5E><>eユ*PJBウ「ヨサ|ァヌ開碗ヒ<E7A297>」gヤa=゚十騨剽ヌC舟<43>C」du覡粟ネァj€ッ&U<>fル鑑?D`

View file

@ -1,16 +0,0 @@
age-encryption.org/v1
-> X25519 bq+eQrKzKWG2cvp+7cKzpkN7KEbxf4H8aSOBxOBNeVE
uiZloroeAw+q0T9CTGbAg6cdHShGaa5YOVk0iE5FLMM
-> piv-p256 XTQkUA A5CqoI0rxRrOyHv6LksBqtzWPapfCLi6IdK3KAUATzJF
d3VMdZpw0TjU8kZ6WNLcbvenDD4WWxJp2rEogNnW43o
-> piv-p256 ZFgiIw Ah/2IZobkAFu0r0rSHvB9RyQhXh+wk1R9Vlky8J44xib
5GXXZuXybVXcrpU8G8bWYwMOjnzdw7X+YjQaQlA1F4E
-> piv-p256 5vmPtQ AjmJ3ZgFxcbSbGefvufWZNzo0nOc8vl+4jA7kb5kwSbI
2ks2FzxZ/YloeAVCRT/0NEo4hRWzUbknj+pnwtGuEZM
-> piv-p256 ZFgiIw ApvFPxETdpXGYLa9srv+pKFHNOGfa7ie8oyOInKDbOqC
8rIukUZzrkWdH11pnTYfPd259ql/UGg5/Z6SuNvslUA
-> X=N9-grease CPXXj9j! Mf6?oC AuDyAWo z5x1TGOh
CYoYan7n
--- 9xwTgosTBqh7i3YCpHUhvkYV6bormJ3hYP4WHTwwQk4
íIˆy»0Ö[Ûž$ÞŒ‹Ž- ¦¨:k@™ Ê}é9 Ÿåµ
2òîl'*Ô­[Sêr$ÿ*ÅWjüBì,-w£RÒ1ãBý&ø!€.ó@

Some files were not shown because too many files have changed in this diff Show more