feat: infinitely expansible microvms

This commit is contained in:
Nick 2025-12-09 03:46:57 -06:00
parent a92229390e
commit 4c767d369f
55 changed files with 1089 additions and 1015 deletions

View file

@ -27,6 +27,18 @@ let
}) (builtins.genList (i: i) numOptions)
);
genOptionsSubmodule =
typeConfig: prefix:
builtins.listToAttrs (
map (i: {
name = "${prefix}${toString i}";
value = lib.mkOption {
type = typeConfig;
default = { };
};
}) (builtins.genList (i: i) numOptions)
);
mkOptionsFromDir =
path:
builtins.listToAttrs (
@ -54,30 +66,49 @@ let
};
};
interfaceConfig = {
domain = stringType;
email = stringType;
ip = stringType;
subdomain = stringType;
wireguard = stringType;
microvm = {
gate = stringType;
id = stringType;
idUser = stringType;
mac = stringType;
macUser = stringType;
num = intType;
ssh = intType;
};
ssl = {
cert = stringType;
key = stringType;
path = stringType;
};
paths = {
mntPaths = genOptions stringType "path";
secretPaths = genOptions stringType "path";
varPaths = genOptions stringType "path";
interfaceConfig = lib.types.submodule {
options = {
domain = stringType;
email = stringType;
ip = stringType;
subdomain = stringType;
wireguard = stringType;
microvm = lib.mkOption {
type = lib.types.submodule {
options = {
gate = stringType;
id = stringType;
idUser = stringType;
mac = stringType;
macUser = stringType;
num = intType;
ip = stringType; # Add this if it's missing
ssh = intType;
port = intType;
};
};
default = { };
};
ssl = lib.mkOption {
type = lib.types.submodule {
options = {
cert = stringType;
key = stringType;
path = stringType;
};
};
default = { };
};
paths = lib.mkOption {
type = lib.types.submodule {
options = {
mntPaths = genOptions stringType "path";
secretPaths = genOptions stringType "path";
varPaths = genOptions stringType "path";
};
};
default = { };
};
};
};
@ -90,7 +121,7 @@ let
dns = genOptions stringType "provider";
ports = genOptions intType "port";
addresses = genOptions stringType "address";
interfaces = genOptions interfaceConfig "interface";
interfaces = genOptionsSubmodule interfaceConfig "interface";
};
};

View file

@ -33,7 +33,7 @@ in
ssl = "${sslPath}/${domain}";
in
{
domains = domain;
domain = domain;
subdomain = subdomain;
email = "${emailNoReply}@${domain1}";
microvm = {

View file

@ -23,7 +23,7 @@ in
ports = {
port0 = 80;
};
interface = {
interfaces = {
interface0 =
let
subdomain = "social";
@ -32,7 +32,7 @@ in
ssl = "${sslPath}/${domain}";
in
{
domains = domain;
domain = domain;
subdomain = subdomain;
email = "noreply@${domain1}";
microvm = {

View file

@ -61,7 +61,7 @@ in
path0 = "${varPath}/${name}";
};
mntPaths = {
path1 = "${mntPath}/${name}/${world}";
path0 = "${mntPath}/${name}/${world}";
};
secretPaths = {
path0 = "${secretPath}/${name}";

View file

@ -23,7 +23,7 @@ in
port0 = 8088; # Open-WebUI (Ollama Front End)
port1 = 11434; # Ollama API
};
interface = {
interfaces = {
interface0 =
let
domain = "${name}.${domain0}";

View file

@ -23,7 +23,7 @@ in
ports = {
port0 = 9200;
};
interface = {
interfaces = {
interface0 =
let
domain = "${short}.${domain0}";

View file

@ -23,7 +23,7 @@ in
ports = {
port0 = 3030;
};
interface = {
interfaces = {
interface0 =
let
domain = "${short}.${domain0}";

View file

@ -23,7 +23,7 @@ in
ports = {
port0 = 3030;
};
interface = {
interfaces = {
interface0 =
let
domain = "${short}.${domain0}";

View file

@ -24,7 +24,7 @@ in
ports = {
port0 = 8085;
};
interface = {
interfaces = {
interface0 =
let
domain = "${name}.${domain0}";

View file

@ -16,7 +16,7 @@ in
name
"blog"
];
interface = {
interfaces = {
interface0 =
let
ssl = "${sslPath}/${domain1}";

View file

@ -9,8 +9,6 @@ in
{
label = label;
name = name;
sops = {
};
ports = {
port0 = 53;
port1 = 51821;

View file

@ -11,7 +11,7 @@ in
{
label = label;
name = name;
interface = {
interfaces = {
interface0 = {
microvm = {
id = "vm-boonbot";

View file

@ -56,23 +56,19 @@ in
acme
caddy
comfyui
firefly-iii
forgejo
forgejoCeres
impermanence
jellyfin
jellyfinCeres
lix
mastodon
mastodonCeres
microvm
minecraft
ollama
opencloud1
projectSite
qbittorrent
minecraftCeres
opencloudCeres
websiteCeres
qbittorrentCeres
restic
sambaCeres
vaultwarden
website
zookeeper
zookeeperCeres
;
};
};
@ -83,14 +79,13 @@ in
impermanence
lix
microvm
nas
sambaEris
fireflyEris
opencloudEris
vaultwardenEris
syncthingEris
photoprismEris
;
inherit (modules.guests.firefly-iii) fireflyNick;
inherit (modules.guests.opencloud) opencloudNick;
inherit (modules.guests.photoprism) photoprismNick;
inherit (modules.guests.vaultwarden) vaultwardenNick;
inherit (modules.guests.syncthing) syncthingNick;
};
};

View file

@ -7,6 +7,7 @@ let
inherit (flake.config.people.users.${user0}) email;
inherit (flake.config.services) instances;
serviceCfg = instances.acme;
interfaceCfg = serviceCfg.interfaces.interface0;
dns0 = instances.web.dns.provider0;
dns1 = instances.web.dns.provider1;
in
@ -50,7 +51,7 @@ in
systemd = {
tmpfiles.rules = [
"Z ${serviceCfg.secretPaths.path0} 755 ${serviceCfg.name} ${serviceCfg.name} -"
"Z ${interfaceCfg.paths.secretPaths.path0} 755 ${serviceCfg.name} ${serviceCfg.name} -"
];
};
}

View file

@ -0,0 +1,11 @@
let
importList =
let
content = builtins.readDir ./.;
dirContent = builtins.filter (n: content.${n} == "directory") (builtins.attrNames content);
in
map (name: ./. + "/${name}") dirContent;
in
{
imports = importList;
}

View file

@ -7,7 +7,7 @@ let
inherit (flake.config.services) instances;
serviceCfg = instances.firefly-iii;
interfaceCfg = serviceCfg.intefaces.interface0;
interfaceCfg = serviceCfg.interfaces.interface0;
host = interfaceCfg.domain;
dns0 = instances.web.dns.provider0;
dns0Path = "dns/${dns0}";

View file

@ -24,7 +24,7 @@ in
virtualHosts = {
"${host0}" = {
extraConfig = ''
reverse_proxy ${interface0Cfg.interface.ip}:${toString serviceCfg.ports.port0} {
reverse_proxy ${interface0Cfg.microvm.ip}:${toString serviceCfg.ports.port0} {
header_up X-Real-IP {remote_host}
}
tls ${interface0Cfg.ssl.cert} ${interface0Cfg.ssl.key}

View file

@ -22,7 +22,7 @@ in
virtualHosts = {
"${host0}" = {
extraConfig = ''
reverse_proxy ${interface0Cfg.interface.ip}:${toString serviceCfg.ports.port0} {
reverse_proxy ${interface0Cfg.microvm.ip}:${toString serviceCfg.ports.port0} {
header_up X-Real-IP {remote_host}
}
tls ${interface0Cfg.ssl.cert} ${interface0Cfg.ssl.key}

View file

@ -18,7 +18,7 @@ in
enable = true;
};
tmpfiles.rules = [
systemd.tmpfiles.rules = [
"d /run/secrets/caddy 755 caddy caddy -"
"d /var/log/caddy 755 caddy caddy -"
];

View file

@ -1,11 +1,10 @@
let
inherit (import ./helpers.nix) labHelpers;
importList =
let
content = builtins.readDir ./.;
dirContent = builtins.filter (n: content.${n} == "directory") (builtins.attrNames content);
in
map (name: ./. + "/${name}" { inherit labHelpers; }) dirContent;
map (name: ./. + "/${name}") dirContent;
in
{
imports = importList;

View file

@ -1,50 +1,11 @@
{
config,
flake,
pkgs,
labHelpers,
...
}:
let
inherit (labHelpers) guestPath;
inherit (import ./config { inherit config flake pkgs; }) fireflyVM;
inherit (flake.config.people) user0;
inherit (flake.config.people.users.${user0}) email;
inherit (flake.config.services.instances) firefly-iii;
interface0Cfg = firefly-iii.interfaces.interface0;
importList =
let
content = builtins.readDir ./.;
dirContent = builtins.filter (n: content.${n} == "directory") (builtins.attrNames content);
in
map (name: ./. + "/${name}") dirContent;
in
{
fireflyNick = fireflyVM {
user = user0;
ip = interface0Cfg.microvm.ip;
mac = interface0Cfg.microvm.mac;
userMac = interface0Cfg.microvm.macUser;
ssh = interface0Cfg.microvm.ssh0;
host = interface0Cfg.domain;
mnt = guestPath user0;
owner = email.address2;
};
# fireflyStacie = fireflyVM {
# user = "stacie";
# ip = ipAddress (id1);
# mac = "02:00:00:00:${id1}:${id1}";
# userMac = "02:00:00:00:00:${id1}";
# ssh = fireflyEris.ssh1;
# host = "";
# mnt = guestPath "stacie";
# owner = "";
# };
# fireflyGarnet = fireflyVM {
# user = "garnet";
# ip = ipAddress (id2);
# mac = "02:00:00:00:${id2}:${id2}";
# userMac = "02:00:00:00:00:${id2}";
# ssh = fireflyEris.ssh2;
# mnt = guestPath "garnet";
# host = "";
# owner = "";
# };
imports = importList;
}

View file

@ -0,0 +1,27 @@
{
config,
flake,
pkgs,
...
}:
let
inherit (import ../../helpers.nix) labHelpers;
inherit (labHelpers) guestPath;
inherit (import ../config { inherit config flake pkgs; }) fireflyVM;
inherit (flake.config.people) user0;
inherit (flake.config.people.users.${user0}) email;
inherit (flake.config.services.instances) firefly-iii;
interface0Cfg = firefly-iii.interfaces.interface0;
fireflyNick = fireflyVM {
user = user0;
ip = interface0Cfg.microvm.ip;
mac = interface0Cfg.microvm.mac;
userMac = interface0Cfg.microvm.macUser;
ssh = interface0Cfg.microvm.ssh;
host = interface0Cfg.domain;
owner = email.address2;
mnt = guestPath user0;
};
in
fireflyNick

View file

@ -52,7 +52,7 @@ in
};
mailer = {
ENABLED = true;
SMTP_ADDR = smtpCfg.interface.interface1.domain;
SMTP_ADDR = smtpCfg.interfaces.interface1.domain;
FROM = smtpCfg.interfaces.interface1.email;
USER = smtpCfg.interfaces.interface1.email;
PROTOCOL = "smtp+starttls";

28
modules/nixos/homelab/guests/forgejo/default.nix Normal file → Executable file
View file

@ -1,25 +1,11 @@
{
flake,
pkgs,
labHelpers,
...
}:
let
inherit (labHelpers) mntPath;
inherit (import ./config { inherit flake pkgs; }) forgejoVM;
inherit (flake.config.people) user0;
inherit (flake.config.services) instances;
interface0Cfg = instances.forgejo.interfaces.interface0;
importList =
let
content = builtins.readDir ./.;
dirContent = builtins.filter (n: content.${n} == "directory") (builtins.attrNames content);
in
map (name: ./. + "/${name}") dirContent;
in
{
forgejoNick = forgejoVM {
user = user0;
ip = interface0Cfg.microvm.ip;
mac = interface0Cfg.microvm.mac;
userMac = interface0Cfg.microvm.macUser;
ssh = interface0Cfg.microvm.ssh;
mnt = mntPath;
host = interface0Cfg.domain;
};
imports = importList;
}

View file

@ -0,0 +1,25 @@
{
flake,
pkgs,
...
}:
let
inherit (import ../../helpers.nix) labHelpers;
inherit (labHelpers) mntPath;
inherit (import ../config { inherit flake pkgs; }) forgejoVM;
inherit (flake.config.people) user0;
inherit (flake.config.services) instances;
interface0Cfg = instances.forgejo.interfaces.interface0;
forgejoNick = forgejoVM {
user = user0;
ip = interface0Cfg.microvm.ip;
mac = interface0Cfg.microvm.mac;
userMac = interface0Cfg.microvm.macUser;
ssh = interface0Cfg.microvm.ssh;
mnt = mntPath;
host = interface0Cfg.domain;
};
in
forgejoNick

View file

@ -10,7 +10,7 @@ let
id = 993;
in
{
forgejoVM =
jellyfinVM =
{
user,
ip,

29
modules/nixos/homelab/guests/jellyfin/default.nix Normal file → Executable file
View file

@ -1,26 +1,11 @@
{
flake,
pkgs,
labHelpers,
...
}:
let
inherit (labHelpers) mntPath;
inherit (import ./config { inherit flake pkgs; }) jellyfinVM;
inherit (flake.config.people) user0;
inherit (flake.config.services) instances;
interface0Cfg = instances.jellyfin.interfaces.interface0;
importList =
let
content = builtins.readDir ./.;
dirContent = builtins.filter (n: content.${n} == "directory") (builtins.attrNames content);
in
map (name: ./. + "/${name}") dirContent;
in
{
jellyfinNick = jellyfinVM {
user = user0;
ip = interface0Cfg.microvm.ip;
mac = interface0Cfg.microvm.mac;
userMac = interface0Cfg.microvm.macUser;
ssh = interface0Cfg.microvm.ssh;
mnt = mntPath;
host = interface0Cfg.domain;
};
imports = importList;
}

View file

@ -0,0 +1,26 @@
{
flake,
config,
...
}:
let
inherit (import ../../helpers.nix) labHelpers;
inherit (labHelpers) mntPath;
inherit (import ../config { inherit flake config; }) jellyfinVM;
inherit (flake.config.people) user0;
inherit (flake.config.services) instances;
interface0Cfg = instances.jellyfin.interfaces.interface0;
jellyfinCeres = jellyfinVM {
user = user0;
ip = interface0Cfg.microvm.ip;
mac = interface0Cfg.microvm.mac;
userMac = interface0Cfg.microvm.macUser;
ssh = interface0Cfg.microvm.ssh;
mnt = mntPath;
host = interface0Cfg.domain;
};
in
jellyfinCeres

29
modules/nixos/homelab/guests/mastodon/default.nix Normal file → Executable file
View file

@ -1,26 +1,11 @@
{
flake,
pkgs,
labHelpers,
...
}:
let
inherit (labHelpers) mntPath;
inherit (import ./config { inherit flake pkgs; }) mastodonVM;
inherit (flake.config.people) user0;
inherit (flake.config.services) instances;
interface0Cfg = instances.mastodon.interfaces.interface0;
importList =
let
content = builtins.readDir ./.;
dirContent = builtins.filter (n: content.${n} == "directory") (builtins.attrNames content);
in
map (name: ./. + "/${name}") dirContent;
in
{
mastodonNick = mastodonVM {
user = user0;
ip = interface0Cfg.microvm.ip;
mac = interface0Cfg.microvm.mac;
userMac = interface0Cfg.microvm.macUser;
ssh = interface0Cfg.microvm.ssh;
mnt = mntPath;
host = interface0Cfg.domain;
};
imports = importList;
}

View file

@ -0,0 +1,37 @@
{
flake,
pkgs,
config,
lib,
...
}:
let
inherit (import ../../helpers.nix) labHelpers;
inherit (labHelpers) mntPath;
inherit
(import ../config {
inherit
flake
config
pkgs
lib
;
})
mastodonVM
;
inherit (flake.config.people) user0;
inherit (flake.config.services) instances;
interface0Cfg = instances.mastodon.interfaces.interface0;
mastodonNick = mastodonVM {
user = user0;
ip = interface0Cfg.microvm.ip;
mac = interface0Cfg.microvm.mac;
userMac = interface0Cfg.microvm.macUser;
ssh = interface0Cfg.microvm.ssh;
mnt = mntPath;
host = interface0Cfg.domain;
};
in
mastodonNick

View file

@ -1,107 +1,11 @@
{
flake,
pkgs,
labHelpers,
...
}:
let
inherit (labHelpers) mntPath;
inherit (import ./config { inherit flake pkgs; }) minecraftVM;
inherit (flake.config.services) instances;
inherit (flake.config.people) user0;
importList =
let
content = builtins.readDir ./.;
dirContent = builtins.filter (n: content.${n} == "directory") (builtins.attrNames content);
in
map (name: ./. + "/${name}") dirContent;
in
{
minecraftNick01 =
let
interfaceCfg = instances.minecraft.interfaces.interface0;
in
minecraftVM {
user = user0;
ip = interfaceCfg.microvm.ip;
mac = interfaceCfg.microvm.mac;
userMac = interfaceCfg.microvm.macUser;
ssh = interfaceCfg.microvm.ssh;
port = interfaceCfg.microvm.port;
mnt = mntPath;
worldNumber = "01";
config = {
allow-flight = false;
allow-nether = true;
difficulty = 2;
enable-command-block = false;
enable-rcon = true;
enable-status = true;
force-gamemode = true;
gamemode = 0;
generate-structures = true;
hardcore = false;
hide-online-players = false;
level-name = "Brix on Nix";
level-seed = "9064150133272194";
max-players = 10;
max-world-size = 64000000;
motd = "A cool Minecraft server powered by NixOS";
online-mode = true;
pvp = true;
spawn-animals = true;
spawn-monsters = true;
spawn-npcs = true;
spawn-protection = 16;
view-distance = 32;
white-list = true;
};
whitelist = {
Hefty_Chungus = "b75a9816-d408-4c54-b226-385b59ea1cb3";
Hefty_Chungus_Jr = "c3bf8cac-e953-4ea4-ae5f-7acb92a51a85";
EclipseMoon01 = "adef4af7-d8c6-4627-b492-e990ea1bb993";
Fallaryn = "d8baa117-ab58-4b07-92a5-48fb1978eb49";
};
};
minecraftNick02 =
let
interfaceCfg = instances.minecraft.interfaces.interface1;
in
minecraftVM {
user = user0;
ip = interfaceCfg.microvm.ip;
mac = interfaceCfg.microvm.mac;
userMac = interfaceCfg.microvm.macUser;
ssh = interfaceCfg.microvm.ssh;
port = interfaceCfg.microvm.port;
mnt = mntPath;
worldNumber = "02";
config = {
allow-flight = false;
allow-nether = true;
difficulty = 2;
enable-command-block = false;
enable-rcon = true;
enable-status = true;
force-gamemode = true;
gamemode = 0;
generate-structures = true;
hardcore = false;
hide-online-players = false;
level-name = "Cuddle Cubes";
level-seed = "-2332803749585407299";
max-players = 10;
max-world-size = 64000000;
motd = "A cool Minecraft server powered by NixOS";
online-mode = true;
pvp = true;
spawn-animals = true;
spawn-monsters = true;
spawn-npcs = true;
spawn-protection = 16;
view-distance = 32;
white-list = true;
};
whitelist = {
Hefty_Chungus = "b75a9816-d408-4c54-b226-385b59ea1cb3";
Fallaryn = "d8baa117-ab58-4b07-92a5-48fb1978eb49";
};
};
imports = importList;
}

View file

@ -0,0 +1,105 @@
{
flake,
pkgs,
...
}:
let
inherit (import ../../helpers.nix) labHelpers;
inherit (labHelpers) mntPath;
inherit (import ../config { inherit flake pkgs; }) minecraftVM;
inherit (flake.config.services) instances;
inherit (flake.config.people) user0;
minecraftNick01 =
let
interfaceCfg = instances.minecraft.interfaces.interface0;
in
minecraftVM {
user = user0;
ip = interfaceCfg.microvm.ip;
mac = interfaceCfg.microvm.mac;
userMac = interfaceCfg.microvm.macUser;
ssh = interfaceCfg.microvm.ssh;
port = interfaceCfg.microvm.port;
mnt = mntPath;
worldNumber = "01";
config = {
allow-flight = false;
allow-nether = true;
difficulty = 2;
enable-command-block = false;
enable-rcon = true;
enable-status = true;
force-gamemode = true;
gamemode = 0;
generate-structures = true;
hardcore = false;
hide-online-players = false;
level-name = "Brix on Nix";
level-seed = "9064150133272194";
max-players = 10;
max-world-size = 64000000;
motd = "A cool Minecraft server powered by NixOS";
online-mode = true;
pvp = true;
spawn-animals = true;
spawn-monsters = true;
spawn-npcs = true;
spawn-protection = 16;
view-distance = 32;
white-list = true;
};
whitelist = {
Hefty_Chungus = "b75a9816-d408-4c54-b226-385b59ea1cb3";
Hefty_Chungus_Jr = "c3bf8cac-e953-4ea4-ae5f-7acb92a51a85";
EclipseMoon01 = "adef4af7-d8c6-4627-b492-e990ea1bb993";
Fallaryn = "d8baa117-ab58-4b07-92a5-48fb1978eb49";
};
};
minecraftNick02 =
let
interfaceCfg = instances.minecraft.interfaces.interface1;
in
minecraftVM {
user = user0;
ip = interfaceCfg.microvm.ip;
mac = interfaceCfg.microvm.mac;
userMac = interfaceCfg.microvm.macUser;
ssh = interfaceCfg.microvm.ssh;
port = interfaceCfg.microvm.port;
mnt = mntPath;
worldNumber = "02";
config = {
allow-flight = false;
allow-nether = true;
difficulty = 2;
enable-command-block = false;
enable-rcon = true;
enable-status = true;
force-gamemode = true;
gamemode = 0;
generate-structures = true;
hardcore = false;
hide-online-players = false;
level-name = "Cuddle Cubes";
level-seed = "-2332803749585407299";
max-players = 10;
max-world-size = 64000000;
motd = "A cool Minecraft server powered by NixOS";
online-mode = true;
pvp = true;
spawn-animals = true;
spawn-monsters = true;
spawn-npcs = true;
spawn-protection = 16;
view-distance = 32;
white-list = true;
};
whitelist = {
Hefty_Chungus = "b75a9816-d408-4c54-b226-385b59ea1cb3";
Fallaryn = "d8baa117-ab58-4b07-92a5-48fb1978eb49";
};
};
in
minecraftNick01 // minecraftNick02

View file

@ -1,57 +1,11 @@
{
flake,
pkgs,
labHelpers,
...
}:
let
inherit (labHelpers) mntPath guestPath;
inherit (import ./config { inherit flake pkgs; }) opencloudVM;
inherit (flake.config.people) user0;
inherit (flake.config.services.instances) opencloud;
interface0Cfg = opencloud.interfaces.interface0;
interface1Cfg = opencloud.interfaces.interface1;
importList =
let
content = builtins.readDir ./.;
dirContent = builtins.filter (n: content.${n} == "directory") (builtins.attrNames content);
in
map (name: ./. + "/${name}") dirContent;
in
{
opencloudNick = opencloudVM {
user = user0;
ip = interface0Cfg.microvm.ip;
mac = interface0Cfg.microvm.mac;
userMac = interface0Cfg.microvm.macUser;
ssh = interface0Cfg.microvm.ssh;
mnt = guestPath user0;
host = interface0Cfg.domain;
};
opencloudProject = opencloudVM {
user = user0;
ip = interface1Cfg.microvm.ip;
mac = interface1Cfg.microvm.mac;
userMac = interface1Cfg.microvm.macUser;
ssh = interface1Cfg.microvm.ssh;
mnt = mntPath;
host = "${interface0Cfg.subdomain}.${flake.inputs.linkpage.secrets.domains.projectsite}";
};
# opencloudStacie = opencloudVM {
# user = "stacie";
# ip = ipAddress id1;
# mac = "02:00:00:00:${id1}:${id1}";
# userMac = "02:00:00:00:00:${id1}";
# ssh = opencloud.ssh1;
# mnt = guestPath "stacie";
# host = "";
# };
# opencloudGarnet = opencloudVM {
# user = "garnet";
# ip = ipAddress id2;
# mac = "02:00:00:00:${id2}:${id2}";
# userMac = "02:00:00:00:00:${id2}";
# ssh = opencloud.ssh2;
# mnt = guestPath "garnet";
# host = "";
# };
imports = importList;
}

View file

@ -0,0 +1,25 @@
{
flake,
pkgs,
...
}:
let
inherit (import ../../helpers.nix) labHelpers;
inherit (labHelpers) mntPath;
inherit (import ../config { inherit flake pkgs; }) opencloudVM;
inherit (flake.config.people) user0;
inherit (flake.config.services.instances) opencloud;
interface0Cfg = opencloud.interfaces.interface0;
opencloudProject = opencloudVM {
user = user0;
ip = interface0Cfg.microvm.ip;
mac = interface0Cfg.microvm.mac;
userMac = interface0Cfg.microvm.macUser;
ssh = interface0Cfg.microvm.ssh;
mnt = mntPath;
host = "${interface0Cfg.subdomain}.${flake.inputs.linkpage.secrets.domains.projectsite}";
};
in
opencloudProject

View file

@ -0,0 +1,25 @@
{
flake,
pkgs,
...
}:
let
inherit (import ../../helpers.nix) labHelpers;
inherit (labHelpers) guestPath;
inherit (import ../config { inherit flake pkgs; }) opencloudVM;
inherit (flake.config.people) user0;
inherit (flake.config.services.instances) opencloud;
interface0Cfg = opencloud.interfaces.interface0;
opencloudNick = opencloudVM {
user = user0;
ip = interface0Cfg.microvm.ip;
mac = interface0Cfg.microvm.mac;
userMac = interface0Cfg.microvm.macUser;
ssh = interface0Cfg.microvm.ssh;
mnt = guestPath user0;
host = interface0Cfg.domain;
};
in
opencloudNick

View file

@ -15,6 +15,7 @@ in
ip,
mac,
userMac,
host,
ssh,
mnt,
data,
@ -32,6 +33,8 @@ in
${serviceCfg.name} = {
enable = true;
settings = {
PHOTOPRISM_SITE_URL = "https://${host}";
PHOTOPRISM_DISABLE_TLS = "true";
PHOTOPRISM_ADMIN_USER = user;
PHOTOPRISM_DEFAULT_LOCAL = "en";
};

View file

@ -1,47 +1,11 @@
{
flake,
lib,
labHelpers,
...
}:
let
inherit (labHelpers) guestPath mediaPath;
inherit (import ./config { inherit flake lib; }) photoprismVM;
inherit (flake.config.services.instances) photoprism;
inherit (flake.config.people) user0;
interface0Cfg = photoprism.interfaces.interface0;
importList =
let
content = builtins.readDir ./.;
dirContent = builtins.filter (n: content.${n} == "directory") (builtins.attrNames content);
in
map (name: ./. + "/${name}") dirContent;
in
{
photoprismNick = photoprismVM {
user = user0;
ip = interface0Cfg.microvm.ip;
mac = interface0Cfg.microvm.mac;
userMac = interface0Cfg.microvm.macUser;
ssh = interface0Cfg.microvm.ssh;
mnt = guestPath user0;
data = mediaPath user0;
host = interface0Cfg.domain;
};
# photoprismStacie = photoprismVM {
# user = "stacie";
# ip = ipAddress id1;
# mac = "02:00:00:00:${id1}:${id1}";
# userMac = "02:00:00:00:00:${id1}";
# ssh = photoprism.ssh1;
# mnt = guestPath "stacie";
# data = mediaPath "stacie";
# };
# photoprismGarnet = photoprismVM {
# user = "garnet";
# ip = ipAddress id2;
# mac = "02:00:00:00:${id2}:${id2}";
# userMac = "02:00:00:00:00:${id2}";
# ssh = photoprism.ssh2;
# mnt = guestPath "garnet";
# data = mediaPath "stacie";
# };
imports = importList;
}

View file

@ -0,0 +1,26 @@
{
flake,
lib,
labHelpers,
...
}:
let
inherit (import ../../helpers.nix) labHelpers;
inherit (labHelpers) guestPath mediaPath;
inherit (import ../config { inherit flake lib; }) photoprismVM;
inherit (flake.config.services.instances) photoprism;
inherit (flake.config.people) user0;
interface0Cfg = photoprism.interfaces.interface0;
photoprismNick = photoprismVM {
user = user0;
ip = interface0Cfg.microvm.ip;
mac = interface0Cfg.microvm.mac;
userMac = interface0Cfg.microvm.macUser;
ssh = interface0Cfg.microvm.ssh;
mnt = guestPath user0;
data = mediaPath user0;
host = interface0Cfg.domain;
};
in
photoprismNick

View file

@ -8,8 +8,6 @@ let
inherit (flake.config.people) user0;
inherit (flake.config.services) instances;
serviceCfg = instances.qbittorrent;
dns0 = instances.web.dns.provider0;
dns0Path = "dns/${dns0}";
in
{
qbittorrentVM =
@ -27,404 +25,403 @@ in
dns,
key,
}:
let
torrentPort = port;
vpnEndpoint = endpoint;
localNet = "192.168.50.0/24";
in
{
microvm.vms =
let
torrentPort = port;
vpnEndpoint = endpoint;
localNet = "192.168.50.0/24";
in
{
"${serviceCfg.name}-${user}" = {
autostart = true;
config = {
system.stateVersion = "25.05";
microvm.vms = {
"${serviceCfg.name}-${user}" = {
autostart = true;
config = {
system.stateVersion = "25.05";
# VPN Killswitch - configured BEFORE networking starts
boot.kernel.sysctl = {
"net.ipv4.ip_forward" = 1;
# VPN Killswitch - configured BEFORE networking starts
boot.kernel.sysctl = {
"net.ipv4.ip_forward" = 1;
};
networking = {
# Disable default firewall - we're doing it manually
firewall.enable = false;
wg-quick.interfaces = {
wg0 = {
address = address;
dns = dns;
privateKeyFile = "/run/secrets/${user}-wireguard-pass";
peers = [
{
publicKey = key;
endpoint = "${vpnEndpoint}:${toString torrentPort}";
allowedIPs = [
"0.0.0.0/0"
"::/0"
];
persistentKeepalive = 25;
}
];
# Now we can safely open the VPN tunnel for all traffic
postUp = ''
echo "VPN UP: Opening network for VPN and local traffic"
# Allow ALL traffic through VPN interface
${pkgs.iptables}/bin/iptables -A INPUT -i wg0 -j ACCEPT
${pkgs.iptables}/bin/iptables -A OUTPUT -o wg0 -j ACCEPT
# Allow local network traffic (WebUI, management)
${pkgs.iptables}/bin/iptables -A INPUT -i enp0s5 -s ${localNet} -j ACCEPT
${pkgs.iptables}/bin/iptables -A OUTPUT -o enp0s5 -d ${localNet} -j ACCEPT
# NAT for VPN
${pkgs.iptables}/bin/iptables -t nat -A POSTROUTING -o wg0 -j MASQUERADE
# Allow forwarding through VPN (for port forwarding)
${pkgs.iptables}/bin/iptables -A FORWARD -i wg0 -j ACCEPT
${pkgs.iptables}/bin/iptables -A FORWARD -o wg0 -j ACCEPT
${pkgs.iptables}/bin/iptables -A FORWARD -i enp0s5 -o wg0 -j ACCEPT
${pkgs.iptables}/bin/iptables -A FORWARD -o enp0s5 -i wg0 -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
echo "VPN UP: Network opened for VPN and local traffic"
'';
preDown = ''
echo "VPN DOWN: Removing VPN rules, killswitch remains active"
${pkgs.iptables}/bin/iptables -D INPUT -i wg0 -j ACCEPT 2>/dev/null || true
${pkgs.iptables}/bin/iptables -D OUTPUT -o wg0 -j ACCEPT 2>/dev/null || true
${pkgs.iptables}/bin/iptables -D INPUT -i enp0s5 -s ${localNet} -j ACCEPT 2>/dev/null || true
${pkgs.iptables}/bin/iptables -D OUTPUT -o enp0s5 -d ${localNet} -j ACCEPT 2>/dev/null || true
${pkgs.iptables}/bin/iptables -t nat -D POSTROUTING -o wg0 -j MASQUERADE 2>/dev/null || true
${pkgs.iptables}/bin/iptables -D FORWARD -i wg0 -j ACCEPT 2>/dev/null || true
${pkgs.iptables}/bin/iptables -D FORWARD -o wg0 -j ACCEPT 2>/dev/null || true
${pkgs.iptables}/bin/iptables -D FORWARD -i enp0s5 -o wg0 -j ACCEPT 2>/dev/null || true
${pkgs.iptables}/bin/iptables -D FORWARD -o enp0s5 -i wg0 -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT 2>/dev/null || true
echo "VPN DOWN: Killswitch rules remain - no internet access"
'';
};
};
networking = {
# Disable default firewall - we're doing it manually
firewall.enable = false;
dhcpcd.enable = false;
useNetworkd = true;
};
wg-quick.interfaces = {
wg0 = {
address = address;
dns = dns;
privateKeyFile = "/run/secrets/${user}-wireguard-pass";
services = {
qbittorrent = {
enable = true;
webuiPort = serviceCfg.ports.port0;
torrentingPort = torrentPort;
openFirewall = false; # We're managing firewall manually
peers = [
{
publicKey = key;
endpoint = "${vpnEndpoint}:${toString torrentPort}";
allowedIPs = [
"0.0.0.0/0"
"::/0"
];
persistentKeepalive = 25;
}
];
serverConfig = {
LegalNotice.Accepted = true;
# Now we can safely open the VPN tunnel for all traffic
postUp = ''
echo "VPN UP: Opening network for VPN and local traffic"
BitTorrent = {
Session = {
Interface = "wg0";
InterfaceName = "wg0";
Port = torrentPort;
MaxConnections = -1;
MaxConnectionsPerTorrent = -1;
MaxUploads = -1;
MaxUploadsPerTorrent = -1;
QueueingSystemEnabled = false;
uTPRateLimited = false;
uTPEnabled = true;
AlternativeGlobalDLSpeedLimit = 0;
AlternativeGlobalUPSpeedLimit = 0;
GlobalMaxInactiveSeedingMinutes = 10224;
GlobalMaxRatio = -1;
};
};
# Allow ALL traffic through VPN interface
${pkgs.iptables}/bin/iptables -A INPUT -i wg0 -j ACCEPT
${pkgs.iptables}/bin/iptables -A OUTPUT -o wg0 -j ACCEPT
Preferences = {
WebUI = {
Username = "user";
Password_PBKDF2 = "@ByteArray(1bJKXLVSLU6kgCHbCS2lDg==:BmyrMaod6dbJqEe7Ud/JgKAxRMqzsAuEjHcTvLzIBgc5rc5Z7J2X9mbH0cDEAhXqc+O3gQxrckt8S2Gf+zlO9w==)";
};
# Allow local network traffic (WebUI, management)
${pkgs.iptables}/bin/iptables -A INPUT -i enp0s5 -s ${localNet} -j ACCEPT
${pkgs.iptables}/bin/iptables -A OUTPUT -o enp0s5 -d ${localNet} -j ACCEPT
General = {
Locale = "en";
};
# NAT for VPN
${pkgs.iptables}/bin/iptables -t nat -A POSTROUTING -o wg0 -j MASQUERADE
# Allow forwarding through VPN (for port forwarding)
${pkgs.iptables}/bin/iptables -A FORWARD -i wg0 -j ACCEPT
${pkgs.iptables}/bin/iptables -A FORWARD -o wg0 -j ACCEPT
${pkgs.iptables}/bin/iptables -A FORWARD -i enp0s5 -o wg0 -j ACCEPT
${pkgs.iptables}/bin/iptables -A FORWARD -o enp0s5 -i wg0 -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
echo "VPN UP: Network opened for VPN and local traffic"
'';
preDown = ''
echo "VPN DOWN: Removing VPN rules, killswitch remains active"
${pkgs.iptables}/bin/iptables -D INPUT -i wg0 -j ACCEPT 2>/dev/null || true
${pkgs.iptables}/bin/iptables -D OUTPUT -o wg0 -j ACCEPT 2>/dev/null || true
${pkgs.iptables}/bin/iptables -D INPUT -i enp0s5 -s ${localNet} -j ACCEPT 2>/dev/null || true
${pkgs.iptables}/bin/iptables -D OUTPUT -o enp0s5 -d ${localNet} -j ACCEPT 2>/dev/null || true
${pkgs.iptables}/bin/iptables -t nat -D POSTROUTING -o wg0 -j MASQUERADE 2>/dev/null || true
${pkgs.iptables}/bin/iptables -D FORWARD -i wg0 -j ACCEPT 2>/dev/null || true
${pkgs.iptables}/bin/iptables -D FORWARD -o wg0 -j ACCEPT 2>/dev/null || true
${pkgs.iptables}/bin/iptables -D FORWARD -i enp0s5 -o wg0 -j ACCEPT 2>/dev/null || true
${pkgs.iptables}/bin/iptables -D FORWARD -o enp0s5 -i wg0 -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT 2>/dev/null || true
echo "VPN DOWN: Killswitch rules remain - no internet access"
'';
Downloads = {
SavePath = "${mnt}/${serviceCfg.name}/downloads";
TempPathEnabled = false;
PreAllocation = false;
};
};
};
dhcpcd.enable = false;
useNetworkd = true;
};
openssh = {
enable = true;
settings.PasswordAuthentication = false;
};
};
users.users.root.openssh.authorizedKeys.keys = flake.config.people.users.${user0}.sshKeys;
systemd = {
network = {
enable = true;
networks."10-enp" = {
matchConfig.Name = "enp0s5";
addresses = [ { Address = "${ip}/24"; } ];
gateway = [ "192.168.50.1" ];
};
};
tmpfiles.rules = [
"d ${mnt}/${serviceCfg.name} 755 ${serviceCfg.name} ${serviceCfg.name} -"
"d ${mnt}/${serviceCfg.name}/downloads 755 ${serviceCfg.name} ${serviceCfg.name} -"
];
services = {
# Ensure qBittorrent ONLY starts after VPN is up
qbittorrent = {
enable = true;
webuiPort = serviceCfg.ports.port0;
torrentingPort = torrentPort;
openFirewall = false; # We're managing firewall manually
after = [
"wg-quick-wg0.service"
"network-online.target"
];
requires = [ "wg-quick-wg0.service" ];
wants = [ "network-online.target" ];
bindsTo = [ "wg-quick-wg0.service" ]; # Stop if VPN stops
serverConfig = {
LegalNotice.Accepted = true;
BitTorrent = {
Session = {
Interface = "wg0";
InterfaceName = "wg0";
Port = torrentPort;
MaxConnections = -1;
MaxConnectionsPerTorrent = -1;
MaxUploads = -1;
MaxUploadsPerTorrent = -1;
QueueingSystemEnabled = false;
uTPRateLimited = false;
uTPEnabled = true;
AlternativeGlobalDLSpeedLimit = 0;
AlternativeGlobalUPSpeedLimit = 0;
GlobalMaxInactiveSeedingMinutes = 10224;
GlobalMaxRatio = -1;
};
};
Preferences = {
WebUI = {
Username = "user";
Password_PBKDF2 = "@ByteArray(1bJKXLVSLU6kgCHbCS2lDg==:BmyrMaod6dbJqEe7Ud/JgKAxRMqzsAuEjHcTvLzIBgc5rc5Z7J2X9mbH0cDEAhXqc+O3gQxrckt8S2Gf+zlO9w==)";
};
General = {
Locale = "en";
};
Downloads = {
SavePath = "${mnt}/${serviceCfg.name}/downloads";
TempPathEnabled = false;
PreAllocation = false;
};
};
serviceConfig = {
Restart = "on-failure";
RestartSec = "10s";
};
};
openssh = {
enable = true;
settings.PasswordAuthentication = false;
};
};
natpmp-portforward = {
description = "NAT-PMP Port Forwarding for VPN";
after = [
"wg-quick-wg0.service"
"qbittorrent.service"
];
requires = [
"wg-quick-wg0.service"
"qbittorrent.service"
];
wantedBy = [ "multi-user.target" ];
users.users.root.openssh.authorizedKeys.keys = flake.config.people.users.${user0}.sshKeys;
systemd = {
network = {
enable = true;
networks."10-enp" = {
matchConfig.Name = "enp0s5";
addresses = [ { Address = "${ip}/24"; } ];
gateway = [ "192.168.50.1" ];
};
};
tmpfiles.rules = [
"d ${mnt}/${serviceCfg.name} 755 ${serviceCfg.name} ${serviceCfg.name} -"
"d ${mnt}/${serviceCfg.name}/downloads 755 ${serviceCfg.name} ${serviceCfg.name} -"
];
services = {
# Ensure qBittorrent ONLY starts after VPN is up
qbittorrent = {
after = [
"wg-quick-wg0.service"
"network-online.target"
];
requires = [ "wg-quick-wg0.service" ];
wants = [ "network-online.target" ];
bindsTo = [ "wg-quick-wg0.service" ]; # Stop if VPN stops
serviceConfig = {
Restart = "on-failure";
RestartSec = "10s";
};
serviceConfig = {
Type = "simple";
Restart = "always";
RestartSec = "10s";
};
natpmp-portforward = {
description = "NAT-PMP Port Forwarding for VPN";
after = [
"wg-quick-wg0.service"
"qbittorrent.service"
];
requires = [
"wg-quick-wg0.service"
"qbittorrent.service"
];
wantedBy = [ "multi-user.target" ];
script = ''
PASSWORD=$(cat /run/secrets/${user}-qbittorrent-pass)
echo "Waiting for qBittorrent to start..."
sleep 10
serviceConfig = {
Type = "simple";
Restart = "always";
RestartSec = "10s";
};
script = ''
PASSWORD=$(cat /run/secrets/${user}-qbittorrent-pass)
echo "Waiting for qBittorrent to start..."
sleep 10
while true; do
echo "Requesting port forwarding from VPN..."
while true; do
echo "Requesting port forwarding from VPN..."
UDP_OUTPUT=$(${pkgs.libnatpmp}/bin/natpmpc -a 1 0 udp 60 -g 10.2.0.1 2>&1)
UDP_PORT=$(echo "$UDP_OUTPUT" | ${pkgs.gnugrep}/bin/grep "Mapped public port" | ${pkgs.gawk}/bin/awk '{print $4}' | head -1)
TCP_OUTPUT=$(${pkgs.libnatpmp}/bin/natpmpc -a 1 0 tcp 60 -g 10.2.0.1 2>&1)
TCP_PORT=$(echo "$TCP_OUTPUT" | ${pkgs.gnugrep}/bin/grep "Mapped public port" | ${pkgs.gawk}/bin/awk '{print $4}' | head -1)
if [ -n "$UDP_PORT" ] && [ -n "$TCP_PORT" ]; then
echo "Port forwarding successful: UDP=$UDP_PORT, TCP=$TCP_PORT"
UDP_OUTPUT=$(${pkgs.libnatpmp}/bin/natpmpc -a 1 0 udp 60 -g 10.2.0.1 2>&1)
UDP_PORT=$(echo "$UDP_OUTPUT" | ${pkgs.gnugrep}/bin/grep "Mapped public port" | ${pkgs.gawk}/bin/awk '{print $4}' | head -1)
# Clean up old dynamic rules
${pkgs.iptables}/bin/iptables -t nat -D PREROUTING -i enp0s5 -s ${localNet} -p tcp -j DNAT 2>/dev/null || true
${pkgs.iptables}/bin/iptables -t nat -D PREROUTING -i enp0s5 -s ${localNet} -p udp -j DNAT 2>/dev/null || true
${pkgs.iptables}/bin/iptables -D FORWARD -i enp0s5 -o wg0 -p tcp -j ACCEPT 2>/dev/null || true
${pkgs.iptables}/bin/iptables -D FORWARD -i enp0s5 -o wg0 -p udp -j ACCEPT 2>/dev/null || true
${pkgs.iptables}/bin/iptables -D FORWARD -i wg0 -o enp0s5 -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT 2>/dev/null || true
TCP_OUTPUT=$(${pkgs.libnatpmp}/bin/natpmpc -a 1 0 tcp 60 -g 10.2.0.1 2>&1)
TCP_PORT=$(echo "$TCP_OUTPUT" | ${pkgs.gnugrep}/bin/grep "Mapped public port" | ${pkgs.gawk}/bin/awk '{print $4}' | head -1)
# DNAT: Forward LAN traffic to qBittorrent on WireGuard interface
${pkgs.iptables}/bin/iptables -t nat -A PREROUTING -i enp0s5 -s ${localNet} -p tcp --dport "$TCP_PORT" -j DNAT --to-destination 10.2.0.2:"$TCP_PORT"
${pkgs.iptables}/bin/iptables -t nat -A PREROUTING -i enp0s5 -s ${localNet} -p udp --dport "$UDP_PORT" -j DNAT --to-destination 10.2.0.2:"$UDP_PORT"
if [ -n "$UDP_PORT" ] && [ -n "$TCP_PORT" ]; then
echo "Port forwarding successful: UDP=$UDP_PORT, TCP=$TCP_PORT"
# Allow forwarding for these specific ports
${pkgs.iptables}/bin/iptables -A FORWARD -i enp0s5 -o wg0 -d 10.2.0.2 -p tcp --dport "$TCP_PORT" -j ACCEPT
${pkgs.iptables}/bin/iptables -A FORWARD -i enp0s5 -o wg0 -d 10.2.0.2 -p udp --dport "$UDP_PORT" -j ACCEPT
${pkgs.iptables}/bin/iptables -A FORWARD -i wg0 -o enp0s5 -s 10.2.0.2 -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
echo "Firewall forwarding rules updated for ports: UDP=$UDP_PORT, TCP=$TCP_PORT"
# Update qBittorrent listening port via API
echo "Logging into qBittorrent API..."
COOKIE=$(${pkgs.curl}/bin/curl -s -i \
--header "Referer: http://localhost:${toString serviceCfg.ports.port0}" \
--data "username=user&password=$PASSWORD" \
"http://localhost:${toString serviceCfg.ports.port0}/api/v2/auth/login" | \
${pkgs.gnugrep}/bin/grep -i "set-cookie" | ${pkgs.gawk}/bin/awk -F'SID=|;' '{print $2}')
if [ -n "$COOKIE" ]; then
echo "Authentication successful, updating port..."
${pkgs.curl}/bin/curl -s \
--cookie "SID=$COOKIE" \
--data "json={\"listen_port\":$UDP_PORT}" \
"http://localhost:${toString serviceCfg.ports.port0}/api/v2/app/setPreferences"
# Clean up old dynamic rules
${pkgs.iptables}/bin/iptables -t nat -D PREROUTING -i enp0s5 -s ${localNet} -p tcp -j DNAT 2>/dev/null || true
${pkgs.iptables}/bin/iptables -t nat -D PREROUTING -i enp0s5 -s ${localNet} -p udp -j DNAT 2>/dev/null || true
${pkgs.iptables}/bin/iptables -D FORWARD -i enp0s5 -o wg0 -p tcp -j ACCEPT 2>/dev/null || true
${pkgs.iptables}/bin/iptables -D FORWARD -i enp0s5 -o wg0 -p udp -j ACCEPT 2>/dev/null || true
${pkgs.iptables}/bin/iptables -D FORWARD -i wg0 -o enp0s5 -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT 2>/dev/null || true
# DNAT: Forward LAN traffic to qBittorrent on WireGuard interface
${pkgs.iptables}/bin/iptables -t nat -A PREROUTING -i enp0s5 -s ${localNet} -p tcp --dport "$TCP_PORT" -j DNAT --to-destination 10.2.0.2:"$TCP_PORT"
${pkgs.iptables}/bin/iptables -t nat -A PREROUTING -i enp0s5 -s ${localNet} -p udp --dport "$UDP_PORT" -j DNAT --to-destination 10.2.0.2:"$UDP_PORT"
# Allow forwarding for these specific ports
${pkgs.iptables}/bin/iptables -A FORWARD -i enp0s5 -o wg0 -d 10.2.0.2 -p tcp --dport "$TCP_PORT" -j ACCEPT
${pkgs.iptables}/bin/iptables -A FORWARD -i enp0s5 -o wg0 -d 10.2.0.2 -p udp --dport "$UDP_PORT" -j ACCEPT
${pkgs.iptables}/bin/iptables -A FORWARD -i wg0 -o enp0s5 -s 10.2.0.2 -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
echo "Firewall forwarding rules updated for ports: UDP=$UDP_PORT, TCP=$TCP_PORT"
# Update qBittorrent listening port via API
echo "Logging into qBittorrent API..."
COOKIE=$(${pkgs.curl}/bin/curl -s -i \
--header "Referer: http://localhost:${toString serviceCfg.ports.port0}" \
--data "username=user&password=$PASSWORD" \
"http://localhost:${toString serviceCfg.ports.port0}/api/v2/auth/login" | \
${pkgs.gnugrep}/bin/grep -i "set-cookie" | ${pkgs.gawk}/bin/awk -F'SID=|;' '{print $2}')
if [ -n "$COOKIE" ]; then
echo "Authentication successful, updating port..."
${pkgs.curl}/bin/curl -s \
--cookie "SID=$COOKIE" \
--data "json={\"listen_port\":$UDP_PORT}" \
"http://localhost:${toString serviceCfg.ports.port0}/api/v2/app/setPreferences"
echo "Updated qBittorrent listening port to $UDP_PORT"
else
echo "WARNING: Failed to authenticate with qBittorrent API"
fi
echo "Updated qBittorrent listening port to $UDP_PORT"
else
echo "ERROR: Failed to get forwarded ports"
echo "UDP output: $UDP_OUTPUT"
echo "TCP output: $TCP_OUTPUT"
echo "WARNING: Failed to authenticate with qBittorrent API"
fi
sleep 45
done
'';
else
echo "ERROR: Failed to get forwarded ports"
echo "UDP output: $UDP_OUTPUT"
echo "TCP output: $TCP_OUTPUT"
fi
sleep 45
done
'';
};
killswitch-init = {
description = "Initialize VPN Killswitch Before Network";
wantedBy = [ "network-pre.target" ];
before = [
"network-pre.target"
"network.target"
];
after = [ "systemd-modules-load.service" ];
serviceConfig = {
Type = "oneshot";
RemainAfterExit = true;
};
killswitch-init = {
description = "Initialize VPN Killswitch Before Network";
wantedBy = [ "network-pre.target" ];
before = [
"network-pre.target"
"network.target"
];
after = [ "systemd-modules-load.service" ];
serviceConfig = {
Type = "oneshot";
RemainAfterExit = true;
};
script = ''
echo "KILLSWITCH: Setting up firewall rules BEFORE network services"
script = ''
echo "KILLSWITCH: Setting up firewall rules BEFORE network services"
# Default DROP everything
${pkgs.iptables}/bin/iptables -P INPUT DROP
${pkgs.iptables}/bin/iptables -P OUTPUT DROP
${pkgs.iptables}/bin/iptables -P FORWARD DROP
# Default DROP everything
${pkgs.iptables}/bin/iptables -P INPUT DROP
${pkgs.iptables}/bin/iptables -P OUTPUT DROP
${pkgs.iptables}/bin/iptables -P FORWARD DROP
${pkgs.iptables}/bin/iptables -F
${pkgs.iptables}/bin/iptables -t nat -F
${pkgs.iptables}/bin/iptables -X
${pkgs.iptables}/bin/iptables -F
${pkgs.iptables}/bin/iptables -t nat -F
${pkgs.iptables}/bin/iptables -X
# Allow loopback
${pkgs.iptables}/bin/iptables -A INPUT -i lo -j ACCEPT
${pkgs.iptables}/bin/iptables -A OUTPUT -o lo -j ACCEPT
# Allow loopback
${pkgs.iptables}/bin/iptables -A INPUT -i lo -j ACCEPT
${pkgs.iptables}/bin/iptables -A OUTPUT -o lo -j ACCEPT
# CRITICAL: Only allow WireGuard endpoint traffic before VPN is up
${pkgs.iptables}/bin/iptables -A OUTPUT -o enp0s5 -p udp --dport ${toString torrentPort} -d ${vpnEndpoint} -j ACCEPT
${pkgs.iptables}/bin/iptables -A INPUT -i enp0s5 -p udp --sport ${toString torrentPort} -s ${vpnEndpoint} -j ACCEPT
# CRITICAL: Only allow WireGuard endpoint traffic before VPN is up
${pkgs.iptables}/bin/iptables -A OUTPUT -o enp0s5 -p udp --dport ${toString torrentPort} -d ${vpnEndpoint} -j ACCEPT
${pkgs.iptables}/bin/iptables -A INPUT -i enp0s5 -p udp --sport ${toString torrentPort} -s ${vpnEndpoint} -j ACCEPT
# Allow SSH from local network (for management)
${pkgs.iptables}/bin/iptables -A INPUT -i enp0s5 -s ${localNet} -p tcp --dport 22 -j ACCEPT
${pkgs.iptables}/bin/iptables -A OUTPUT -o enp0s5 -d ${localNet} -p tcp --sport 22 -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
# Allow SSH from local network (for management)
${pkgs.iptables}/bin/iptables -A INPUT -i enp0s5 -s ${localNet} -p tcp --dport 22 -j ACCEPT
${pkgs.iptables}/bin/iptables -A OUTPUT -o enp0s5 -d ${localNet} -p tcp --sport 22 -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
# Block IPv6 completely
${pkgs.iptables}/bin/ip6tables -P INPUT DROP 2>/dev/null || true
${pkgs.iptables}/bin/ip6tables -P OUTPUT DROP 2>/dev/null || true
${pkgs.iptables}/bin/ip6tables -P FORWARD DROP 2>/dev/null || true
# Block IPv6 completely
${pkgs.iptables}/bin/ip6tables -P INPUT DROP 2>/dev/null || true
${pkgs.iptables}/bin/ip6tables -P OUTPUT DROP 2>/dev/null || true
${pkgs.iptables}/bin/ip6tables -P FORWARD DROP 2>/dev/null || true
echo "KILLSWITCH: Initialized - Network locked down"
'';
};
echo "KILLSWITCH: Initialized - Network locked down"
'';
};
};
microvm = {
vcpu = 1;
mem = 1024 * 1;
hypervisor = "qemu";
interfaces = [
{
type = "tap";
id = "vm-qb-${user}";
mac = mac;
}
{
type = "user";
id = "vmuser-share";
mac = userMac;
}
];
forwardPorts = [
{
from = "host";
host.port = ssh;
guest.port = 22;
}
];
shares = [
{
source = "/nix/store";
mountPoint = "/nix/.ro-store";
tag = "ro-store";
proto = "virtiofs";
}
{
mountPoint = "/var/lib/${serviceCfg.name}";
proto = "virtiofs";
source = "${mnt}/${serviceCfg.name}";
tag = "${serviceCfg.name}_data";
}
{
mountPoint = "/run/secrets";
proto = "virtiofs";
source = "/run/secrets/torrent";
tag = "host_secrets";
}
];
};
environment.systemPackages = builtins.attrValues {
inherit (pkgs)
conntrack-tools
gawk
iptables
libnatpmp
speedtest-go
wireguard-tools
;
};
};
};
sops.secrets = {
"torrent/${user}-wireguard-pass" = {
owner = "root";
mode = "0400";
};
"torrent/${user}-qbittorrent-pass" = {
owner = "root";
mode = "0400";
};
};
systemd = {
services.caddy = {
serviceConfig = {
EnvironmentFile = config.sops.secrets."caddy/share-auth".path;
};
};
tmpfiles.rules = [
"d ${mnt}/${serviceCfg.name} 0755 microvm wheel - -"
];
};
microvm = {
vcpu = 1;
mem = 1024 * 1;
hypervisor = "qemu";
networking.firewall = {
allowedTCPPorts = [
38834
torrentPort
];
allowedUDPPorts = [
38834
torrentPort
];
interfaces = [
{
type = "tap";
id = "vm-qb-${user}";
mac = mac;
}
{
type = "user";
id = "vmuser-share";
mac = userMac;
}
];
forwardPorts = [
{
from = "host";
host.port = ssh;
guest.port = 22;
}
];
shares = [
{
source = "/nix/store";
mountPoint = "/nix/.ro-store";
tag = "ro-store";
proto = "virtiofs";
}
{
mountPoint = "/var/lib/${serviceCfg.name}";
proto = "virtiofs";
source = "${mnt}/${serviceCfg.name}";
tag = "${serviceCfg.name}_data";
}
{
mountPoint = "/run/secrets";
proto = "virtiofs";
source = "/run/secrets/torrent";
tag = "host_secrets";
}
];
};
environment.systemPackages = builtins.attrValues {
inherit (pkgs)
conntrack-tools
gawk
iptables
libnatpmp
speedtest-go
wireguard-tools
;
};
};
};
};
sops.secrets = {
"torrent/${user}-wireguard-pass" = {
owner = "root";
mode = "0400";
};
"torrent/${user}-qbittorrent-pass" = {
owner = "root";
mode = "0400";
};
};
systemd = {
services.caddy = {
serviceConfig = {
EnvironmentFile = config.sops.secrets."caddy/share-auth".path;
};
};
tmpfiles.rules = [
"d ${mnt}/${serviceCfg.name} 0755 microvm wheel - -"
];
};
networking.firewall = {
allowedTCPPorts = [
38834
torrentPort
];
allowedUDPPorts = [
38834
torrentPort
];
};
};
}

34
modules/nixos/homelab/guests/qbittorrent/default.nix Normal file → Executable file
View file

@ -1,31 +1,11 @@
{
flake,
pkgs,
labHelpers,
...
}:
let
inherit (labHelpers) mntPath;
inherit (import ./config { inherit flake pkgs; }) qbittorrentVM;
inherit (flake.config.people) user0;
inherit (flake.config.services) instances;
interface0Cfg = instances.qbittorrent.interfaces.interface0;
importList =
let
content = builtins.readDir ./.;
dirContent = builtins.filter (n: content.${n} == "directory") (builtins.attrNames content);
in
map (name: ./. + "/${name}") dirContent;
in
{
qbittorrentCeres = qbittorrentVM {
user = user0;
ip = interface0Cfg.microvm.ip;
mac = interface0Cfg.microvm.mac;
userMac = interface0Cfg.microvm.macUser;
ssh = interface0Cfg.microvm.ssh;
mnt = mntPath;
host = interface0Cfg.domain;
port = 51820;
endpoint = "185.111.110.1";
address = [ "10.2.0.2/32" ];
dns = [ "10.2.0.1" ];
key = "QPfiwJQmt5VLEOh1ufLbi1lj6LUnwQY0tgDSh3pWx1k=";
};
imports = importList;
}

View file

@ -0,0 +1,31 @@
{
flake,
config,
pkgs,
...
}:
let
inherit (import ../../helpers.nix) labHelpers;
inherit (labHelpers) mntPath;
inherit (import ../config { inherit config flake pkgs; }) qbittorrentVM;
inherit (flake.config.people) user0;
inherit (flake.config.services) instances;
interface0Cfg = instances.qbittorrent.interfaces.interface0;
qbittorrentCeres = qbittorrentVM {
user = user0;
ip = interface0Cfg.microvm.ip;
mac = interface0Cfg.microvm.mac;
userMac = interface0Cfg.microvm.macUser;
ssh = interface0Cfg.microvm.ssh;
mnt = mntPath;
host = interface0Cfg.domain;
port = 51820;
endpoint = "185.111.110.1";
address = [ "10.2.0.2/32" ];
dns = [ "10.2.0.1" ];
key = "QPfiwJQmt5VLEOh1ufLbi1lj6LUnwQY0tgDSh3pWx1k=";
};
in
qbittorrentCeres

View file

@ -1,123 +1,11 @@
{
flake,
labHelpers,
...
}:
let
inherit (labHelpers)
docsPath
guestPath
mediaPath
miscPath
;
inherit (import ./config { inherit flake; }) syncthingVM;
inherit (flake.config.services) instances;
inherit (flake.config.people) user0;
serviceCfg = instances.syncthing;
interface0Cfg = serviceCfg.interfaces.interface0;
foldersHelper = user: {
docs = {
enable = true;
id = "docs";
path = "/var/lib/${serviceCfg.name}/docs";
devices = [
"${user}Phone"
];
};
media = {
enable = true;
id = "media";
path = "/var/lib/${serviceCfg.name}/media";
devices = [
"${user}Phone"
];
};
misc = {
enable = true;
id = "misc";
path = "/var/lib/${serviceCfg.name}/misc";
devices = [
"${user}Phone"
];
};
};
devicesHelper = user: syncID: device: deviceIP: {
"${user}${device}" = {
autoAcceptFolders = true;
name = "${user}${device}";
addresses = [
"tcp://${deviceIP}:${toString serviceCfg.ports.port2}"
];
id = syncID;
};
};
mountsHelper = user: [
{
mountPoint = "/var/lib/${serviceCfg.name}/docs";
proto = "virtiofs";
source = docsPath user;
tag = "${serviceCfg.name}_${user}_docs";
}
{
mountPoint = "/var/lib/${serviceCfg.name}/media";
proto = "virtiofs";
source = mediaPath user;
tag = "${serviceCfg.name}_${user}_media";
}
{
mountPoint = "/var/lib/${serviceCfg.name}/misc";
proto = "virtiofs";
source = miscPath user;
tag = "${serviceCfg.name}_${user}_misc";
}
];
tmpRules = [
"d /var/lib/${serviceCfg.name}/docs 0755 ${serviceCfg.name} ${serviceCfg.name} -"
"d /var/lib/${serviceCfg.name}/media 0755 ${serviceCfg.name} ${serviceCfg.name} -"
"d /var/lib/${serviceCfg.name}/misc 0755 ${serviceCfg.name} ${serviceCfg.name} -"
];
importList =
let
content = builtins.readDir ./.;
dirContent = builtins.filter (n: content.${n} == "directory") (builtins.attrNames content);
in
map (name: ./. + "/${name}") dirContent;
in
{
syncthingNick =
let
phoneID = "OALKHLZ-OODUWVX-PAC2LI7-UMZMSZO-FELLRCD-RS4DHJS-PVA5YQK-WTFXXQI";
in
syncthingVM {
user = user0;
ip = interface0Cfg.microvm.ip;
mac = interface0Cfg.microvm.mac;
userMac = interface0Cfg.microvm.macUser;
ssh = interface0Cfg.microvm.ssh;
mnt = guestPath user0;
host = interface0Cfg.domain;
folders = foldersHelper user0;
devices = devicesHelper user0 phoneID "Phone" "192.168.50.8";
tmp = tmpRules;
mounts = mountsHelper user0;
};
# syncthingStacie = syncthingVM {
# user = "stacie";
# ip = ipAddress id0;
# mac = "02:00:00:00:${id0}:${id0}";
# userMac = "02:00:00:00:00:${id0}";
# ssh = syncthing.ssh0;
# syncID = "";
# deviceIP = "";
# };
# syncthingGarnet = syncthingVM {
# user = "garnet";
# ip = ipAddress id0;
# mac = "02:00:00:00:${id0}:${id0}";
# userMac = "02:00:00:00:00:${id0}";
# ssh = syncthing.ssh0;
# syncID = "";
# deviceIP = "";
# };
imports = importList;
}

View file

@ -0,0 +1,101 @@
{
flake,
...
}:
let
inherit (import ../../helpers.nix) labHelpers;
inherit (labHelpers)
guestPath
mediaPath
docsPath
miscPath
;
inherit (import ../config { inherit flake; }) syncthingVM;
inherit (flake.config.services) instances;
inherit (flake.config.people) user0;
serviceCfg = instances.syncthing;
interface0Cfg = serviceCfg.interfaces.interface0;
foldersHelper = user: {
docs = {
enable = true;
id = "docs";
path = "/var/lib/${serviceCfg.name}/docs";
devices = [
"${user}Phone"
];
};
media = {
enable = true;
id = "media";
path = "/var/lib/${serviceCfg.name}/media";
devices = [
"${user}Phone"
];
};
misc = {
enable = true;
id = "misc";
path = "/var/lib/${serviceCfg.name}/misc";
devices = [
"${user}Phone"
];
};
};
devicesHelper = user: syncID: device: deviceIP: {
"${user}${device}" = {
autoAcceptFolders = true;
name = "${user}${device}";
addresses = [
"tcp://${deviceIP}:${toString serviceCfg.ports.port2}"
];
id = syncID;
};
};
mountsHelper = user: [
{
mountPoint = "/var/lib/${serviceCfg.name}/docs";
proto = "virtiofs";
source = docsPath user;
tag = "${serviceCfg.name}_${user}_docs";
}
{
mountPoint = "/var/lib/${serviceCfg.name}/media";
proto = "virtiofs";
source = mediaPath user;
tag = "${serviceCfg.name}_${user}_media";
}
{
mountPoint = "/var/lib/${serviceCfg.name}/misc";
proto = "virtiofs";
source = miscPath user;
tag = "${serviceCfg.name}_${user}_misc";
}
];
tmpRules = [
"d /var/lib/${serviceCfg.name}/docs 0755 ${serviceCfg.name} ${serviceCfg.name} -"
"d /var/lib/${serviceCfg.name}/media 0755 ${serviceCfg.name} ${serviceCfg.name} -"
"d /var/lib/${serviceCfg.name}/misc 0755 ${serviceCfg.name} ${serviceCfg.name} -"
];
syncthingNick =
let
phoneID = "OALKHLZ-OODUWVX-PAC2LI7-UMZMSZO-FELLRCD-RS4DHJS-PVA5YQK-WTFXXQI";
in
syncthingVM {
user = user0;
ip = interface0Cfg.microvm.ip;
mac = interface0Cfg.microvm.mac;
userMac = interface0Cfg.microvm.macUser;
ssh = interface0Cfg.microvm.ssh;
mnt = guestPath user0;
folders = foldersHelper user0;
devices = devicesHelper user0 phoneID "Phone" "192.168.50.8";
tmp = tmpRules;
mounts = mountsHelper user0;
};
in
syncthingNick

View file

@ -1,46 +1,11 @@
{
flake,
labHelpers,
...
}:
let
inherit (labHelpers) guestPath;
inherit (import ./config { inherit flake; }) vaultwardenVM;
inherit (flake.config.people) user0;
inherit (flake.config.services.instances) vaultwarden;
interface0Cfg = vaultwarden.interfaces.interface0;
importList =
let
content = builtins.readDir ./.;
dirContent = builtins.filter (n: content.${n} == "directory") (builtins.attrNames content);
in
map (name: ./. + "/${name}") dirContent;
in
{
vaultwardenNick = vaultwardenVM {
user = user0;
ip = interface0Cfg.microvm.ip;
mac = interface0Cfg.microvm.mac;
userMac = interface0Cfg.microvm.macUser;
ssh = interface0Cfg.microvm.ssh;
mnt = guestPath user0;
host = interface0Cfg.domain;
};
# vaultwardenStacie = vaultwardenVM {
# user = "stacie";
# ip = ipAddress id1;
# mac = "02:00:00:00:${id1}:${id1}";
# userMac = "02:00:00:00:00:${id1}";
# ssh = vaultwarden.ssh1;
# mnt = guestPath "stacie";
# host = "";
# };
# vaultwardenGarnet = vaultwardenVM {
# user = "garnet";
# ip = ipAddress id2;
# mac = "02:00:00:00:${id2}:${id2}";
# userMac = "02:00:00:00:00:${id2}";
# ssh = vaultwarden.ssh2;
# mnt = guestPath "garnet";
# host = "";
# };
imports = importList;
}

View file

@ -0,0 +1,26 @@
{
flake,
labHelpers,
...
}:
let
inherit (import ../../helpers.nix) labHelpers;
inherit (labHelpers) guestPath;
inherit (import ../config { inherit flake; }) vaultwardenVM;
inherit (flake.config.people) user0;
inherit (flake.config.services.instances) vaultwarden;
interface0Cfg = vaultwarden.interfaces.interface0;
vaultwardenNick = vaultwardenVM {
user = user0;
ip = interface0Cfg.microvm.ip;
mac = interface0Cfg.microvm.mac;
userMac = interface0Cfg.microvm.macUser;
ssh = interface0Cfg.microvm.ssh;
mnt = guestPath user0;
host = interface0Cfg.domain;
};
in
vaultwardenNick

42
modules/nixos/homelab/guests/website/default.nix Normal file → Executable file
View file

@ -1,39 +1,11 @@
{
flake,
pkgs,
labHelpers,
...
}:
let
inherit (import ./config { inherit flake pkgs; }) websiteVM;
inherit (flake.config.services) instances;
importList =
let
content = builtins.readDir ./.;
dirContent = builtins.filter (n: content.${n} == "directory") (builtins.attrNames content);
in
map (name: ./. + "/${name}") dirContent;
in
{
websiteUpRoot =
let
websitePkg = flake.self.packages.${pkgs.system}.website;
interfaceCfg = instances.website.interfaces.interface0;
in
websiteVM {
user = "uproot";
ip = interfaceCfg.microvm.ip;
mac = interfaceCfg.microvm.mac;
userMac = interfaceCfg.microvm.macUser;
package = websitePkg;
};
websiteProject =
let
websitePkg = flake.inputs.linkpage.packages.${pkgs.stdenv.hostPlatform.system}.websiteFrontend;
interfaceCfg = instances.website.interfaces.interface1;
in
websiteVM {
user = "project";
ip = interfaceCfg.microvm.ip;
mac = interfaceCfg.microvm.mac;
userMac = interfaceCfg.microvm.macUser;
package = websitePkg;
};
imports = importList;
}

View file

@ -0,0 +1,37 @@
{
flake,
config,
pkgs,
...
}:
let
inherit (import ../config { inherit flake config pkgs; }) websiteVM;
inherit (flake.config.services) instances;
websiteUpRoot =
let
websitePkg = flake.self.packages.${pkgs.system}.website;
interfaceCfg = instances.website.interfaces.interface0;
in
websiteVM {
user = "uproot";
ip = interfaceCfg.microvm.ip;
mac = interfaceCfg.microvm.mac;
userMac = interfaceCfg.microvm.macUser;
package = websitePkg;
};
websiteProject =
let
websitePkg = flake.inputs.linkpage.packages.${pkgs.stdenv.hostPlatform.system}.websiteFrontend;
interfaceCfg = instances.website.interfaces.interface1;
in
websiteVM {
user = "project";
ip = interfaceCfg.microvm.ip;
mac = interfaceCfg.microvm.mac;
userMac = interfaceCfg.microvm.macUser;
package = websitePkg;
};
in
websiteProject // websiteUpRoot

View file

@ -10,7 +10,7 @@ let
serviceCfg = instances.zookeeper;
in
{
websiteVM =
zookeeperVM =
{
user,
ip,

29
modules/nixos/homelab/guests/zookeeper/default.nix Normal file → Executable file
View file

@ -1,26 +1,11 @@
{
flake,
pkgs,
labHelpers,
...
}:
let
inherit (import ./config { inherit flake pkgs; }) zookeeperVM;
inherit (flake.config.services) instances;
interfaceCfg = instances.zookeeper.interfaces.interface0;
importList =
let
content = builtins.readDir ./.;
dirContent = builtins.filter (n: content.${n} == "directory") (builtins.attrNames content);
in
map (name: ./. + "/${name}") dirContent;
in
{
zookeeperBot =
let
appPackage = flake.self.packages.${pkgs.system}.zookeeper;
in
zookeeperVM {
user = "boon";
ip = interfaceCfg.microvm.ip;
mac = interfaceCfg.microvm.mac;
userMac = interfaceCfg.microvm.macUser;
package = appPackage;
};
imports = importList;
}

View file

@ -0,0 +1,24 @@
{
flake,
pkgs,
lib,
...
}:
let
inherit (import ../config { inherit flake pkgs lib; }) zookeeperVM;
inherit (flake.config.services) instances;
interfaceCfg = instances.zookeeper.interfaces.interface0;
zookeeperBot =
let
appPackage = flake.self.packages.${pkgs.system}.zookeeper;
in
zookeeperVM {
user = "boon";
ip = interfaceCfg.microvm.ip;
mac = interfaceCfg.microvm.mac;
userMac = interfaceCfg.microvm.macUser;
package = appPackage;
};
in
zookeeperBot

View file

@ -6,8 +6,8 @@
let
inherit (flake.config.services) instances;
serviceCfg = instances.comfyui;
localhost = instances.web.localhost.address1;
host = serviceCfg.domains.url0;
interfaceCfg = serviceCfg.interfaces.interface0;
host = interfaceCfg.domain;
dns = instances.web.dns.provider0;
dnsPath = "dns/${dns}";
@ -29,16 +29,16 @@ in
autoStart = true;
ports = [
"${localhost}:${toString serviceCfg.ports.port0}:8188"
"0.0.0.0:${toString serviceCfg.ports.port0}:8188"
];
volumes = [
"${serviceCfg.varPaths.path0}:/root"
"${serviceCfg.varPaths.path0}/models:/root/models"
"${serviceCfg.varPaths.path0}/custom_nodes:/root/custom_nodes"
"${serviceCfg.varPaths.path0}/output:/root/output"
"${serviceCfg.varPaths.path0}/input:/root/input"
"${serviceCfg.varPaths.path0}/user:/root/user"
"${interfaceCfg.paths.varPaths.path0}:/root"
"${interfaceCfg.paths.varPaths.path0}/models:/root/models"
"${interfaceCfg.paths.varPaths.path0}/custom_nodes:/root/custom_nodes"
"${interfaceCfg.paths.varPaths.path0}/output:/root/output"
"${interfaceCfg.paths.varPaths.path0}/input:/root/input"
"${interfaceCfg.paths.varPaths.path0}/user:/root/user"
];
environment = {
@ -77,7 +77,7 @@ in
}
# Main reverse proxy with WebSocket support
reverse_proxy ${localhost}:${toString serviceCfg.ports.port0} {
reverse_proxy 0.0.0.0:${toString serviceCfg.ports.port0} {
header_up Host {host}
header_up X-Real-IP {remote}
header_up X-Forwarded-For {remote}
@ -94,7 +94,7 @@ in
}
}
tls ${serviceCfg.ssl.cert} ${serviceCfg.ssl.key}
tls ${interfaceCfg.ssl.cert} ${interfaceCfg.ssl.key}
# Security headers
header {
@ -117,8 +117,8 @@ in
};
systemd.tmpfiles.rules = [
"d ${serviceCfg.varPaths.path0} 755 root root -"
"d ${serviceCfg.secretPaths.path0}/caddy 755 caddy caddy -"
"d ${interfaceCfg.paths.varPaths.path0} 755 root root -"
"d ${interfaceCfg.paths.secretPaths.path0}/caddy 755 caddy caddy -"
"d /var/log/caddy 755 caddy caddy -"
];

View file

@ -8,4 +8,19 @@ let
in
{
imports = importList;
services = {
samba = {
enable = true;
};
};
networking = {
firewall = {
allowedTCPPorts = [
445
];
};
};
}

View file

@ -5,7 +5,6 @@
let
inherit (flake.config.services) instances;
inherit (flake.config.people) user0;
service = instances.samba;
jellyfin = instances.jellyfin;
in
{
@ -14,7 +13,6 @@ in
services = {
samba = {
# package = pkgs.samba4Full;
enable = true;
openFirewall = true;
settings = {
"storage" = {
@ -46,12 +44,4 @@ in
systemd.tmpfiles.rules = [
"d /mnt/transfer 2775 ${user0} users -"
];
networking = {
firewall = {
allowedTCPPorts = [
service.ports.port0
];
};
};
}

View file

@ -3,16 +3,13 @@
...
}:
let
inherit (flake.config.services) instances;
inherit (flake.config.people) user0;
service = instances.samba;
in
{
# If you ever need to start fresh, you need to add yourself to the Samba users database:
# sudo smbpasswd -a username
services = {
samba = {
enable = true;
openFirewall = true;
settings = {
"storage" = {
@ -27,12 +24,4 @@ in
};
};
};
networking = {
firewall = {
allowedTCPPorts = [
service.ports.port0
];
};
};
}

View file

@ -2,7 +2,8 @@
let
inherit (flake.config.services) instances;
inherit (flake.config.machines.devices) mars deimos ceres;
service = instances.wireGuard;
serviceCfg = instances.wireGuard;
interfaceCfg = serviceCfg.interfaces.interface0;
in
{
networking = {
@ -19,8 +20,8 @@ in
wireguard.interfaces = {
wg0 = {
ips = [ "${ceres.wireguard.ip0}/24" ];
listenPort = service.ports.port1;
privateKeyFile = config.sops.secrets."${service.name}/private".path;
listenPort = serviceCfg.ports.port1;
privateKeyFile = config.sops.secrets."${serviceCfg.name}/private".path;
peers = [
# if you need to create a new key pair
# wg genkey | save --raw --force privatekey
@ -41,7 +42,7 @@ in
sops =
let
sopsPath = secret: {
path = "${service.sops.path0}/${service.name}-${secret}";
path = "${interfaceCfg.paths.secretPaths.path0}/${serviceCfg.name}-${secret}";
owner = "root";
mode = "600";
};
@ -50,7 +51,7 @@ in
secrets = builtins.listToAttrs (
map
(secret: {
name = "${service.name}/${secret}";
name = "${serviceCfg.name}/${secret}";
value = sopsPath secret;
})
[