mirror of
https://github.com/SebastianStork/nixos-config.git
synced 2026-03-22 17:49:07 +01:00
Rename modules directory system to nixos
This commit is contained in:
parent
653a6f310b
commit
1c1b9221fc
48 changed files with 1 additions and 1 deletions
11
modules/nixos/boot/loader/grub.nix
Normal file
11
modules/nixos/boot/loader/grub.nix
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
{ config, lib, ... }:
|
||||
{
|
||||
options.custom.boot.loader.grub.enable = lib.mkEnableOption "";
|
||||
|
||||
config = lib.mkIf config.custom.boot.loader.grub.enable {
|
||||
boot = {
|
||||
tmp.cleanOnBoot = true;
|
||||
loader.grub.enable = true;
|
||||
};
|
||||
};
|
||||
}
|
||||
18
modules/nixos/boot/loader/systemd-boot.nix
Normal file
18
modules/nixos/boot/loader/systemd-boot.nix
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
{ config, lib, ... }:
|
||||
{
|
||||
options.custom.boot.loader.systemd-boot.enable = lib.mkEnableOption "";
|
||||
|
||||
config = lib.mkIf config.custom.boot.loader.systemd-boot.enable {
|
||||
boot = {
|
||||
tmp.cleanOnBoot = true;
|
||||
loader = {
|
||||
systemd-boot = {
|
||||
enable = true;
|
||||
editor = false;
|
||||
configurationLimit = 20;
|
||||
};
|
||||
efi.canTouchEfiVariables = true;
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
22
modules/nixos/boot/silent.nix
Normal file
22
modules/nixos/boot/silent.nix
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
{ config, lib, ... }:
|
||||
{
|
||||
options.custom.boot.silent = lib.mkEnableOption "";
|
||||
|
||||
config = lib.mkIf config.custom.boot.silent {
|
||||
boot = {
|
||||
loader.timeout = 0;
|
||||
kernelParams = [
|
||||
"quiet"
|
||||
"rd.systemd.show_status=false"
|
||||
"rd.udev.log_level=3"
|
||||
"udev.log_priority=3"
|
||||
];
|
||||
initrd = {
|
||||
verbose = false;
|
||||
systemd.enable = true;
|
||||
};
|
||||
consoleLogLevel = 3;
|
||||
plymouth.enable = true;
|
||||
};
|
||||
};
|
||||
}
|
||||
20
modules/nixos/de/hyprland.nix
Normal file
20
modules/nixos/de/hyprland.nix
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
{
|
||||
config,
|
||||
pkgs-unstable,
|
||||
lib,
|
||||
...
|
||||
}:
|
||||
{
|
||||
options.custom.de.hyprland.enable = lib.mkEnableOption "";
|
||||
|
||||
config = lib.mkIf config.custom.de.hyprland.enable {
|
||||
programs.hyprland = {
|
||||
enable = true;
|
||||
package = pkgs-unstable.hyprland;
|
||||
};
|
||||
|
||||
environment.sessionVariables.NIXOS_OZONE_WL = "1";
|
||||
|
||||
services.gvfs.enable = true;
|
||||
};
|
||||
}
|
||||
40
modules/nixos/dm/tuigreet.nix
Normal file
40
modules/nixos/dm/tuigreet.nix
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
{
|
||||
config,
|
||||
pkgs,
|
||||
lib,
|
||||
...
|
||||
}:
|
||||
let
|
||||
cfg = config.custom.dm.tuigreet;
|
||||
in
|
||||
{
|
||||
options.custom.dm.tuigreet = {
|
||||
enable = lib.mkEnableOption "";
|
||||
autoLogin = lib.mkEnableOption "";
|
||||
};
|
||||
|
||||
config = lib.mkIf cfg.enable {
|
||||
services.greetd = {
|
||||
enable = true;
|
||||
useTextGreeter = true;
|
||||
settings = {
|
||||
default_session.command =
|
||||
let
|
||||
sessionData = config.services.displayManager.sessionData.desktops;
|
||||
in
|
||||
lib.concatStringsSep " " [
|
||||
(lib.getExe pkgs.tuigreet)
|
||||
"--time"
|
||||
"--asterisks"
|
||||
"--remember"
|
||||
"--remember-user-session"
|
||||
"--sessions '${sessionData}/share/wayland-sessions:${sessionData}/share/xsessions'"
|
||||
];
|
||||
initial_session = lib.mkIf (cfg.autoLogin && config.custom.de.hyprland.enable) {
|
||||
command = lib.getExe pkgs.hyprland;
|
||||
user = "seb";
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
39
modules/nixos/networking/default.nix
Normal file
39
modules/nixos/networking/default.nix
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
{
|
||||
config,
|
||||
lib,
|
||||
allHosts,
|
||||
...
|
||||
}:
|
||||
let
|
||||
cfg = config.custom.networking;
|
||||
in
|
||||
{
|
||||
options.custom.networking = {
|
||||
hostName = lib.mkOption {
|
||||
type = lib.types.nonEmptyStr;
|
||||
default = config.networking.hostName;
|
||||
readOnly = true;
|
||||
};
|
||||
|
||||
nodes = lib.mkOption {
|
||||
type = lib.types.anything;
|
||||
default =
|
||||
allHosts
|
||||
|> lib.attrValues
|
||||
|> lib.map (host: host.config.custom.networking)
|
||||
|> lib.map (
|
||||
node:
|
||||
lib.removeAttrs node [
|
||||
"nodes"
|
||||
"peers"
|
||||
]
|
||||
);
|
||||
readOnly = true;
|
||||
};
|
||||
peers = lib.mkOption {
|
||||
type = lib.types.anything;
|
||||
default = cfg.nodes |> lib.filter (node: node.hostName != cfg.hostName);
|
||||
readOnly = true;
|
||||
};
|
||||
};
|
||||
}
|
||||
75
modules/nixos/networking/overlay.nix
Normal file
75
modules/nixos/networking/overlay.nix
Normal file
|
|
@ -0,0 +1,75 @@
|
|||
{
|
||||
config,
|
||||
lib,
|
||||
allHosts,
|
||||
...
|
||||
}:
|
||||
let
|
||||
cfg = config.custom.networking.overlay;
|
||||
in
|
||||
{
|
||||
options.custom.networking.overlay = {
|
||||
networkCidr = lib.mkOption {
|
||||
type = lib.types.nonEmptyStr;
|
||||
default = "10.254.250.0/24";
|
||||
};
|
||||
networkAddress = lib.mkOption {
|
||||
type = lib.types.nonEmptyStr;
|
||||
default = cfg.networkCidr |> lib.splitString "/" |> lib.head;
|
||||
readOnly = true;
|
||||
};
|
||||
prefixLength = lib.mkOption {
|
||||
type = lib.types.ints.between 0 32;
|
||||
default = cfg.networkCidr |> lib.splitString "/" |> lib.last |> lib.toInt;
|
||||
readOnly = true;
|
||||
};
|
||||
domain = lib.mkOption {
|
||||
type = lib.types.nonEmptyStr;
|
||||
default = "splitleaf.de";
|
||||
};
|
||||
fqdn = lib.mkOption {
|
||||
type = lib.types.nonEmptyStr;
|
||||
default = "${config.custom.networking.hostName}.${cfg.domain}";
|
||||
};
|
||||
|
||||
address = lib.mkOption {
|
||||
type = lib.types.nonEmptyStr;
|
||||
default = "";
|
||||
};
|
||||
cidr = lib.mkOption {
|
||||
type = lib.types.nonEmptyStr;
|
||||
default = "${cfg.address}/${toString cfg.prefixLength}";
|
||||
readOnly = true;
|
||||
};
|
||||
interface = lib.mkOption {
|
||||
type = lib.types.nonEmptyStr;
|
||||
default = "nebula";
|
||||
};
|
||||
systemdUnit = lib.mkOption {
|
||||
type = lib.types.nonEmptyStr;
|
||||
default = "nebula@mesh.service";
|
||||
};
|
||||
|
||||
isLighthouse = lib.mkEnableOption "";
|
||||
role = lib.mkOption {
|
||||
type = lib.types.enum [
|
||||
"client"
|
||||
"server"
|
||||
];
|
||||
};
|
||||
|
||||
dnsServers = lib.mkOption {
|
||||
type = lib.types.anything;
|
||||
default =
|
||||
allHosts
|
||||
|> lib.attrValues
|
||||
|> lib.filter (host: host.config.custom.services.dns.enable)
|
||||
|> lib.map (host: host.config.custom.networking.overlay.address);
|
||||
};
|
||||
|
||||
implementation = lib.mkOption {
|
||||
type = lib.types.enum [ "nebula" ];
|
||||
default = "nebula";
|
||||
};
|
||||
};
|
||||
}
|
||||
92
modules/nixos/networking/underlay.nix
Normal file
92
modules/nixos/networking/underlay.nix
Normal file
|
|
@ -0,0 +1,92 @@
|
|||
{
|
||||
config,
|
||||
self,
|
||||
pkgs,
|
||||
lib,
|
||||
...
|
||||
}:
|
||||
let
|
||||
cfg = config.custom.networking.underlay;
|
||||
in
|
||||
{
|
||||
options.custom.networking.underlay = {
|
||||
interface = lib.mkOption {
|
||||
type = lib.types.nonEmptyStr;
|
||||
default = "";
|
||||
};
|
||||
useDhcp = lib.mkEnableOption "";
|
||||
isPublic = lib.mkEnableOption "";
|
||||
cidr = lib.mkOption {
|
||||
type = lib.types.nullOr lib.types.nonEmptyStr;
|
||||
default = null;
|
||||
};
|
||||
address = lib.mkOption {
|
||||
type = lib.types.nullOr lib.types.nonEmptyStr;
|
||||
default = if cfg.cidr != null then cfg.cidr |> lib.splitString "/" |> lib.head else null;
|
||||
readOnly = true;
|
||||
};
|
||||
gateway = lib.mkOption {
|
||||
type = lib.types.nullOr lib.types.nonEmptyStr;
|
||||
default = null;
|
||||
};
|
||||
wireless = {
|
||||
enable = lib.mkEnableOption "";
|
||||
networks = lib.mkOption {
|
||||
type = lib.types.listOf lib.types.nonEmptyStr;
|
||||
default = config.custom.sops.secrets.iwd |> lib.attrNames;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
config = lib.mkMerge [
|
||||
{
|
||||
networking = {
|
||||
useNetworkd = true;
|
||||
useDHCP = false;
|
||||
};
|
||||
|
||||
systemd.network = {
|
||||
enable = true;
|
||||
networks."10-${cfg.interface}" = {
|
||||
matchConfig.Name = cfg.interface;
|
||||
linkConfig.RequiredForOnline = "routable";
|
||||
networkConfig.DHCP = lib.mkIf cfg.useDhcp "yes";
|
||||
address = lib.optional (cfg.cidr != null) cfg.cidr;
|
||||
routes = lib.optional (cfg.gateway != null) {
|
||||
Gateway = cfg.gateway;
|
||||
GatewayOnLink = true;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
services.resolved = {
|
||||
enable = true;
|
||||
dnssec = "allow-downgrade";
|
||||
dnsovertls = "opportunistic";
|
||||
};
|
||||
}
|
||||
|
||||
(lib.mkIf cfg.wireless.enable {
|
||||
environment.systemPackages = [ pkgs.iwgtk ];
|
||||
|
||||
networking.wireless.iwd = {
|
||||
enable = true;
|
||||
settings.Settings.AutoConnect = true;
|
||||
};
|
||||
|
||||
systemd.network.networks."10-${cfg.interface}".networkConfig.IgnoreCarrierLoss = "3s";
|
||||
|
||||
sops.secrets =
|
||||
cfg.wireless.networks
|
||||
|> lib.map (name: "iwd/${name}")
|
||||
|> self.lib.genAttrs (_: {
|
||||
restartUnits = [ "iwd.service" ];
|
||||
});
|
||||
|
||||
systemd.services.iwd = {
|
||||
preStart = "install -m 600 /run/secrets/iwd/* /var/lib/iwd";
|
||||
postStop = "rm --force /var/lib/iwd/*.{open,psk,8021x}";
|
||||
};
|
||||
})
|
||||
];
|
||||
}
|
||||
44
modules/nixos/persistence.nix
Normal file
44
modules/nixos/persistence.nix
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
{
|
||||
config,
|
||||
inputs,
|
||||
lib,
|
||||
...
|
||||
}:
|
||||
let
|
||||
cfg = config.custom.persistence;
|
||||
in
|
||||
{
|
||||
imports = [ inputs.impermanence.nixosModules.impermanence ];
|
||||
|
||||
options.custom.persistence = {
|
||||
enable = lib.mkEnableOption "";
|
||||
directories = lib.mkOption {
|
||||
type = lib.types.listOf (lib.types.coercedTo lib.types.str (d: { directory = d; }) lib.types.attrs);
|
||||
default = [ ];
|
||||
};
|
||||
};
|
||||
|
||||
config = lib.mkIf cfg.enable {
|
||||
fileSystems."/persist".neededForBoot = true;
|
||||
|
||||
security.sudo.extraConfig = "Defaults lecture=never";
|
||||
|
||||
environment.persistence."/persist" = {
|
||||
hideMounts = true;
|
||||
|
||||
# See https://nixos.org/manual/nixos/stable/#ch-system-state
|
||||
directories = [
|
||||
"/var/lib/nixos"
|
||||
"/var/lib/systemd"
|
||||
"/var/log"
|
||||
]
|
||||
++ config.custom.persistence.directories;
|
||||
|
||||
files = [
|
||||
"/etc/machine-id"
|
||||
"/etc/ssh/ssh_host_ed25519_key"
|
||||
"/etc/ssh/ssh_host_ed25519_key.pub"
|
||||
];
|
||||
};
|
||||
};
|
||||
}
|
||||
25
modules/nixos/programs/steam.nix
Normal file
25
modules/nixos/programs/steam.nix
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
{
|
||||
config,
|
||||
pkgs,
|
||||
lib,
|
||||
...
|
||||
}:
|
||||
{
|
||||
options.custom.programs.steam.enable = lib.mkEnableOption "";
|
||||
|
||||
config = lib.mkIf config.custom.programs.steam.enable {
|
||||
programs = {
|
||||
steam.enable = true;
|
||||
|
||||
gamemode = {
|
||||
enable = true;
|
||||
settings.custom = {
|
||||
start = "${lib.getExe pkgs.libnotify} 'GameMode started'";
|
||||
end = "${lib.getExe pkgs.libnotify} 'GameMode ended'";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
environment.systemPackages = [ pkgs.mangohud ];
|
||||
};
|
||||
}
|
||||
16
modules/nixos/programs/winboat.nix
Normal file
16
modules/nixos/programs/winboat.nix
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
{
|
||||
config,
|
||||
pkgs-unstable,
|
||||
lib,
|
||||
...
|
||||
}:
|
||||
{
|
||||
options.custom.programs.winboat.enable = lib.mkEnableOption "";
|
||||
|
||||
config = lib.mkIf config.custom.programs.winboat.enable {
|
||||
virtualisation.docker.enable = true;
|
||||
users.users.seb.extraGroups = [ config.users.groups.docker.name ];
|
||||
|
||||
environment.systemPackages = [ pkgs-unstable.winboat ];
|
||||
};
|
||||
}
|
||||
15
modules/nixos/programs/wireshark.nix
Normal file
15
modules/nixos/programs/wireshark.nix
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
{
|
||||
config,
|
||||
pkgs,
|
||||
lib,
|
||||
...
|
||||
}:
|
||||
{
|
||||
options.custom.programs.wireshark.enable = lib.mkEnableOption "";
|
||||
|
||||
config = lib.mkIf config.custom.programs.wireshark.enable {
|
||||
programs.wireshark.enable = true;
|
||||
environment.systemPackages = [ pkgs.wireshark ];
|
||||
users.users.seb.extraGroups = [ config.users.groups.wireshark.name ];
|
||||
};
|
||||
}
|
||||
152
modules/nixos/services/alloy.nix
Normal file
152
modules/nixos/services/alloy.nix
Normal file
|
|
@ -0,0 +1,152 @@
|
|||
{ config, lib, ... }:
|
||||
let
|
||||
cfg = config.custom.services.alloy;
|
||||
in
|
||||
{
|
||||
options.custom.services.alloy = {
|
||||
enable = lib.mkEnableOption "";
|
||||
domain = lib.mkOption {
|
||||
type = lib.types.nonEmptyStr;
|
||||
default = "";
|
||||
};
|
||||
port = lib.mkOption {
|
||||
type = lib.types.port;
|
||||
default = 12345;
|
||||
};
|
||||
metricsEndpoint = lib.mkOption {
|
||||
type = lib.types.nonEmptyStr;
|
||||
default = "https://metrics.${config.custom.networking.overlay.domain}/prometheus/api/v1/write";
|
||||
};
|
||||
logsEndpoint = lib.mkOption {
|
||||
type = lib.types.nonEmptyStr;
|
||||
default = "https://logs.${config.custom.networking.overlay.domain}/insert/loki/api/v1/push";
|
||||
};
|
||||
collect = {
|
||||
metrics = {
|
||||
system = lib.mkEnableOption "" // {
|
||||
default = true;
|
||||
};
|
||||
victorialogs = lib.mkEnableOption "" // {
|
||||
default = config.services.victorialogs.enable;
|
||||
};
|
||||
caddy = lib.mkEnableOption "" // {
|
||||
default = config.services.caddy.enable;
|
||||
};
|
||||
};
|
||||
logs.openssh = lib.mkEnableOption "" // {
|
||||
default = config.services.openssh.enable;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
config = lib.mkIf cfg.enable {
|
||||
assertions =
|
||||
let
|
||||
metricsAssertions =
|
||||
cfg.collect.metrics
|
||||
|> lib.attrNames
|
||||
|> lib.filter (name: name != "system")
|
||||
|> lib.map (name: {
|
||||
assertion = cfg.collect.metrics.${name} -> config.services.${name}.enable;
|
||||
message = "Alloy cannot collect `${name}` metrics without the `${name}` service";
|
||||
});
|
||||
logsAssertions =
|
||||
cfg.collect.logs
|
||||
|> lib.attrNames
|
||||
|> lib.map (name: {
|
||||
assertion = cfg.collect.logs.${name} -> config.services.${name}.enable;
|
||||
message = "Alloy cannot collect '${name}' logs without the '${name}' service";
|
||||
});
|
||||
in
|
||||
metricsAssertions ++ logsAssertions;
|
||||
|
||||
services.alloy = {
|
||||
enable = true;
|
||||
extraFlags = [
|
||||
"--server.http.listen-addr=localhost:${toString cfg.port}"
|
||||
"--disable-reporting"
|
||||
];
|
||||
};
|
||||
|
||||
environment.etc =
|
||||
let
|
||||
isTrue = x: x;
|
||||
anyIsTrue = attrs: attrs |> lib.attrValues |> lib.any isTrue;
|
||||
in
|
||||
{
|
||||
"alloy/metrics-endpoint.alloy" = {
|
||||
enable = cfg.collect.metrics |> anyIsTrue;
|
||||
text = ''
|
||||
prometheus.remote_write "default" {
|
||||
endpoint {
|
||||
url = "${cfg.metricsEndpoint}"
|
||||
}
|
||||
}
|
||||
'';
|
||||
};
|
||||
"alloy/logs-endpoint.alloy" = {
|
||||
enable = cfg.collect.logs |> anyIsTrue;
|
||||
text = ''
|
||||
loki.write "default" {
|
||||
endpoint {
|
||||
url = "${cfg.logsEndpoint}"
|
||||
}
|
||||
}
|
||||
'';
|
||||
};
|
||||
"alloy/system-metrics.alloy" = {
|
||||
enable = cfg.collect.metrics.system;
|
||||
text = ''
|
||||
prometheus.exporter.unix "default" {
|
||||
enable_collectors = ["systemd"]
|
||||
}
|
||||
|
||||
prometheus.scrape "node_exporter" {
|
||||
targets = prometheus.exporter.unix.default.targets
|
||||
forward_to = [prometheus.remote_write.default.receiver]
|
||||
scrape_interval = "15s"
|
||||
}
|
||||
'';
|
||||
};
|
||||
"alloy/victorialogs-metrics.alloy" = {
|
||||
enable = cfg.collect.metrics.victorialogs;
|
||||
text = ''
|
||||
prometheus.scrape "victorialogs" {
|
||||
targets = [{
|
||||
__address__ = "localhost:${toString config.custom.web-services.victorialogs.port}",
|
||||
job = "victorialogs",
|
||||
instance = constants.hostname,
|
||||
}]
|
||||
forward_to = [prometheus.remote_write.default.receiver]
|
||||
scrape_interval = "15s"
|
||||
}
|
||||
'';
|
||||
};
|
||||
"alloy/caddy-metrics.alloy" = {
|
||||
enable = cfg.collect.metrics.caddy;
|
||||
text = ''
|
||||
prometheus.scrape "caddy" {
|
||||
targets = [{
|
||||
__address__ = "localhost:${toString config.custom.services.caddy.metricsPort}",
|
||||
job = "caddy",
|
||||
instance = constants.hostname,
|
||||
}]
|
||||
forward_to = [prometheus.remote_write.default.receiver]
|
||||
scrape_interval = "15s"
|
||||
}
|
||||
'';
|
||||
};
|
||||
"alloy/sshd-logs.alloy" = {
|
||||
enable = cfg.collect.logs.openssh;
|
||||
text = ''
|
||||
loki.source.journal "sshd" {
|
||||
matches = "_SYSTEMD_UNIT=sshd.service"
|
||||
forward_to = [loki.write.default.receiver]
|
||||
}
|
||||
'';
|
||||
};
|
||||
};
|
||||
|
||||
custom.services.caddy.virtualHosts.${cfg.domain}.port = cfg.port;
|
||||
};
|
||||
}
|
||||
52
modules/nixos/services/atuin.nix
Normal file
52
modules/nixos/services/atuin.nix
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
{ config, lib, ... }:
|
||||
let
|
||||
cfg = config.custom.services.atuin;
|
||||
dataDir = "/var/lib/atuin";
|
||||
in
|
||||
{
|
||||
options.custom.services.atuin = {
|
||||
enable = lib.mkEnableOption "";
|
||||
domain = lib.mkOption {
|
||||
type = lib.types.nonEmptyStr;
|
||||
default = "";
|
||||
};
|
||||
port = lib.mkOption {
|
||||
type = lib.types.port;
|
||||
default = 8849;
|
||||
};
|
||||
};
|
||||
|
||||
config = lib.mkIf cfg.enable {
|
||||
services.atuin = {
|
||||
enable = true;
|
||||
inherit (cfg) port;
|
||||
openRegistration = true;
|
||||
database = {
|
||||
createLocally = false;
|
||||
uri = "sqlite://${dataDir}/atuin.db";
|
||||
};
|
||||
};
|
||||
|
||||
users = {
|
||||
users.atuin = {
|
||||
isSystemUser = true;
|
||||
group = config.users.groups.atuin.name;
|
||||
};
|
||||
groups.atuin = { };
|
||||
};
|
||||
|
||||
systemd.services.atuin.serviceConfig = {
|
||||
DynamicUser = lib.mkForce false;
|
||||
User = config.users.users.atuin.name;
|
||||
Group = config.users.groups.atuin.name;
|
||||
StateDirectory = "atuin";
|
||||
StateDirectoryMode = "0700";
|
||||
};
|
||||
|
||||
custom = {
|
||||
services.caddy.virtualHosts.${cfg.domain}.port = cfg.port;
|
||||
|
||||
persistence.directories = [ dataDir ];
|
||||
};
|
||||
};
|
||||
}
|
||||
27
modules/nixos/services/auto-gc.nix
Normal file
27
modules/nixos/services/auto-gc.nix
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
{ config, lib, ... }:
|
||||
let
|
||||
cfg = config.custom.services.auto-gc;
|
||||
in
|
||||
{
|
||||
options.custom.services.auto-gc = {
|
||||
enable = lib.mkEnableOption "";
|
||||
onlyCleanRoots = lib.mkEnableOption "";
|
||||
};
|
||||
|
||||
config = lib.mkIf cfg.enable {
|
||||
programs.nh = {
|
||||
enable = true;
|
||||
clean = {
|
||||
enable = true;
|
||||
dates = "weekly";
|
||||
extraArgs =
|
||||
[
|
||||
"--keep 10"
|
||||
"--keep-since 7d"
|
||||
]
|
||||
++ lib.optional cfg.onlyCleanRoots "--no-gc"
|
||||
|> lib.concatStringsSep " ";
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
16
modules/nixos/services/bluetooth.nix
Normal file
16
modules/nixos/services/bluetooth.nix
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
{ config, lib, ... }:
|
||||
{
|
||||
options.custom.services.bluetooth.enable = lib.mkEnableOption "";
|
||||
|
||||
config = lib.mkIf config.custom.services.bluetooth.enable {
|
||||
hardware = {
|
||||
bluetooth = {
|
||||
enable = true;
|
||||
powerOnBoot = true;
|
||||
};
|
||||
logitech.wireless.enable = true;
|
||||
};
|
||||
|
||||
services.blueman.enable = true;
|
||||
};
|
||||
}
|
||||
170
modules/nixos/services/caddy.nix
Normal file
170
modules/nixos/services/caddy.nix
Normal file
|
|
@ -0,0 +1,170 @@
|
|||
{
|
||||
config,
|
||||
self,
|
||||
lib,
|
||||
...
|
||||
}:
|
||||
let
|
||||
cfg = config.custom.services.caddy;
|
||||
netCfg = config.custom.networking;
|
||||
|
||||
virtualHosts = cfg.virtualHosts |> lib.attrValues |> lib.filter (vHost: vHost.enable);
|
||||
|
||||
publicHostsExist = virtualHosts |> lib.any (vHost: (!self.lib.isPrivateDomain vHost.domain));
|
||||
privateHostsExist = virtualHosts |> lib.any (vHost: self.lib.isPrivateDomain vHost.domain);
|
||||
|
||||
mkVirtualHost =
|
||||
{
|
||||
domain,
|
||||
port,
|
||||
files,
|
||||
extraConfig,
|
||||
...
|
||||
}:
|
||||
lib.nameValuePair domain {
|
||||
logFormat = "output file ${config.services.caddy.logDir}/${domain}.log { mode 640 }";
|
||||
extraConfig =
|
||||
let
|
||||
certDir = config.security.acme.certs.${domain}.directory;
|
||||
in
|
||||
[
|
||||
(lib.optionals (self.lib.isPrivateDomain domain) [
|
||||
"tls ${certDir}/fullchain.pem ${certDir}/key.pem"
|
||||
"bind ${config.custom.networking.overlay.address}"
|
||||
])
|
||||
(lib.optional (port != null) "reverse_proxy localhost:${toString port}")
|
||||
(lib.optionals (files != null) [
|
||||
"root * ${files}"
|
||||
"encode"
|
||||
"file_server"
|
||||
])
|
||||
(lib.optional (extraConfig != null) extraConfig)
|
||||
]
|
||||
|> lib.concatLists
|
||||
|> lib.concatLines;
|
||||
};
|
||||
in
|
||||
{
|
||||
options.custom.services.caddy = {
|
||||
metricsPort = lib.mkOption {
|
||||
type = lib.types.port;
|
||||
default = 49514;
|
||||
};
|
||||
virtualHosts = lib.mkOption {
|
||||
type = lib.types.attrsOf (
|
||||
lib.types.submodule (
|
||||
{ name, ... }:
|
||||
{
|
||||
options = {
|
||||
enable = lib.mkEnableOption "" // {
|
||||
default = true;
|
||||
};
|
||||
domain = lib.mkOption {
|
||||
type = lib.types.nonEmptyStr;
|
||||
default = name;
|
||||
};
|
||||
port = lib.mkOption {
|
||||
type = lib.types.nullOr lib.types.port;
|
||||
default = null;
|
||||
};
|
||||
files = lib.mkOption {
|
||||
type = lib.types.nullOr lib.types.path;
|
||||
default = null;
|
||||
};
|
||||
extraConfig = lib.mkOption {
|
||||
type = lib.types.nullOr lib.types.lines;
|
||||
default = null;
|
||||
};
|
||||
};
|
||||
}
|
||||
)
|
||||
);
|
||||
default = { };
|
||||
};
|
||||
};
|
||||
|
||||
config = lib.mkIf (virtualHosts != [ ]) (
|
||||
lib.mkMerge [
|
||||
{
|
||||
assertions =
|
||||
virtualHosts
|
||||
|> lib.concatMap (vHost: [
|
||||
{
|
||||
assertion = (vHost.port == null) || (vHost.files == null);
|
||||
message = "Caddy virtual host `${vHost.domain}` cannot set both `port` and `files`";
|
||||
}
|
||||
{
|
||||
assertion = (vHost.port != null) || (vHost.files != null) || (vHost.extraConfig != null);
|
||||
message = "Caddy virtual host `${vHost.domain}` must set at least one of `port`, `files` or `extraConfig`";
|
||||
}
|
||||
]);
|
||||
|
||||
networking.firewall.allowedTCPPorts = lib.mkIf publicHostsExist [
|
||||
80
|
||||
443
|
||||
];
|
||||
|
||||
services.caddy = {
|
||||
enable = true;
|
||||
enableReload = false;
|
||||
globalConfig = ''
|
||||
admin off
|
||||
metrics { per_host }
|
||||
'';
|
||||
extraConfig = ":${toString cfg.metricsPort} { metrics /metrics }";
|
||||
virtualHosts = virtualHosts |> lib.map mkVirtualHost |> lib.listToAttrs;
|
||||
};
|
||||
|
||||
custom.persistence.directories = [ "/var/lib/caddy" ];
|
||||
}
|
||||
|
||||
(lib.mkIf privateHostsExist {
|
||||
sops.secrets = {
|
||||
"porkbun/api-key".owner = config.users.users.acme.name;
|
||||
"porkbun/secret-api-key".owner = config.users.users.acme.name;
|
||||
};
|
||||
|
||||
security.acme = {
|
||||
acceptTerms = true;
|
||||
defaults = {
|
||||
email = "acme@sstork.dev";
|
||||
dnsProvider = "porkbun";
|
||||
dnsResolver = "1.1.1.1:53";
|
||||
group = config.users.users.caddy.name;
|
||||
credentialFiles = {
|
||||
PORKBUN_API_KEY_FILE = config.sops.secrets."porkbun/api-key".path;
|
||||
PORKBUN_SECRET_API_KEY_FILE = config.sops.secrets."porkbun/secret-api-key".path;
|
||||
};
|
||||
reloadServices = [ "caddy.service" ];
|
||||
};
|
||||
|
||||
certs =
|
||||
virtualHosts
|
||||
|> lib.filter (host: self.lib.isPrivateDomain host.domain)
|
||||
|> lib.map (host: lib.nameValuePair host.domain { })
|
||||
|> lib.listToAttrs;
|
||||
};
|
||||
|
||||
services.nebula.networks.mesh.firewall.inbound = [
|
||||
{
|
||||
port = "80";
|
||||
proto = "tcp";
|
||||
host = "any";
|
||||
}
|
||||
{
|
||||
port = "443";
|
||||
proto = "tcp";
|
||||
host = "any";
|
||||
}
|
||||
];
|
||||
|
||||
systemd.services.caddy = {
|
||||
requires = [ netCfg.overlay.systemdUnit ];
|
||||
after = [ netCfg.overlay.systemdUnit ];
|
||||
};
|
||||
|
||||
custom.persistence.directories = [ "/var/lib/acme" ];
|
||||
})
|
||||
]
|
||||
);
|
||||
}
|
||||
22
modules/nixos/services/comin.nix
Normal file
22
modules/nixos/services/comin.nix
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
{
|
||||
config,
|
||||
inputs,
|
||||
lib,
|
||||
...
|
||||
}:
|
||||
{
|
||||
imports = [ inputs.comin.nixosModules.comin ];
|
||||
|
||||
options.custom.services.comin.enable = lib.mkEnableOption "";
|
||||
|
||||
config = lib.mkIf config.custom.services.comin.enable {
|
||||
services.comin = {
|
||||
enable = true;
|
||||
remotes = lib.singleton {
|
||||
name = "origin";
|
||||
url = "https://github.com/SebastianStork/nixos-config.git";
|
||||
branches.main.name = "deploy";
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
56
modules/nixos/services/dns.nix
Normal file
56
modules/nixos/services/dns.nix
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
{
|
||||
config,
|
||||
self,
|
||||
lib,
|
||||
allHosts,
|
||||
...
|
||||
}:
|
||||
let
|
||||
cfg = config.custom.services.dns;
|
||||
netCfg = config.custom.networking;
|
||||
in
|
||||
{
|
||||
options.custom.services.dns.enable = lib.mkEnableOption "";
|
||||
|
||||
config = lib.mkIf cfg.enable {
|
||||
services = {
|
||||
unbound = {
|
||||
enable = true;
|
||||
|
||||
settings.server = {
|
||||
interface = [ netCfg.overlay.interface ];
|
||||
access-control = [ "${toString netCfg.overlay.networkCidr} allow" ];
|
||||
|
||||
local-zone = "\"${netCfg.overlay.domain}.\" static";
|
||||
local-data =
|
||||
let
|
||||
nodeRecords = netCfg.nodes |> lib.map (node: "\"${node.overlay.fqdn}. A ${node.overlay.address}\"");
|
||||
serviceRecords =
|
||||
allHosts
|
||||
|> lib.attrValues
|
||||
|> lib.concatMap (
|
||||
host:
|
||||
host.config.custom.services.caddy.virtualHosts
|
||||
|> lib.attrValues
|
||||
|> lib.map (vHost: vHost.domain)
|
||||
|> lib.filter (domain: self.lib.isPrivateDomain domain)
|
||||
|> lib.map (domain: "\"${domain}. A ${host.config.custom.networking.overlay.address}\"")
|
||||
);
|
||||
in
|
||||
nodeRecords ++ serviceRecords;
|
||||
};
|
||||
};
|
||||
|
||||
nebula.networks.mesh.firewall.inbound = lib.singleton {
|
||||
port = 53;
|
||||
proto = "any";
|
||||
host = "any";
|
||||
};
|
||||
};
|
||||
|
||||
systemd.services.unbound = {
|
||||
requires = [ netCfg.overlay.systemdUnit ];
|
||||
after = [ netCfg.overlay.systemdUnit ];
|
||||
};
|
||||
};
|
||||
}
|
||||
5
modules/nixos/services/nebula/ca.crt
Normal file
5
modules/nixos/services/nebula/ca.crt
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
-----BEGIN NEBULA CERTIFICATE V2-----
|
||||
MHugFYAEbWFpboQB/4UEaUdKdYYEayh99YIg5FsAhFthpvA/ELlR7NVFGvuIB5Zv
|
||||
66n1h1qg0vumHY+DQHGky+1qxbGswdyDZBYfqctktyfJUMKk0TZIn6cqYLbydSZJ
|
||||
J9HxMj2JWu/d/2nsh11uhRwquBH733AmXZ2DDgE=
|
||||
-----END NEBULA CERTIFICATE V2-----
|
||||
139
modules/nixos/services/nebula/default.nix
Normal file
139
modules/nixos/services/nebula/default.nix
Normal file
|
|
@ -0,0 +1,139 @@
|
|||
{
|
||||
config,
|
||||
self,
|
||||
lib,
|
||||
...
|
||||
}:
|
||||
let
|
||||
cfg = config.custom.services.nebula;
|
||||
netCfg = config.custom.networking;
|
||||
|
||||
publicPort = 47141;
|
||||
|
||||
lighthouses =
|
||||
netCfg.peers
|
||||
|> lib.filter (peer: peer.overlay.isLighthouse)
|
||||
|> lib.map (lighthouse: lighthouse.overlay.address);
|
||||
in
|
||||
{
|
||||
options.custom.services.nebula = {
|
||||
enable = lib.mkEnableOption "" // {
|
||||
default = netCfg.overlay.implementation == "nebula";
|
||||
};
|
||||
groups = lib.mkOption {
|
||||
type = lib.types.nonEmptyListOf lib.types.nonEmptyStr;
|
||||
default =
|
||||
lib.singleton netCfg.overlay.role
|
||||
++ lib.optional config.custom.services.syncthing.enable "syncthing";
|
||||
};
|
||||
|
||||
caCertificateFile = lib.mkOption {
|
||||
type = self.lib.types.existingPath;
|
||||
default = ./ca.crt;
|
||||
};
|
||||
publicKeyFile = lib.mkOption {
|
||||
type = self.lib.types.existingPath;
|
||||
default = "${self}/hosts/${netCfg.hostName}/keys/nebula.pub";
|
||||
};
|
||||
certificateFile = lib.mkOption {
|
||||
type = self.lib.types.existingPath;
|
||||
default = "${self}/hosts/${netCfg.hostName}/keys/nebula.crt";
|
||||
};
|
||||
privateKeyFile = lib.mkOption {
|
||||
type = lib.types.nullOr lib.types.path;
|
||||
default = null;
|
||||
};
|
||||
};
|
||||
|
||||
config = lib.mkIf cfg.enable {
|
||||
assertions = lib.singleton {
|
||||
assertion = netCfg.overlay.isLighthouse -> netCfg.underlay.isPublic;
|
||||
message = "`${netCfg.hostName}` is a Nebula lighthouse, but `underlay.isPublic` is not set. Lighthouses must be publicly reachable.";
|
||||
};
|
||||
|
||||
sops.secrets."nebula/host-key" = lib.mkIf (cfg.privateKeyFile == null) {
|
||||
owner = config.users.users.nebula-mesh.name;
|
||||
restartUnits = [ "nebula@mesh.service" ];
|
||||
};
|
||||
|
||||
environment.etc = {
|
||||
"nebula/ca.crt" = {
|
||||
source = cfg.caCertificateFile;
|
||||
mode = "0440";
|
||||
user = config.systemd.services."nebula@mesh".serviceConfig.User;
|
||||
};
|
||||
"nebula/host.crt" = {
|
||||
source = cfg.certificateFile;
|
||||
mode = "0440";
|
||||
user = config.systemd.services."nebula@mesh".serviceConfig.User;
|
||||
};
|
||||
};
|
||||
|
||||
services.nebula.networks.mesh = {
|
||||
enable = true;
|
||||
|
||||
ca = "/etc/nebula/ca.crt";
|
||||
cert = "/etc/nebula/host.crt";
|
||||
key =
|
||||
if (cfg.privateKeyFile != null) then
|
||||
cfg.privateKeyFile
|
||||
else
|
||||
config.sops.secrets."nebula/host-key".path;
|
||||
|
||||
tun.device = netCfg.overlay.interface;
|
||||
listen = {
|
||||
host = lib.mkIf (netCfg.underlay.address != null) netCfg.underlay.address;
|
||||
port = lib.mkIf netCfg.underlay.isPublic publicPort;
|
||||
};
|
||||
|
||||
inherit (netCfg.overlay) isLighthouse;
|
||||
lighthouses = lib.mkIf (!netCfg.overlay.isLighthouse) lighthouses;
|
||||
|
||||
isRelay = netCfg.overlay.isLighthouse;
|
||||
relays = lib.mkIf (!netCfg.overlay.isLighthouse) lighthouses;
|
||||
|
||||
staticHostMap =
|
||||
netCfg.peers
|
||||
|> lib.filter (peer: peer.underlay.isPublic)
|
||||
|> lib.map (publicPeer: {
|
||||
name = publicPeer.overlay.address;
|
||||
value = lib.singleton "${publicPeer.underlay.address}:${toString publicPort}";
|
||||
})
|
||||
|> lib.listToAttrs;
|
||||
|
||||
firewall = {
|
||||
outbound = lib.singleton {
|
||||
port = "any";
|
||||
proto = "any";
|
||||
host = "any";
|
||||
};
|
||||
inbound = lib.singleton {
|
||||
port = "any";
|
||||
proto = "icmp";
|
||||
host = "any";
|
||||
};
|
||||
};
|
||||
|
||||
settings = {
|
||||
pki.disconnect_invalid = true;
|
||||
cipher = "aes";
|
||||
};
|
||||
};
|
||||
|
||||
networking.firewall.trustedInterfaces = [ netCfg.overlay.interface ];
|
||||
|
||||
systemd = {
|
||||
services."nebula@mesh" = {
|
||||
wants = [ "network-online.target" ];
|
||||
after = [ "network-online.target" ];
|
||||
};
|
||||
|
||||
network.networks."40-nebula" = {
|
||||
matchConfig.Name = netCfg.overlay.interface;
|
||||
address = [ netCfg.overlay.cidr ];
|
||||
dns = netCfg.overlay.dnsServers;
|
||||
domains = [ netCfg.overlay.domain ];
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
15
modules/nixos/services/printing.nix
Normal file
15
modules/nixos/services/printing.nix
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
{ config, lib, ... }:
|
||||
{
|
||||
options.custom.services.printing.enable = lib.mkEnableOption "";
|
||||
|
||||
config = lib.mkIf config.custom.services.printing.enable {
|
||||
services = {
|
||||
printing.enable = true;
|
||||
avahi = {
|
||||
enable = true;
|
||||
nssmdns4 = true;
|
||||
openFirewall = true;
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
112
modules/nixos/services/restic/backups.nix
Normal file
112
modules/nixos/services/restic/backups.nix
Normal file
|
|
@ -0,0 +1,112 @@
|
|||
{
|
||||
config,
|
||||
pkgs,
|
||||
lib,
|
||||
...
|
||||
}:
|
||||
let
|
||||
backups =
|
||||
config.custom.services.restic.backups |> lib.attrValues |> lib.filter (backup: backup.enable);
|
||||
in
|
||||
{
|
||||
options.custom.services.restic.backups = lib.mkOption {
|
||||
type = lib.types.attrsOf (
|
||||
lib.types.submodule (
|
||||
{ name, ... }:
|
||||
{
|
||||
options = {
|
||||
enable = lib.mkEnableOption "" // {
|
||||
default = true;
|
||||
};
|
||||
name = lib.mkOption {
|
||||
type = lib.types.nonEmptyStr;
|
||||
default = name;
|
||||
};
|
||||
conflictingService = lib.mkOption {
|
||||
type = lib.types.nullOr lib.types.nonEmptyStr;
|
||||
default = null;
|
||||
};
|
||||
paths = lib.mkOption {
|
||||
type = lib.types.listOf lib.types.path;
|
||||
default = [ ];
|
||||
};
|
||||
extraConfig = lib.mkOption {
|
||||
type = lib.types.attrsOf lib.types.anything;
|
||||
default = { };
|
||||
};
|
||||
};
|
||||
}
|
||||
)
|
||||
);
|
||||
default = { };
|
||||
};
|
||||
|
||||
config = lib.mkIf (backups != [ ]) {
|
||||
sops = {
|
||||
secrets = {
|
||||
"backblaze/key-id" = { };
|
||||
"backblaze/application-key" = { };
|
||||
"restic/password" = { };
|
||||
};
|
||||
|
||||
templates."restic/environment".content = ''
|
||||
AWS_ACCESS_KEY_ID=${config.sops.placeholder."backblaze/key-id"}
|
||||
AWS_SECRET_ACCESS_KEY=${config.sops.placeholder."backblaze/application-key"}
|
||||
'';
|
||||
};
|
||||
|
||||
systemd.tmpfiles.rules =
|
||||
backups |> lib.map (backup: "d /var/cache/restic-backups-${backup.name} 700 - - -");
|
||||
|
||||
services.restic.backups =
|
||||
backups
|
||||
|> lib.map (backup: {
|
||||
inherit (backup) name;
|
||||
value = lib.mkMerge [
|
||||
{
|
||||
inherit (backup) paths;
|
||||
initialize = true;
|
||||
repository = "s3:https://s3.eu-central-003.backblazeb2.com/stork-atlas/${backup.name}";
|
||||
environmentFile = config.sops.templates."restic/environment".path;
|
||||
passwordFile = config.sops.secrets."restic/password".path;
|
||||
pruneOpts = [
|
||||
"--keep-daily 7"
|
||||
"--keep-weekly 4"
|
||||
"--keep-monthly 6"
|
||||
];
|
||||
timerConfig = {
|
||||
OnCalendar = "03:00";
|
||||
RandomizedDelaySec = "1h";
|
||||
};
|
||||
}
|
||||
backup.extraConfig
|
||||
];
|
||||
})
|
||||
|> lib.listToAttrs;
|
||||
|
||||
systemd.services =
|
||||
backups
|
||||
|> lib.filter (backup: backup.conflictingService != null)
|
||||
|> lib.map (backup: {
|
||||
name = "restic-backups-${backup.name}";
|
||||
value = {
|
||||
unitConfig.Conflicts = [ backup.conflictingService ];
|
||||
after = [ backup.conflictingService ];
|
||||
onSuccess = [ backup.conflictingService ];
|
||||
onFailure = [ backup.conflictingService ];
|
||||
};
|
||||
})
|
||||
|> lib.listToAttrs;
|
||||
|
||||
environment.systemPackages =
|
||||
let
|
||||
backupAllScript = pkgs.writeShellApplication {
|
||||
name = "restic-backup-all";
|
||||
text = "systemctl start restic-backups-{${
|
||||
backups |> lib.map (backup: backup.name) |> lib.concatStringsSep ","
|
||||
}}";
|
||||
};
|
||||
in
|
||||
[ backupAllScript ];
|
||||
};
|
||||
}
|
||||
58
modules/nixos/services/restic/healthchecks.nix
Normal file
58
modules/nixos/services/restic/healthchecks.nix
Normal file
|
|
@ -0,0 +1,58 @@
|
|||
{
|
||||
config,
|
||||
pkgs,
|
||||
lib,
|
||||
...
|
||||
}:
|
||||
let
|
||||
backupsWithHealthchecks =
|
||||
config.custom.services.restic.backups
|
||||
|> lib.attrValues
|
||||
|> lib.filter (backup: backup.enable && backup.doHealthchecks);
|
||||
in
|
||||
{
|
||||
options.custom.services.restic.backups = lib.mkOption {
|
||||
type = lib.types.attrsOf (
|
||||
lib.types.submodule {
|
||||
options.doHealthchecks = lib.mkEnableOption "" // {
|
||||
default = true;
|
||||
};
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
config = lib.mkIf (backupsWithHealthchecks != [ ]) {
|
||||
sops.secrets."healthchecks/ping-key" = { };
|
||||
|
||||
systemd.services = {
|
||||
"healthcheck-ping@" = {
|
||||
description = "Pings healthcheck (%i)";
|
||||
serviceConfig.Type = "oneshot";
|
||||
scriptArgs = "%i";
|
||||
script = ''
|
||||
ping_key="$(cat ${config.sops.secrets."healthchecks/ping-key".path})"
|
||||
slug="$(echo "$1" | tr _ /)"
|
||||
|
||||
${lib.getExe pkgs.curl} \
|
||||
--fail \
|
||||
--silent \
|
||||
--show-error \
|
||||
--max-time 10 \
|
||||
--retry 5 "https://hc-ping.com/$ping_key/$slug?create=1"
|
||||
'';
|
||||
};
|
||||
}
|
||||
// (
|
||||
backupsWithHealthchecks
|
||||
|> lib.map (backup: {
|
||||
name = "restic-backups-${backup.name}";
|
||||
value = {
|
||||
wants = [ "healthcheck-ping@${backup.name}-backup_start.service" ];
|
||||
onSuccess = [ "healthcheck-ping@${backup.name}-backup.service" ];
|
||||
onFailure = [ "healthcheck-ping@${backup.name}-backup_fail.service" ];
|
||||
};
|
||||
})
|
||||
|> lib.listToAttrs
|
||||
);
|
||||
};
|
||||
}
|
||||
66
modules/nixos/services/restic/restore.nix
Normal file
66
modules/nixos/services/restic/restore.nix
Normal file
|
|
@ -0,0 +1,66 @@
|
|||
{
|
||||
config,
|
||||
pkgs,
|
||||
lib,
|
||||
...
|
||||
}:
|
||||
let
|
||||
backupsWithRestoreCommand =
|
||||
config.custom.services.restic.backups
|
||||
|> lib.attrValues
|
||||
|> lib.filter (backup: backup.enable && backup.restoreCommand.enable);
|
||||
in
|
||||
{
|
||||
options.custom.services.restic.backups = lib.mkOption {
|
||||
type = lib.types.attrsOf (
|
||||
lib.types.submodule {
|
||||
options.restoreCommand = {
|
||||
enable = lib.mkEnableOption "" // {
|
||||
default = true;
|
||||
};
|
||||
preRestore = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
default = "";
|
||||
};
|
||||
postRestore = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
default = "";
|
||||
};
|
||||
};
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
config = {
|
||||
environment.systemPackages =
|
||||
let
|
||||
restoreScripts =
|
||||
backupsWithRestoreCommand
|
||||
|> lib.map (
|
||||
backup:
|
||||
let
|
||||
inherit (backup) name conflictingService;
|
||||
inherit (backup.restoreCommand) preRestore postRestore;
|
||||
hasConflictingService = conflictingService != null;
|
||||
in
|
||||
pkgs.writeShellApplication {
|
||||
name = "restic-restore-${name}";
|
||||
text = ''
|
||||
${lib.optionalString hasConflictingService "systemctl stop ${conflictingService}"}
|
||||
${preRestore}
|
||||
restic-${name} restore latest --target /
|
||||
${postRestore}
|
||||
${lib.optionalString hasConflictingService "systemctl start ${conflictingService}"}
|
||||
'';
|
||||
}
|
||||
);
|
||||
|
||||
restoreAllScript = pkgs.writeShellApplication {
|
||||
name = "restic-restore-all";
|
||||
text =
|
||||
backupsWithRestoreCommand |> lib.map (backup: "restic-restore-${backup.name}") |> lib.concatLines;
|
||||
};
|
||||
in
|
||||
restoreScripts ++ [ restoreAllScript ];
|
||||
};
|
||||
}
|
||||
16
modules/nixos/services/sound.nix
Normal file
16
modules/nixos/services/sound.nix
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
{ config, lib, ... }:
|
||||
{
|
||||
options.custom.services.sound.enable = lib.mkEnableOption "";
|
||||
|
||||
config = lib.mkIf config.custom.services.sound.enable {
|
||||
security.rtkit.enable = true;
|
||||
services.pipewire = {
|
||||
enable = true;
|
||||
pulse.enable = true;
|
||||
alsa = {
|
||||
enable = true;
|
||||
support32Bit = true;
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
52
modules/nixos/services/sshd.nix
Normal file
52
modules/nixos/services/sshd.nix
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
{
|
||||
config,
|
||||
lib,
|
||||
allHosts,
|
||||
...
|
||||
}:
|
||||
let
|
||||
cfg = config.custom.services.sshd;
|
||||
netCfg = config.custom.networking;
|
||||
in
|
||||
{
|
||||
options.custom.services.sshd.enable = lib.mkEnableOption "";
|
||||
|
||||
config = lib.mkIf cfg.enable {
|
||||
services = {
|
||||
openssh = {
|
||||
enable = true;
|
||||
openFirewall = false;
|
||||
ports = lib.mkForce [ ];
|
||||
listenAddresses = lib.singleton {
|
||||
addr = netCfg.overlay.address;
|
||||
port = 22;
|
||||
};
|
||||
settings = {
|
||||
PasswordAuthentication = false;
|
||||
KbdInteractiveAuthentication = false;
|
||||
PermitRootLogin = "no";
|
||||
};
|
||||
};
|
||||
|
||||
nebula.networks.mesh.firewall.inbound = lib.singleton {
|
||||
port = 22;
|
||||
proto = "tcp";
|
||||
group = "client";
|
||||
};
|
||||
};
|
||||
|
||||
systemd.services.sshd = {
|
||||
requires = [ netCfg.overlay.systemdUnit ];
|
||||
after = [ netCfg.overlay.systemdUnit ];
|
||||
};
|
||||
|
||||
users.users.seb.openssh.authorizedKeys.keyFiles =
|
||||
allHosts
|
||||
|> lib.attrValues
|
||||
|> lib.filter (host: host.config.networking.hostName != netCfg.hostName)
|
||||
|> lib.filter (host: host.config |> lib.hasAttr "home-manager")
|
||||
|> lib.map (host: host.config.home-manager.users.seb.custom.programs.ssh)
|
||||
|> lib.filter (ssh: ssh.enable)
|
||||
|> lib.map (ssh: ssh.publicKeyFile);
|
||||
};
|
||||
}
|
||||
148
modules/nixos/services/syncthing.nix
Normal file
148
modules/nixos/services/syncthing.nix
Normal file
|
|
@ -0,0 +1,148 @@
|
|||
{
|
||||
config,
|
||||
self,
|
||||
lib,
|
||||
allHosts,
|
||||
...
|
||||
}:
|
||||
let
|
||||
cfg = config.custom.services.syncthing;
|
||||
netCfg = config.custom.networking;
|
||||
|
||||
inherit (config.services.syncthing) dataDir;
|
||||
|
||||
useSopsSecrets = config.custom.sops.secrets |> lib.hasAttr "syncthing";
|
||||
in
|
||||
{
|
||||
options.custom.services.syncthing = {
|
||||
enable = lib.mkEnableOption "";
|
||||
isServer = lib.mkEnableOption "";
|
||||
doBackups = lib.mkEnableOption "";
|
||||
deviceId = lib.mkOption {
|
||||
type = lib.types.nonEmptyStr;
|
||||
default = "${self}/hosts/${netCfg.hostName}/keys/syncthing.id" |> lib.readFile |> lib.trim;
|
||||
};
|
||||
syncPort = lib.mkOption {
|
||||
type = lib.types.port;
|
||||
default = 22000;
|
||||
};
|
||||
gui = {
|
||||
domain = lib.mkOption {
|
||||
type = lib.types.nullOr lib.types.nonEmptyStr;
|
||||
default = null;
|
||||
};
|
||||
port = lib.mkOption {
|
||||
type = lib.types.port;
|
||||
default = 8384;
|
||||
};
|
||||
};
|
||||
folders = lib.mkOption {
|
||||
type = lib.types.nonEmptyListOf lib.types.nonEmptyStr;
|
||||
default = [
|
||||
"Documents"
|
||||
"Downloads"
|
||||
"Music"
|
||||
"Pictures"
|
||||
"Projects"
|
||||
"Videos"
|
||||
];
|
||||
};
|
||||
};
|
||||
|
||||
config = lib.mkIf cfg.enable {
|
||||
assertions = [
|
||||
{
|
||||
assertion = cfg.isServer -> (cfg.gui.domain != null);
|
||||
message = "Syncthing requires `gui.domain` to be set when `isServer` is enabled";
|
||||
}
|
||||
{
|
||||
assertion = (cfg.gui.domain != null) -> (self.lib.isPrivateDomain cfg.gui.domain);
|
||||
message = self.lib.mkUnprotectedMessage "Syncthing-GUI";
|
||||
}
|
||||
];
|
||||
|
||||
sops.secrets = lib.mkIf useSopsSecrets {
|
||||
"syncthing/cert" = {
|
||||
owner = config.services.syncthing.user;
|
||||
restartUnits = [ "syncthing.service" ];
|
||||
};
|
||||
"syncthing/key" = {
|
||||
owner = config.services.syncthing.user;
|
||||
restartUnits = [ "syncthing.service" ];
|
||||
};
|
||||
};
|
||||
|
||||
services = {
|
||||
syncthing = {
|
||||
enable = true;
|
||||
|
||||
user = lib.mkIf (!cfg.isServer) "seb";
|
||||
group = lib.mkIf (!cfg.isServer) "users";
|
||||
dataDir = lib.mkIf (!cfg.isServer) "/home/seb";
|
||||
|
||||
guiAddress = "localhost:${toString cfg.gui.port}";
|
||||
|
||||
cert = lib.mkIf useSopsSecrets config.sops.secrets."syncthing/cert".path;
|
||||
key = lib.mkIf useSopsSecrets config.sops.secrets."syncthing/key".path;
|
||||
|
||||
settings =
|
||||
let
|
||||
hosts =
|
||||
allHosts
|
||||
|> lib.filterAttrs (_: host: host.config.networking.hostName != config.networking.hostName)
|
||||
|> lib.filterAttrs (_: host: host.config.custom.services.syncthing.enable);
|
||||
in
|
||||
{
|
||||
devices =
|
||||
hosts
|
||||
|> lib.mapAttrs (
|
||||
_: host: {
|
||||
id = host.config.custom.services.syncthing.deviceId;
|
||||
addresses = lib.singleton "tcp://${host.config.custom.networking.overlay.address}:${toString host.config.custom.services.syncthing.syncPort}";
|
||||
}
|
||||
);
|
||||
|
||||
folders =
|
||||
cfg.folders
|
||||
|> self.lib.genAttrs (folder: {
|
||||
path = "${dataDir}/${folder}";
|
||||
devices =
|
||||
hosts
|
||||
|> lib.filterAttrs (_: host: host.config.custom.services.syncthing.folders |> lib.elem folder)
|
||||
|> lib.attrNames;
|
||||
});
|
||||
|
||||
options = {
|
||||
listenAddress = "tcp://${netCfg.overlay.address}:${toString cfg.syncPort}";
|
||||
globalAnnounceEnabled = false;
|
||||
localAnnounceEnabled = false;
|
||||
relaysEnabled = false;
|
||||
natEnabled = false;
|
||||
urAccepted = -1;
|
||||
autoUpgradeIntervalH = 0;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
nebula.networks.mesh.firewall.inbound = lib.singleton {
|
||||
port = cfg.syncPort;
|
||||
proto = "tcp";
|
||||
group = "syncthing";
|
||||
};
|
||||
};
|
||||
|
||||
custom = {
|
||||
services = {
|
||||
caddy.virtualHosts.${cfg.gui.domain}.port = lib.mkIf (cfg.gui.domain != null) cfg.gui.port;
|
||||
|
||||
restic.backups.syncthing = lib.mkIf cfg.doBackups {
|
||||
conflictingService = "syncthing.service";
|
||||
paths = [ dataDir ];
|
||||
extraConfig.exclude = [ "${dataDir}/Downloads" ];
|
||||
};
|
||||
};
|
||||
|
||||
persistence.directories = [ dataDir ];
|
||||
};
|
||||
};
|
||||
}
|
||||
38
modules/nixos/sops.nix
Normal file
38
modules/nixos/sops.nix
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
{
|
||||
config,
|
||||
inputs,
|
||||
self,
|
||||
lib,
|
||||
...
|
||||
}:
|
||||
let
|
||||
cfg = config.custom.sops;
|
||||
in
|
||||
{
|
||||
imports = [ inputs.sops.nixosModules.sops ];
|
||||
|
||||
options.custom.sops = {
|
||||
enable = lib.mkEnableOption "";
|
||||
agePublicKey = lib.mkOption {
|
||||
type = lib.types.nonEmptyStr;
|
||||
default = "${self}/hosts/${config.networking.hostName}/keys/age.pub" |> lib.readFile |> lib.trim;
|
||||
};
|
||||
secretsFile = lib.mkOption {
|
||||
type = self.lib.types.existingPath;
|
||||
default = "${self}/hosts/${config.networking.hostName}/secrets.json";
|
||||
};
|
||||
secrets = lib.mkOption {
|
||||
type = lib.types.anything;
|
||||
default = cfg.secretsFile |> lib.readFile |> lib.strings.fromJSON;
|
||||
};
|
||||
};
|
||||
|
||||
config = lib.mkIf cfg.enable {
|
||||
sops = {
|
||||
age.sshKeyPaths = [
|
||||
"${lib.optionalString config.custom.persistence.enable "/persist"}/etc/ssh/ssh_host_ed25519_key"
|
||||
];
|
||||
defaultSopsFile = cfg.secretsFile;
|
||||
};
|
||||
};
|
||||
}
|
||||
57
modules/nixos/web-services/actualbudget.nix
Normal file
57
modules/nixos/web-services/actualbudget.nix
Normal file
|
|
@ -0,0 +1,57 @@
|
|||
{ config, lib, ... }:
|
||||
let
|
||||
cfg = config.custom.web-services.actualbudget;
|
||||
|
||||
inherit (config.services.actual.settings) dataDir;
|
||||
in
|
||||
{
|
||||
options.custom.web-services.actualbudget = {
|
||||
enable = lib.mkEnableOption "";
|
||||
domain = lib.mkOption {
|
||||
type = lib.types.nonEmptyStr;
|
||||
default = "";
|
||||
};
|
||||
port = lib.mkOption {
|
||||
type = lib.types.port;
|
||||
default = 5006;
|
||||
};
|
||||
doBackups = lib.mkEnableOption "";
|
||||
};
|
||||
|
||||
config = lib.mkIf cfg.enable {
|
||||
users = {
|
||||
users.actual = {
|
||||
isSystemUser = true;
|
||||
group = config.users.groups.actual.name;
|
||||
};
|
||||
groups.actual = { };
|
||||
};
|
||||
|
||||
systemd.services.actual.serviceConfig = {
|
||||
DynamicUser = lib.mkForce false;
|
||||
PrivateTmp = true;
|
||||
RemoveIPC = true;
|
||||
};
|
||||
|
||||
services.actual = {
|
||||
enable = true;
|
||||
settings = {
|
||||
hostname = "localhost";
|
||||
inherit (cfg) port;
|
||||
};
|
||||
};
|
||||
|
||||
custom = {
|
||||
services = {
|
||||
caddy.virtualHosts.${cfg.domain}.port = cfg.port;
|
||||
|
||||
restic.backups.actual = lib.mkIf cfg.doBackups {
|
||||
conflictingService = "actual.service";
|
||||
paths = [ dataDir ];
|
||||
};
|
||||
};
|
||||
|
||||
persistence.directories = [ dataDir ];
|
||||
};
|
||||
};
|
||||
}
|
||||
53
modules/nixos/web-services/filebrowser.nix
Normal file
53
modules/nixos/web-services/filebrowser.nix
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
{
|
||||
config,
|
||||
self,
|
||||
lib,
|
||||
...
|
||||
}:
|
||||
let
|
||||
cfg = config.custom.web-services.filebrowser;
|
||||
|
||||
dataDir = "/var/lib/filebrowser";
|
||||
in
|
||||
{
|
||||
options.custom.web-services.filebrowser = {
|
||||
enable = lib.mkEnableOption "";
|
||||
domain = lib.mkOption {
|
||||
type = lib.types.nonEmptyStr;
|
||||
default = "";
|
||||
};
|
||||
port = lib.mkOption {
|
||||
type = lib.types.port;
|
||||
default = 44093;
|
||||
};
|
||||
doBackups = lib.mkEnableOption "";
|
||||
};
|
||||
|
||||
config = lib.mkIf cfg.enable {
|
||||
assertions = lib.singleton {
|
||||
assertion = self.lib.isPrivateDomain cfg.domain;
|
||||
message = self.lib.mkUnprotectedMessage "Filebrowser";
|
||||
};
|
||||
|
||||
services.filebrowser = {
|
||||
enable = true;
|
||||
settings = {
|
||||
inherit (cfg) port;
|
||||
noauth = true;
|
||||
};
|
||||
};
|
||||
|
||||
custom = {
|
||||
services = {
|
||||
caddy.virtualHosts.${cfg.domain}.port = cfg.port;
|
||||
|
||||
restic.backups.filebrowser = lib.mkIf cfg.doBackups {
|
||||
conflictingService = "filebrowser.service";
|
||||
paths = [ dataDir ];
|
||||
};
|
||||
};
|
||||
|
||||
persistence.directories = [ dataDir ];
|
||||
};
|
||||
};
|
||||
}
|
||||
88
modules/nixos/web-services/forgejo.nix
Normal file
88
modules/nixos/web-services/forgejo.nix
Normal file
|
|
@ -0,0 +1,88 @@
|
|||
{ config, lib, ... }:
|
||||
let
|
||||
cfg = config.custom.web-services.forgejo;
|
||||
in
|
||||
{
|
||||
options.custom.web-services.forgejo = {
|
||||
enable = lib.mkEnableOption "";
|
||||
domain = lib.mkOption {
|
||||
type = lib.types.nonEmptyStr;
|
||||
default = "";
|
||||
};
|
||||
port = lib.mkOption {
|
||||
type = lib.types.port;
|
||||
default = 3003;
|
||||
};
|
||||
doBackups = lib.mkEnableOption "";
|
||||
};
|
||||
|
||||
config = lib.mkIf cfg.enable {
|
||||
users = {
|
||||
users.git = {
|
||||
isSystemUser = true;
|
||||
useDefaultShell = true;
|
||||
group = config.users.groups.git.name;
|
||||
home = config.services.forgejo.stateDir;
|
||||
};
|
||||
groups.git = { };
|
||||
};
|
||||
|
||||
services.forgejo = {
|
||||
enable = true;
|
||||
|
||||
user = "git";
|
||||
group = "git";
|
||||
|
||||
settings = {
|
||||
server = {
|
||||
DOMAIN = cfg.domain;
|
||||
ROOT_URL = "https://${cfg.domain}/";
|
||||
HTTP_PORT = cfg.port;
|
||||
LANDING_PAGE = "/SebastianStork";
|
||||
};
|
||||
service.DISABLE_REGISTRATION = true;
|
||||
session.PROVIDER = "db";
|
||||
mirror.DEFAULT_INTERVAL = "1h";
|
||||
other = {
|
||||
SHOW_FOOTER_VERSION = false;
|
||||
SHOW_FOOTER_TEMPLATE_LOAD_TIME = false;
|
||||
SHOW_FOOTER_POWERED_BY = false;
|
||||
};
|
||||
|
||||
cron.ENABLED = true;
|
||||
"cron.git_gc_repos".ENABLED = true;
|
||||
|
||||
repository.ENABLE_PUSH_CREATE_USER = true;
|
||||
|
||||
# https://forgejo.org/docs/latest/admin/recommendations
|
||||
database.SQLITE_JOURNAL_MODE = "WAL";
|
||||
cache = {
|
||||
ADAPTER = "twoqueue";
|
||||
HOST = lib.strings.toJSON {
|
||||
size = 100;
|
||||
recent_ratio = 0.25;
|
||||
ghost_ratio = 0.5;
|
||||
};
|
||||
};
|
||||
"repository.signing".DEFAULT_TRUST_MODEL = "committer";
|
||||
security.LOGIN_REMEMBER_DAYS = 365;
|
||||
};
|
||||
};
|
||||
|
||||
# Create user: `forgejo-user create --admin --username NAME --email EMAIL --password PASSWORD`
|
||||
environment.shellAliases.forgejo-user = "sudo --user=${config.services.forgejo.user} ${lib.getExe config.services.forgejo.package} admin user --config /var/lib/forgejo/custom/conf/app.ini";
|
||||
|
||||
custom = {
|
||||
services = {
|
||||
caddy.virtualHosts.${cfg.domain}.port = cfg.port;
|
||||
|
||||
restic.backups.forgejo = lib.mkIf cfg.doBackups {
|
||||
conflictingService = "forgejo.service";
|
||||
paths = [ config.services.forgejo.stateDir ];
|
||||
};
|
||||
};
|
||||
|
||||
persistence.directories = [ config.services.forgejo.stateDir ];
|
||||
};
|
||||
};
|
||||
}
|
||||
54
modules/nixos/web-services/freshrss.nix
Normal file
54
modules/nixos/web-services/freshrss.nix
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
{
|
||||
config,
|
||||
self,
|
||||
lib,
|
||||
...
|
||||
}:
|
||||
let
|
||||
cfg = config.custom.web-services.freshrss;
|
||||
|
||||
inherit (config.services.freshrss) dataDir;
|
||||
in
|
||||
{
|
||||
options.custom.web-services.freshrss = {
|
||||
enable = lib.mkEnableOption "";
|
||||
domain = lib.mkOption {
|
||||
type = lib.types.nonEmptyStr;
|
||||
default = "";
|
||||
};
|
||||
port = lib.mkOption {
|
||||
type = lib.types.port;
|
||||
default = 22055;
|
||||
};
|
||||
doBackups = lib.mkEnableOption "";
|
||||
};
|
||||
|
||||
config = lib.mkIf cfg.enable {
|
||||
assertions = lib.singleton {
|
||||
assertion = self.lib.isPrivateDomain cfg.domain;
|
||||
message = self.lib.mkUnprotectedMessage "FreshRSS";
|
||||
};
|
||||
|
||||
services.freshrss = {
|
||||
enable = true;
|
||||
baseUrl = "https://${cfg.domain}";
|
||||
webserver = "caddy";
|
||||
virtualHost = ":${toString cfg.port}";
|
||||
defaultUser = "seb";
|
||||
authType = "none";
|
||||
};
|
||||
|
||||
custom = {
|
||||
services = {
|
||||
caddy.virtualHosts.${cfg.domain}.port = cfg.port;
|
||||
|
||||
restic.backups.freshrss = lib.mkIf cfg.doBackups {
|
||||
conflictingService = "freshrss-updater.service";
|
||||
paths = [ dataDir ];
|
||||
};
|
||||
};
|
||||
|
||||
persistence.directories = [ dataDir ];
|
||||
};
|
||||
};
|
||||
}
|
||||
212
modules/nixos/web-services/gatus.nix
Normal file
212
modules/nixos/web-services/gatus.nix
Normal file
|
|
@ -0,0 +1,212 @@
|
|||
{
|
||||
config,
|
||||
lib,
|
||||
allHosts,
|
||||
...
|
||||
}:
|
||||
let
|
||||
cfg = config.custom.web-services.gatus;
|
||||
dataDir = "/var/lib/gatus";
|
||||
in
|
||||
{
|
||||
options.custom.web-services.gatus = {
|
||||
enable = lib.mkEnableOption "";
|
||||
domain = lib.mkOption {
|
||||
type = lib.types.nonEmptyStr;
|
||||
default = "";
|
||||
};
|
||||
port = lib.mkOption {
|
||||
type = lib.types.port;
|
||||
default = 8080;
|
||||
};
|
||||
ntfyUrl = lib.mkOption {
|
||||
type = lib.types.nonEmptyStr;
|
||||
default = "https://${config.custom.web-services.ntfy.domain}";
|
||||
};
|
||||
generateDefaultEndpoints = lib.mkEnableOption "";
|
||||
endpoints = lib.mkOption {
|
||||
type = lib.types.attrsOf (
|
||||
lib.types.submodule (
|
||||
{ name, ... }:
|
||||
{
|
||||
options = {
|
||||
enable = lib.mkEnableOption "" // {
|
||||
default = true;
|
||||
};
|
||||
name = lib.mkOption {
|
||||
type = lib.types.nonEmptyStr;
|
||||
default = name;
|
||||
};
|
||||
group = lib.mkOption {
|
||||
type = lib.types.nonEmptyStr;
|
||||
default = "";
|
||||
};
|
||||
protocol = lib.mkOption {
|
||||
type = lib.types.nonEmptyStr;
|
||||
default = "https";
|
||||
};
|
||||
domain = lib.mkOption {
|
||||
type = lib.types.nonEmptyStr;
|
||||
default = "";
|
||||
};
|
||||
path = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
default = "";
|
||||
};
|
||||
interval = lib.mkOption {
|
||||
type = lib.types.nonEmptyStr;
|
||||
default = "30s";
|
||||
};
|
||||
extraConditions = lib.mkOption {
|
||||
type = lib.types.listOf lib.types.nonEmptyStr;
|
||||
default = [ ];
|
||||
};
|
||||
enableAlerts = lib.mkEnableOption "" // {
|
||||
default = true;
|
||||
};
|
||||
};
|
||||
}
|
||||
)
|
||||
);
|
||||
default = { };
|
||||
};
|
||||
};
|
||||
|
||||
config = lib.mkIf cfg.enable {
|
||||
sops = {
|
||||
secrets."healthchecks/ping-key" = { };
|
||||
templates."gatus.env" = {
|
||||
content = "HEALTHCHECKS_PING_KEY=${config.sops.placeholder."healthchecks/ping-key"}";
|
||||
owner = config.users.users.gatus.name;
|
||||
restartUnits = [ "gatus.service" ];
|
||||
};
|
||||
};
|
||||
|
||||
users = {
|
||||
users.gatus = {
|
||||
isSystemUser = true;
|
||||
group = config.users.groups.gatus.name;
|
||||
};
|
||||
groups.gatus = { };
|
||||
};
|
||||
|
||||
systemd.services.gatus.serviceConfig = {
|
||||
DynamicUser = lib.mkForce false;
|
||||
ProtectSystem = "strict";
|
||||
ProtectHome = "read-only";
|
||||
PrivateTmp = true;
|
||||
RemoveIPC = true;
|
||||
};
|
||||
|
||||
services.gatus = {
|
||||
enable = true;
|
||||
environmentFile = config.sops.templates."gatus.env".path;
|
||||
|
||||
settings = {
|
||||
web = {
|
||||
address = "localhost";
|
||||
inherit (cfg) port;
|
||||
};
|
||||
storage = {
|
||||
type = "sqlite";
|
||||
path = "${dataDir}/data.db";
|
||||
};
|
||||
connectivity.checker.target = "1.1.1.1:53"; # Cloudflare DNS
|
||||
alerting.ntfy = {
|
||||
topic = "uptime";
|
||||
url = cfg.ntfyUrl;
|
||||
click = "https://${cfg.domain}";
|
||||
default-alert = {
|
||||
enable = true;
|
||||
failure-threshold = 8;
|
||||
success-threshold = 4;
|
||||
send-on-resolved = true;
|
||||
};
|
||||
overrides = lib.singleton {
|
||||
group = config.networking.hostName;
|
||||
topic = "splitleaf";
|
||||
url = "https://ntfy.sh";
|
||||
default-alert = {
|
||||
failure-threshold = 4;
|
||||
success-threshold = 2;
|
||||
};
|
||||
};
|
||||
};
|
||||
ui.default-sort-by = "group";
|
||||
maintenance = {
|
||||
start = "03:00";
|
||||
duration = "1h";
|
||||
timezone = "Europe/Berlin";
|
||||
};
|
||||
|
||||
endpoints =
|
||||
let
|
||||
mkEndpoint = endpoint: {
|
||||
inherit (endpoint) name group interval;
|
||||
url = "${endpoint.protocol}://${endpoint.domain}${endpoint.path}";
|
||||
alerts = lib.mkIf endpoint.enableAlerts [ { type = "ntfy"; } ];
|
||||
ssh = lib.mkIf (endpoint.protocol == "ssh") {
|
||||
username = "";
|
||||
password = "";
|
||||
};
|
||||
conditions = lib.concatLists [
|
||||
endpoint.extraConditions
|
||||
(lib.optional (lib.elem endpoint.protocol [
|
||||
"http"
|
||||
"https"
|
||||
]) "[STATUS] == 200")
|
||||
(lib.optional (lib.elem endpoint.protocol [
|
||||
"tcp"
|
||||
"ssh"
|
||||
"icmp"
|
||||
]) "[CONNECTED] == true")
|
||||
];
|
||||
};
|
||||
in
|
||||
cfg.endpoints |> lib.attrValues |> lib.filter (endpoint: endpoint.enable) |> lib.map mkEndpoint;
|
||||
};
|
||||
};
|
||||
|
||||
systemd.services.gatus.environment.GATUS_DELAY_START_SECONDS = "5";
|
||||
|
||||
custom = {
|
||||
web-services.gatus.endpoints =
|
||||
let
|
||||
defaultEndpoints =
|
||||
allHosts
|
||||
|> lib.mapAttrs (
|
||||
_: host:
|
||||
host.config.custom.services.caddy.virtualHosts |> lib.attrValues |> lib.map (vHost: vHost.domain)
|
||||
)
|
||||
|> lib.concatMapAttrs (
|
||||
hostName: domains:
|
||||
domains
|
||||
|> lib.filter (domain: domain != cfg.domain)
|
||||
|> lib.map (
|
||||
domain:
|
||||
lib.nameValuePair domain {
|
||||
inherit domain;
|
||||
group = hostName;
|
||||
}
|
||||
)
|
||||
|> lib.listToAttrs
|
||||
);
|
||||
in
|
||||
lib.mkIf cfg.generateDefaultEndpoints (
|
||||
defaultEndpoints
|
||||
// {
|
||||
"healthchecks.io" = {
|
||||
group = "external";
|
||||
domain = "hc-ping.com";
|
||||
path = "/\${HEALTHCHECKS_PING_KEY}/${config.networking.hostName}-gatus-uptime?create=1";
|
||||
interval = "2h";
|
||||
};
|
||||
}
|
||||
);
|
||||
|
||||
services.caddy.virtualHosts.${cfg.domain}.port = cfg.port;
|
||||
|
||||
persistence.directories = [ dataDir ];
|
||||
};
|
||||
};
|
||||
}
|
||||
180
modules/nixos/web-services/grafana.nix
Normal file
180
modules/nixos/web-services/grafana.nix
Normal file
|
|
@ -0,0 +1,180 @@
|
|||
{
|
||||
config,
|
||||
pkgs,
|
||||
lib,
|
||||
...
|
||||
}:
|
||||
let
|
||||
cfg = config.custom.web-services.grafana;
|
||||
in
|
||||
{
|
||||
options.custom.web-services.grafana = {
|
||||
enable = lib.mkEnableOption "";
|
||||
domain = lib.mkOption {
|
||||
type = lib.types.nonEmptyStr;
|
||||
default = "";
|
||||
};
|
||||
port = lib.mkOption {
|
||||
type = lib.types.port;
|
||||
default = 3000;
|
||||
};
|
||||
datasources = {
|
||||
prometheus = {
|
||||
enable = lib.mkEnableOption "" // {
|
||||
default = config.custom.web-services.victoriametrics.enable;
|
||||
};
|
||||
url = lib.mkOption {
|
||||
type = lib.types.nonEmptyStr;
|
||||
default = "https://${config.custom.web-services.victoriametrics.domain}";
|
||||
};
|
||||
};
|
||||
victoriametrics = {
|
||||
enable = lib.mkEnableOption "" // {
|
||||
default = config.custom.web-services.victoriametrics.enable;
|
||||
};
|
||||
url = lib.mkOption {
|
||||
type = lib.types.nonEmptyStr;
|
||||
default = "https://${config.custom.web-services.victoriametrics.domain}";
|
||||
};
|
||||
};
|
||||
victorialogs = {
|
||||
enable = lib.mkEnableOption "" // {
|
||||
default = config.custom.web-services.victorialogs.enable;
|
||||
};
|
||||
url = lib.mkOption {
|
||||
type = lib.types.nonEmptyStr;
|
||||
default = "https://${config.custom.web-services.victorialogs.domain}";
|
||||
};
|
||||
};
|
||||
};
|
||||
dashboards = {
|
||||
nodeExporter.enable = lib.mkEnableOption "" // {
|
||||
default = true;
|
||||
};
|
||||
victoriametrics.enable = lib.mkEnableOption "" // {
|
||||
default = config.custom.web-services.victoriametrics.enable;
|
||||
};
|
||||
victorialogs.enable = lib.mkEnableOption "" // {
|
||||
default = config.custom.web-services.victorialogs.enable;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
config = lib.mkIf cfg.enable {
|
||||
sops.secrets."grafana/admin-password" = {
|
||||
owner = config.users.users.grafana.name;
|
||||
restartUnits = [ "grafana.service" ];
|
||||
};
|
||||
|
||||
services.grafana = {
|
||||
enable = true;
|
||||
|
||||
settings = {
|
||||
server = {
|
||||
inherit (cfg) domain;
|
||||
http_port = cfg.port;
|
||||
enforce_domain = true;
|
||||
enable_gzip = true;
|
||||
};
|
||||
security.admin_password = "$__file{${config.sops.secrets."grafana/admin-password".path}}";
|
||||
users.default_theme = "system";
|
||||
analytics.reporting_enabled = false;
|
||||
};
|
||||
|
||||
provision = {
|
||||
enable = true;
|
||||
|
||||
dashboards.settings = {
|
||||
providers = lib.singleton {
|
||||
name = "Dashboards";
|
||||
options.path = "/etc/grafana-dashboards";
|
||||
};
|
||||
};
|
||||
|
||||
datasources.settings = {
|
||||
prune = true;
|
||||
datasources = [
|
||||
(lib.mkIf cfg.datasources.prometheus.enable {
|
||||
name = "Prometheus";
|
||||
type = "prometheus";
|
||||
inherit (cfg.datasources.prometheus) url;
|
||||
isDefault = true;
|
||||
jsonData = {
|
||||
prometheusType = "Prometheus";
|
||||
prometheusVersion = "2.50.0";
|
||||
};
|
||||
})
|
||||
(lib.mkIf cfg.datasources.victoriametrics.enable {
|
||||
name = "VictoriaMetrics";
|
||||
type = "victoriametrics-metrics-datasource";
|
||||
inherit (cfg.datasources.victoriametrics) url;
|
||||
isDefault = false;
|
||||
})
|
||||
(lib.mkIf cfg.datasources.victorialogs.enable {
|
||||
name = "VictoriaLogs";
|
||||
type = "victoriametrics-logs-datasource";
|
||||
inherit (cfg.datasources.victorialogs) url;
|
||||
isDefault = false;
|
||||
})
|
||||
];
|
||||
};
|
||||
};
|
||||
|
||||
declarativePlugins =
|
||||
let
|
||||
plugins = pkgs.grafanaPlugins;
|
||||
in
|
||||
[
|
||||
(lib.optional cfg.datasources.victoriametrics.enable plugins.victoriametrics-metrics-datasource)
|
||||
(lib.optional cfg.datasources.victorialogs.enable plugins.victoriametrics-logs-datasource)
|
||||
]
|
||||
|> lib.concatLists;
|
||||
};
|
||||
|
||||
environment.etc = {
|
||||
# https://grafana.com/grafana/dashboards/1860-node-exporter-full/
|
||||
"grafana-dashboards/node-exporter-full.json" = {
|
||||
enable = cfg.dashboards.nodeExporter.enable;
|
||||
source = pkgs.fetchurl {
|
||||
name = "node-exporter-full.json";
|
||||
url = "https://grafana.com/api/dashboards/1860/revisions/41/download";
|
||||
hash = "sha256-EywgxEayjwNIGDvSmA/S56Ld49qrTSbIYFpeEXBJlTs=";
|
||||
};
|
||||
};
|
||||
# https://grafana.com/grafana/dashboards/10229-victoriametrics-single-node/
|
||||
"grafana-dashboards/victoriametrics-single-node-patched.json" = {
|
||||
enable = cfg.dashboards.victoriametrics.enable;
|
||||
source =
|
||||
pkgs.fetchurl {
|
||||
name = "victoriametrics-single-node.json";
|
||||
url = "https://grafana.com/api/dashboards/10229/revisions/41/download";
|
||||
hash = "sha256-mwtah8A2w81WZjf5bUXoTJfS1R9UX+tua2PiDrBKJCQ=";
|
||||
}
|
||||
|> (
|
||||
src:
|
||||
pkgs.runCommand "victoriametrics-single-node-patched.json" { buildInputs = [ pkgs.gnused ]; } ''
|
||||
sed 's/victoriametrics-logs-//g' ${src} > $out
|
||||
''
|
||||
);
|
||||
};
|
||||
# https://grafana.com/grafana/dashboards/22084-victorialogs-single-node/
|
||||
"grafana-dashboards/victorialogs-single-node-patched.json" = {
|
||||
enable = cfg.dashboards.victorialogs.enable;
|
||||
source =
|
||||
pkgs.fetchurl {
|
||||
name = "victorialogs-single-node.json";
|
||||
url = "https://grafana.com/api/dashboards/22084/revisions/8/download";
|
||||
hash = "sha256-/a3Rbp/6oyiLBnQtGupyFZW+fIHQfkyKRRTyfofxVTM=";
|
||||
}
|
||||
|> (
|
||||
src:
|
||||
pkgs.runCommand "victorialogs-single-node-patched.json" { buildInputs = [ pkgs.gnused ]; } ''
|
||||
sed 's/victoria-logs-//g' ${src} > $out
|
||||
''
|
||||
);
|
||||
};
|
||||
};
|
||||
|
||||
custom.services.caddy.virtualHosts.${cfg.domain}.port = cfg.port;
|
||||
};
|
||||
}
|
||||
22
modules/nixos/web-services/it-tools.nix
Normal file
22
modules/nixos/web-services/it-tools.nix
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
{
|
||||
config,
|
||||
pkgs,
|
||||
lib,
|
||||
...
|
||||
}:
|
||||
let
|
||||
cfg = config.custom.web-services.it-tools;
|
||||
in
|
||||
{
|
||||
options.custom.web-services.it-tools = {
|
||||
enable = lib.mkEnableOption "";
|
||||
domain = lib.mkOption {
|
||||
type = lib.types.nonEmptyStr;
|
||||
default = "";
|
||||
};
|
||||
};
|
||||
|
||||
config = lib.mkIf cfg.enable {
|
||||
custom.services.caddy.virtualHosts.${cfg.domain}.files = "${pkgs.it-tools}/lib";
|
||||
};
|
||||
}
|
||||
62
modules/nixos/web-services/karakeep.nix
Normal file
62
modules/nixos/web-services/karakeep.nix
Normal file
|
|
@ -0,0 +1,62 @@
|
|||
{ config, lib, ... }:
|
||||
let
|
||||
cfg = config.custom.web-services.karakeep;
|
||||
in
|
||||
{
|
||||
options.custom.web-services.karakeep = {
|
||||
enable = lib.mkEnableOption "";
|
||||
domain = lib.mkOption {
|
||||
type = lib.types.nonEmptyStr;
|
||||
default = "";
|
||||
};
|
||||
port = lib.mkOption {
|
||||
type = lib.types.port;
|
||||
default = 18195;
|
||||
};
|
||||
};
|
||||
|
||||
config = lib.mkIf cfg.enable {
|
||||
sops = {
|
||||
secrets."karakeep/openai-api-key" = { };
|
||||
templates."karakeep.env" = {
|
||||
content = "OPENAI_API_KEY=${config.sops.placeholder."karakeep/openai-api-key"}";
|
||||
owner = config.users.users.karakeep.name;
|
||||
restartUnits = [ "karakeep-web.service" ];
|
||||
};
|
||||
};
|
||||
|
||||
services.karakeep = {
|
||||
enable = true;
|
||||
environmentFile = config.sops.templates."karakeep.env".path;
|
||||
extraEnvironment = {
|
||||
PORT = toString cfg.port;
|
||||
DISABLE_NEW_RELEASE_CHECK = "true";
|
||||
OCR_LANGS = "eng,deu";
|
||||
};
|
||||
};
|
||||
|
||||
users = {
|
||||
users.meilisearch = {
|
||||
isSystemUser = true;
|
||||
group = config.users.groups.meilisearch.name;
|
||||
};
|
||||
groups.meilisearch = { };
|
||||
};
|
||||
|
||||
systemd.services.meilisearch.serviceConfig = {
|
||||
DynamicUser = lib.mkForce false;
|
||||
User = config.users.users.meilisearch.name;
|
||||
Group = config.users.groups.meilisearch.name;
|
||||
ReadWritePaths = lib.mkForce [ ];
|
||||
};
|
||||
|
||||
custom = {
|
||||
services.caddy.virtualHosts.${cfg.domain}.port = cfg.port;
|
||||
|
||||
persistence.directories = [
|
||||
"/var/lib/karakeep"
|
||||
"/var/lib/meilisearch"
|
||||
];
|
||||
};
|
||||
};
|
||||
}
|
||||
27
modules/nixos/web-services/networking-toolbox.nix
Normal file
27
modules/nixos/web-services/networking-toolbox.nix
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
{ config, lib, ... }:
|
||||
let
|
||||
cfg = config.custom.web-services.networking-toolbox;
|
||||
in
|
||||
{
|
||||
options.custom.web-services.networking-toolbox = {
|
||||
enable = lib.mkEnableOption "";
|
||||
domain = lib.mkOption {
|
||||
type = lib.types.nonEmptyStr;
|
||||
default = "";
|
||||
};
|
||||
port = lib.mkOption {
|
||||
type = lib.types.port;
|
||||
default = 4479;
|
||||
};
|
||||
};
|
||||
|
||||
config = lib.mkIf cfg.enable {
|
||||
virtualisation.oci-containers.containers.networking-toolbox = {
|
||||
image = "lissy93/networking-toolbox";
|
||||
ports = [ "127.0.0.1:${toString cfg.port}:3000" ];
|
||||
pull = "newer";
|
||||
};
|
||||
|
||||
custom.services.caddy.virtualHosts.${cfg.domain}.port = cfg.port;
|
||||
};
|
||||
}
|
||||
31
modules/nixos/web-services/ntfy.nix
Normal file
31
modules/nixos/web-services/ntfy.nix
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
{ config, lib, ... }:
|
||||
let
|
||||
cfg = config.custom.web-services.ntfy;
|
||||
in
|
||||
{
|
||||
options.custom.web-services.ntfy = {
|
||||
enable = lib.mkEnableOption "";
|
||||
domain = lib.mkOption {
|
||||
type = lib.types.nonEmptyStr;
|
||||
default = "";
|
||||
};
|
||||
port = lib.mkOption {
|
||||
type = lib.types.port;
|
||||
default = 2586;
|
||||
};
|
||||
};
|
||||
|
||||
config = lib.mkIf cfg.enable {
|
||||
services.ntfy-sh = {
|
||||
enable = true;
|
||||
settings = lib.mkForce {
|
||||
base-url = "https://${cfg.domain}";
|
||||
listen-http = "localhost:${toString cfg.port}";
|
||||
behind-proxy = true;
|
||||
web-root = "disable";
|
||||
};
|
||||
};
|
||||
|
||||
custom.services.caddy.virtualHosts.${cfg.domain}.port = cfg.port;
|
||||
};
|
||||
}
|
||||
90
modules/nixos/web-services/outline.nix
Normal file
90
modules/nixos/web-services/outline.nix
Normal file
|
|
@ -0,0 +1,90 @@
|
|||
{
|
||||
config,
|
||||
pkgs,
|
||||
lib,
|
||||
...
|
||||
}:
|
||||
let
|
||||
cfg = config.custom.web-services.outline;
|
||||
in
|
||||
{
|
||||
options.custom.web-services.outline = {
|
||||
enable = lib.mkEnableOption "";
|
||||
domain = lib.mkOption {
|
||||
type = lib.types.nonEmptyStr;
|
||||
default = "";
|
||||
};
|
||||
port = lib.mkOption {
|
||||
type = lib.types.port;
|
||||
default = 32886;
|
||||
};
|
||||
doBackups = lib.mkEnableOption "";
|
||||
};
|
||||
|
||||
config = lib.mkIf cfg.enable {
|
||||
sops.secrets."outline/gitlab-auth-secret" = {
|
||||
owner = config.users.users.outline.name;
|
||||
restartUnits = [ "outline.service" ];
|
||||
};
|
||||
|
||||
services.outline = {
|
||||
enable = true;
|
||||
publicUrl = "https://${cfg.domain}";
|
||||
inherit (cfg) port;
|
||||
forceHttps = false;
|
||||
storage.storageType = "local";
|
||||
|
||||
# See https://docs.getoutline.com/s/hosting/doc/rate-limiter-HSqErsUgXH
|
||||
rateLimiter = {
|
||||
enable = true;
|
||||
requests = 1000;
|
||||
};
|
||||
|
||||
# See https://docs.getoutline.com/s/hosting/doc/gitlab-GjNVvyv7vW
|
||||
oidcAuthentication =
|
||||
let
|
||||
baseURL = "https://code.fbi.h-da.de/oauth";
|
||||
in
|
||||
{
|
||||
clientId = "3153f617736d608c0c10ddc4dd83f16c6a1f1b92d7f09a0493a924d389244295";
|
||||
clientSecretFile = config.sops.secrets."outline/gitlab-auth-secret".path;
|
||||
authUrl = "${baseURL}/authorize";
|
||||
tokenUrl = "${baseURL}/token";
|
||||
userinfoUrl = "${baseURL}/userinfo";
|
||||
usernameClaim = "username";
|
||||
displayName = "GitLab";
|
||||
scopes = [
|
||||
"openid"
|
||||
"email"
|
||||
];
|
||||
};
|
||||
};
|
||||
|
||||
systemd.services.outline.enableStrictShellChecks = false;
|
||||
|
||||
custom =
|
||||
let
|
||||
dataDir = "/var/lib/outline";
|
||||
inherit (config.services.outline) user;
|
||||
in
|
||||
{
|
||||
services = {
|
||||
caddy.virtualHosts.${cfg.domain}.port = cfg.port;
|
||||
|
||||
restic.backups.outline = lib.mkIf cfg.doBackups {
|
||||
conflictingService = "outline.service";
|
||||
paths = [ dataDir ];
|
||||
extraConfig.backupPrepareCommand = ''
|
||||
${lib.getExe pkgs.sudo} --user=${user} ${lib.getExe' config.services.postgresql.package "pg_dump"} outline --format=custom --file=${dataDir}/db.dump
|
||||
'';
|
||||
restoreCommand.postRestore = "sudo --user=${user} pg_restore --clean --if-exists --dbname outline ${dataDir}/db.dump";
|
||||
};
|
||||
};
|
||||
|
||||
persistence.directories = [
|
||||
dataDir
|
||||
config.services.postgresql.dataDir
|
||||
];
|
||||
};
|
||||
};
|
||||
}
|
||||
32
modules/nixos/web-services/personal-blog.nix
Normal file
32
modules/nixos/web-services/personal-blog.nix
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
{
|
||||
config,
|
||||
pkgs,
|
||||
lib,
|
||||
...
|
||||
}:
|
||||
let
|
||||
cfg = config.custom.web-services.personal-blog;
|
||||
|
||||
dataDir = "/var/lib/personal-blog";
|
||||
in
|
||||
{
|
||||
options.custom.web-services.personal-blog = {
|
||||
enable = lib.mkEnableOption "";
|
||||
domain = lib.mkOption {
|
||||
type = lib.types.nonEmptyStr;
|
||||
default = "";
|
||||
};
|
||||
};
|
||||
|
||||
config = lib.mkIf cfg.enable {
|
||||
systemd.services.generate-blog = {
|
||||
serviceConfig.Type = "oneshot";
|
||||
wantedBy = [ "multi-user.target" ];
|
||||
startAt = "*:0/5"; # Every 5 minutes
|
||||
path = [ pkgs.nix ];
|
||||
script = "nix build github:SebastianStork/blog --out-link ${dataDir} --refresh";
|
||||
};
|
||||
|
||||
custom.services.caddy.virtualHosts.${cfg.domain}.files = dataDir;
|
||||
};
|
||||
}
|
||||
42
modules/nixos/web-services/privatebin.nix
Normal file
42
modules/nixos/web-services/privatebin.nix
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
{ config, lib, ... }:
|
||||
let
|
||||
cfg = config.custom.web-services.privatebin;
|
||||
in
|
||||
{
|
||||
options.custom.web-services.privatebin = {
|
||||
enable = lib.mkEnableOption "";
|
||||
domain = lib.mkOption {
|
||||
type = lib.types.nonEmptyStr;
|
||||
default = "";
|
||||
};
|
||||
port = lib.mkOption {
|
||||
type = lib.types.port;
|
||||
default = 61009;
|
||||
};
|
||||
branding.name = lib.mkOption {
|
||||
type = lib.types.nonEmptyStr;
|
||||
default = "PrivateBin";
|
||||
};
|
||||
};
|
||||
|
||||
config = lib.mkIf cfg.enable {
|
||||
services = {
|
||||
privatebin = {
|
||||
enable = true;
|
||||
enableNginx = true;
|
||||
virtualHost = "privatebin";
|
||||
settings.main = {
|
||||
basepath = "https://${cfg.domain}";
|
||||
inherit (cfg.branding) name;
|
||||
};
|
||||
};
|
||||
|
||||
nginx.virtualHosts.privatebin.listen = lib.singleton {
|
||||
addr = "localhost";
|
||||
inherit (cfg) port;
|
||||
};
|
||||
};
|
||||
|
||||
custom.services.caddy.virtualHosts.${cfg.domain}.port = cfg.port;
|
||||
};
|
||||
}
|
||||
114
modules/nixos/web-services/radicale.nix
Normal file
114
modules/nixos/web-services/radicale.nix
Normal file
|
|
@ -0,0 +1,114 @@
|
|||
{
|
||||
config,
|
||||
inputs,
|
||||
pkgs,
|
||||
lib,
|
||||
...
|
||||
}:
|
||||
let
|
||||
cfg = config.custom.web-services.radicale;
|
||||
dataDir = config.services.radicale.settings.storage.filesystem_folder;
|
||||
|
||||
initScript =
|
||||
let
|
||||
gitignore = pkgs.writeText "radicale-collection-gitignore" ''
|
||||
.Radicale.cache
|
||||
.Radicale.lock
|
||||
.Radicale.tmp-*
|
||||
'';
|
||||
in
|
||||
pkgs.writeShellApplication {
|
||||
name = "radicale-git-init";
|
||||
runtimeInputs = [ pkgs.git ];
|
||||
text = ''
|
||||
cd ${dataDir}
|
||||
|
||||
if [[ ! -e .git ]]; then
|
||||
git init --initial-branch main
|
||||
fi
|
||||
|
||||
git config user.name "Radicale"
|
||||
git config user.email "radicale@${config.networking.hostName}"
|
||||
|
||||
cat ${gitignore} > .gitignore
|
||||
git add .gitignore
|
||||
if ! git diff --cached --quiet; then
|
||||
git commit --message "Update .gitignore"
|
||||
fi
|
||||
'';
|
||||
};
|
||||
|
||||
hookScript = pkgs.writeShellApplication {
|
||||
name = "radicale-git-hook";
|
||||
runtimeInputs = [
|
||||
pkgs.git
|
||||
pkgs.gawk
|
||||
(pkgs.python3.withPackages (python-pkgs: [
|
||||
python-pkgs.python-dateutil
|
||||
python-pkgs.vobject
|
||||
]))
|
||||
];
|
||||
text = ''
|
||||
username="$1"
|
||||
create_birthday_calendar="${inputs.radicale-birthday-calendar}/create_birthday_calendar.py"
|
||||
|
||||
git status --porcelain | awk '{print $2}' | python3 $create_birthday_calendar
|
||||
|
||||
git add -A
|
||||
if ! git diff --cached --quiet; then
|
||||
git commit --message "Changes by $username"
|
||||
fi
|
||||
'';
|
||||
};
|
||||
in
|
||||
{
|
||||
options.custom.web-services.radicale = {
|
||||
enable = lib.mkEnableOption "";
|
||||
domain = lib.mkOption {
|
||||
type = lib.types.nonEmptyStr;
|
||||
default = "";
|
||||
};
|
||||
port = lib.mkOption {
|
||||
type = lib.types.port;
|
||||
default = 5232;
|
||||
};
|
||||
doBackups = lib.mkEnableOption "";
|
||||
};
|
||||
|
||||
config = lib.mkIf cfg.enable {
|
||||
sops.secrets."radicale/htpasswd" = {
|
||||
owner = config.users.users.radicale.name;
|
||||
restartUnits = [ "radicale.service" ];
|
||||
};
|
||||
|
||||
services.radicale = {
|
||||
enable = true;
|
||||
settings = {
|
||||
server.hosts = "localhost:${toString cfg.port}";
|
||||
auth = {
|
||||
type = "htpasswd";
|
||||
htpasswd_filename = config.sops.secrets."radicale/htpasswd".path;
|
||||
htpasswd_encryption = "bcrypt";
|
||||
};
|
||||
storage.filesystem_folder = "/var/lib/radicale/collections";
|
||||
|
||||
storage.hook = "${lib.getExe hookScript} %(user)s";
|
||||
};
|
||||
};
|
||||
|
||||
systemd.services.radicale.serviceConfig.ExecStartPre = lib.getExe initScript;
|
||||
|
||||
custom = {
|
||||
services = {
|
||||
caddy.virtualHosts.${cfg.domain}.port = cfg.port;
|
||||
|
||||
restic.backups.radicale = lib.mkIf cfg.doBackups {
|
||||
conflictingService = "radicale.service";
|
||||
paths = [ dataDir ];
|
||||
};
|
||||
};
|
||||
|
||||
persistence.directories = [ dataDir ];
|
||||
};
|
||||
};
|
||||
}
|
||||
31
modules/nixos/web-services/screego.nix
Normal file
31
modules/nixos/web-services/screego.nix
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
{ config, lib, ... }:
|
||||
let
|
||||
cfg = config.custom.web-services.screego;
|
||||
in
|
||||
{
|
||||
options.custom.web-services.screego = {
|
||||
enable = lib.mkEnableOption "";
|
||||
domain = lib.mkOption {
|
||||
type = lib.types.nonEmptyStr;
|
||||
default = "";
|
||||
};
|
||||
port = lib.mkOption {
|
||||
type = lib.types.port;
|
||||
default = 5050;
|
||||
};
|
||||
};
|
||||
|
||||
config = lib.mkIf cfg.enable {
|
||||
services.screego = {
|
||||
enable = true;
|
||||
openFirewall = true;
|
||||
settings = {
|
||||
SCREEGO_EXTERNAL_IP = config.custom.networking.underlay.address;
|
||||
SCREEGO_SERVER_ADDRESS = "127.0.0.1:${toString cfg.port}";
|
||||
SCREEGO_TURN_ADDRESS = "${config.custom.networking.underlay.address}:3478";
|
||||
};
|
||||
};
|
||||
|
||||
custom.services.caddy.virtualHosts.${cfg.domain}.port = cfg.port;
|
||||
};
|
||||
}
|
||||
45
modules/nixos/web-services/stirling-pdf.nix
Normal file
45
modules/nixos/web-services/stirling-pdf.nix
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
{ config, lib, ... }:
|
||||
let
|
||||
cfg = config.custom.web-services.stirling-pdf;
|
||||
in
|
||||
{
|
||||
options.custom.web-services.stirling-pdf = {
|
||||
enable = lib.mkEnableOption "";
|
||||
domain = lib.mkOption {
|
||||
type = lib.types.nonEmptyStr;
|
||||
default = "";
|
||||
};
|
||||
port = lib.mkOption {
|
||||
type = lib.types.port;
|
||||
default = 56191;
|
||||
};
|
||||
branding = {
|
||||
name = lib.mkOption {
|
||||
type = lib.types.nonEmptyStr;
|
||||
default = "Stirling PDF";
|
||||
};
|
||||
description = lib.mkOption {
|
||||
type = lib.types.nonEmptyStr;
|
||||
default = "Your locally hosted one-stop-shop for all your PDF needs.";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
config = lib.mkIf cfg.enable {
|
||||
services.stirling-pdf = {
|
||||
enable = true;
|
||||
environment = {
|
||||
SERVER_ADDRESS = "127.0.0.1";
|
||||
SERVER_PORT = cfg.port;
|
||||
SYSTEM_ENABLEANALYTICS = "false";
|
||||
LANGS = "de_DE";
|
||||
|
||||
UI_APPNAME = cfg.branding.name;
|
||||
UI_APPNAVBARNAME = cfg.branding.name;
|
||||
UI_HOMEDESCRIPTION = cfg.branding.description;
|
||||
};
|
||||
};
|
||||
|
||||
custom.services.caddy.virtualHosts.${cfg.domain}.port = cfg.port;
|
||||
};
|
||||
}
|
||||
44
modules/nixos/web-services/victorialogs.nix
Normal file
44
modules/nixos/web-services/victorialogs.nix
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
{ config, lib, ... }:
|
||||
let
|
||||
cfg = config.custom.web-services.victorialogs;
|
||||
in
|
||||
{
|
||||
options.custom.web-services.victorialogs = {
|
||||
enable = lib.mkEnableOption "";
|
||||
domain = lib.mkOption {
|
||||
type = lib.types.nonEmptyStr;
|
||||
default = "";
|
||||
};
|
||||
port = lib.mkOption {
|
||||
type = lib.types.port;
|
||||
default = 9428;
|
||||
};
|
||||
};
|
||||
|
||||
config = lib.mkIf cfg.enable {
|
||||
users = {
|
||||
users.victorialogs = {
|
||||
isSystemUser = true;
|
||||
group = config.users.groups.victoriametrics.name;
|
||||
};
|
||||
groups.victorialogs = { };
|
||||
};
|
||||
|
||||
systemd.services.victorialogs.serviceConfig = {
|
||||
DynamicUser = lib.mkForce false;
|
||||
User = config.users.users.victorialogs.name;
|
||||
Group = config.users.groups.victorialogs.name;
|
||||
};
|
||||
|
||||
services.victorialogs = {
|
||||
enable = true;
|
||||
listenAddress = "localhost:${toString cfg.port}";
|
||||
};
|
||||
|
||||
custom = {
|
||||
services.caddy.virtualHosts.${cfg.domain}.port = cfg.port;
|
||||
|
||||
persistence.directories = [ "/var/lib/${config.services.victorialogs.stateDir}" ];
|
||||
};
|
||||
};
|
||||
}
|
||||
49
modules/nixos/web-services/victoriametrics.nix
Normal file
49
modules/nixos/web-services/victoriametrics.nix
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
{ config, lib, ... }:
|
||||
let
|
||||
cfg = config.custom.web-services.victoriametrics;
|
||||
in
|
||||
{
|
||||
options.custom.web-services.victoriametrics = {
|
||||
enable = lib.mkEnableOption "";
|
||||
domain = lib.mkOption {
|
||||
type = lib.types.nonEmptyStr;
|
||||
default = "";
|
||||
};
|
||||
port = lib.mkOption {
|
||||
type = lib.types.port;
|
||||
default = 8428;
|
||||
};
|
||||
};
|
||||
|
||||
config = lib.mkIf cfg.enable {
|
||||
users = {
|
||||
users.victoriametrics = {
|
||||
isSystemUser = true;
|
||||
group = config.users.groups.victoriametrics.name;
|
||||
};
|
||||
groups.victoriametrics = { };
|
||||
};
|
||||
|
||||
systemd.services.victoriametrics.serviceConfig = {
|
||||
DynamicUser = lib.mkForce false;
|
||||
User = config.users.users.victoriametrics.name;
|
||||
Group = config.users.groups.victoriametrics.name;
|
||||
};
|
||||
|
||||
services.victoriametrics = {
|
||||
enable = true;
|
||||
listenAddress = "localhost:${toString cfg.port}";
|
||||
extraOptions = [
|
||||
"-selfScrapeInterval=15s"
|
||||
"-selfScrapeJob=victoriametrics"
|
||||
"-selfScrapeInstance=${config.networking.hostName}"
|
||||
];
|
||||
};
|
||||
|
||||
custom = {
|
||||
services.caddy.virtualHosts.${cfg.domain}.port = cfg.port;
|
||||
|
||||
persistence.directories = [ "/var/lib/${config.services.victoriametrics.stateDir}" ];
|
||||
};
|
||||
};
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue