wip: migrate client modules

This commit is contained in:
Leon Schwarzäugl 2026-04-02 19:25:58 +02:00
parent f6d2ff1544
commit 7ce27d5d2f
Signed by: swarsel
GPG key ID: 26A54C31F2A4FD84
245 changed files with 20254 additions and 188 deletions

View file

@ -0,0 +1,225 @@
{ self, config, lib, globals, inputs, outputs, minimal, ... }:
let
domainDefault = service: config.repo.secrets.common.services.domains.${service};
proxyDefault = config.swarselsystems.proxyHost;
addressDefault =
if
config.swarselsystems.proxyHost != config.node.name
then
if
config.swarselsystems.server.wireguard.interfaces.wgProxy.isClient
then
globals.networks."${config.swarselsystems.server.wireguard.interfaces.wgProxy.serverNetConfigPrefix}-wgProxy".hosts.${config.node.name}.ipv4
else
globals.networks.${config.swarselsystems.server.netConfigName}.hosts.${config.node.name}.ipv4
else
"localhost";
in
{
_module.args = {
confLib = rec {
getConfig = config;
gen = { name ? "n/a", user ? name, group ? user, dir ? null, port ? null, domain ? (domainDefault name), address ? addressDefault, proxy ? proxyDefault }: rec {
servicePort = port;
serviceName = name;
specificServiceName = "${name}-${config.node.name}";
serviceUser = user;
serviceGroup = group;
serviceDomain = domain;
baseDomain = lib.swarselsystems.getBaseDomain domain;
subDomain = lib.swarselsystems.getSubDomain domain;
serviceDir = dir;
serviceAddress = address;
serviceProxy = proxy;
serviceNode = config.node.name;
topologyContainerName = "${serviceNode}-${config.virtualisation.oci-containers.backend}-${name}";
proxyAddress4 = globals.hosts.${proxy}.wanAddress4 or null;
proxyAddress6 = globals.hosts.${proxy}.wanAddress6 or null;
};
static = rec {
inherit (globals.hosts.${config.node.name}) isHome;
inherit (globals.general) homeProxy webProxy dnsServer homeDnsServer homeWebProxy idmServer oauthServer;
webProxyIf = "${webProxy}-wgProxy";
homeProxyIf = "home-wgHome";
isProxied = config.node.name != webProxy;
nginxAccessRules = ''
allow ${globals.networks.home-lan.vlans.home.cidrv4};
allow ${globals.networks.home-lan.vlans.home.cidrv6};
allow ${globals.networks.home-lan.vlans.services.hosts.${homeProxy}.ipv4};
allow ${globals.networks.home-lan.vlans.services.hosts.${homeProxy}.ipv6};
deny all;
'';
homeServiceAddress = lib.optionalString (config.swarselsystems.server.wireguard.interfaces ? wgHome) globals.networks."${config.swarselsystems.server.wireguard.interfaces.wgHome.serverNetConfigPrefix}-wgHome".hosts.${config.node.name}.ipv4;
};
mkIds = id: {
uid = id;
gid = id;
};
mkDeviceMac = id:
let
mod = n: d: n - (n / d) * d;
toHexByte = n:
let
hex = "0123456789abcdef";
hi = n / 16;
lo = mod n 16;
in
builtins.substring hi 1 hex
+ builtins.substring lo 1 hex;
max = 16777215; # 256^3 - 1
b1 = id / (256 * 256);
r1 = mod id (256 * 256);
b2 = r1 / 256;
b3 = mod r1 256;
in
if
(id <= max)
then
(builtins.concatStringsSep ":"
(map toHexByte [ b1 b2 b3 ]))
else
(throw "Device MAC ID too large (max is 16777215)");
mkMicrovm =
if config.swarselsystems.withMicroVMs then
(guestName:
{ eternorPaths ? [ ]
, withZfs ? false
, ...
}:
{
${guestName} =
{
backend = "microvm";
autostart = true;
zfs = lib.mkIf withZfs
({
# stateful config usually bind-mounted to /var/lib/ that should be backed up remotely
"/state" = {
pool = "Vault";
dataset = "guests/${guestName}/state";
};
# other stuff that should only reside on zfs, not backed up remotely
"/persist" = {
pool = "Vault";
dataset = "guests/${guestName}/persist";
};
} // lib.optionalAttrs (eternorPaths != [ ])
(lib.listToAttrs (map
# data that is pulled in externally by services, some of which is backed up externally
(eternorPath:
lib.nameValuePair "/storage/${eternorPath}" {
pool = "Vault";
dataset = "Eternor/${eternorPath}";
})
eternorPaths)));
modules = [
(config.node.configDir + /guests/${guestName}/default.nix)
{
node.secretsDir = config.node.configDir + /secrets/${guestName};
node.configDir = config.node.configDir + /guests/${guestName};
networking.nftables.firewall = {
zones.untrusted.interfaces = lib.mkIf
(
lib.length config.guests.${guestName}.networking.links == 1
)
config.guests.${guestName}.networking.links;
};
fileSystems = {
"/persist".neededForBoot = true;
} // lib.optionalAttrs withZfs {
"/state".neededForBoot = true;
};
}
"${self}/modules/nixos/optional/microvm-guest.nix"
"${self}/modules/nixos/optional/systemd-networkd-base.nix"
];
microvm = {
system = config.node.arch;
baseMac = config.repo.secrets.local.networking.networks.lan.mac;
interfaces.vlan-services = {
mac = lib.mkForce "02:${lib.substring 3 5 config.guests.${guestName}.microvm.baseMac}:${mkDeviceMac globals.networks.home-lan.vlans.services.hosts."${config.node.name}-${guestName}".id}";
};
};
extraSpecialArgs = {
inherit (inputs.self) nodes;
inherit (inputs.self.pkgs.${config.node.arch}) lib;
inherit inputs outputs minimal;
inherit (inputs) self;
withHomeManager = false;
microVMParent = config.node.name;
globals = inputs.self.globals.${config.node.arch};
};
};
}) else
(_: {
_ = { };
});
overrideTarget = target: {
Unit = {
PartOf = lib.mkForce [ target ];
After = lib.mkForce [ target ];
};
Install.WantedBy = lib.mkForce [ target ];
};
genNginx =
{ serviceAddress
, serviceName
, serviceDomain
, servicePort
, protocol ? "http"
, maxBody ? (-1)
, maxBodyUnit ? ""
, noSslVerify ? false
, proxyWebsockets ? false
, oauth2 ? false
, oauth2Groups ? [ ]
, extraConfig ? ""
, extraConfigLoc ? ""
}: {
upstreams = {
${serviceName} = {
servers = {
"${serviceAddress}:${builtins.toString servicePort}" = { };
};
};
};
virtualHosts = {
"${serviceDomain}" = {
useACMEHost = globals.domains.main;
forceSSL = true;
acmeRoot = null;
oauth2 = {
enable = lib.mkIf oauth2 true;
allowedGroups = lib.mkIf (oauth2Groups != [ ]) oauth2Groups;
};
locations = {
"/" = {
proxyPass = "${protocol}://${serviceName}";
proxyWebsockets = lib.mkIf proxyWebsockets true;
extraConfig = lib.optionalString (maxBody != (-1)) ''
client_max_body_size ${builtins.toString maxBody}${maxBodyUnit};
'' + extraConfigLoc;
};
};
extraConfig = lib.optionalString noSslVerify ''
proxy_ssl_verify off;
'' + extraConfig;
};
};
};
};
};
}

