This commit is contained in:
Lucy Hochkamp 2025-08-26 00:58:27 +02:00
parent 414e830efa
commit d3a93fd115
No known key found for this signature in database
35 changed files with 1832 additions and 228 deletions

View file

@ -7,7 +7,7 @@
let
cfg = config.xyno.desktop.mako;
makoConf = pkgs.writeText "mako.conf" ''
font=Source Sans Pro Nerd Font 11
font=Source Sans 3 11
background-color=#1d2021ff
border-color=#3c3836FF
text-color=#ebdbb2ff

View file

@ -28,6 +28,7 @@ in
options.xyno.desktop.niri.enable = lib.mkEnableOption "enable the niri desktop with xynos config";
options.xyno.desktop.niri.launcher = lib.mkOption { type = lib.types.str; };
options.xyno.desktop.niri.term = lib.mkOption { type = lib.types.str; };
options.xyno.desktop.niri.extraConfig = lib.mkOption { type = lib.types.lines; };
config = lib.mkIf cfg.enable {
xyno.desktop = {
foot.enable = lib.mkDefault true;
@ -38,6 +39,9 @@ in
waybar.enable = lib.mkDefault true;
wpaperd.enable = lib.mkDefault true;
};
nixpkgs.overlays = [
inputs.niri.overlays.default
];
home-manager.users.${config.xyno.system.user.name} =
lib.mkIf config.xyno.presets.home-manager.enable
(
@ -77,17 +81,11 @@ in
xwayland-satellite
];
programs.niri.enable = true;
programs.niri.package = inputs.niri.packages.${pkgs.system}.default.overrideAttrs (prev: {
patches = prev.patches ++ [
(pkgs.fetchurl {
url = "https://patch-diff.githubusercontent.com/raw/YaLTeR/niri/pull/1907.patch";
hash = "sha256-XhG8Ga1/QMPXrF0FjQuBk8KZISbof4Md4kM73cG1SYQ=";
})
];
});
environment.etc."niri/config.kdl".mode = "444"; # copy file so niri detects changes
environment.etc."niri/config.kdl".text = ''
xwayland-satellite {
path "${pkgs.xwayland-satellite}/bin/xwayland-satellite"
}
animations {
off
}
@ -351,11 +349,6 @@ in
// scratchpad
// workspace "scratchpad"
// Put swaybg inside the overview backdrop.
layer-rule {
match namespace="^wpaperd.*$"
place-within-backdrop true
}
screenshot-path "~/Pictures/screenshots/screenshot-%Y-%m-%d %H-%M-%S.png"
// Indicate screencasted windows with red colors.
@ -425,6 +418,7 @@ in
}
// autogenerated from here on
${matchFloat}
${cfg.extraConfig}
'';
};
}

View file