View file

@ -0,0 +1,30 @@
{ lib, ... }:
{
options = {
node = {
secretsDir = lib.mkOption {
description = "Path to the secrets directory for this node.";
type = lib.types.path;
default = ./.;
};
configDir = lib.mkOption {
description = "Path to the base directory for this node.";
type = lib.types.path;
default = ./.;
};
name = lib.mkOption {
type = lib.types.str;
};
arch = lib.mkOption {
type = lib.types.str;
};
type = lib.mkOption {
type = lib.types.str;
};
lockFromBootstrapping = lib.mkOption {
description = "Whether this host should be marked to not be bootstrapped again using swarsel-bootstrap.";
type = lib.types.bool;
};
};
};
}

View file

@ -0,0 +1,105 @@
{ self, config, lib, ... }:
{
options.swarselsystems = {
proxyHost = lib.mkOption {
type = lib.types.str;
default = config.node.name;
};
isBastionTarget = lib.mkOption {
type = lib.types.bool;
default = false;
};
isCloud = lib.mkOption {
type = lib.types.bool;
default = false;
};
isServer = lib.mkOption {
type = lib.types.bool;
default = config.swarselsystems.isCloud;
};
isClient = lib.mkOption {
type = lib.types.bool;
default = config.swarselsystems.isLaptop;
};
isMicroVM = lib.mkOption {
type = lib.types.bool;
default = false;
};
isSwap = lib.mkOption {
type = lib.types.bool;
default = true;
};
writeGlobalNetworks = lib.mkOption {
type = lib.types.bool;
default = true;
};
swapSize = lib.mkOption {
type = lib.types.str;
default = "8G";
};
rootDisk = lib.mkOption {
type = lib.types.str;
default = "";
};
# @ future me: dont put this under server prefix
# home-manager would then try to import all swarselsystems.server.* options
localVLANs = lib.mkOption {
type = lib.types.listOf lib.types.str;
default = [ ];
};
# @ future me: dont put this under server prefix
# home-manager would then try to import all swarselsystems.server.* options
initrdVLAN = lib.mkOption {
type = lib.types.nullOr lib.types.str;
default = null;
};
mainUser = lib.mkOption {
type = lib.types.str;
default = "swarsel";
};
isCrypted = lib.mkEnableOption "uses full disk encryption";
withMicroVMs = lib.mkEnableOption "enable MicroVMs on this host";
isImpermanence = lib.mkEnableOption "use impermanence on this system";
isSecureBoot = lib.mkEnableOption "use secure boot on this system";
isLaptop = lib.mkEnableOption "laptop host";
isNixos = lib.mkEnableOption "nixos host";
isPublic = lib.mkEnableOption "is a public machine (no secrets)";
isDarwin = lib.mkEnableOption "darwin host";
isLinux = lib.mkEnableOption "whether this is a linux machine";
isBtrfs = lib.mkEnableOption "use btrfs filesystem";
sopsFile = lib.mkOption {
type = lib.types.either lib.types.str lib.types.path;
# default = (if config.swarselsystems.isImpermanence then "/persist" else "") + config.node.secretsDir + "/secrets.yaml";
default = config.node.secretsDir + "/secrets.yaml";
};
homeDir = lib.mkOption {
type = lib.types.str;
default = "/home/swarsel";
};
xdgDir = lib.mkOption {
type = lib.types.str;
default = "/run/user/1000";
};
flakePath = lib.mkOption {
type = lib.types.str;
default = "/home/swarsel/.dotfiles";
};
wallpaper = lib.mkOption {
type = lib.types.path;
default = "${self}/files/wallpaper/landscape/lenovowp.png";
};
sharescreen = lib.mkOption {
type = lib.types.str;
default = "";
};
lowResolution = lib.mkOption {
type = lib.types.str;
default = "";
};
highResolution = lib.mkOption {
type = lib.types.str;
default = "";
};
};
}

View file

@ -0,0 +1,243 @@
{ self, pkgs, ... }:
{
_module.args = {
vars = rec {
waylandSessionVariables = {
ANKI_WAYLAND = "1";
MOZ_ENABLE_WAYLAND = "1";
MOZ_WEBRENDER = "1";
NIXOS_OZONE_WL = "1";
OBSIDIAN_USE_WAYLAND = "1";
QT_QPA_PLATFORM = "wayland-egl";
QT_WAYLAND_DISABLE_WINDOWDECORATION = "1";
SDL_VIDEODRIVER = "wayland";
_JAVA_AWT_WM_NONREPARENTING = "1";
};
waylandExports =
let
renderedWaylandExports = map (key: "export ${key}=${waylandSessionVariables.${key}};") (builtins.attrNames waylandSessionVariables);
in
builtins.concatStringsSep "\n" renderedWaylandExports;
stylix = {
polarity = "dark";
opacity.popups = 0.5;
cursor = {
package = pkgs.banana-cursor;
# package = pkgs.capitaine-cursors;
name = "Banana";
# name = "capitaine-cursors";
size = 16;
};
fonts = {
sizes = {
terminal = 10;
applications = 11;
};
serif = {
# package = (pkgs.nerdfonts.override { fonts = [ "FiraMono" "FiraCode"]; });
# package = pkgs.cantarell-fonts;
# package = pkgs.montserrat;
# name = "Cantarell";
package = pkgs.iosevka-bin.override { variant = "Aile"; };
name = "Iosevka Aile";
# name = "FiraCode Nerd Font Propo";
# name = "Montserrat";
};
sansSerif = {
# package = (pkgs.nerdfonts.override { fonts = [ "FiraMono" "FiraCode"]; });
# package = pkgs.cantarell-fonts;
# package = pkgs.montserrat;
# name = "Cantarell";
package = pkgs.iosevka-bin.override { variant = "Aile"; };
name = "Iosevka Aile";
# name = "FiraCode Nerd Font Propo";
# name = "Montserrat";
};
monospace = {
package = pkgs.nerd-fonts.fira-code; # has overrides
name = "FiraCode Nerd Font";
};
emoji = {
package = pkgs.noto-fonts-color-emoji;
name = "Noto Color Emoji";
};
};
};
stylixHomeTargets = {
emacs.enable = false;
waybar.enable = false;
sway.useWallpaper = false;
spicetify.enable = true;
firefox.profileNames = [ "default" ];
};
firefox = {
userChrome = builtins.readFile "${self}/files/firefox/chrome/userChrome.css";
extensions = {
packages = with pkgs.nur.repos.rycee.firefox-addons; [
tridactyl
tampermonkey
sidebery
browserpass
clearurls
darkreader
# enhancer-for-youtube
istilldontcareaboutcookies
translate-web-pages
ublock-origin
reddit-enhancement-suite
sponsorblock
web-archives
onepassword-password-manager
single-file
widegithub
enhanced-github
unpaywall
don-t-fuck-with-paste
# plasma-integration
noscript
# configure a shortcut 'ctrl+shift+c' with behaviour 'do nothing' in order to disable the dev console shortcut
# (buildFirefoxXpiAddon {
# pname = "shortkeys";
# version = "4.0.2";
# addonId = "Shortkeys@Shortkeys.com";
# url = "https://addons.mozilla.org/firefox/downloads/file/3673761/shortkeys-4.0.2.xpi";
# sha256 = "c6fe12efdd7a871787ac4526eea79ecc1acda8a99724aa2a2a55c88a9acf467c";
# meta = with lib;
# {
# description = "Easily customizable custom keyboard shortcuts for Firefox. To configure this addon go to Addons (ctrl+shift+a) ->Shortkeys ->Options. Report issues here (please specify that the issue is found in Firefox): https://github.com/mikecrittenden/shortkeys";
# mozPermissions = [
# "tabs"
# "downloads"
# "clipboardWrite"
# "browsingData"
# "storage"
# "bookmarks"
# "sessions"
# "<all_urls>"
# ];
# platforms = platforms.all;
# };
# })
];
};
settings =
{
"extensions.autoDisableScopes" = 0;
"browser.bookmarks.showMobileBookmarks" = true;
"browser.autofocus" = false;
"toolkit.legacyUserProfileCustomizations.stylesheets" = true;
"browser.search.suggest.enabled" = false;
"browser.search.suggest.enabled.private" = false;
"browser.urlbar.suggest.searches" = false;
"browser.urlbar.showSearchSuggestionsFirst" = false;
"browser.topsites.contile.enabled" = false;
"browser.newtabpage.activity-stream.feeds.section.topstories" = false;
"browser.newtabpage.activity-stream.feeds.snippets" = false;
"browser.newtabpage.activity-stream.section.highlights.includePocket" = false;
"browser.newtabpage.activity-stream.section.highlights.includeBookmarks" = false;
"browser.newtabpage.activity-stream.section.highlights.includeDownloads" = false;
"browser.newtabpage.activity-stream.section.highlights.includeVisited" = false;
"browser.newtabpage.activity-stream.showSponsored" = false;
"browser.newtabpage.activity-stream.system.showSponsored" = false;
"browser.newtabpage.activity-stream.showSponsoredTopSites" = false;
};
search = {
# default = "Kagi";
default = "google";
# privateDefault = "Kagi";
privateDefault = "google";
engines = {
"Kagi" = {
urls = [{
template = "https://kagi.com/search";
params = [
{ name = "q"; value = "{searchTerms}"; }
];
}];
icon = "https://kagi.com/favicon.ico";
updateInterval = 24 * 60 * 60 * 1000; # every day
definedAliases = [ "@k" ];
};
"Nix Packages" = {
urls = [{
template = "https://search.nixos.org/packages";
params = [
{ name = "type"; value = "packages"; }
{ name = "query"; value = "{searchTerms}"; }
];
}];
icon = "${pkgs.nixos-icons}/share/icons/hicolor/scalable/apps/nix-snowflake.svg";
definedAliases = [ "@np" ];
};
"NixOS Wiki" = {
urls = [{
template = "https://nixos.wiki/index.php?search={searchTerms}";
}];
icon = "https://nixos.wiki/favicon.png";
updateInterval = 24 * 60 * 60 * 1000; # every day
definedAliases = [ "@nw" ];
};
"NixOS Options" = {
urls = [{
template = "https://search.nixos.org/options";
params = [
{ name = "query"; value = "{searchTerms}"; }
];
}];
icon = "${pkgs.nixos-icons}/share/icons/hicolor/scalable/apps/nix-snowflake.svg";
definedAliases = [ "@no" ];
};
"Home Manager Options" = {
urls = [{
template = "https://home-manager-options.extranix.com/";
params = [
{ name = "query"; value = "{searchTerms}"; }
];
}];
icon = "${pkgs.nixos-icons}/share/icons/hicolor/scalable/apps/nix-snowflake.svg";
definedAliases = [ "@hm" "@ho" "@hmo" ];
};
"Confluence search" = {
urls = [{
template = "https://vbc.atlassian.net/wiki/search";
params = [
{ name = "text"; value = "{searchTerms}"; }
];
}];
definedAliases = [ "@c" "@cf" "@confluence" ];
};
"Jira search" = {
urls = [{
template = "https://vbc.atlassian.net/issues/";
params = [
{ name = "jql"; value = "textfields ~ \"{searchTerms}*\"&wildcardFlag=true"; }
];
}];
definedAliases = [ "@j" "@jire" ];
};
"google".metaData.alias = "@g";
};
force = true; # this is required because otherwise the search.json.mozlz4 symlink gets replaced on every firefox restart
};
};
};
};
}