@ -10,7 +10,7 @@ let
waybarCfg = {
layer = "top";
position = "top";
height = 15;
height = 20;
modules-left =
(lib.optionals (cfg.mode == "river") [
"river/tags"
@ -50,7 +50,7 @@ let
max-length = 40;
};
"niri/window" = {
max-length = 40;
max-length = 80;
};
wireplumber = {
"format" = "{icon} {volume}%";
@ -66,7 +66,7 @@ let
};
"backlight" = {
"device" = "amdgpu_bl1";
"format" = "{icon} {percent}%";
"format" = "{icon} {percent}%";
"format-icons" = [
"󰃚"
"󰃛"
@ -91,7 +91,7 @@ let
"warning" = 30;
"critical" = 15;
};
"format" = "{icon} {capacity}%";
"format" = "{icon} {capacity}%";
"format-icons" = [
""
""
@ -112,11 +112,11 @@ let
};
memory = {
interval = 30;
format = " {used:0.0f}/{total:0.0f}GB";
format = " {used:0.0f}/{total:0.0f}GB";
};
clock = {
interval = 1;
format = "{:%Y-%m-%dT%H:%M:%S%z}";
format = "{:%a %Y-%m-%dT%H:%M:%S%z}";
"tooltip-format" = "<tt><small>{calendar}</small></tt>";
"calendar" = {
"mode" = "year";
@ -146,9 +146,9 @@ let
"on-click" =
"${pkgs.alacritty}/bin/alacritty --class floating-alacritty -e ${pkgs.impala}/bin/impala";
"format" = "{ifname}";
"format-wifi" = "󰖩 {essid}";
"format-ethernet" = "󰈀 {ifname}";
"format-disconnected" = "󰖪";
"format-wifi" = "󰖩 {essid}";
"format-ethernet" = "󰈀 {ifname}";
"format-disconnected" = "󰖪 ";
"tooltip-format" = "{ifname} via {gwaddr}\n{ipaddr}/{cidr}";
"tooltip-format-wifi" = "{essid} ({signaldBm} dBm) {frequency} GHz\n{ipaddr}/{cidr}";
"tooltip-format-ethernet" = "{ifname}\n{ipaddr}/{cidr}";
@ -161,17 +161,14 @@ let
* {
/* `otf-font-awesome` is required to be installed for icons */
font-family: "Source Sans Pro Nerd Font";
font-size: 12px;
font-family: "Source Sans 3";
font-size: 11px;
}
window#waybar {
/* background-color: rgba(43, 48, 59, 0.5);
border-bottom: 3px solid rgba(100, 114, 125, 0.5);*/
color: #a89984;
background-color: #1d2021;
/* transition-property: background-color;
transition-duration: .5s;*/
}
window#waybar.hidden {

View file

@ -26,6 +26,15 @@ in
};
config = lib.mkIf cfg.enable {
environment.systemPackages = [ cfg.package ];
xyno.desktop.niri.extraConfig = ''
// Put swww inside the overview backdrop.
layer-rule {
match namespace="^swww.*$"
place-within-backdrop true
}
'';
systemd.user.services.swww-daemon = {
unitConfig.PartOf = "graphical-session.target";
unitConfig.After = "graphical-session.target";
@ -42,6 +51,7 @@ in
serviceConfig.Restart = "on-failure";
wantedBy = [ "swww-daemon.service" ];
script = ''
set -eox
export DEFAULT_INTERVAL=300 # In seconds
export DIR=''$HOME/Pictures/backgrounds

View file

@ -19,7 +19,13 @@
./presets/common.nix
./presets/gui.nix
./presets/home-manager.nix
./services/authentik.nix
./services/caddy.nix
./services/monitoring.nix
./services/wireguard.nix
./system/impermanence.nix
./system/user.nix
./user-services/syncthing.nix
./to-upstream/fido2-hid-bridge.nix
]

View file

@ -57,7 +57,7 @@ in
# # ipv6AcceptRAConfig.UsePREF64 = true;
# };
networking.wireless.iwd.enable = cfg.enableWifi;
xyno.impermanence.extraDirectories = lib.mkOptionals cfg.enableWifi [ "/var/lib/iwd" ];
xyno.impermanence.directories = lib.optionals cfg.enableWifi [ "/var/lib/iwd" ];
# services.clatd.enable = true;
};
}

View file

@ -1,6 +1,7 @@
{
pkgs,
config,
inputs,
lib,
...
}:
@ -13,13 +14,30 @@ in
boot.initrd.systemd.enable = true;
hardware.keyboard.zsa.enable = true;
programs.nh.enable = true;
# patch in auth_keep for run0
security.polkit.debug = true;
security.polkit.package = pkgs.polkit.overrideAttrs (old: {
version = old.version + "-git";
src = inputs.polkit;
patches = lib.take 1 old.patches;
# patches = [
# (pkgs.fetchpatch2 {
# url = "https://patch-diff.githubusercontent.com/raw/polkit-org/polkit/pull/533.patch";
# hash = "sha256-noR87BAzgBWtYDb0j9jkM/8wEkp7H+nArvKZrz69wfQ=";
# })
# ];
});
security.polkit.extraConfig = ''
polkit.addRule(function(action, subject) {
polkit.log("action=" + action);
polkit.log("subject=" + subject);
});
polkit.addRule(function(action, subject) { // make run0 keep pw for some time (tm)
if (
subject.isInGroup("wheel")
&& action.id == "org.freedesktop.systemd1.manage-units"
) {
return polkit.Result.AUTH_ADMIN_KEEP;
return polkit.Result.YES;
}
});
'';

View file

@ -22,6 +22,8 @@ in
xyno.desktop.audio.enable = lib.mkDefault true;
security.soteria.enable = true;
security.rtkit.enable = true;
services.pcscd.enable = true;
services.pcscd.plugins = [ pkgs.pcsc-scm-scl011];
xyno.hardware.kmonad.enable = true;
# wayland on electron
environment.sessionVariables.NIXOS_OZONE_WL = "1";
@ -36,7 +38,7 @@ in
qt = {
enable = true;
style = "breeze";
platformTheme = "lxqt";
platformTheme = "gnome";
};
programs.yazi = {
@ -111,16 +113,24 @@ in
kdePackages.breeze-icons
];
# fonts
fonts.fontconfig.defaultFonts = {
sansSerif = ["Source Sans 3" "Noto Sans Symbols 2"];
monospace = ["JetBrainsMono Nerd Font" "Noto Sans Symbols 2"];
};
fonts.packages = with pkgs; [
nerd-fonts.jetbrains-mono
# nerd-fonts.source-sans
# nerd-fonts.b612
cantarell-fonts
dejavu_fonts
source-code-pro # Default monospace font in 3.32
source-sans
b612
lxqt.lxqt-config
ptouch-print
noto-fonts
noto-fonts-color-emoji
];

View file

@ -0,0 +1,50 @@
{
config,
lib,
pkgs,
...
}:
let
cfg = config.xyno.presets.server;
in
{
options.xyno.presets.server.enable =
lib.mkEnableOption "enables xynos base server config (ssh/smart/email/zed/...)";
config = lib.mkIf cfg.enable {
services.openssh.enable = true;
users.users.root.openssh.authorizedKeys.keys = ["ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAID/oMAi5jyQsNohfhcSH2ItisTpBGB0WtYTVxJYKKqhj"]; # theseus
environment.etc."msmtprc".enable = false;
sops.secrets."msmtp/rc" = {
path = "/etc/msmtprc";
};
sops.secrets."msmtp/aliases" = {
path = "/etc/aliases";
};
programs.msmtp = {
enable = true;
};
services.smartd = {
enable = true;
extraOptions = [ "--interval=7200" ];
notifications.test = true;
};
# emails for zfs
services.zfs.zed.enableMail = true;
services.zfs.zed.settings = {
ZED_EMAIL_ADDR = [ "root" ];
ZED_EMAIL_PROG = "${pkgs.msmtp}/bin/msmtp";
ZED_EMAIL_OPTS = "@ADDRESS@";
ZED_NOTIFY_INTERVAL_SECS = 7200;
ZED_NOTIFY_VERBOSE = true;
ZED_USE_ENCLOSURE_LEDS = false;
ZED_SCRUB_AFTER_RESILVER = true;
};
};
}

View file

@ -1,9 +1,160 @@
{ pkgs, lib, config, ... }:
let cfg = config.xyno.services.authentik; in
{
options.xyno.services.authentik.enable = lib.mkEnableOption "enables the authentik SSO thing";
config = lib.mkIf cfg.enable {
pkgs,
inputs,
lib,
config,
...
}:
with lib;
let
cfg = config.xyno.services.authentik;
defaultAppOptions = {
options = {
name = mkOption {
type = types.str;
};
group = mkOption {
type = types.nullOr types.str;
default = null;
};
groups = mkOption {
type = types.listOf types.str;
default = [];
};
meta_description = mkOption {
type = types.nullOr types.str;
default = null;
};
meta_icon = mkOption {
type = types.nullOr types.str;
default = null;
};
meta_launch_url = mkOption {
type = types.nullOr types.str;
default = null;
};
meta_publisher = mkOption {
type = types.nullOr types.str;
default = null;
};
};
};
terrraformStateDir = "/var/lib/authentik-terraform-config";
environmentFileDir = "/run/authentik-terraform-config";
terranixConfig = inputs.terranix.lib.terranixConfiguration {
system = pkgs.system;
modules = [
./authentik/provider.nix
{
inherit (cfg) oauthApps ldapApps proxyApps;
stateFile = "${terrraformStateDir}/state.tfstate";
}
];
};
in
{
options.xyno.services.authentik.enable = mkEnableOption "enables the authentik SSO thing";
options.xyno.services.authentik.oauthApps = mkOption {
default = { };
type = types.attrsOf (
types.submodule (
{ name, ... }:
({
options = {
environmentFile = mkOption {
type = types.str;
default = "${environmentFileDir}/${name}_environment";
};
}
// defaultAppOptions.options;
})
)
);
};
options.xyno.services.authentik.ldapApps = mkOption {
default = { };
type = types.attrsOf (types.submodule (defaultAppOptions));
};
options.xyno.services.authentik.proxyApps = mkOption {
default = { };
type = types.attrsOf (
types.submodule ({
options = {
externalHost = mkOption {
type = types.str;
};
}
// defaultAppOptions.options;
})
);
};
config = lib.mkIf cfg.enable {
environment.etc."authentik-config/config.tf.json".source = terranixConfig;
xyno.impermanence.directories = [
terrraformStateDir
];
services.authentik = {
enable = true;
createDatabase = true;
environmentFile = config.sops.secrets."authentik/env".path;
};
systemd.services.authentik-ldap.after = [ "authentik-config.service" ];
services.authentik-ldap = {
environmentFile = "${environmentFileDir}/ldap_config";
enable = true;
};
systemd.services.authentik-proxy.after = [ "authentik-config.service" ];
services.authentik-proxy = {
enable = true;
environmentFile = "${environmentFileDir}/proxy_config";
};
systemd.services.authentik-config = {
after = [ "authentik.service" ];
serviceConfig = {
Type = "oneshot";
RemainAfterExit = true;
StateDirectory = terrraformStateDir;
};
script = ''
umask u=rw,go=
export PATH=$PATH:${pkgs.opentofu}/bin
cd terrraformStateDir
cp ${terranixConfig} ./main.tf.json
source ${config.services.authentik.environmentFile}
export AUTHENTIK_URL=http://localhost:9000
export AUTHENTIK_TOKEN=$AUTHENTIK_BOOTSTRAP_TOKEN
tofu init
tofu validate || exit 1
tofu apply
tofu output -raw proxy_config > ${environmentFileDir}/proxy_config
tofu output -raw ldap_config > ${environmentFileDir}/ldap_config
${concatStringsSep "\n" (
mapAttrsToList (n: v: "tofu output -raw ${n}_environment > ${v.environmentFile}") cfg.oauthApps
)}
'';
};
sops.secrets."authentik/env" = {
};
services.caddy.extraConfig = ''
(reverse_proxy_auth) {
route {
# always forward outpost path to actual outpost
reverse_proxy /outpost.goauthentik.io/* http://[::1]:9000 {
}
forward_auth http://[::1]:9000 {
uri /outpost.goauthentik.io/auth/caddy
copy_headers X-Authentik-Username X-Copyparty-Group X-Authentik-Groups X-Authentik-Entitlements X-Authentik-Email X-Authentik-Name X-Authentik-Uid X-Authentik-Jwt X-Authentik-Meta-Jwks X-Authentik-Meta-Outpost X-Authentik-Meta-Provider X-Authentik-Meta-App X-Authentik-Meta-Version X-Grafana-Role
}
reverse_proxy {args[:]}
}
'';
};
}

View file

@ -0,0 +1,144 @@
{ lib, config, ... }:
with lib;
let
# { ldapApps = { appName = { name = str?; group = str?; meta_desc = str?; meta_icon = str?; meta_launch_url = str?; meta_publisher = str?; }; }; oauthApps = { appName = {}; ] }; proxyApps = { appName = { externalHost = ""; }; }; }
authorizationFlow = tfRef "data.authentik_flow.default-authorization-flow.id";
authenticationFlow = tfRef "data.authentik_flow.default-authentication-flow.id";
genApp = provider: n: v: {
protocol_provider = provider;
slug = n;
inherit (v)
name
group
meta_description
meta_icon
meta_launch_url
meta_publisher
;
};
in
{
options = {
stateFile = mkOption { type = types.str; };
oauthApps = mkOption { type = types.attrs; };
proxyApps = mkOption { type = types.attrs; };
ldapApps = mkOption { type = types.attrs; };
};
config = {
terraform.backend.local.path = config.stateFile;
provider.authentik = { };
data.authentik_flow."default-authorization-flow" = {
slug = "default-provider-authorization-implicit-consent";
};
data."authentik_flow"."default-authentication-flow" = {
slug = "default-authentication-flow";
};
resource.authentik_outpost.proxy = {
name = "proxy";
type = "proxy";
protocol_providers = mapAttrsToList (
n: v: (tfRef "authentik_provider_proxy.${n}.id")
) config.proxyApps;
};
resource.authentik_outpost.ldap = {
name = "ldap";
type = "ldap";
protocol_providers = mapAttrsToList (
n: v: (tfRef "authentik_provider_ldap.${n}.id")
) config.ldapApps;
};
resource.authentik_provider_oauth2 = mapAttrs (n: v: {
name = n;
client_id = n;
authorization_flow = authorizationFlow;
}) config.oauthApps;
data.authentik_provider_oauth2_config = mapAttrs (n: v: {
provider_id = tfRef "resource.authentik_provider_oauth2.${n}.id";
}) config.oauthApps;
resource.authentik_provider_proxy = mapAttrs (n: v: {
name = n;
mode = "forward-single";
external_host = v.externalHost;
authorization_flow = authorizationFlow;
}) config.proxyApps;
resource.authentik_provider_ldap = mapAttrs (n: v: {
name = n;
base_dn = "dc=ldap,dc=goauthentik,dc=io";
bind_flow = authenticationFlow;
}) config.ldapApps;
output =
(mapAttrs' (
n: v:
nameValuePair ("${n}_environment") ({
value =
let
val = val: tfRef "resource.authentik_provider_oauth2.${n}.${val}";
cfgVal = val: tfRef "data.authentik_provider_oauth2_config.${n}.${val}";
in
''
CLIENT_ID=${val "client_id"}
CLIENT_SECRET=${val "client_secret"}
USER_INFO_URL=${cfgVal "user_info_url"}
TOKEN_URL=${cfgVal "token_url"}
AUTHORIZE_URL=${cfgVal "authorize_url"}
'';
})
) config.oauthApps)
// {
proxy_config.value = tfRef "resource.authentik_outpost.proxy.config";
ldap_config.value = tfRef "resource.authentik_outpost.ldap.config";
};
resource.authentik_application = mkMerge [
(mapAttrs (n: v: genApp (tfRef "authentik_provider_oauth2.${n}.id") n v) config.oauthApps)
(mapAttrs (n: v: genApp (tfRef "authentik_provider_proxy.${n}.id") n v) config.proxyApps)
(mapAttrs (n: v: genApp (tfRef "authentik_provider_ldap.${n}.id") n v) config.ldapApps)
];
# group stuff
resource.authentik_group.admin = {
name = "admin";
};
resource.authentik_application_entitlement =
let
genEnts =
apps:
mapAttrs (n: v: {
name = "${n}-ent";
application = tfRef "authentik_application.${n}.uuid";
}) (filterAttrs (n: v: (builtins.length v.groups) > 0) apps);
in
mkMerge [
(genEnts config.oauthApps)
(genEnts config.proxyApps)
(genEnts config.ldapApps)
];
resource.authentik_policy_binding =
let
genEnts =
apps:
lib.flatten (
mapAttrsToList (
n: v:
(map (g: {
"${n}-${g}-access" = {
target = tfRef "authentik_application_entitlement.${n}.uuid";
group = tfRef "authentik_group.${g}.id";
order = 0;
};
}) v.groups)
) apps
);
in
mkMerge [
(genEnts config.oauthApps)
(genEnts config.proxyApps)
(genEnts config.ldapApps)
];
};
}

102
modules/services/caddy.nix Normal file
View file

@ -0,0 +1,102 @@
{
pkgs,
lib,
config,
...
}:
with lib;
let
cfg = config.xyno.services.caddy;
wildcardMatcherStr = wildcard: hostName: content: ''
@${hostName} host ${hostName}.${wildcard}
handle @${hostName} {
${content.extraConfig}
}
'';
genOneWildcard = wildcard: host: {
extraConfig = ''
# extra pre
${host.extraConfigPre}
# block bots
${optionalString host.blockBots "import blockBots"}
# hosts handler
${concatStrings (mapAttrsToList (n: v: wildcardMatcherStr wildcard n v) host.hosts)}
# extra post
${host.extraConfigPost}
abort
'';
};
genVHostsFromWildcard = mapAttrs' (
n: v: nameValuePair "*.${n}" (genOneWildcard n v)
) cfg.wildcardHosts;
in
{
options.xyno.services.caddy.enable = mkEnableOption "enables caddy with the desec plugin";
options.xyno.services.caddy.wildcardHosts = mkOption {
example = {
"hailsatan.eu" = {
blockBots = true;
hosts.md.extraConfig = ''reverse_proxy ...'';
};
};
default = { };
type =
with types;
attrsOf (submodule {
options = {
blockBots = mkOption {
type = bool;
default = false;
};
extraConfigPre = mkOption {
type = str;
default = "";
};
extraConfigPost = mkOption {
type = str;
default = "";
};
hosts = attrsOf (submodule {
options = {
extraConfig = mkOption { type = lines; };
};
});
};
});
};
config = lib.mkIf cfg.enable {
networking.firewall.allowedTCPPorts = [ 80 443 ];
networking.firewall.allowedUDPPorts = [ 443 ];
services.caddy = {
enable = true;
package = pkgs.caddy-desec;
virtualHosts = genVHostsFromWildcard;
email = mkDefault "ssl@xyno.systems";
acmeCA = mkDefault "https://acme-v02.api.letsencrypt.org/directory";
globalConfig = ''
metrics {
per_host
}
admin ${config.xyno.monitoring.ip}:2019
'';
extraConfig = ''
(blockBots) {
@botForbidden header_regexp User-Agent "(?i)AdsBot-Google|Amazonbot|anthropic-ai|Applebot|Applebot-Extended|AwarioRssBot|AwarioSmartBot|Bytespider|CCBot|ChatGPT|ChatGPT-User|Claude-Web|ClaudeBot|cohere-ai|DataForSeoBot|Diffbot|FacebookBot|Google-Extended|GPTBot|ImagesiftBot|magpie-crawler|omgili|Omgilibot|peer39_crawler|PerplexityBot|YouBot"
handle @botForbidden {
redir https://hil-speed.hetzner.com/10GB.bin
}
handle /robots.txt {
respond <<TXT
User-Agent: *
Disallow: /
TXT 200
}
}
'';
};
xyno.services.monitoring.exporters.caddy = 2019;
};
}

View file

@ -2,21 +2,88 @@
pkgs,
lib,
config,
instanceConfig,
instanceConfigs,
# inputs,
...
}:
with lib;
let
cfg = config.xyno.services.monitoring;
firstInstanceWithPromServer = (builtins.head (
builtins.filter (x: x ? prometheusServer && x.prometheusServer) (attrValues instanceConfigs)
)).hostName;
vmBasicAuthUsername = "xyno-monitoring";
in
{
options.xyno.services.monitoring.enable =
lib.mkEnableOption "enables monitoring (prometheus exporters and stuff)";
options.xyno.services.monitoring.ip = lib.mkOption {
type = lib.types.str;
default = "::1";
description = "the ip prometheus exporters should listen to";
mkEnableOption "enables monitoring (prometheus exporters and stuff)";
options.xyno.services.monitoring.remoteWriteUrl = mkOption {
type = types.str;
default = "http://${firstInstanceWithPromServer}.${config.xyno.services.wireguard.monHostsDomain}:8428/api/v1/write";
description = "where prometheus metrics should be pushed to";
};
options.xyno.services.monitoring.exporters = mkOption {
type = types.attrsOf (types.either types.int types.str);
description = "names of exporters and their ports (to open fw and generate prometheus config)";
example = ''
{
node = 9100;
postgres = "unix:///run/postgres-exporter.sock";
}
'';
};
config = lib.mkIf cfg.enable {
config = mkMerge [
(mkIf cfg.enable {
services.prometheus.exporters.node = {
enable = true;
enabledCollectors = [ "systemd" ];
};
xyno.services.monitoring.exporters.node = config.services.prometheus.exporters.node.port;
services.vmagent = {
remoteWrite.url = cfg.remoteWriteUrl;
remoteWrite.basicAuthUsername = vmBasicAuthUsername;
remoteWrite.basicAuthPasswordFile = config.sops.secrets."victoriametrics/basicAuthPassword".path;
};
prometheusConfig.scrape_configs = mapAttrsToList (name: value: {
job_name = "${name}-exporter";
metrics_path = "/metrics";
staticConfigs = [
{
targets = [ (if ((builtins.typeOf value) == "string") then value else "[::1]:${toString value}") ];
labels.type = name;
labels.host = config.networking.hostName;
}
];
}) cfg.exporters;
};
sops.secrets."victoriametrics/basicAuthPassword" = {
reloadUnits = [ "vmagent.service" ];
};
})
(mkIf (cfg.enable && instanceConfig ? prometheusServer && instanceConfig.prometheusServer) {
xyno.impermanence.directories = [ "/var/lib/${config.services.victoriametrics.stateDir}" ];
sops.secrets."victoriametrics/basicAuthPassword" = {
reloadUnits = [ "victoriametrics.service" ];
};
networking.firewall.extraInputRules = ''tcp dport 8428 ip6 daddr ${config.xyno.services.wireguard.monIp6}/128 accept comment "victoriametrics-http"'';
systemd.services.victoriametrics.serviceConfig.LoadCredential = [
"basic_auth_pw:${config.sops.secrets."victoriametrics/basicAuthPassword".path}"
];
services.victoriametrics = {
enable = true;
listenAddress = "${config.xyno.services.wireguard.monIp6}:8428";
extraOptions = [
"-httpAuth.username=${vmBasicAuthUsername}"
"-httpAuth.password=file://\${CREDENTIALS_DIRECTORY}/basic_auth_pw"
];
};
services.grafana.declarativePlugins = with pkgs.grafanaPlugins; [ victoriametrics-metrics-datasource ];
})
];
}

View file

@ -23,9 +23,8 @@ in
};
services.prometheus.exporters.postgres = lib.mkIf config.xyno.services.monitoring.enable {
enable = true;
listenAddress = config.xyno.services.monitoring.ip;
port = 9187;
};
xyno.services.monitoring.exporters.postgres = config.services.prometheus.exporters.postgres.port;
xyno.impermanence.extraDirectories = [ "/var/lib/postgresql" ];

View file

@ -7,8 +7,9 @@
...
}:
let
wgServer = instanceConfig?wg.server && instanceConfig.wg.server;
cfg = config.xyno.services.wireguard;
ula = "fd68:b6a4:36e4";
ula = cfg.ula;
ulaPrefix = "${ula}:1337"; # /64 for normal vpn
monitoringUlaPrefix = "${ula}:2337"; # /64 for monitoring
@ -17,55 +18,84 @@ let
prefix: hostName:
let
hostHash = builtins.hashString "sha512" hostName;
localParts = map (n: builtins.substring (n * 4) 4 hostHash) hostHash;
localParts = map (n: builtins.substring (n * 4) 4 hostHash) (lib.range 0 3);
localPart = lib.concatStringsSep ":" localParts;
in
"${prefix}:${localPart}";
# peers list for networkd
wgPeers = map (
filteredConfigs = builtins.filter (x: x.hostName != config.networking.hostName ) (lib.attrValues instanceConfigs);
wgPeersLists = map (
c:
(
(lib.optionals (lib.hasAttr c "publicHostname") {
(lib.optional (c?publicHostname) {
# if peer is publicly on the internet
AllowedIPs =
(lib.optionals (c.wgServer) [
(lib.optionals (c.wg.server) [
"${ulaPrefix}::/48" # all traffic in the ula shall be sent to the server
])
++ (lib.optionals (!c.wgServer) [
++ (lib.optionals (!c.wg.server) [
"${genUlaForHost ulaPrefix c.hostName}/128" # if a host is reachable but shouldn't play server, send only to the hosts ip
]);
Endpoint = "${c.publicHostname}:51820";
PersistentKeepalive = 25;
PublicKey = c.wgPubKey;
})
++ (lib.optionals (!(lib.hasAttr c "publicHostname") && instanceConfig.wgServer && (lib.hasAttr c "wgPubKey")) {
# if this is the server and the peer isn't reachable on the internet
AllowedIPs = [
"${genUlaForHost ulaPrefix c.hostName}/128"
"${genUlaForHost monitoringUlaPrefix c.hostName}/128"
];
PublicKey = c.wgPubKey;
# TODO: preshared keys
PublicKey = c.wg.pubKey;
})
++ (lib.optional
((!c?publicHostname) && wgServer && (c?wg.pubKey))
{
# if this is the server and the peer isn't reachable on the internet
AllowedIPs = [
"${genUlaForHost ulaPrefix c.hostName}/128"
"${genUlaForHost monitoringUlaPrefix c.hostName}/128"
];
PublicKey = c.wg.pubKey;
PresharedKeyFile = config.sops.secrets."wg/psk".path; # TODO
}
)
)
) instanceConfigs;
) filteredConfigs;
wgPeers = lib.flatten wgPeersLists;
in
{
options.xyno.services.wireguard.enable = lib.mkEnableOption "enables wireguard";
options.xyno.services.wireguard.hostsDomain = lib.mkOpion { type = lib.types.str; default = "wg.hailsatan.eu"; };
options.xyno.services.wireguard.monHostsDomain = lib.mkOption {
type = lib.types.str;
default = "mon.wg.hailsatan.eu";
};
options.xyno.services.wireguard.hostsDomain = lib.mkOption {
type = lib.types.str;
default = "wg.hailsatan.eu";
};
options.xyno.services.wireguard.ula = lib.mkOption {
type = lib.types.str;
default = "fd68:b6a4:36e4";
};
options.xyno.services.wireguard.ip6 = lib.mkOption {
type = lib.types.str;
default = genUlaForHost ulaPrefix config.networking.hostName;
};
options.xyno.services.wireguard.monIp6 = lib.mkOption {
type = lib.types.str;
default = genUlaForHost monitoringUlaPrefix config.networking.hostName;
};
config = lib.mkIf cfg.enable {
xyno.services.monitoring.ip = genUlaForHost monitoringUlaPrefix config.networking.hostName;
networking.hosts = lib.mapAttrs' (
networking.hosts = (lib.mapAttrs' (
n: v: {
name = "${v.hostName}.${cfg.hostsDomain}";
value = [ (genUlaForHost ulaPrefix v.hostName) ];
value = ["${v.hostName}.${cfg.hostsDomain}"];
name = (genUlaForHost ulaPrefix v.hostName);
}
);
networking.firewall.allowedUDPPorts = lib.mkIf instanceConfig.wgServer [ 51820 ];
) instanceConfigs) // (lib.mapAttrs' (
n: v: {
value = ["${v.hostName}.${cfg.monHostsDomain}"];
name = (genUlaForHost monitoringUlaPrefix v.hostName);
}
) instanceConfigs);
networking.firewall.allowedUDPPorts = lib.optional wgServer [ 51820 ];
networking.firewall.interfaces."wg0".allowedUDPPorts = lib.optional wgServer [ 53 ];
systemd.network.netdevs."wg0" = {
wireguardConfig = {
ListenPort = lib.mkIf instanceConfig.wgServer 51820;
PrivateKeyFile = config.sops.secrets.wg_privkey.path; # TODO
ListenPort = lib.mkIf wgServer 51820;
PrivateKeyFile = config.sops.secrets."wg/privkey".path; # TODO
};
wireguardPeers = wgPeers;
};
@ -79,8 +109,28 @@ in
"${(genUlaForHost monitoringUlaPrefix config.networking.hostName)}/128"
];
};
services.prometheus.exporters.wireguard = lib.mkIf (wgServer && config.xyno.services.monitoring.enable) {
enable = true;
interfaces = [ "wg0" ];
};
sops.secrets.wg_privkey = {
services.coredns = lib.mkIf wgServer { # for non nixos devices to be able to resolve vpn hostnames
enable = true;
config = ''
. {
bind wg0
prometheus
hosts ${cfg.hostsDomain}
forward . /etc/resolv.conf
}
'';
};
xyno.services.monitoring.exporters.coredns = lib.mkIf wgServer 9153;
xyno.services.monitoring.exporters.wireguard = lib.mkIf wgServer config.services.prometheus.exporters.wireguard.port;
sops.secrets."wg/privkey" = {
reloadUnits = [ "systemd-networkd.service" ];
};
sops.secrets."wg/psk" = {
reloadUnits = [ "systemd-networkd.service" ];
};

View file

@ -8,45 +8,12 @@
let
cfg = config.xyno.impermanence;
genImpermanenceCfg = cfg: {
hideMounts = true;
directories = [
"/var/log"
"/var/lib/systemd/coredump"
]
++ cfg.extraDirectories;
files = [
"/etc/machine-id"
]
++ cfg.extraFiles;
directories = cfg.directories;
files = cfg.files;
users.${config.xyno.system.user.name} = {
directories = [
"Downloads"
"Music"
"Pictures"
"Documents"
"Videos"
"docs"
"proj"
"git"
{
directory = ".gnupg";
mode = "0700";
}
{
directory = ".ssh";
mode = "0700";
}
{
directory = ".local/share/keyrings";
mode = "0700";
}
".local/share/direnv"
]
++ cfg.user.extraDirectories;
files = cfg.user.extraFiles;
directories = cfg.user.directories;
files = cfg.user.files;
};
};
@ -54,43 +21,75 @@ in
{
options.xyno.impermanence = {
enable = lib.mkEnableOption "erase all your darlings (they hate you anyways)";
extraFiles = lib.mkOption { type = lib.types.listOf lib.types.str; };
extraDirectories = lib.mkOption { type = lib.types.listOf lib.types.str; };
files = lib.mkOption { type = lib.types.listOf lib.types.str; };
directories = lib.mkOption { type = lib.types.listOf lib.types.str; };
user = {
extraFiles = lib.mkOption { type = lib.types.listOf lib.types.str; };
extraDirectories = lib.mkOption { type = lib.types.listOf lib.types.str; };
files = lib.mkOption { type = lib.types.listOf lib.types.str; };
directories = lib.mkOption { type = lib.types.listOf lib.types.str; };
};
# have a seperate impermanence tree for "cache" files that can just be deleted if wanted
cache = {
extraFiles = lib.mkOption { type = lib.types.listOf lib.types.str; };
extraDirectories = lib.mkOption { type = lib.types.listOf lib.types.str; };
files = lib.mkOption { type = lib.types.listOf lib.types.str; };
directories = lib.mkOption { type = lib.types.listOf lib.types.str; };
user = {
extraFiles = lib.mkOption { type = lib.types.listOf lib.types.str; };
extraDirectories = lib.mkOption { type = lib.types.listOf lib.types.str; };
files = lib.mkOption { type = lib.types.listOf lib.types.str; };
directories = lib.mkOption { type = lib.types.listOf lib.types.str; };
};
};
};
config = lib.mkIf cfg.enable {
imports = [
inputs.impermanence.nixosModules.impermanence
xyno.impermanence.files = [
"/etc/machine-id" # systemd/zfs unhappy otherwise
];
xyno.impermanence.cache.extraDirectories = [ "/var/cache" ];
xyno.impermanence.cache.user.extraDirectories = [ ".cache" ];
xyno.impermanence.directories = [
"/var/log"
"/var/lib/systemd/coredump"
"/etc/ssh" # host keys
];
xyno.impermanence.user.directories = [
"Downloads"
"Music"
"Pictures"
"Documents"
"Videos"
"docs"
"proj"
"git"
{
directory = ".gnupg";
mode = "0700";
}
{
directory = ".ssh";
mode = "0700";
}
{
directory = ".local/share/keyrings";
mode = "0700";
}
".local/share/direnv"
];
xyno.impermanence.cache.directories = [ "/var/cache" ];
xyno.impermanence.cache.user.directories = [ ".cache" ];
environment.persistence."/persistent" = genImpermanenceCfg cfg;
environment.persistence."/persistent/cache" = genImpermanenceCfg cfg.cache;
# https://github.com/nix-community/impermanence/issues/254#issuecomment-2683859091
system.activationScripts."createPersistentStorageDirs".deps = [
"var-lib-private-permissions"
"users"
"groups"
];
# https://github.com/nix-community/impermanence/issues/254#issuecomment-2683859091
system.activationScripts = {
"var-lib-private-permissions" = {
deps = [ "specialfs" ];
text = ''
mkdir -p /persistent/var/lib/private
mkdir -p /persistent/var/lib/private /persistent/cache
chmod 0700 /persistent/var/lib/private
touch /persistent/cache/.nobackup
'';
};
};

View file

@ -17,6 +17,7 @@ in
config = lib.mkIf cfg.enable {
environment.homeBinInPath = true;
users.users.${cfg.name} = {
openssh.authorizedKeys.keys = ["ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAID/oMAi5jyQsNohfhcSH2ItisTpBGB0WtYTVxJYKKqhj"]; # theseus
isNormalUser = true;
uid = 1000;
extraGroups = [

View file

@ -0,0 +1,26 @@
{
pkgs,
lib,
config,
...
}:
let
cfg = config.services.fido2-hid-bridge;
in
{
options.services.fido2-hid-bridge.enable = lib.mkEnableOption "enables fido2-hid-bridge";
config = lib.mkIf cfg.enable {
systemd.services."fido2-hid-bridge" = {
after = [
"auditd.service"
"syslog.target"
"network.target"
"local-fs.target"
"pcscd.service"
];
requires = [ "pcscd.service" ];
script = "exec ${pkgs.fido2-hid-bridge}/bin/fido2-hid-bridge";
wantedBy = [ "multi-user.target" ];
};
};
}