Compare commits

..

3 Commits

123 changed files with 2275 additions and 5394 deletions

2
.gitignore vendored
View File

@ -1,5 +1,3 @@
result result
result/ result/
.aider*
.env

View File

@ -1,15 +0,0 @@
# Nix Flake README
This Nix Flake defines the NixOS configurations and Home-Manager configurations for my personal systems.
## NixOS Configurations
- `architect`
- `gAluminum`
## Home-Manager Configurations
- `giulioMac`
- `giulioAarch`
- `giulioX64`
- `giulioX64NoSSH`

View File

@ -9,5 +9,6 @@ let
imports = lib.mapAttrsToList toImport (lib.filterAttrs filterCaches (builtins.readDir folder)); imports = lib.mapAttrsToList toImport (lib.filterAttrs filterCaches (builtins.readDir folder));
in { in {
inherit imports; inherit imports;
nix.settings.substituters = ["https://cache.nixos.org/"]; nix.binaryCaches = ["https://cache.nixos.org/"];
} }

View File

@ -1,13 +0,0 @@
{
nix = {
settings = {
substituters = [
"https://nix-community.cachix.org"
];
trusted-public-keys = [
"nix-community.cachix.org-1:mB9FSh9qf2dCimDSUo8Zy7bkq5CX+/rkCWyvRCYg3Fs="
];
};
};
}

12
cachix/ropfuscator.nix Normal file
View File

@ -0,0 +1,12 @@
{
nix = {
binaryCaches = [
"https://ropfuscator.cachix.org"
];
binaryCachePublicKeys = [
"ropfuscator.cachix.org-1:LZ03aJ1yqFlxpU+wfGhLlOkA3MwXqnntd2Wk7u2LnHQ="
];
};
}

248
flake.lock generated
View File

@ -1,125 +1,33 @@
{ {
"nodes": { "nodes": {
"agenix-flake": {
"inputs": {
"darwin": "darwin",
"home-manager": "home-manager",
"nixpkgs": "nixpkgs",
"systems": "systems"
},
"locked": {
"lastModified": 1747575206,
"narHash": "sha256-NwmAFuDUO/PFcgaGGr4j3ozG9Pe5hZ/ogitWhY+D81k=",
"owner": "ryantm",
"repo": "agenix",
"rev": "4835b1dc898959d8547a871ef484930675cb47f1",
"type": "github"
},
"original": {
"owner": "ryantm",
"repo": "agenix",
"type": "github"
}
},
"darwin": {
"inputs": {
"nixpkgs": [
"agenix-flake",
"nixpkgs"
]
},
"locked": {
"lastModified": 1744478979,
"narHash": "sha256-dyN+teG9G82G+m+PX/aSAagkC+vUv0SgUw3XkPhQodQ=",
"owner": "lnl7",
"repo": "nix-darwin",
"rev": "43975d782b418ebf4969e9ccba82466728c2851b",
"type": "github"
},
"original": {
"owner": "lnl7",
"ref": "master",
"repo": "nix-darwin",
"type": "github"
}
},
"home-manager": { "home-manager": {
"inputs": { "inputs": {
"nixpkgs": [ "nixpkgs": [
"agenix-flake",
"nixpkgs" "nixpkgs"
] ]
}, },
"locked": { "locked": {
"lastModified": 1745494811, "lastModified": 1639871969,
"narHash": "sha256-YZCh2o9Ua1n9uCvrvi5pRxtuVNml8X2a03qIFfRKpFs=", "narHash": "sha256-6feWUnMygRzA9tzkrfAzpA5/NBYg75bkFxnqb1DtD7E=",
"owner": "nix-community", "owner": "rycee",
"repo": "home-manager", "repo": "home-manager",
"rev": "abfad3d2958c9e6300a883bd443512c55dfeb1be", "rev": "697cc8c68ed6a606296efbbe9614c32537078756",
"type": "github" "type": "github"
}, },
"original": { "original": {
"owner": "nix-community", "owner": "rycee",
"ref": "release-21.11",
"repo": "home-manager", "repo": "home-manager",
"type": "github" "type": "github"
} }
}, },
"home-manager_2": {
"inputs": {
"nixpkgs": [
"nixpkgs"
]
},
"locked": {
"lastModified": 1748665073,
"narHash": "sha256-RMhjnPKWtCoIIHiuR9QKD7xfsKb3agxzMfJY8V9MOew=",
"owner": "nix-community",
"repo": "home-manager",
"rev": "282e1e029cb6ab4811114fc85110613d72771dea",
"type": "github"
},
"original": {
"owner": "nix-community",
"ref": "release-25.05",
"repo": "home-manager",
"type": "github"
}
},
"local-unstable": {
"locked": {
"lastModified": 0,
"narHash": "sha256-eCA4jXsPHiBkrf1sNOfQPYS2g9DoCsICVzk4ec0cEdo=",
"path": "/home/giulio/dev/nixpkgs",
"type": "path"
},
"original": {
"path": "/home/giulio/dev/nixpkgs",
"type": "path"
}
},
"nixos-unstable": { "nixos-unstable": {
"locked": { "locked": {
"lastModified": 1750506804, "lastModified": 1639699734,
"narHash": "sha256-VLFNc4egNjovYVxDGyBYTrvVCgDYgENp5bVi9fPTDYc=", "narHash": "sha256-tlX6WebGmiHb2Hmniff+ltYp+7dRfdsBxw9YczLsP60=",
"owner": "NixOS", "owner": "NixOS",
"repo": "nixpkgs", "repo": "nixpkgs",
"rev": "4206c4cb56751df534751b058295ea61357bbbaa", "rev": "03ec468b14067729a285c2c7cfa7b9434a04816c",
"type": "github"
},
"original": {
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "4206c4cb56751df534751b058295ea61357bbbaa",
"type": "github"
}
},
"nixpkgs": {
"locked": {
"lastModified": 1745391562,
"narHash": "sha256-sPwcCYuiEopaafePqlG826tBhctuJsLx/mhKKM5Fmjo=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "8a2f738d9d1f1d986b5a4cd2fd2061a7127237d7",
"type": "github" "type": "github"
}, },
"original": { "original": {
@ -129,151 +37,27 @@
"type": "github" "type": "github"
} }
}, },
"nixpkgs-master": { "nixpkgs": {
"locked": { "locked": {
"lastModified": 1749040375, "lastModified": 1639794673,
"narHash": "sha256-zwVvfxgrXcInI2E/dDg9v80OrvKUT7HtPECu53Khcq0=", "narHash": "sha256-bjauV0+Z4WmxeiHXecyiEOEwo+XysO6kx36beeatbl0=",
"owner": "NixOS", "owner": "NixOS",
"repo": "nixpkgs", "repo": "nixpkgs",
"rev": "57afa2783caf7d6713f63c8e29fba6c52a3a5300", "rev": "2627c4b795107ba94562626925f5a9a2bc62ebc6",
"type": "github" "type": "github"
}, },
"original": { "original": {
"owner": "NixOS", "owner": "NixOS",
"ref": "master", "ref": "nixos-21.11",
"repo": "nixpkgs", "repo": "nixpkgs",
"type": "github" "type": "github"
} }
}, },
"nixpkgs_2": {
"locked": {
"lastModified": 1750622754,
"narHash": "sha256-kMhs+YzV4vPGfuTpD3mwzibWUE6jotw5Al2wczI0Pv8=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "c7ab75210cb8cb16ddd8f290755d9558edde7ee1",
"type": "github"
},
"original": {
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "c7ab75210cb8cb16ddd8f290755d9558edde7ee1",
"type": "github"
}
},
"nixpkgs_3": {
"locked": {
"lastModified": 1735264675,
"narHash": "sha256-MgdXpeX2GuJbtlBrH9EdsUeWl/yXEubyvxM1G+yO4Ak=",
"owner": "nixos",
"repo": "nixpkgs",
"rev": "d49da4c08359e3c39c4e27c74ac7ac9b70085966",
"type": "github"
},
"original": {
"owner": "nixos",
"ref": "nixos-24.11",
"repo": "nixpkgs",
"type": "github"
}
},
"nvidia-patch": {
"inputs": {
"nixpkgs": [
"nixpkgs"
],
"utils": "utils"
},
"locked": {
"lastModified": 1748931931,
"narHash": "sha256-0NUhiMITsYlXDjgcVbLayI0rgrEdf5NIbpW3oLueYUs=",
"owner": "icewind1991",
"repo": "nvidia-patch-nixos",
"rev": "fa8f006a236349790c94801ac85e43f103b35baf",
"type": "github"
},
"original": {
"owner": "icewind1991",
"repo": "nvidia-patch-nixos",
"type": "github"
}
},
"pepeflake": {
"inputs": {
"nixpkgs": "nixpkgs_3"
},
"locked": {
"lastModified": 1739457875,
"narHash": "sha256-ak7EyrCKa79mZjr98PE1lFBj3UuDDIIpaYtky1MN2a0=",
"ref": "refs/heads/master",
"rev": "249e02c5c915fed09c448092d9066257317a4d68",
"revCount": 10,
"type": "git",
"url": "https://git.giugl.io/peperunas/pepeflake"
},
"original": {
"type": "git",
"url": "https://git.giugl.io/peperunas/pepeflake"
}
},
"root": { "root": {
"inputs": { "inputs": {
"agenix-flake": "agenix-flake", "home-manager": "home-manager",
"home-manager": "home-manager_2",
"local-unstable": "local-unstable",
"nixos-unstable": "nixos-unstable", "nixos-unstable": "nixos-unstable",
"nixpkgs": "nixpkgs_2", "nixpkgs": "nixpkgs"
"nixpkgs-master": "nixpkgs-master",
"nvidia-patch": "nvidia-patch",
"pepeflake": "pepeflake"
}
},
"systems": {
"locked": {
"lastModified": 1681028828,
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
"owner": "nix-systems",
"repo": "default",
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
"type": "github"
},
"original": {
"owner": "nix-systems",
"repo": "default",
"type": "github"
}
},
"systems_2": {
"locked": {
"lastModified": 1681028828,
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
"owner": "nix-systems",
"repo": "default",
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
"type": "github"
},
"original": {
"owner": "nix-systems",
"repo": "default",
"type": "github"
}
},
"utils": {
"inputs": {
"systems": "systems_2"
},
"locked": {
"lastModified": 1710146030,
"narHash": "sha256-SZ5L6eA7HJ/nmkzGG7/ISclqe6oZdOZTNoesiInkXPQ=",
"owner": "numtide",
"repo": "flake-utils",
"rev": "b1d9ab70662946ef0850d488da1c9019f3a9752a",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "flake-utils",
"type": "github"
} }
} }
}, },

172
flake.nix
View File

@ -1,160 +1,62 @@
{ {
inputs = { inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/c7ab75210cb8cb16ddd8f290755d9558edde7ee1"; nixpkgs.url = "github:NixOS/nixpkgs/nixos-21.11";
nixos-unstable.url = "github:NixOS/nixpkgs/4206c4cb56751df534751b058295ea61357bbbaa"; nixos-unstable.url = "github:NixOS/nixpkgs/nixos-unstable";
nixpkgs-master.url = "github:NixOS/nixpkgs/master";
local-unstable.url = "path:///home/giulio/dev/nixpkgs";
pepeflake.url = "git+https://git.giugl.io/peperunas/pepeflake";
agenix-flake.url = "github:ryantm/agenix";
home-manager = { home-manager = {
url = "github:nix-community/home-manager/release-25.05"; url = "github:rycee/home-manager/release-21.11";
inputs.nixpkgs.follows = "nixpkgs";
};
nvidia-patch = {
url = "github:icewind1991/nvidia-patch-nixos";
inputs.nixpkgs.follows = "nixpkgs"; inputs.nixpkgs.follows = "nixpkgs";
}; };
navidrome.url = "github:antifuchs/nixpkgs/fix-151550";
}; };
outputs = outputs = inputs@{ self, nixpkgs, nixos-unstable, home-manager, navidrome}:
{ self
, nixpkgs
, nixos-unstable
, nixpkgs-master
, local-unstable
, home-manager
, nvidia-patch
, agenix-flake
, pepeflake
}:
let let
sysLinuxX64 = "x86_64-linux"; system = "x86_64-linux";
sysDarwin = "aarch64-darwin";
sysLinuxAarch = "aarch64-linux";
wrapPkgsSystem = { system, cudaSupport ? false }: pkgs = import nixpkgs {
let inherit system;
config = { config.allowUnfree = true;
inherit cudaSupport; overlays = [ (final: prev: { inherit unstable; }) ];
};
allowUnfree = true; unstable = import nixos-unstable {
}; inherit system;
config.allowUnfree = true;
};
cachixOverlay = final: prev: { utils = import ./lib {
nixosModules = (prev.nixosModules or { }) // { inherit pkgs unstable nixpkgs nixos-unstable home-manager;
cachixConfig = import ./cachix.nix; };
};
};
inherit (utils) host;
extOverlays = [ inherit (utils) user;
(nvidia-patch.overlays.default) in {
cachixOverlay
];
importNixpkgs = { flake }:
import flake {
inherit system config;
overlays = extOverlays;
};
unstablePkgs = importNixpkgs { flake = nixos-unstable; };
masterPkgs = importNixpkgs { flake = nixpkgs-master; };
localPkgs = importNixpkgs { flake = local-unstable; };
agenixPkgs = importNixpkgs { flake = agenix-flake; };
pepePkgs = pepeflake.packages.${system} // pepeflake.legacyPackages.${system} or { };
additionalOverlays = [
(final: prev: { inherit unstablePkgs; })
(final: prev: { inherit localPkgs; })
(final: prev: { inherit agenixPkgs; })
(final: prev: { inherit masterPkgs; })
(final: prev: { inherit pepePkgs; })
];
in
import nixpkgs {
inherit system config;
overlays = additionalOverlays ++ extOverlays ++ [
(final: prev: {
ctranslate2 = prev.ctranslate2.override {
withCUDA = true;
withCuDNN = true;
};
})
];
};
wrapUtils = { pkgs }:
let
inherit (pkgs.lib) makeScope;
inherit (pkgs) newScope;
in
makeScope newScope (self: rec {
inherit nixpkgs home-manager nixos-unstable;
inherit (self.callPackage ./lib/utils.nix { }) mkSysRole mkHomeRole;
inherit (user) mkUser;
user = self.callPackage ./lib/user.nix { };
host = self.callPackage ./lib/host.nix { };
});
pkgsLinuxX64Cuda = wrapPkgsSystem { system = sysLinuxX64; cudaSupport = true; };
utilsLinuxX64Cuda = wrapUtils { pkgs = pkgsLinuxX64Cuda; };
pkgsLinuxAarch = wrapPkgsSystem { system = sysLinuxAarch; };
utilsLinuxAarch = wrapUtils { pkgs = pkgsLinuxAarch; };
pkgsDarwin = wrapPkgsSystem { system = sysDarwin; };
utilsDarwin = wrapUtils { pkgs = pkgsDarwin; };
in
{
nixosConfigurations = { nixosConfigurations = {
architect = utilsLinuxX64Cuda.host.mkHost { architect = host.mkHost {
name = "architect"; name = "architect";
users = [{ users = [{
user = "giulio"; user = "giulio";
roles = [ ]; roles = [ ];
}]; }];
imports = [ imports = [
agenix-flake.nixosModules.default {
disabledModules = [ "services/audio/navidrome.nix" ];
imports =
[ (navidrome + "/nixos/modules/services/audio/navidrome.nix") ];
}
]; ];
}; };
}; gAluminum = host.mkHost {
name = "gAluminum";
homeConfigurations = { users = [{
giulioMac = utilsDarwin.user.mkHMUser { user = "giulio";
name = "giulio"; roles = [ "desktop" "ssh" "git" ];
roles = [ "ssh" ]; }];
roles = [ "gnome" ];
}; };
gdepasqualeMac = utilsDarwin.user.mkHMUser { proxy = host.mkHost {
name = "gdepasquale"; name = "proxy";
roles = [ "ssh" ]; users = [ ];
};
giulioAarch = utilsLinuxAarch.user.mkHMUser {
name = "giulio";
roles = [ "ssh" ];
};
giulioX64 = utilsLinuxX64Cuda.user.mkHMUser {
name = "giulio";
roles = [ "ssh" "go" ];
};
giulioX64NoSSH = utilsLinuxX64Cuda.user.mkHMUser {
name = "giulio";
roles = [ "go" ];
};
gdepasqualeX64 = utilsLinuxX64Cuda.user.mkHMUser {
name = "gdepasquale";
roles = [ "ssh" "go" ];
};
};
defaultTemplate = self.templates.basicShell;
templates = {
basicShell = {
path = ./templates/basicShell;
description = "A barebone shell with custom defined packages";
}; };
}; };
}; };

View File

@ -1,25 +1,13 @@
{ config, ... }: { config, lib, ... }:
{ {
age.secrets = {
restic-passwords = {
file = ../../secrets/restic-passwords.age;
};
restic-environment = {
file = ../../secrets/restic-environment.age;
};
};
services.restic.backups = { services.restic.backups = {
backblaze = { backblaze = {
initialize = true; initialize = true;
passwordFile = config.age.secrets.restic-passwords.path; passwordFile = "/secrets/restic/data.key";
environmentFile = config.age.secrets.restic-environment.path; environmentFile = "/secrets/restic/credentials.txt";
repository = "b2:architect:/"; repository = "b2:architect:/";
paths = [ "/var/lib" "/services" ]; paths = [ "/var/lib" "/secrets" ];
exclude = [
"/var/lib/ollama"
];
pruneOpts = [ pruneOpts = [
"--keep-daily 45" "--keep-daily 45"
"--keep-weekly 12" "--keep-weekly 12"
@ -27,8 +15,8 @@
"--keep-yearly 3" "--keep-yearly 3"
]; ];
timerConfig = { timerConfig = {
OnCalendar = "monday 09:00"; OnCalendar = "monday 00:05";
RandomizedDelaySec = "1h"; RandomizedDelaySec = "2h";
}; };
}; };
}; };

View File

@ -0,0 +1,33 @@
{ lib, ... }:
let
domain = "htbaz.giugl.io";
network = import ./network.nix;
in {
services = {
bazarr = {
enable = true;
group = "media";
};
nginx.virtualHosts.${domain} = {
forceSSL = true;
enableACME = true;
locations."/" = {
proxyPass = "http://localhost:6767";
extraConfig = ''
allow 10.0.0.0/24;
${lib.concatMapStrings (x: "allow ${x};") network.gdevices-wg}
deny all;
'';
};
};
};
networking.extraHosts = ''
${network.architect-lan} ${domain}
${network.architect-wg} ${domain}
'';
users.groups.media.members = [ "bazarr" ];
}

View File

@ -1,39 +0,0 @@
{ config, lib, ... }:
let
domain = "books.giugl.io";
auth_block = (import ./openid.nix { inherit lib; }).openresty_oidc_block;
utilities = import ./utilities.nix { inherit lib config; };
inherit (utilities) architectInterfaceAddress;
in
{
services = {
calibre-web = {
enable = true;
group = "media";
options = {
enableBookConversion = true;
enableBookUploading = true;
};
};
nginx.virtualHosts.${domain} = {
forceSSL = true;
enableACME = true;
locations."/" = {
proxyPass = "http://127.0.0.1:8083";
extraConfig = ''
client_max_body_size 500M;
'' + auth_block { access_role = "calibre"; };
};
};
};
networking.extraHosts = ''
${architectInterfaceAddress "lan"} ${domain}
${architectInterfaceAddress "tailscale"} ${domain}
'';
users.groups.media.members = [ "calibre-web" ];
}

View File

@ -0,0 +1,6 @@
{
programs.ccache.enable = true;
nix.extraOptions = ''
extra-sandbox-paths = /nix/var/cache/ccache
'';
}

View File

@ -1,40 +1,59 @@
{ config, pkgs, ... }: { config, pkgs, ... }:
let let
macbookPubkey = (import ../pubkeys.nix).macbook; pubkeys = [
pubkeys = [ macbookPubkey ]; "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQC1we38/N+t8Ah5yrLof8QUwhrob7/VXFKIddaJeOVBLuDVnW7ljiAtdtEiL69D/DV4Ohmt5wMvkAAjfuHmim6FD9A6lzPbSU4KH9W2dcckszKbbI636kuDwem/xui6BW3wJa6P+0xW5ksygEAkzcK2PXuC2b4B9uwhuUdKahiGMKDxISG/WianqAe72cGMfNkYvion3Y1VsMLUdm48d2ABnxNpr7NI9B5iJ8dziOft9gpgfz13CCQRlReo75gk/4xI+vSNrQp7eR+wzJy2/dZg/T8jtyA9Q6jVxrxBpqQ1LNXkAKaJkGo9OabF6Wgpzp+YTAurL4nwR2NaJxwFuyoKvACQy0ai4jrS3206gC6JXZv8ktZMZrwUN+jPqCwfgh5qObFkAqKCxbp52ioDek2MQLdOvzQBX//DBhGEp5rzHGLZ3vhRIiiQiaof5sF5zWiYDW5mqezSPNxJPX/BrTP/Wbs/jpwTLBh3wytiia0S1WXQmya89bqzTPFiDWvTRA62EVKB/JaQtPQQOFAxWwg799DMycPeZ81xttZOyMtI/MZSddyqx2S8fWGwvToZQvuZ38mSIpFseLM1IkgabRIrAmat5SBNGGy9Dqa0eMEa7bwIY/4CMB1y6HMTnaoMXA6cnQfHMoB/zyTZ6oTXIeqeOyiZsK+RN0Mvahj8mXi7dw== giulio@giulio-X230"
domain = "devs.giugl.io"; ];
in hostname = "architect";
{ network = import ./network.nix;
imports = [ in {
./options.nix imports = [ # Include the results of the hardware scan.
./backup.nix ./backup.nix
./hardware.nix ./hardware.nix
./firewall.nix ./firewall.nix
./nginx.nix ./nginx.nix
./gitea.nix
./sonarr.nix
./radarr.nix
./bazarr.nix
./nzbget.nix
./nextcloud.nix ./nextcloud.nix
./wireguard.nix
./minio.nix
./matrix.nix ./matrix.nix
./fail2ban.nix ./fail2ban.nix
./dns.nix ./dns.nix
./tailscale.nix #./minecraft.nix
./sunshine.nix ./prowlarr.nix
./postgres.nix ./plex.nix
./netdata.nix ./githubrunner.nix
./searx.nix ./libreddit.nix
./invidious.nix
./nitter.nix
./ccache.nix
./lidarr.nix
./navidrome.nix
./jellyfin.nix
./prosody.nix
./deluge.nix
]; ];
age.identityPaths = [ "/root/.ssh/id_ed25519" ]; time.timeZone = "Europe/Rome";
system.stateVersion = "21.11"; # Did you read the comment?
architect = {
firewall = {
openTCP = [ 22 ];
};
};
time.timeZone = "Europe/London";
users.users.giulio.openssh.authorizedKeys.keys = pubkeys; users.users.giulio.openssh.authorizedKeys.keys = pubkeys;
services.fwupd.enable = true;
boot = { boot = {
kernelParams = [
"ip=${network.architect-lan}::10.0.0.1:255.255.255.0::${network.wan-if}:off"
"nvme_core.default_ps_max_latency_us=5500"
];
kernel.sysctl= {
"net.ipv4.ip_forward" = 1;
"fs.protected_regular" = 0;
};
initrd = { initrd = {
availableKernelModules = [ "igc" "r8169" ]; availableKernelModules = [ "igc" "r8169" ];
network = { network = {
@ -42,20 +61,22 @@ in
ssh = { ssh = {
enable = true; enable = true;
port = 22; port = 22;
hostKeys = [ /secrets/ssh_host_rsa_key ]; hostKeys = [ /boot/ssh_host_rsa_key ];
authorizedKeys = pubkeys; authorizedKeys = pubkeys;
}; };
postCommands = ''
zpool import backedpool
zpool import zpool
mkdir /mnt-root
echo "zfs load-key -ar; mount -t zfs zpool/nixos/root /mnt-root; zfs load-key -a; umount /mnt-root; rmdir /mnt-root; killall zfs" >> /root/.profile
'';
}; };
}; };
kernelParams = with config.pepe.core.network.interfaces.lan; [
"ip=${devices.architect.address}::${devices.brigettine.address}:255.255.255.0::${interface}:off"
];
kernel.sysctl = { "net.ipv4.ip_forward" = 1; };
loader = { loader = {
systemd-boot = { systemd-boot ={
enable = true; enable = true;
memtest86.enable = true; memtest86.enable = true;
}; };
@ -64,212 +85,71 @@ in
supportedFilesystems = [ "zfs" ]; supportedFilesystems = [ "zfs" ];
zfs.requestEncryptionCredentials = true; zfs.requestEncryptionCredentials = true;
tmp.tmpfsSize = "50%"; tmpOnTmpfsSize = "80%";
}; };
networking = with config.pepe.core.network.interfaces.lan; { networking = {
hostName = "architect"; hostName = hostname;
hostId = "49350853"; hostId = "49350853";
useDHCP = false; useDHCP = false;
defaultGateway = devices.brigettine.address; defaultGateway = "10.0.0.1";
interfaces = { interfaces = {
${interface}.ipv4.addresses = [{ enp5s0.ipv4.addresses = [{
address = devices.architect.address; address = network.architect-lan;
prefixLength = 24; prefixLength = 24;
}]; }];
enp6s0.useDHCP = false;
wlp4s0.useDHCP = false;
}; };
extraHosts = ''
127.0.0.1 ${hostname}.devs.giugl.io localhost
# LAN
${network.architect-lan} ${hostname}.devs.giugl.io
${network.dvr-lan} dvr.devs.giugl.io
${network.nas-lan} nas.devs.giugl.io
192.168.1.1 vodafone.station
# Blacklist
0.0.0.0 metrics.plex.tv
0.0.0.0 analytics.plex.tv
0.0.0.0 cdn.luckyorange.com
0.0.0.0 w1.luckyorange.com
0.0.0.0 browser.sentry-cdn.com
0.0.0.0 analytics.facebook.com
0.0.0.0 ads.facebook.com
0.0.0.0 extmaps-api.yandex.net
0.0.0.0 logservice.hicloud.com
0.0.0.0 logbak.hicloud.com
0.0.0.0 logservice1.hicloud.com
0.0.0.0 samsung-com.112.2o7.net
0.0.0.0 supportmetrics.apple.com
0.0.0.0 analytics.oneplus.cn
0.0.0.0 click.oneplus.cn
0.0.0.0 analytics-api.samsunghealthcn.com
'';
}; };
services = { environment.systemPackages = with pkgs; [ cudatoolkit cachix ];
fwupd.enable = true;
das_watchdog.enable = true;
zfs.autoScrub.enable = true;
hardware = {
opengl.enable = true;
opengl.extraPackages = with pkgs; [ vaapiVdpau ];
opengl.driSupport = true;
};
services.das_watchdog.enable = true;
services = {
zfs.autoScrub.enable = true;
xserver.videoDrivers = [ "nvidia" ];
openssh = { openssh = {
enable = true; enable = true;
passwordAuthentication = false;
settings = { challengeResponseAuthentication = false;
PasswordAuthentication = false;
KbdInteractiveAuthentication = false;
};
extraConfig = ''
MaxAuthTries 15
'';
}; };
smartd.enable = true; smartd.enable = true;
}; };
pepe = { environment.variables = { LIBVA_DRIVER_NAME = "vdpau"; };
core = {
media = {
enable = true;
path = "/media";
};
network.interfaces = {
lan = {
interface = "enp6s0";
type = "lan";
net = "10.0.0.0/24";
devices = {
architect = { address = "10.0.0.250"; hostname = "architect.${domain}"; isEndpoint = true; };
brigettine = { address = "10.0.0.1"; hostname = "router.${domain}"; };
dreamel10 = { address = "10.0.0.199"; hostname = "dreamel10.${domain}"; };
reolinkcamera = { address = "10.0.0.200"; hostname = "reolinkcamera.${domain}"; };
lgtv = { address = "10.0.0.202"; hostname = "lgtv.${domain}"; };
};
};
};
};
services = {
gitea = {
enable = true;
domain = "git.giugl.io";
};
immich = {
enable = true;
domain = "photos.giugl.io";
package = pkgs.unstablePkgs.immich;
};
radarr = {
enable = true;
domain = "htrad.giugl.io";
package = pkgs.unstablePkgs.radarr;
};
sonarr = {
enable = true;
domain = "htson.giugl.io";
package = pkgs.unstablePkgs.sonarr;
};
bazarr = {
enable = true;
domain = "htbaz.giugl.io";
package = pkgs.unstablePkgs.bazarr;
};
nzbget = {
enable = true;
domain = "htnzb.giugl.io";
package = pkgs.unstablePkgs.nzbget;
};
jellyfin = {
enable = true;
domain = "media.giugl.io";
package = pkgs.unstablePkgs.jellyfin;
};
jellyseer = {
enable = true;
domain = "aumm-aumm.giugl.io";
};
prowlarr = {
enable = true;
domain = "htpro.giugl.io";
};
redlib = {
enable = true;
domain = "reddit.giugl.io";
package = pkgs.unstablePkgs.redlib;
settings = {
REDLIB_ROBOTS_DISABLE_INDEXING = "on";
REDLIB_DEFAULT_THEME = "dracula";
REDLIB_DEFAULT_SHOW_NSFW = "on";
REDLIB_DEFAULT_BLUR_NSFW = "off";
REDLIB_DEFAULT_USE_HLS = "on";
REDLIB_DEFAULT_HIDE_HLS_NOTIFICATION = "on";
};
};
llm = {
enable = true;
package = pkgs.unstablePkgs.ollama-cuda;
backendDomain = "ollama.giugl.io";
acceleration = "cuda";
};
homeassistant = {
enable = true;
package = pkgs.unstablePkgs.home-assistant;
domain = "home.giugl.io";
extraComponents = [
"otbr"
"litterrobot"
"apple_tv"
"homekit"
"homekit_controller"
"spotify"
"hue"
"sonos"
"tplink"
"ollama"
"wyoming"
"whisper"
"piper"
"isal"
"radarr"
"sonarr"
"mqtt"
"mqtt_eventstream"
"mqtt_json"
"mqtt_room"
"mqtt_statestream"
"github"
"webostv"
"reolink"
"onvif"
"xiaomi_miio"
"ring"
];
extraPackages = python3Packages: with pkgs.unstablePkgs.python3Packages; [
pyporscheconnectapi
];
config = {
http = {
server_host = "127.0.0.1";
server_port = 8123;
use_x_forwarded_for = true;
trusted_proxies = [ "127.0.0.1" ];
};
homeassistant = {
name = "Brigettine Square";
latitude = 52.1958;
longitude = 0.180746;
unit_system = "metric";
};
default_config = { };
automation = "!include automations.yaml";
frontend.themes = "!include_dir_merge_named themes";
};
};
headscale = {
enable = true;
domain = "vipienne.giugl.io";
settings = with config.pepe.core.network.interfaces.tailscale; {
server_url = "https://${domain}";
prefixes.v4 = net;
dns = {
magic_dns = false;
override_local_dns = true;
global = [ devices.architect.address ];
nameservers.global = [ devices.architect.address ];
};
log.level = "debug";
};
};
};
};
} }

View File

@ -0,0 +1,52 @@
{ lib, config, pkgs, ... }:
let
domain = "htdel.giugl.io";
network = import ./network.nix;
in {
services = {
deluge = {
enable = true;
group = "media";
declarative = true;
config = {
download_location = "/media/deluge";
max_upload_speed = 20;
# full-stream
enc_level = 1;
# forced
enc_in_policy = 0;
# forced
enc_out_policy = 0;
max_active_seeding = 100;
max_connections_global = 1000;
max_active_limit = 100;
max_active_downloading = 100;
listen_ports = [ 51413 51414 ];
random_port = false;
enabled_plugins = [ "Label" "Extractor" ];
};
web.enable = true;
authFile = "/secrets/deluge/auth";
extraPackages = [ pkgs.unrar ];
};
nginx.virtualHosts.${domain} = {
locations."/" = {
proxyPass = "http://localhost:8112";
extraConfig = ''
allow 10.0.0.0/24;
${lib.concatMapStrings (x: "allow ${x};") network.gdevices-wg}
deny all;
'';
};
};
};
networking.extraHosts = ''
${network.architect-lan} ${domain}
${network.architect-wg} ${domain}
'';
users.groups.media.members = [ "deluge" ];
}

View File

@ -1,12 +1,40 @@
{ ... }: { config, pkgs, lib, ... }:
{ {
pepe.core.dns = { services = {
enable = true; dnsmasq = {
nextDNSId = "d65174"; enable = true;
extraDomains = { servers = [ "127.0.0.1#5300" ];
"architect.devs.giugl.io" = { extraConfig = ''
dnsInterfaces = [ "lan" "tailscale" ]; localise-queries
min-cache-ttl=120
max-cache-ttl=2400
'';
};
adguardhome = {
enable = true;
port = 3031;
};
dnscrypt-proxy2 = {
enable = true;
settings = {
listen_addresses = [ "127.0.0.1:5353" ];
ipv4_servers = true;
ipv6_servers = false;
block_ipv6 = true;
dnscrypt_servers = true;
doh_servers = true;
require_nolog = true;
require_nofilter = true;
timeout = 350;
lb_strategy = "p4";
lb_estimator = true;
ignore_system_dns = true;
fallback_resolvers = [ "1.1.1.1:53" "9.9.9.9:53" ];
cache_min_ttl = 450;
cache_max_ttl = 2400;
}; };
}; };
}; };

View File

@ -1,25 +0,0 @@
{ config, ... }:
{
architect.networks.docker = {
interface = "docker0";
net = "172.17.0.0/16";
};
hardware.nvidia-container-toolkit.enable = true;
virtualisation = {
oci-containers.backend = "docker";
docker = {
enable = true;
extraOptions = ''
--dns 127.0.0.1 --dns ${config.architect.networks.lan.devices.architect.address} --data-root /docker
'';
enableOnBoot = false;
daemon.settings.iptables = false;
};
};
users.users.giulio.extraGroups = [ "docker" ];
}

View File

@ -1,14 +1,9 @@
{ config, pkgs, ... }: { config, pkgs, ... }: {
{
services.fail2ban = { services.fail2ban = {
enable = true; enable = true;
package = pkgs.fail2ban; package = pkgs.fail2ban;
packageFirewall = pkgs.nftables; packageFirewall = pkgs.nftables;
bantime-increment.enable = true; bantime-increment.enable = true;
ignoreIP = [ ignoreIP = [ "10.0.0.0/24" "10.3.0.0/24" ];
config.pepe.core.network.interfaces.tailscale.net
config.pepe.core.network.interfaces.lan.net
];
}; };
} }

View File

@ -1,21 +1,33 @@
{ config, lib, ... }: { config, lib, ... }:
with import ./network.nix;
let let
firewallRules = config.pepe.core.firewall; open_tcp_ports = lib.concatMapStringsSep "," (x: toString x) [
openTCP = lib.concatMapStringsSep "," (x: toString x) firewallRules.openTCP; 22 # ssh
openUDP = lib.concatMapStringsSep "," (x: toString x) firewallRules.openUDP; 80 # http
ifaces = config.pepe.core.network.interfaces; 443 # https
8448 # matrix
deviceAddress = interface: device: 10022 # gitea
ifaces.${interface}.devices.${device}.address; 18080 # monero
51413 # transmission
gdevices = [
(deviceAddress "tailscale" "architect")
(deviceAddress "tailscale" "manduria")
(deviceAddress "tailscale" "kmerr")
]; ];
in open_udp_ports = lib.concatMapStringsSep "," (x: toString x) [
{ 1194 # wireguard
51413 # transmission
];
open_tcp_ports_vpn = lib.concatMapStringsSep "," (x: toString x) [
22
80
443
32400 # plex
];
open_udp_ports_vpn = lib.concatMapStringsSep "," (x: toString x) [
53 # dns
1194 # vpn
];
in {
networking = { networking = {
# needed to use nftables # needed to use nftables
firewall.enable = false; firewall.enable = false;
@ -23,150 +35,170 @@ in
nftables = { nftables = {
enable = true; enable = true;
ruleset = with config.pepe.core.network.interfaces; '' ruleset = ''
table ip raw { table ip raw {
chain PREROUTING { chain PREROUTING {
type filter hook prerouting priority raw; policy accept; type filter hook prerouting priority raw; policy accept;
} }
chain OUTPUT { chain OUTPUT {
type filter hook output priority raw; policy accept; type filter hook output priority raw; policy accept;
} }
} }
table ip nat { table ip nat {
chain DOCKER { chain PREROUTING {
type nat hook prerouting priority dstnat; policy accept; type nat hook prerouting priority dstnat; policy accept;
} }
chain PREROUTING {
type nat hook prerouting priority dstnat; policy accept;
}
chain INPUT { chain INPUT {
type nat hook input priority 100; policy accept; type nat hook input priority 100; policy accept;
} }
chain OUTPUT { chain OUTPUT {
type nat hook output priority -100; policy accept; type nat hook output priority -100; policy accept;
} }
chain POSTROUTING { chain POSTROUTING {
type nat hook postrouting priority srcnat; policy accept; type nat hook postrouting priority srcnat; policy accept;
oifname ${lan.interface} ip saddr ${tailscale.net} masquerade oifname ${wan-if} ip saddr {${
} lib.concatStringsSep "," towan-wg
} }} masquerade
}
}
table ip mangle { table ip mangle {
chain PREROUTING { chain PREROUTING {
type filter hook prerouting priority mangle; policy drop; type filter hook prerouting priority mangle; policy drop;
ct state invalid,untracked drop comment "drop invalid" ct state invalid,untracked drop comment "drop invalid"
ip daddr 255.255.255.255 accept comment "allow broadcast traffic" ip daddr 255.255.255.255 accept comment "allow broadcast traffic"
ip daddr 224.0.0.0/4 accept comment "allow multicast traffic" ip daddr 224.0.0.0/4 accept comment "allow multicast traffic"
iifname ${lan.interface} ip saddr 127.0.0.0/8 drop comment "bind any ip to intf ${lan.interface}" iifname ${wan-if} ip saddr ${vpn-net} drop comment "bind any ip to intf ${wan-if}"
iifname ${lan.interface} accept comment "bind any ip to intf ${lan.interface}" iifname ${wan-if} ip saddr 127.0.0.0/8 drop comment "bind any ip to intf ${wan-if}"
iifname ${tailscale.interface} ip saddr ${tailscale.net} accept iifname ${wan-if} accept comment "bind any ip to intf ${wan-if}"
iifname ${tailscale.interface} ip saddr 100.100.100.100/32 accept iifname ${proxy-if} ip saddr ${proxy-net} accept comment "bind ip ${proxy-net} to intf ${proxy-if}"
iifname "lo" accept comment "bind any ip to intf lo" iifname ${vpn-if} ip saddr ${vpn-net} accept comment "bind ip ${vpn-net} to intf ${vpn-if}"
jump mangle_drop iifname "lo" accept comment "bind any ip to intf lo"
} jump mangle_drop
}
chain INPUT { chain INPUT {
type filter hook input priority mangle; policy accept; type filter hook input priority mangle; policy accept;
} }
chain FORWARD { chain FORWARD {
type filter hook forward priority mangle; policy accept; type filter hook forward priority mangle; policy accept;
} }
chain OUTPUT { chain OUTPUT {
type route hook output priority mangle; policy accept; type route hook output priority mangle; policy accept;
} }
chain POSTROUTING { chain POSTROUTING {
type filter hook postrouting priority mangle; policy accept; type filter hook postrouting priority mangle; policy accept;
} }
chain mangle_drop { chain mangle_drop {
ip protocol icmp jump mangle_drop_icmp ip protocol icmp jump mangle_drop_icmp
ip protocol udp jump mangle_drop_udp ip protocol udp jump mangle_drop_udp
ip protocol tcp jump mangle_drop_tcp ip protocol tcp jump mangle_drop_tcp
log prefix "MANGLE-DROP-UNK " log prefix "MANGLE-DROP-UNK "
drop drop
} }
chain mangle_drop_icmp { chain mangle_drop_icmp {
log prefix "MANGLE-DROP-ICMP " log prefix "MANGLE-DROP-ICMP "
drop drop
} }
chain mangle_drop_tcp { chain mangle_drop_tcp {
log prefix "MANGLE-DROP-TCP " log prefix "MANGLE-DROP-TCP "
drop drop
} }
chain mangle_drop_udp { chain mangle_drop_udp {
log prefix "MANGLE-DROP-UDP " log prefix "MANGLE-DROP-UDP "
drop drop
} }
} }
table ip filter { table ip filter {
chain INPUT { chain INPUT {
type filter hook input priority filter; policy drop; type filter hook input priority filter; policy drop;
ct state established,related accept ct state established,related accept
iifname "lo" accept comment "loopback" iifname "lo" accept comment "loopback"
ip daddr 255.255.255.255 accept comment "allow broadcast traffic" ip daddr 255.255.255.255 accept comment "allow broadcast traffic"
ip daddr 224.0.0.0/4 accept comment "allow multicast traffic" ip daddr 224.0.0.0/4 accept comment "allow multicast traffic"
ip saddr ${lan.net} accept comment "lan > local" ip saddr ${lan-net} accept comment "lan > local"
ip saddr ${tailscale.net} accept comment "tailscale > local" ip saddr ${proxy-wg} accept comment "proxy > local"
ip saddr {${lib.concatStringsSep "," gdevices}} accept comment "vpn > local" ip saddr {${lib.concatStringsSep "," gdevices-wg}} accept comment "vpn > local"
iifname ${lan.interface} tcp dport {${openTCP}} accept iifname ${wan-if} tcp dport {${open_tcp_ports}} accept
iifname ${lan.interface} udp dport {${openUDP}} accept iifname ${wan-if} udp dport {${open_udp_ports}} accept
jump filter_drop iifname ${vpn-if} tcp dport {${open_tcp_ports_vpn}} accept
} iifname ${vpn-if} udp dport {${open_udp_ports_vpn}} accept
iifname ${vpn-if} icmp type echo-request accept
chain FORWARD { jump filter_drop
type filter hook forward priority filter; policy drop; }
ct state established,related accept
oifname ${lan.interface} ip saddr ${tailscale.net} accept chain FORWARD {
type filter hook forward priority filter; policy drop;
ct state established,related accept
# client to client
ip saddr {${lib.concatStringsSep "," c2c-wg}} ip daddr {${
lib.concatStringsSep "," c2c-wg
}} accept
jump filter_drop # gdevices talking to everyone in VPN
} ip saddr {${
lib.concatStringsSep "," gdevices-wg
}} ip daddr ${vpn-net} accept
ip saddr {${
lib.concatStringsSep "," gamenet-wg
}} ip daddr {${lib.concatStringsSep "," gamenet-wg}} accept
# nat to wan
oifname ${wan-if} ip saddr {${
lib.concatStringsSep "," towan-wg
}} accept
jump filter_drop
}
chain OUTPUT { chain OUTPUT {
type filter hook output priority filter; policy drop; type filter hook output priority filter; policy drop;
ct state established,related accept ct state established,related accept
accept comment "local > *" accept comment "local > *"
jump filter_drop jump filter_drop
} }
chain filter_drop { chain filter_drop {
ip protocol icmp jump filter_drop_icmp ip protocol icmp jump filter_drop_icmp
ip protocol udp jump filter_drop_udp ip protocol udp jump filter_drop_udp
ip protocol tcp jump filter_drop_tcp ip protocol tcp jump filter_drop_tcp
log prefix "DROP-UNK " log prefix "DROP-UNK "
drop drop
} }
chain filter_drop_icmp { chain filter_drop_icmp {
log prefix "DROP-icmp " log prefix "DROP-icmp "
drop drop
} }
chain filter_drop_tcp { chain filter_drop_tcp {
log prefix "DROP-tcp " log prefix "DROP-tcp "
drop drop
} }
chain filter_drop_udp { chain filter_drop_udp {
log prefix "DROP-udp " log prefix "DROP-udp "
drop drop
} }
} }
''; '';
}; };
}; };
} }

37
hosts/architect/gitea.nix Normal file
View File

@ -0,0 +1,37 @@
{ lib, ... }:
let
domain = "git.giugl.io";
network = import ./network.nix;
in {
services.gitea = {
enable = true;
database.type = "sqlite3";
domain = domain;
appName = "Gitea";
rootUrl = "https://${domain}";
ssh.clonePort = 22;
settings.server.LFS_START_SERVER = true;
};
services.nginx.virtualHosts.${domain} = {
forceSSL = true;
enableACME = true;
locations."/" = {
proxyPass = "http://localhost:3000";
extraConfig = ''
allow 127.0.0.1;
allow 10.0.0.0/24;
${lib.concatMapStrings (x: "allow ${x};") network.gdevices-wg}
allow 10.4.0.0/24;
deny all;
'';
};
};
networking.extraHosts = ''
${network.architect-lan} ${domain}
${network.architect-wg} ${domain}
'';
}

View File

@ -0,0 +1,15 @@
{ ... }:
{
services.github-runner = {
enable = true;
url = "https://github.com/ropfuscator";
tokenFile = "/secrets/github-runner/token";
replace = true;
};
nix.extraOptions = ''
tarball-ttl = 0
access-tokens = github.com=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
'';
}

View File

@ -1,69 +1,50 @@
{ config, lib, modulesPath, ... }: # Do not modify this file! It was generated by nixos-generate-config
# and may be overwritten by future invocations. Please make changes
# to /etc/nixos/configuration.nix instead.
{ config, lib, pkgs, modulesPath, ... }:
{ {
imports = [ (modulesPath + "/installer/scan/not-detected.nix") ]; imports = [ (modulesPath + "/installer/scan/not-detected.nix") ];
hardware.cpu.amd.updateMicrocode =
lib.mkDefault config.hardware.enableRedistributableFirmware;
environment.etc."crypttab".text = '' boot.initrd.availableKernelModules =
backedNvme /dev/disk/by-uuid/92cfaa4a-82a1-4336-b552-b7f4f3c68613 /newdrive.key [ "nvme" "xhci_pci" "ahci" "usbhid" "usb_storage" "sd_mod" ];
''; boot.initrd.kernelModules = [ ];
boot.kernelModules = [ "kvm-amd" ];
boot.extraModulePackages = [ ];
boot = { fileSystems."/" = {
kernelModules = [ "kvm-amd" "dm-snapshot" ]; device = "zpool/nixos/root";
initrd = { fsType = "zfs";
luks.devices = {
# backedNvme = {
# device = "/dev/disk/by-uuid/92cfaa4a-82a1-4336-b552-b7f4f3c68613";
# keyFile = "/newdrive.key";
# allowDiscards = true;
# };
root = {
device = "/dev/disk/by-uuid/bdd5f111-ecec-48d8-861f-94083098c724";
preLVM = true;
allowDiscards = true;
fallbackToPassword = true;
};
};
availableKernelModules =
[ "nvme" "xhci_pci" "ahci" "usbhid" "usb_storage" "sd_mod" ];
};
}; };
fileSystems = { fileSystems."/home" = {
"/" = { device = "zpool/data/home";
device = "/dev/disk/by-uuid/28ce6650-de21-4c1d-ae42-95d1e3507740"; fsType = "zfs";
fsType = "ext4";
};
"/boot" = {
device = "/dev/disk/by-uuid/B790-869D";
fsType = "vfat";
};
"/backednvme" = {
device = "/dev/mapper/backedNvme";
};
"/services" = {
device = "/backednvme/services";
options = [ "bind" ];
};
"/secrets" = {
device = "/backednvme/secrets";
options = [ "bind" ];
};
"${config.pepe.core.media.path}" = lib.mkIf config.pepe.core.media.enable {
device = "nvmedata/media";
fsType = "zfs";
};
}; };
fileSystems."/media" = {
device = "datapool/media";
fsType = "zfs";
};
fileSystems."/secrets" = {
device = "backedpool/secrets";
fsType = "zfs";
};
fileSystems."/var/lib" = {
device = "backedpool/services";
fsType = "zfs";
};
fileSystems."/boot" = {
device = "/dev/disk/by-uuid/AF19-5616";
fsType = "vfat";
};
swapDevices = [{ swapDevices = [{
device = "/swapfile"; device = "/dev/sdc1";
size = 1024 * 64; size = 10000;
}]; }];
} }

View File

@ -1,22 +1,24 @@
{ config, lib, pkgs, ... }: { lib, ... }:
let let
domain = "tube.giugl.io"; domain = "tube.giugl.io";
in network = import ./network.nix;
{ in {
services.invidious = { services = {
enable = true; invidious = {
package = pkgs.unstablePkgs.invidious; enable = true;
settings = { port = 9092;
hmac_key = "a2a91eca269d26de1221285e8981879834045bff"; };
nginx.virtualHosts.${domain} = {
forceSSL = true;
enableACME = true;
locations."/" = { proxyPass = "http://localhost:9092"; };
}; };
}; };
architect.vhost.${domain} = { networking.extraHosts = ''
dnsInterfaces = [ "lan" "tailscale" ]; ${network.architect-lan} ${domain}
locations."/" = { ${network.architect-wg} ${domain}
port = config.services.invidious.port; '';
allowWAN = true;
};
};
} }

View File

@ -0,0 +1,45 @@
{ pkgs, ... }:
let
network = import ./network.nix;
domain = "jellyfin.giugl.io";
in {
disabledModules = [ "services/misc/jellyfin.nix" ];
imports = [ ./modules/jellyfin.nix ];
services = {
jellyfin = {
enable = true;
group = "media";
package = pkgs.unstable.jellyfin;
};
nginx.virtualHosts.${domain} = {
# forceSSL = true;
# enableACME = true;
locations."/" = {
proxyPass = "http://localhost:8096";
extraConfig = ''
allow 10.0.0.0/24;
allow 10.3.0.0/24;
deny all;
'';
};
};
};
networking.extraHosts = ''
${network.architect-lan} ${domain}
${network.architect-wg} ${domain}
'';
users.groups.media.members = [ "jellyfin" ];
users.groups.video.members = [ "jellyfin" ];
users.groups.render.members = [ "jellyfin" ];
fileSystems."/tmp/jellyfin" = {
device = "none";
fsType = "tmpfs";
options = [ "defaults" "size=20G" "uid=jellyfin" ];
};
}

View File

@ -0,0 +1,24 @@
{ lib, pkgs, ... }:
let
domain = "reddit.giugl.io";
network = import ./network.nix;
in {
services = {
libreddit = {
enable = true;
port = 9090;
};
nginx.virtualHosts.${domain} = {
forceSSL = true;
enableACME = true;
locations."/" = { proxyPass = "http://localhost:9090"; };
};
};
networking.extraHosts = ''
${network.architect-lan} ${domain}
${network.architect-wg} ${domain}
'';
}

View File

@ -1,82 +0,0 @@
{ config, lib, ... }:
let
domain = "photos.giugl.io";
backendPort = 8001;
frontendPort = 3000;
in
{
architect.vhost.${domain} = {
dnsInterfaces = [ "tailscale" ];
locations."/" = {
host = "172.17.0.1";
port = frontendPort;
# allowLan = true;
# allow = [ config.architect.networks."tailscale".net ];
};
locations."~ ^/(api|media)/" = {
host = "172.17.0.1";
port = backendPort;
# allowLan = true;
# allow = [ config.architect.networks."tailscale".net ];
};
locations."/ws" = {
host = "172.17.0.1";
port = backendPort;
proxyWebsockets = true;
# allowLan = true;
# allow = [ config.architect.networks."tailscale".net ];
};
};
services.redis.servers."librephotos" = {
enable = true;
port = 1233;
bind = "172.17.0.1";
extraParams = [ "--protected-mode no" ];
};
virtualisation.oci-containers = {
containers = {
librephotos-front = {
image = "reallibrephotos/librephotos-frontend:latest";
autoStart = true;
ports = [
"172.17.0.1:${toString frontendPort}:${toString frontendPort}"
];
};
librephotos-back = {
image = "reallibrephotos/librephotos:latest";
autoStart = true;
ports = [
"172.17.0.1:${toString backendPort}:${toString backendPort}"
];
environment = {
SECRET_KEY = "LOLOL";
BACKEND_HOST = domain;
ADMIN_EMAIL = "me@giugl.io";
ADMIN_USERNAME = "giulio";
ADMIN_PASSWORD = "giulio";
ALLOWED_HOSTS = domain;
DB_BACKEND = "mysql";
DB_NAME = "librephotos";
DB_USER = "librephotos";
DB_PASS = "librephotos";
DB_HOST = "172.17.0.1";
DB_PORT = toString config.services.mysql.settings.mysqld.port;
REDIS_HOST = "172.17.0.1";
REDIS_PORT = toString config.services.redis.servers."librephotos".port;
MAPBOX_API_KEY = "SOME_KEY";
WEB_CONCURRENCY = "24";
DEBUG = "0";
};
};
};
};
}

View File

@ -1,25 +1,34 @@
{ config, ... }: { lib, ... }:
let let
domain = "htlid.giugl.io"; domain = "htlid.giugl.io";
in network = import ./network.nix;
{ in {
services = { services = {
lidarr = { lidarr = {
enable = true; enable = true;
group = "media"; group = "media";
}; };
};
architect.vhost.${domain} = { nginx.virtualHosts.${domain} = {
dnsInterfaces = [ "lan" "tailscale" ]; forceSSL = true;
locations."/" = { enableACME = true;
port = 8686; locations."/" = {
allowLan = true; proxyPass = "http://localhost:8686";
allowWAN = false; extraConfig = ''
allow = [ config.architect.networks."tailscale".net ]; allow 10.0.0.0/24;
${lib.concatMapStrings (x: "allow ${x};") network.gdevices-wg}
deny all;
deny 10.0.0.1;
'';
};
}; };
}; };
networking.extraHosts = ''
${network.architect-lan} ${domain}
${network.architect-wg} ${domain}
'';
users.groups.media.members = [ "lidarr" ]; users.groups.media.members = [ "lidarr" ];
} }

View File

@ -1,45 +1,85 @@
{ config, pkgs, lib, ... }: { pkgs, ... }:
let let
domain = "runas.rocks"; domain = "matrix.giugl.io";
utilities = import ./utilities.nix { inherit lib config; }; webui_domain = "chat.giugl.io";
inherit (utilities) architectInterfaceAddress; network = import ./network.nix;
in in {
{
age.secrets.matrix = {
file = ../../secrets/matrix-synapse.age;
owner = "matrix-synapse";
};
services = { services = {
matrix-synapse = { matrix-synapse = {
enable = true; enable = true;
# Database config is in the .age file server_name = "${domain}";
extraConfigFiles = [ config.age.secrets.matrix.path ]; database_name = "synapse";
settings = { public_baseurl = "https://${domain}";
server_name = "${domain}"; registration_shared_secret = "runas!";
public_baseurl = "https://${domain}"; url_preview_enabled = true;
registration_shared_secret = "runas!"; dynamic_thumbnails = true;
url_preview_enabled = true; withJemalloc = true;
dynamic_thumbnails = true; # enable_registration = true;
withJemalloc = true; app_service_config_files = [
enable_registration = false; "/var/lib/matrix-synapse/discord-registration.yaml"
password_config.enabled = true; # "/var/lib/matrix-synapse/hookshot-registration.yml"
# "/var/lib/matrix-synapse/telegram-registration.yaml"
auto_join_rooms = [ "#general:${domain}" "#music:${domain}" "#movies:${domain}" ]; ];
extraConfig = ''
listeners = [{ auto_join_rooms:
port = 8008; - "#general:matrix.giugl.io"
bind_addresses = [ "127.0.0.1" ]; max_upload_size: "50M"
type = "http"; '';
tls = false; listeners = [{
x_forwarded = true; port = 8008;
resources = [{ bind_address = "::1";
names = [ "client" "federation" ]; type = "http";
compress = false; tls = false;
}]; x_forwarded = true;
resources = [{
names = [ "client" "federation" ];
compress = false;
}]; }];
}; }];
turn_uris = [
"turns:turn.giugl.io:5349?transport=udp"
"turns:turn.giugl.io:5349?transport=tcp"
];
turn_shared_secret = "69duck duck fuck420";
turn_user_lifetime = "1h";
logConfig = ''
version: 1
# In systemd's journal, loglevel is implicitly stored, so let's omit it
# from the message text.
formatters:
journal_fmt:
format: '%(name)s: [%(request)s] %(message)s'
filters:
context:
(): synapse.util.logcontext.LoggingContextFilter
request: ""
handlers:
journal:
class: systemd.journal.JournalHandler
formatter: journal_fmt
filters: [context]
SYSLOG_IDENTIFIER: synapse
root:
level: WARN
handlers: [journal]
disable_existing_loggers: False
'';
};
postgresql = {
enable = true;
package = pkgs.postgresql_11;
ensureDatabases = [ "synapse" ];
ensureUsers = [{
name = "matrix-synapse";
ensurePermissions = { "DATABASE synapse" = "ALL PRIVILEGES"; };
}];
}; };
nginx.virtualHosts = { nginx.virtualHosts = {
@ -52,44 +92,120 @@ in
''; '';
locations."= /.well-known/matrix/server".extraConfig = locations."= /.well-known/matrix/server".extraConfig =
let server = { "m.server" = "${domain}:443"; }; let server = { "m.server" = "${domain}:443"; };
in in ''
''
add_header Content-Type application/json; add_header Content-Type application/json;
return 200 '${builtins.toJSON server}'; return 200 '${builtins.toJSON server}';
''; '';
locations."= /.well-known/matrix/client".extraConfig = locations."= /.well-known/matrix/client".extraConfig = let
let client = {
client = { "m.homeserver" = { "base_url" = "https://${domain}:443"; };
"m.homeserver" = { "base_url" = "https://${domain}:443"; }; "m.identity_server" = { "base_url" = "https://vector.im"; };
"m.identity_server" = { "base_url" = "https://vector.im"; }; };
}; # ACAO required to allow element-web on any URL to request this json file
# ACAO required to allow element-web on any URL to request this json file in ''
in add_header Content-Type application/json;
'' add_header Access-Control-Allow-Origin *;
add_header Content-Type application/json; return 200 '${builtins.toJSON client}';
add_header Access-Control-Allow-Origin *; '';
return 200 '${builtins.toJSON client}';
'';
# locations."/".extraConfig = '' locations."/".extraConfig = ''
# return 404; return 404;
# ''; '';
# forward all Matrix API calls to the synapse Matrix homeserver # forward all Matrix API calls to the synapse Matrix homeserver
locations."/_matrix" = { locations."/_matrix" = {
proxyPass = "http://127.0.0.1:8008"; # without a trailing / proxyPass = "http://[::1]:8008"; # without a trailing /
}; };
};
locations."/_synapse" = { # web client
proxyPass = "http://127.0.0.1:8008"; # without a trailing / "${webui_domain}" = {
enableACME = true;
forceSSL = true;
root = pkgs.element-web.override {
conf = {
default_server_config."m.homeserver" = {
"base_url" = "https://${domain}";
"server_name" = "${domain}";
};
};
}; };
}; };
}; };
# discord bridge
matrix-appservice-discord = {
enable = true;
environmentFile = /secrets/matrix-appservice-discord/tokens.env;
# The appservice is pre-configured to use SQLite by default.
# It's also possible to use PostgreSQL.
settings = {
bridge = {
domain = domain;
homeserverUrl = "https://${domain}";
};
};
};
# telegram bridge
# mautrix-telegram = {
# enable = true;
# environmentFile = /secrets/mautrix-telegram/mautrix-telegram.env;
# settings = {
# homeserver = {
# address = "https://${domain}";
# domain = "${domain}";
# };
# appservice = {
# provisioning.enabled = false;
# id = "telegram";
# };
# bridge = {
# permissions = {
# "@pepe:${domain}" = "admin";
# "${domain}" = "puppeting";
# };
# # Animated stickers conversion requires additional packages in the
# # service's path.
# # If this isn't a fresh installation, clearing the bridge's uploaded
# # file cache might be necessary (make a database backup first!):
# # delete from telegram_file where \
# # mime_type in ('application/gzip', 'application/octet-stream')
# animated_sticker = {
# target = "gif";
# args = {
# width = 256;
# height = 256;
# fps = 30; # only for webm
# background = "020202"; # only for gif, transparency not supported
# };
# };
# encryption = {
# allow = true;
# default = true;
# };
# };
# };
# };
}; };
# systemd.services.mautrix-telegram.path = with pkgs; [
# lottieconverter # for animated stickers conversion, unfree package
# ffmpeg # if converting animated stickers to webm (very slow!)
# ];
networking.extraHosts = '' networking.extraHosts = ''
${architectInterfaceAddress "lan"} ${domain} ${network.architect-lan} ${domain} ${webui_domain}
${architectInterfaceAddress "tailscale"} ${domain} ${network.architect-wg} ${domain} ${webui_domain}
''; '';
} }

View File

@ -1,24 +1,18 @@
{ lib, config, pkgs, ... }: { config, pkgs, ... }:
let let
domain = "minecraft.giugl.io"; domain = "minecraft.giugl.io";
network = import ./network.nix;
utilities = import ./utilities.nix { inherit lib config; }; in {
inherit (utilities) architectInterfaceAddress;
in
{
architect.firewall.openTCP = [ 25565 ];
services.minecraft-server = { services.minecraft-server = {
enable = true; enable = true;
eula = true; eula = true;
declarative = true; declarative = true;
package = pkgs.unstablePkgs.minecraft-server;
serverProperties = { motd = "Welcome on the RuNas server!"; }; serverProperties = { motd = "Welcome on the RuNas server!"; };
}; };
networking.extraHosts = '' networking.extraHosts = ''
${architectInterfaceAddress "lan"} ${domain} ${network.architect-lan} ${domain}
${architectInterfaceAddress "tailscale"} ${domain} ${network.architect-wg} ${domain}
''; '';
} }

28
hosts/architect/minio.nix Normal file
View File

@ -0,0 +1,28 @@
{ lib, ... }:
let
domain = "s3.giugl.io";
network = import ./network.nix;
in {
services = {
minio.enable = true;
nginx.virtualHosts.${domain} = {
forceSSL = true;
enableACME = true;
locations."/" = {
proxyPass = "http://localhost:9000";
extraConfig = ''
allow 10.0.0.0/24;
${lib.concatMapStrings (x: "allow ${x};") network.gdevices-wg}
deny all;
'';
};
};
};
networking.extraHosts = ''
${network.architect-lan} ${domain}
${network.architect-wg} ${domain}
'';
}

View File

@ -0,0 +1,130 @@
{ config, pkgs, lib, ... }:
with lib;
let cfg = config.services.jellyfin;
in {
options = {
services.jellyfin = {
enable = mkEnableOption "Jellyfin Media Server";
user = mkOption {
type = types.str;
default = "jellyfin";
description = "User account under which Jellyfin runs.";
};
package = mkOption {
type = types.package;
default = pkgs.jellyfin;
example = literalExample "pkgs.jellyfin";
description = ''
Jellyfin package to use.
'';
};
group = mkOption {
type = types.str;
default = "jellyfin";
description = "Group under which jellyfin runs.";
};
openFirewall = mkOption {
type = types.bool;
default = false;
description = ''
Open the default ports in the firewall for the media server. The
HTTP/HTTPS ports can be changed in the Web UI, so this option should
only be used if they are unchanged.
'';
};
};
};
config = mkIf cfg.enable {
systemd.services.jellyfin = {
description = "Jellyfin Media Server";
after = [ "network.target" ];
wantedBy = [ "multi-user.target" ];
serviceConfig = rec {
User = cfg.user;
Group = cfg.group;
# # Allows access to drm devices for transcoding with hardware acceleration
# SupplementaryGroups = [ "video" ];
StateDirectory = "jellyfin";
CacheDirectory = "jellyfin";
ExecStart =
"${cfg.package}/bin/jellyfin --datadir '/var/lib/${StateDirectory}' --cachedir '/var/cache/${CacheDirectory}'";
Restart = "on-failure";
# Security options:
NoNewPrivileges = true;
AmbientCapabilities = "";
CapabilityBoundingSet = "";
# # ProtectClock= adds DeviceAllow=char-rtc r
# DeviceAllow = [
# "char-drm r"
# "/dev/nvidia0 r"
# "/dev/nvidiactl r"
# "/dev/nvidia-uvm r"
# "/dev/nvidia-uvm-tools r"
# ];
DeviceAllow = "";
LockPersonality = true;
PrivateTmp = true;
PrivateUsers = true;
# ProtectClock = true;
ProtectControlGroups = true;
ProtectHostname = true;
ProtectKernelLogs = true;
ProtectKernelModules = true;
ProtectKernelTunables = true;
RemoveIPC = true;
RestrictNamespaces = true;
# # AF_NETLINK needed because Jellyfin monitors the network connection
RestrictAddressFamilies = [ "AF_NETLINK" "AF_INET" "AF_INET6" "AF_UNIX" ];
RestrictRealtime = true;
RestrictSUIDSGID = true;
SystemCallArchitectures = "native";
SystemCallErrorNumber = "EPERM";
SystemCallFilter = [
"@system-service"
"~@cpu-emulation"
"~@debug"
"~@keyring"
"~@memlock"
"~@obsolete"
"~@privileged"
"~@setuid"
];
};
};
users.users = mkIf (cfg.user == "jellyfin") {
jellyfin = {
group = cfg.group;
isSystemUser = true;
};
};
users.groups = mkIf (cfg.group == "jellyfin") { jellyfin = { }; };
networking.firewall = mkIf cfg.openFirewall {
# from https://jellyfin.org/docs/general/networking/index.html
allowedTCPPorts = [ 8096 8920 ];
allowedUDPPorts = [ 1900 7359 ];
};
};
meta.maintainers = with lib.maintainers; [ minijackson ];
}

View File

@ -1,83 +1,53 @@
{ config, lib, pkgs, ... }: { lib, pkgs, ... }:
let let
domain = "music.giugl.io"; domain = "music.giugl.io";
library_path = "/media/Music"; network = import ./network.nix;
beets_config = "/media/beets.conf"; in {
in services = {
{ navidrome = {
services.navidrome = { enable = true;
settings = {
MusicFolder = "/media/Music";
LastFM.enable = true;
LastFM.ApiKey = "5cef5cb5f9d31326b97d0f929ca9cf20";
LastFM.Secret = "d1296896126f4caae47407aecf080b25";
Spotify.ID = "3900c029b4f34f3fb61d554dda64794d";
Spotify.Secret = "d931ce5575a9401aa5ff8d37558cca0a";
EnableGravatar = true;
LogLevel = "WARN";
};
};
nginx.virtualHosts.${domain} = {
forceSSL = true;
enableACME = true;
locations."/" = {
proxyPass = "http://localhost:4533";
# extraConfig = ''
# allow 10.0.0.0/24;
# ${lib.concatMapStrings (x: "allow ${x};") network.gdevices-wg}
# deny all;
# '';
};
};
};
systemd.services."beets-rename" = {
enable = true; enable = true;
serviceConfig = {
settings = { Type = "oneshot";
MusicFolder = library_path; ExecStart =
LastFM.enable = true; "${pkgs.findutils}/bin/find /media/Music -type d -mindepth 2 -maxdepth 2 -exec ${pkgs.beets}/bin/beet -c /media/config.conf import --flat -q {} \\;";
LastFM.ApiKey = "5cef5cb5f9d31326b97d0f929ca9cf20";
LastFM.Secret = "d1296896126f4caae47407aecf080b25";
Spotify.ID = "3900c029b4f34f3fb61d554dda64794d";
Spotify.Secret = "d931ce5575a9401aa5ff8d37558cca0a";
EnableGravatar = true;
LogLevel = "WARN";
}; };
startAt = "daily";
}; };
architect.vhost.${domain} = { networking.extraHosts = ''
dnsInterfaces = [ "lan" "tailscale" ]; ${network.architect-lan} ${domain}
locations."/" = { ${network.architect-wg} ${domain}
port = 4533; '';
allowLan = true;
allowWAN = true;
# allow = [ config.architect.networks."tailscale".net ];
};
};
systemd.services = {
"beets-update" = {
enable = true;
# requires = [ "remove-badmp3.service" "remove-badflac.service" ];
before = [ "beets-import.service" ];
serviceConfig = {
Type = "oneshot";
ExecStart = "${pkgs.beets}/bin/beet -c ${beets_config} update";
};
};
"beets-import" = {
enable = true;
path = [ pkgs.imagemagick ];
requires = [ "beets-update.service" ];
after = [ "beets-update.service" ];
serviceConfig = {
Type = "oneshot";
ExecStart =
"${pkgs.beets}/bin/beet -c ${beets_config} import --flat -q ${library_path}";
};
startAt = "weekly";
};
};
# "remove-badmp3" = {
# enable = true;
# before = [ "beets-import.service" "beets-update.service" ];
# serviceConfig = {
# Type = "oneshot";
# ExecStart = ''
# ${pkgs.findutils}/bin/find ${library_path} -name "*.mp3" -type f -exec ${pkgs.bash}/bin/sh -c '${pkgs.mp3val}/bin/mp3val "{}" | grep -Pi error 1>/dev/null && ${pkgs.busybox}/bin/rm "{}"' \;
# '';
# };
# };
# "remove-badflac" = {
# enable = true;
# before = [ "beets-import.service" "beets-update.service" ];
# serviceConfig = {
# Type = "oneshot";
# ExecStart = ''
# ${pkgs.findutils}/bin/find ${library_path} -name "*.flac" -type f -exec ${pkgs.bash}/bin/sh -c '${pkgs.flac}/bin/flac -st "{}" || ${pkgs.busybox}/bin/rm "{}"' \;
# '';
# };
# };
# };
users.groups.media.members = [ "navidrome" ]; users.groups.media.members = [ "navidrome" ];
} }

View File

@ -1,26 +0,0 @@
{ config, pkgs, ... }:
let
domain = "monitor.giugl.io";
in
{
services.netdata = {
enable = true;
package = pkgs.unstablePkgs.netdata;
config = {
db.mode = "dbengine";
};
};
architect.vhost.${domain} = with config.architect.networks; {
dnsInterfaces = [ "tailscale" "lan" ];
locations."/" = {
port = 19999;
allowLan = true;
allow = [
tailscale.net
];
};
};
}

View File

@ -0,0 +1,73 @@
rec {
# interfaces
wan-if = "enp5s0";
vpn-if = "wg0";
proxy-if = "proxy";
# nets
lan-net = "10.0.0.0/24";
vpn-net = "10.3.0.0/24";
proxy-net = "10.4.0.0/24";
external_lan-net = "192.168.1.0/24";
# ips
dvr-lan = "10.0.0.2";
nas-lan = "10.0.0.3";
architect-lan = "10.0.0.250";
proxy-wg = "10.4.0.1";
architect-wg = "10.3.0.1";
galuminum-wg = "10.3.0.2";
oneplus-wg = "10.3.0.3";
ipad-wg = "10.3.0.4";
manduria-wg = "10.3.0.5";
antonio-wg = "10.3.0.6";
gbeast-wg = "10.3.0.7";
parisaphone-wg = "10.3.0.8";
parisapc-wg = "10.3.0.9";
peppiniell-wg = "10.3.0.10";
padulino-wg = "10.3.0.11";
shield-wg = "10.3.0.12";
pepos-wg = "10.3.0.15";
salvatore-wg = "10.3.0.16";
papa-wg = "10.3.0.17";
defy-wg = "10.3.0.18";
germano-wg = "10.3.0.19";
flavio-wg = "10.3.0.20";
tommy-wg = "10.3.0.21";
alain-wg = "10.3.0.22";
dima-wg = "10.3.0.23";
mikey-wg = "10.3.0.24";
andrew-wg = "10.3.0.25";
mikeylaptop-wg = "10.3.0.26";
andrewdesktop-wg = "10.3.0.27";
jacopo-wg = "10.3.0.28";
frznn-wg = "10.3.0.29";
ludo-wg = "10.3.0.30";
parina-wg = "10.3.0.31";
nilo-wg = "10.3.0.32";
parina-ipad-wg = "10.3.0.33";
eleonora-wg = "10.3.0.100";
angellane-wg = "10.3.0.200";
hotpottino-wg = "10.3.0.201";
dodino-wg = "10.3.0.202";
wolfsonhouse-wg = "10.3.0.203";
# groups
gdevices-wg =
[ galuminum-wg oneplus-wg ipad-wg gbeast-wg peppiniell-wg padulino-wg wolfsonhouse-wg ];
routers-wg = [ hotpottino-wg angellane-wg dodino-wg wolfsonhouse-wg ];
c2c-wg = [ ] ++ gdevices-wg;
towan-wg = [ shield-wg parisaphone-wg parisapc-wg parina-wg parina-ipad-wg ] ++ gdevices-wg
++ routers-wg;
gamenet-wg = [
andrew-wg
galuminum-wg
gbeast-wg
mikey-wg
andrewdesktop-wg
mikeylaptop-wg
flavio-wg
salvatore-wg
];
}

View File

@ -1,86 +1,37 @@
{ pkgs, config, lib, ... }: { pkgs, ... }:
let let
domain = "cloud.giugl.io"; domain = "cloud.giugl.io";
redis_port = 6379; network = import ./network.nix;
in {
utilities = import ./utilities.nix { inherit lib config; };
inherit (utilities) architectInterfaceAddress;
in
{
age.secrets = {
nextcloud-admin = {
file = ../../secrets/nextcloud-admin.age;
owner = "nextcloud";
group ="nginx";
};
nextcloud-database = {
file = ../../secrets/nextcloud-database.age;
owner = "nextcloud";
group = "nginx";
};
};
environment.systemPackages = with pkgs; [
ffmpeg
];
services = { services = {
nginx.virtualHosts.${domain} = { mysql.enable = true;
forceSSL = true; mysql.package = pkgs.unstable.mysql80;
enableACME = true;
extraConfig = ''
aio threads;
directio 1M;
output_buffers 3 1M;
sendfile on; redis.enable = true;
sendfile_max_chunk 0;
autoindex on;
'';
};
mysql = {
enable = true;
package = pkgs.mariadb_1011;
};
redis = {
vmOverCommit = true;
servers."nextcloud" = {
enable = true;
port = redis_port;
};
};
nextcloud = { nextcloud = {
enable = true; enable = true;
hostName = domain; hostName = "${domain}";
https = true; https = true;
package = pkgs.nextcloud31; package = pkgs.unstable.nextcloud23;
datadir = "/services/nextcloud";
configureRedis = true; caching.redis = true;
caching = {
redis = true;
};
autoUpdateApps.enable = true; autoUpdateApps.enable = true;
autoUpdateApps.startAt = "05:00:00"; autoUpdateApps.startAt = "05:00:00";
logLevel = 1;
maxUploadSize = "50G";
settings = {
overwriteprotocol = "https";
};
config = { config = {
overwriteProtocol = "https";
dbtype = "mysql"; dbtype = "mysql";
dbuser = "nextcloud"; dbuser = "oc_giulio2";
dbhost = "localhost"; dbhost = "localhost";
dbname = "nextcloud"; dbname = "nextcloud_final";
dbpassFile = config.age.secrets.nextcloud-database.path; dbpassFile = "/secrets/nextcloud/dbpass.txt";
adminpassFile = config.age.secrets.nextcloud-admin.path; adminpassFile = "/secrets/nextcloud/adminpass.txt";
adminuser = "giulio";
extraTrustedDomains = [ "${domain}" ];
}; };
}; };
}; };
@ -91,8 +42,12 @@ in
}; };
networking.extraHosts = '' networking.extraHosts = ''
${architectInterfaceAddress "lan"} ${domain} ${network.architect-lan} ${domain}
${architectInterfaceAddress "tailscale"} ${domain} ${network.architect-wg} ${domain}
''; '';
services.nginx.virtualHosts.${domain} = {
forceSSL = true;
enableACME = true;
};
} }

View File

@ -1,94 +1,36 @@
{ services, pkgs, lib, ... }: { services, ... }:
{ {
architect.firewall = {
openTCP = [ 80 443 ];
};
services.nginx = { services.nginx = {
enable = true; enable = true;
package = pkgs.nginx;
recommendedGzipSettings = true; recommendedGzipSettings = true;
recommendedOptimisation = true; recommendedOptimisation = true;
recommendedProxySettings = true; recommendedProxySettings = true;
recommendedTlsSettings = true; recommendedTlsSettings = true;
virtualHosts."architect.devs.giugl.io" = {
default = true;
enableACME = true;
forceSSL = true;
root = "/var/lib/nginx/error_pages";
extraConfig = "error_page 404 /index.htm;";
locations = { # virtualHosts."giugl.io" = {
"/" = { return = "404"; }; # default = true;
# enableACME = true;
"/index.htm" = { }; # addSSL = true;
# root = "/var/lib/nginx/error_pages";
"/style.css" = { }; # extraConfig = "error_page 404 /index.htm;";
#
"/wat.jpg" = { }; # locations = {
}; # "/" = {
}; # return = "404";
# };
# appendHttpConfig = #
# let # "/index.htm" = {
# extraPureLuaPackages = with pkgs.luajitPackages; [ # };
# lua-resty-openidc #
# lua-resty-http # "/style.css" = {
# lua-resty-session # };
# lua-resty-jwt #
# lua-resty-openssl # "/wat.jpg" = {
# ]; # };
# luaPath = pkg: "${pkg}/share/lua/5.1/?.lua"; # };
# makeLuaPath = lib.concatMapStringsSep ";" luaPath; # };
# in
# ''
# # https://stackoverflow.com/questions/38931468/nginx-reverse-proxy-error14077438ssl-ssl-do-handshake-failed
# proxy_ssl_server_name on;
# lua_package_path '${makeLuaPath extraPureLuaPackages};;';
# lua_ssl_trusted_certificate /etc/ssl/certs/ca-certificates.crt;
# lua_ssl_verify_depth 5;
# # cache for OIDC discovery metadata
# lua_shared_dict discovery 1m;
# lua_shared_dict jwks 1m;
# # https://github.com/openresty/lua-resty-redis/issues/159
# resolver local=on ipv6=off;
# init_worker_by_lua_block {
# function check_role (res, role)
# if res.user.roles == nil then
# return false
# end
# for _,v in pairs(res.user.roles) do
# if string.lower(v) == role then
# return true
# end
# end
# return false
# end
# function is_ip_whitelisted(ip, whitelist)
# for _, x in ipairs(whitelist) do
# if ip == x then
# return true
# end
# end
# return false
# end
# }
# '';
appendConfig = ''
worker_processes 24;
'';
}; };
users.groups.acme.members = [ "nginx" ]; users.groups.acme.members = [ "nginx" ];
} }

View File

@ -0,0 +1,32 @@
{ lib, pkgs, ... }:
let
domain = "tweet.giugl.io";
network = import ./network.nix;
in {
services = {
nitter = {
enable = true;
server = {
port = 9093;
hostname = domain;
staticDir = "${pkgs.unstable.nitter}/share/nitter/public";
};
preferences = {
replaceYouTube = "tube.giugl.io";
replaceTwitter = "tweet.giugl.io";
};
};
nginx.virtualHosts.${domain} = {
forceSSL = true;
enableACME = true;
locations."/" = { proxyPass = "http://localhost:9093"; };
};
};
networking.extraHosts = ''
${network.architect-lan} ${domain}
${network.architect-wg} ${domain}
'';
}

View File

@ -1,24 +1,33 @@
{ config, ... }: { lib, ... }:
let let
domain = "htnzb.giugl.io"; domain = "htnzb.giugl.io";
in network = import ./network.nix;
{ in {
services.nzbget = { services = {
enable = true; nzbget = {
group = "media"; enable = true;
}; group = "media";
};
architect.vhost.${domain} = with config.architect.networks; { nginx.virtualHosts.${domain} = {
dnsInterfaces = [ "tailscale" "lan" ]; forceSSL = true;
enableACME = true;
locations."/" = { locations."/" = {
port = 6789; proxyPass = "http://localhost:6789";
allowLan = true; extraConfig = ''
allow 10.0.0.0/24;
allow = [ tailscale.net ]; ${lib.concatMapStrings (x: "allow ${x};") network.gdevices-wg}
deny all;
'';
};
}; };
}; };
networking.extraHosts = ''
${network.architect-lan} ${domain}
${network.architect-wg} ${domain}
'';
users.groups.media.members = [ "nzbget" ]; users.groups.media.members = [ "nzbget" ];
} }

View File

@ -1,50 +0,0 @@
{ lib }:
{
openresty_oidc_block =
{ access_role ? "", whitelisted_ips ? [ ] }: ''
'';
# access_by_lua_block {
# local opts = {
# discovery = "https://auth.giugl.io/realms/master/.well-known/openid-configuration",
# client_id = "nginx",
# client_secret = "9C6BYxPhTbrRS4DIwd3Smk7e11ABmnt8",
# logout_path = "/logout",
# redirect_after_logout_uri = "/",
# redirect_uri = "/redirect_uri",
# keepalive = "yes",
# accept_none_alg = true,
# revoke_tokens_on_logout = true,
# -- access token valid for a day
# access_token_expires_in = 86400
# }
# ${lib.optionalString (whitelisted_ips != []) ''
# local whitelist = {${lib.strings.concatMapStringsSep "," (x: "\"${x}\"") whitelisted_ips}}
# if is_ip_whitelisted(ngx.var.remote_addr, whitelist) then
# return
# end
# ''}
# -- call introspect for OAuth 2.0 Bearer Access Token validation
# local res, err = require("resty.openidc").authenticate(opts)
# if err then
# ngx.status = 403
# ngx.say(err)
# ngx.exit(ngx.HTTP_FORBIDDEN)
# end
# ${lib.optionalString (access_role != "") ''
# if not check_role(res, "${access_role}") then
# ngx.status = 401
# ngx.header.content_type = 'text/html';
# ngx.say("You are not authorized to access this page. Please contact Er Pepotto.")
# ngx.exit(ngx.HTTP_UNAUTHORIZED)
# end
# ''}
# }
# '';
}

View File

@ -1,25 +0,0 @@
{ config, lib, ... }:
{
options.architect = {
firewall = lib.mkOption {
internal = true;
default = config.pepe.core.firewall;
};
networks = lib.mkOption {
internal = true;
default = config.pepe.core.network.interfaces;
};
vhost = lib.mkOption {
internal = true;
default = config.pepe.core.vhost.hosts;
};
};
config.architect.networks.docker = {
interface = "docker0";
net = "172.17.0.0/16";
};
}

View File

@ -0,0 +1,13 @@
{ ... }:
{
virtualisation.oci-containers.containers."overseerr" = {
image = "sctx/overseerr:latest";
volumes = [ "/var/lib/overseerr:/app/config" ];
environment = {
"LOG_LEVEL" = "debug";
"TZ" = "Europe/Rome";
};
#ports = [ "5055:5055" ];
};
}

View File

@ -1,37 +0,0 @@
{ config, pkgs, lib, ... }:
let
domain = "photos.giugl.io";
in
{
services.photoprism = {
enable = true;
package = pkgs.unstablePkgs.photoprism;
originalsPath = "/var/lib/private/photoprism/originals";
address = "0.0.0.0";
settings = {
PHOTOPRISM_DEFAULT_LOCALE = "en";
PHOTOPRISM_DATABASE_DRIVER = "mysql";
PHOTOPRISM_DATABASE_NAME = "photoprism";
PHOTOPRISM_DATABASE_SERVER = "/run/mysqld/mysqld.sock";
PHOTOPRISM_DATABASE_USER = "photoprism";
PHOTOPRISM_SITE_URL = "https://${domain}";
PHOTOPRISM_SITE_TITLE = "PePrism";
PHOTOPRISM_FFMPEG_ENCODER = "nvidia";
PHOTOPRISM_INIT = "tensorflow";
NVIDIA_VISIBLE_DEVICES = "all";
NVIDIA_DRIVER_CAPABILITIES = "compute,video,utility";
PHOTOPRISM_FFMPEG_BIN = "${pkgs.ffmpeg}/bin/ffmpeg";
};
};
architect.vhost.${domain} = {
dnsInterfaces = [ "tailscale" "lan" ];
locations."/" = {
port = config.services.photoprism.port;
allowLan = true;
allow = [ config.architect.networks."tailscale".net ];
proxyWebsockets = true;
};
};
}

View File

@ -1,79 +1,91 @@
{ pkgs, config, ... }: { pkgs, lib, ... }:
let let
domain = "media.giugl.io"; domain = "media.giugl.io";
port = 32400; network = import ./network.nix;
in in {
{
architect.firewall = {
openTCP = [ 32400 3005 8324 32469 ];
openUDP = [ 1900 5353 32410 32412 32413 32414 ];
};
services.plex = { services.plex = {
enable = true; enable = true;
package = pkgs.unstablePkgs.plex; package = pkgs.unstable.plex;
dataDir = "/plex"; dataDir = "/plex";
}; };
architect.vhost.${domain} = with config.architect.networks; { services.nginx = {
dnsInterfaces = [ "lan" "tailscale" ]; enable = true;
locations = { # give a name to the virtual host. It also becomes the server name.
"/" = { virtualHosts.${domain} = {
inherit port; forceSSL = true;
enableACME = true;
http2 = true;
extraConfig = ''
allow 10.3.0.0/24;
allow 10.0.0.0/24;
deny all;
proxyWebsockets = true; #Some players don't reopen a socket and playback stops totally instead of resuming after an extended pause
allowLan = true; send_timeout 100m;
allow = [
tailscale.net
];
extraConfig = ''
#Some players don't reopen a socket and playback stops totally instead of resuming after an extended pause
send_timeout 100m;
# Forward real ip and host to Plex # Why this is important: https://blog.cloudflare.com/ocsp-stapling-how-cloudflare-just-made-ssl-30/
proxy_set_header X-Real-IP $remote_addr; ssl_stapling on;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; ssl_stapling_verify on;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header Host $server_addr;
proxy_set_header Referer $server_addr;
proxy_set_header Origin $server_addr;
# Plex has A LOT of javascript, xml and html. This helps a lot, but if it causes playback issues with devices turn it off. ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
gzip on; ssl_prefer_server_ciphers on;
gzip_vary on; #Intentionally not hardened for security for player support and encryption video streams has a lot of overhead with something like AES-256-GCM-SHA384.
gzip_min_length 1000; ssl_ciphers 'ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:ECDHE-RSA-DES-CBC3-SHA:ECDHE-ECDSA-DES-CBC3-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:AES:CAMELLIA:DES-CBC3-SHA:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK:!aECDH:!EDH-DSS-DES-CBC3-SHA:!EDH-RSA-DES-CBC3-SHA:!KRB5-DES-CBC3-SHA';
gzip_proxied any;
gzip_types text/plain text/css text/xml application/xml text/javascript application/x-javascript image/svg+xml;
gzip_disable "MSIE [1-6]\.";
# Nginx default client_max_body_size is 1MB, which breaks Camera Upload feature from the phones. # Forward real ip and host to Plex
# Increasing the limit fixes the issue. Anyhow, if 4K videos are expected to be uploaded, the size might need to be increased even more proxy_set_header X-Real-IP $remote_addr;
client_max_body_size 100M; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header Host $server_addr;
proxy_set_header Referer $server_addr;
proxy_set_header Origin $server_addr;
# Plex headers # Plex has A LOT of javascript, xml and html. This helps a lot, but if it causes playback issues with devices turn it off.
proxy_set_header X-Plex-Client-Identifier $http_x_plex_client_identifier; gzip on;
proxy_set_header X-Plex-Device $http_x_plex_device; gzip_vary on;
proxy_set_header X-Plex-Device-Name $http_x_plex_device_name; gzip_min_length 1000;
proxy_set_header X-Plex-Platform $http_x_plex_platform; gzip_proxied any;
proxy_set_header X-Plex-Platform-Version $http_x_plex_platform_version; gzip_types text/plain text/css text/xml application/xml text/javascript application/x-javascript image/svg+xml;
proxy_set_header X-Plex-Product $http_x_plex_product; gzip_disable "MSIE [1-6]\.";
proxy_set_header X-Plex-Token $http_x_plex_token;
proxy_set_header X-Plex-Version $http_x_plex_version;
proxy_set_header X-Plex-Nocache $http_x_plex_nocache;
proxy_set_header X-Plex-Provides $http_x_plex_provides;
proxy_set_header X-Plex-Device-Vendor $http_x_plex_device_vendor;
proxy_set_header X-Plex-Model $http_x_plex_model;
# Buffering off send to the client as soon as the data is received from Plex. # Nginx default client_max_body_size is 1MB, which breaks Camera Upload feature from the phones.
proxy_redirect off; # Increasing the limit fixes the issue. Anyhow, if 4K videos are expected to be uploaded, the size might need to be increased even more
proxy_buffering off; client_max_body_size 100M;
add_header 'Content-Security-Policy' 'upgrade-insecure-requests'; # Plex headers
''; proxy_set_header X-Plex-Client-Identifier $http_x_plex_client_identifier;
}; proxy_set_header X-Plex-Device $http_x_plex_device;
proxy_set_header X-Plex-Device-Name $http_x_plex_device_name;
proxy_set_header X-Plex-Platform $http_x_plex_platform;
proxy_set_header X-Plex-Platform-Version $http_x_plex_platform_version;
proxy_set_header X-Plex-Product $http_x_plex_product;
proxy_set_header X-Plex-Token $http_x_plex_token;
proxy_set_header X-Plex-Version $http_x_plex_version;
proxy_set_header X-Plex-Nocache $http_x_plex_nocache;
proxy_set_header X-Plex-Provides $http_x_plex_provides;
proxy_set_header X-Plex-Device-Vendor $http_x_plex_device_vendor;
proxy_set_header X-Plex-Model $http_x_plex_model;
# Websockets
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
# Buffering off send to the client as soon as the data is received from Plex.
proxy_redirect off;
proxy_buffering off;
'';
locations."/" = { proxyPass = "http://localhost:32400"; };
}; };
}; };
networking.extraHosts = ''
${network.architect-lan} ${domain}
${network.architect-wg} ${domain}
'';
users.groups.media.members = [ "plex" ]; users.groups.media.members = [ "plex" ];
} }

View File

@ -1,8 +0,0 @@
{ pkgs, lib, ... }:
{
services.postgresql = {
enable = true;
package = lib.mkForce pkgs.postgresql_16;
};
}

View File

@ -4,25 +4,19 @@ let
domain = "xmpp.giugl.io"; domain = "xmpp.giugl.io";
conference_domain = "conference.${domain}"; conference_domain = "conference.${domain}";
upload_domain = "uploads.${domain}"; upload_domain = "uploads.${domain}";
network = import ./network.nix;
utilities = import ./utilities.nix { inherit lib config; }; in {
inherit (utilities) architectInterfaceAddress;
in
{
architect.firewall = {
openTCP = [ 5222 5269 ];
};
services = { services = {
prosody = { prosody = {
enable = true; enable = true;
virtualHosts.${domain} = { virtualHosts = {
inherit domain; "${domain}" = {
domain = domain;
enabled = true; enabled = true;
ssl.key = "${config.security.acme.certs.${domain}.directory}/key.pem"; ssl.key = "${config.security.acme.certs.${domain}.directory}/key.pem";
ssl.cert = ssl.cert =
"${config.security.acme.certs.${domain}.directory}/fullchain.pem"; "${config.security.acme.certs.${domain}.directory}/fullchain.pem";
};
}; };
muc = [{ domain = conference_domain; }]; muc = [{ domain = conference_domain; }];
@ -32,24 +26,16 @@ in
#httpInterfaces = [ "wg0" ]; #httpInterfaces = [ "wg0" ];
#httpsInterfaces = [ "wg0" ]; #httpsInterfaces = [ "wg0" ];
}; };
nginx.virtualHosts = {
"${domain}" = {
enableACME = true;
forceSSL = true;
};
# "${conference_domain}".enableACME = true;
# "${upload_domain}".enableACME = true;
};
}; };
services.nginx.virtualHosts."${domain}".enableACME = true;
#services.nginx.virtualHosts."${conference_domain}".enableACME = true;
#services.nginx.virtualHosts."${upload_domain}".enableACME = true;
networking.extraHosts = '' networking.extraHosts = ''
${architectInterfaceAddress "lan"} ${domain} ${network.architect-lan} ${domain}
${architectInterfaceAddress "tailscale"} ${domain} ${network.architect-wg} ${domain}
''; '';
users.groups = { users.groups.acme.members = [ "prosody" ];
acme.members = [ "prosody" ];
nginx.members = [ "prosody" ];
};
} }

View File

@ -0,0 +1,38 @@
{ lib, ... }:
let
domain = "htpro.giugl.io";
network = import ./network.nix;
in {
services = {
prowlarr.enable = true;
nginx.virtualHosts.${domain} = {
forceSSL = true;
enableACME = true;
locations."/" = {
proxyPass = "http://localhost:9696";
extraConfig = ''
allow 10.0.0.0/24;
${lib.concatMapStrings (x: "allow ${x};") network.gdevices-wg}
deny all;
'';
};
# locations."/api" = {
# proxyPass = "http://127.0.0.1:9696/prowlarr/api";
# };
#
# locations."/Content" = {
# proxyPass = "http://127.0.0.1:9696/prowlarr/Content";
# };
};
};
networking.extraHosts = ''
${network.architect-lan} ${domain}
${network.architect-wg} ${domain}
'';
users.groups.media.members = [ "prowlarr" ];
}

View File

@ -0,0 +1,33 @@
{ lib, ... }:
let
domain = "htrad.giugl.io";
network = import ./network.nix;
in {
services = {
radarr = {
enable = true;
group = "media";
};
nginx.virtualHosts.${domain} = {
forceSSL = true;
enableACME = true;
locations."/" = {
proxyPass = "http://localhost:7878";
extraConfig = ''
allow 10.0.0.0/24;
${lib.concatMapStrings (x: "allow ${x};") network.gdevices-wg}
deny all;
'';
};
};
};
networking.extraHosts = ''
${network.architect-lan} ${domain}
${network.architect-wg} ${domain}
'';
users.groups.media.members = [ "radarr" ];
}

View File

@ -1,29 +0,0 @@
{ config, pkgs, ... }:
let
domain = "reddit.giugl.io";
in
{
systemd.services.redlib.environment = {
REDLIB_ROBOTS_DISABLE_INDEXING = "on";
REDLIB_DEFAULT_THEME = "dracula";
REDLIB_DEFAULT_SHOW_NSFW = "on";
REDLIB_DEFAULT_BLUR_NSFW = "off";
REDLIB_DEFAULT_USE_HLS = "on";
REDLIB_DEFAULT_HIDE_HLS_NOTIFICATION = "on";
};
services.redlib = {
enable = true;
port = 9090;
package = pkgs.unstablePkgs.redlib;
};
architect.vhost.${domain} = {
dnsInterfaces = [ "lan" "tailscale" ];
locations."/" = {
port = config.services.redlib.port;
allowWAN = true;
};
};
}

View File

@ -1,48 +0,0 @@
{ config, pkgs, lib, ... }:
let
domain = "runas.rocks";
runas_root = "/var/lib/runas.rocks/dist";
service_name = "runas.rocks-pull";
mkStartScript = name: pkgs.writeShellScript "${name}.sh" ''
set -euo pipefail
cd ${runas_root}
git pull origin main --rebase
'';
utilities = import ./utilities.nix { inherit lib config; };
inherit (utilities) architectInterfaceAddress;
in
{
services.nginx.virtualHosts.${domain} = {
enableACME = true;
forceSSL = true;
locations."/".root = runas_root;
locations."/.git" = { return = "404"; };
};
systemd = {
services.${service_name} = {
path = [ pkgs.git ];
enable = true;
serviceConfig = {
Type = "oneshot";
ExecStart = mkStartScript "${service_name}";
};
};
timers.${service_name} = {
wantedBy = [ "timers.target" ];
timerConfig = {
OnCalendar = "hourly";
Unit = "${service_name}.service";
};
};
};
networking.extraHosts = ''
${architectInterfaceAddress "lan"} ${domain}
${architectInterfaceAddress "tailscale"} ${domain}
'';
}

View File

@ -0,0 +1,49 @@
#!/bin/sh
EASYLIST_HOSTSNAME="easylist_hosts.txt"
EASYPRIVACY_HOSTSNAME="easyprivacy_hosts.txt"
STEVENBLACK_HOSTSNAME="stevenblack_hosts.txt"
get_easylist() {
EASYLIST_URL="https://raw.githubusercontent.com/easylist/easylist/master/easylist/easylist_adservers.txt"
tmpfile=`mktemp`
# download easylist
${pkgs.wget}/bin/wget $EASYLIST_URL -O $tmpfile
# remove IP addresses and prepend 0.0.0.0 to create hosts file
cat $tmpfile | egrep -v "([0-9]{1,3}\.){3}[0-9]{1,3}" | grep -oP "^\|\|(\K[a-zA-Z0-9\.\-]+)" | ${pkgs.gawk}/bin/gawk '{print "0.0.0.0 " $0}' > $EASYLIST_HOSTSNAME
}
get_easyprivacy() {
EASYLIST_URL="https://raw.githubusercontent.com/easylist/easylist/master/easyprivacy/easyprivacy_trackingservers.txt"
tmpfile=`mktemp`
# download easylist
${pkgs.wget}/bin/wget $EASYLIST_URL -O $tmpfile
# remove IP addresses and prepend 0.0.0.0 to create hosts file
cat $tmpfile | egrep -v "([0-9]{1,3}\.){3}[0-9]{1,3}" | grep -oP "^\|\|(\K[a-zA-Z0-9\.\-]+)" | ${pkgs.gawk}/bin/gawk '{print "0.0.0.0 " $0}' > $EASYPRIVACY_HOSTSNAME
}
get_stevenblack() {
STEVENBLACK_URL="https://raw.githubusercontent.com/StevenBlack/hosts/master/alternates/fakenews/hosts"
${pkgs.wget}/bin/wget $STEVENBLACK_URL -O $STEVENBLACK_HOSTSNAME
}
get_easylist
get_easyprivacy
get_stevenblack
# create unified file
cat *hosts.txt | sort | uniq | grep "^0" > /etc/adblock_hosts
rm $EASYLIST_HOSTSNAME $STEVENBLACK_HOSTSNAME

View File

@ -1,64 +0,0 @@
{ config, pkgs, ... }:
let
domain = "search.giugl.io";
in
{
services = {
redis.servers."searx" = { enable = true; port = 4456; };
searx = {
enable = true;
package = pkgs.unstablePkgs.searxng;
environmentFile = /secrets/searx/env;
settings = {
server = {
secret_key = "@SEARX_SECRET_KEY@";
port = 4455;
};
general = {
instance_name = "PepoSearch";
contact_url = "mailto:search@depasquale.giugl.io";
enable_metrics = true;
};
search = {
safe_search = 0;
autocomplete = "google";
prefer_configured_language = false;
formats = [ "html" "json"];
};
ui = {
infinite_scroll = true;
query_in_title = true;
results_on_new_tab = true;
theme_args.simple_style = "dark";
};
redis.url = "redis://127.0.0.1:${toString config.services.redis.servers."searx".port}";
engines = [
{ name = "google"; disabled = false; }
{ name = "bing"; disabled = false; }
{ name = "qwant"; disabled = true; }
{ name = "brave"; disabled = true; }
{ name = "duckduckgo"; disabled = false; }
];
};
};
};
architect.vhost.${domain} = with config.architect.networks; {
dnsInterfaces = [ "tailscale" ];
locations."/" = {
port = config.services.searx.settings.server.port;
allowLan = true;
allowWAN = true;
allow = [
tailscale.net
];
};
};
}

View File

@ -0,0 +1,33 @@
{ lib, ... }:
let
domain = "htson.giugl.io";
network = import ./network.nix;
in {
services = {
sonarr = {
enable = true;
group = "media";
};
nginx.virtualHosts.${domain} = {
forceSSL = true;
enableACME = true;
locations."/" = {
proxyPass = "http://localhost:8989";
extraConfig = ''
allow 10.0.0.0/24;
${lib.concatMapStrings (x: "allow ${x};") network.gdevices-wg}
deny all;
'';
};
};
};
networking.extraHosts = ''
${network.architect-lan} ${domain}
${network.architect-wg} ${domain}
'';
users.groups.media.members = [ "sonarr" ];
}

View File

@ -1,206 +0,0 @@
{ config, pkgs, ... }:
let
user = "sunshine";
resolutionScript = pkgs.writeTextFile {
name = "sunshine-resolution-script";
text = ''
#!${pkgs.bash}/bin/bash
width=''${1:-1280}
height=''${2:-720}
refresh_rate=''${3:-120}
# Get the modeline info from the 2nd row in the cvt output
modeline=$(${pkgs.xorg.libxcvt}/bin/cvt ''${width} ''${height} ''${refresh_rate} | ${pkgs.gawk}/bin/gawk 'FNR == 2')
xrandr_mode_str=''${modeline//Modeline \"*\" /}
mode_alias="''${width}x''${height}"
echo "xrandr setting new mode ''${mode_alias} ''${xrandr_mode_str}"
# Check if mode exists before trying to remove it
if ${pkgs.xorg.xrandr}/bin/xrandr --listmodes | grep -q "^''${mode_alias}"; then
${pkgs.xorg.xrandr}/bin/xrandr --rmmode ''${mode_alias} || echo "Failed to remove existing mode"
fi
${pkgs.xorg.xrandr}/bin/xrandr --newmode ''${mode_alias} ''${xrandr_mode_str} || echo "Failed to create new mode"
${pkgs.xorg.xrandr}/bin/xrandr --addmode DP-0 ''${mode_alias} || echo "Failed to add mode to output"
# Apply new xrandr mode
${pkgs.xorg.xrandr}/bin/xrandr --output DP-0 --primary --mode ''${mode_alias} --pos 0x0 --rotate normal || echo "Failed to apply mode"
${config.boot.kernelPackages.nvidia_x11.settings}/bin/nvidia-settings -a 'SyncToVBlank=0' || echo "Failed to disable VSync"
${config.boot.kernelPackages.nvidia_x11.bin}/bin/nvidia-smi --persistence-mode=ENABLED || echo "Failed to enable persistence mode"
'';
executable = true;
destination = "/bin/resolution.sh";
};
sunshinePkg = (pkgs.unstablePkgs.sunshine.override { cudaSupport = true; });
in
{
pepe.core.graphics = {
enable = true;
nvidia = true;
};
boot.kernelModules = [ "uinput" ];
environment.systemPackages = with pkgs.unstablePkgs; [ gamemode heroic ];
hardware = {
pulseaudio.enable = false;
};
systemd.services.NetworkManager-wait-online.enable = pkgs.lib.mkForce false;
programs.steam = {
enable = true;
gamescopeSession.enable = true;
};
security = {
polkit.extraConfig = ''
polkit.addRule(function(action, subject) {
if (action.id == "org.freedesktop.login1.suspend" ||
action.id == "org.freedesktop.login1.suspend-multiple-sessions" ||
action.id == "org.freedesktop.login1.hibernate" ||
action.id == "org.freedesktop.login1.hibernate-multiple-sessions")
{
return polkit.Result.NO;
}
});
'';
rtkit.enable = true;
};
systemd.user.services.sunshine = {
serviceConfig = {
Restart = pkgs.lib.mkForce "always";
};
};
services = {
sunshine = {
enable = true;
autoStart = true;
package = sunshinePkg;
settings = {
sunshine_name = "The Architect";
capture = "nvfbc";
encoder = "nvenc";
wan_encryption_mode = 0;
lan_encryption_mode = 0;
origin_web_ui_allowed = "lan";
min_threads = 12;
log_path = "sunshine.log";
back_button_timeout = 2500;
};
applications = {
env = {
VDPAU_DRIVER = "nvidia";
LIBVA_DRIVER_NAME = "nvidia";
NVD_BACKEND = "direct";
__GL_SYNC_TO_VBLANK = "0";
__GL_VRR_ALLOWED = "0";
DXVK_ASYNC = "1";
};
apps = [
{
name = "Steam w/ Hue Lights";
cmd = ''${pkgs.bash}/bin/bash -c "${pkgs.gamescope}/bin/gamescope --adaptive-sync --force-composition --immediate-flips --rt -C 3000 -f -e -W ''${SUNSHINE_CLIENT_WIDTH} -H ''${SUNSHINE_CLIENT_HEIGHT} -r ''${SUNSHINE_CLIENT_FPS} -- ${pkgs.steam}/bin/steam -pipewire"'';
detached = [
"${pkgs.pepePkgs.huenicorn}/bin/huenicorn"
];
prep-cmd = [
{
do = ''${pkgs.bash}/bin/bash -c "${resolutionScript}/bin/resolution.sh ''${SUNSHINE_CLIENT_WIDTH} ''${SUNSHINE_CLIENT_HEIGHT}" ''${SUNSHINE_CLIENT_FPS}"'';
undo = ''${pkgs.bash}/bin/bash -c "${pkgs.procps}/bin/pkill gamescope; ${pkgs.procps}/bin/pkill sunshine; ${pkgs.procps}/bin/pkill -KILL huenicorn"'';
}
];
}
{
name = "Steam";
cmd = ''${pkgs.bash}/bin/bash -c "${pkgs.steam}/bin/steam -pipewire"'';
prep-cmd = [
{
do = ''${pkgs.bash}/bin/bash -c "${resolutionScript}/bin/resolution.sh ''${SUNSHINE_CLIENT_WIDTH} ''${SUNSHINE_CLIENT_HEIGHT}" ''${SUNSHINE_CLIENT_FPS}"'';
undo = ''${pkgs.bash}/bin/bash -c "${pkgs.procps}/bin/pkill gamescope; ${pkgs.procps}/bin/pkill sunshine"'';
}
];
}
{
name = "Heroic";
cmd = ''${pkgs.bash}/bin/bash -c "${pkgs.unstablePkgs.heroic}/bin/heroic"'';
prep-cmd = [
{
do = ''${pkgs.bash}/bin/bash -c "${resolutionScript}/bin/resolution.sh ''${SUNSHINE_CLIENT_WIDTH} ''${SUNSHINE_CLIENT_HEIGHT}" ''${SUNSHINE_CLIENT_FPS}"'';
undo = ''${pkgs.bash}/bin/bash -c "${pkgs.procps}/bin/pkill heroic; ${pkgs.procps}/bin/pkill sunshine"'';
}
];
}
];
};
};
displayManager = {
autoLogin = {
inherit user;
enable = true;
};
sddm = {
enable = true;
wayland.enable = false;
};
};
xserver = {
enable = true;
desktopManager.xfce.enable = true;
monitorSection = ''
HorizSync 5.0 - 1000.0
VertRefresh 5.0 - 1000.0
Option "DPMS"
'';
deviceSection = ''
VendorName "NVIDIA Corporation"
Option "CustomEDID" "DFP-1:/etc/X11/120edid.bin"
Option "ConnectedMonitor" "DFP-1"
'';
screenSection = ''
Monitor "Configured Monitor"
DefaultDepth 24
Option "ModeValidation" "NoVertRefreshCheck, NoHorizSyncCheck, NoMaxSizeCheck, NoMaxPClkCheck, AllowNonEdidModes, NoEdidMaxPClkCheck"
Option "UseEdidfreqs" "False"
Option "TripleBuffer" "False"
SubSection "Display"
Depth 24
EndSubSection
'';
};
};
systemd.targets = {
sleep.enable = false;
suspend.enable = false;
hibernate.enable = false;
hybrid-sleep.enable = false;
};
users = {
users.${user} = {
isNormalUser = true;
home = "/home/${user}";
description = "Sunshine Server";
extraGroups = [ "wheel" "networkmanager" "input" "video" "sound" ];
openssh.authorizedKeys.keys = [ "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQC1we38/N+t8Ah5yrLof8QUwhrob7/VXFKIddaJeOVBLuDVnW7ljiAtdtEiL69D/DV4Ohmt5wMvkAAjfuHmim6FD9A6lzPbSU4KH9W2dcckszKbbI636kuDwem/xui6BW3wJa6P+0xW5ksygEAkzcK2PXuC2b4B9uwhuUdKahiGMKDxISG/WianqAe72cGMfNkYvion3Y1VsMLUdm48d2ABnxNpr7NI9B5iJ8dziOft9gpgfz13CCQRlReo75gk/4xI+vSNrQp7eR+wzJy2/dZg/T8jtyA9Q6jVxrxBpqQ1LNXkAKaJkGo9OabF6Wgpzp+YTAurL4nwR2NaJxwFuyoKvACQy0ai4jrS3206gC6JXZv8ktZMZrwUN+jPqCwfgh5qObFkAqKCxbp52ioDek2MQLdOvzQBX//DBhGEp5rzHGLZ3vhRIiiQiaof5sF5zWiYDW5mqezSPNxJPX/BrTP/Wbs/jpwTLBh3wytiia0S1WXQmya89bqzTPFiDWvTRA62EVKB/JaQtPQQOFAxWwg799DMycPeZ81xttZOyMtI/MZSddyqx2S8fWGwvToZQvuZ38mSIpFseLM1IkgabRIrAmat5SBNGGy9Dqa0eMEa7bwIY/4CMB1y6HMTnaoMXA6cnQfHMoB/zyTZ6oTXIeqeOyiZsK+RN0Mvahj8mXi7dw== giulio@giulio-X230" ];
};
groups.media.members = [ user ];
};
}

View File

@ -1,43 +0,0 @@
{ pkgs, config, lib, ... }:
let
domain = "devs.giugl.io";
utilities = import ./utilities.nix { inherit lib config; };
inherit (utilities) generateDeviceStrings;
in
{
pepe.core = {
firewall.openUDP = [ 41641 ];
network.interfaces.tailscale = {
interface = "ts0";
net = "100.64.0.0/10";
type = "vpn";
devices = {
architect = { address = "100.64.0.1"; hostname = "architect.${domain}"; isEndpoint = true; };
kmerr = { address = "100.64.0.2"; hostname = "kmerr.${domain}"; };
work_laptop = { address = "100.64.0.4"; hostname = "work_laptop.${domain}"; };
work_desktop = { address = "100.64.0.5"; hostname = "work_desktop.${domain}"; };
manduria = { address = "100.64.0.6"; hostname = "manduria.${domain}"; };
tommy = { address = "100.64.0.7"; hostname = "tommy.${domain}"; };
alfredo = { address = "100.64.0.9"; hostname = "alfredo.${domain}"; };
appletv = { address = "100.64.0.13"; hostname = "appletv.${domain}"; };
afsun = { address = "100.64.0.15"; hostname = "afsun.${domain}"; };
jacopo-desktop = { address = "100.64.0.21"; hostname = "jacopo-desktop.${domain}"; };
jacopo-tv = { address = "100.64.0.22"; hostname = "jacopo-tv.${domain}"; };
jacopo-phone = { address = "100.64.0.28"; hostname = "jacopo-phone.${domain}"; };
};
};
};
services = {
tailscale = {
enable = true;
interfaceName = config.pepe.core.network.interfaces.tailscale.interface;
package = pkgs.unstablePkgs.tailscale;
};
};
networking.extraHosts = generateDeviceStrings config.pepe.core.network.interfaces.tailscale.devices;
}

View File

@ -1,64 +0,0 @@
{ config, ... }:
let
domain = "tesla.giugl.io";
teslamatePort = 11234;
grafanaPort = 11334;
allowLan = true;
allowWAN = false;
in
{
age.secrets.teslamate = {
file = ../../secrets/teslamate.age;
owner = "teslamate";
};
architect.vhost.${domain} = with config.architect.networks; {
dnsInterfaces = [ "lan" "tailscale" ];
locations = {
"/" = {
inherit allowLan allowWAN;
port = teslamatePort;
proxyWebsockets = true;
allow = [
tailscale.net
];
};
"/live/websocket" = {
inherit allowLan allowWAN;
port = teslamatePort;
proxyWebsockets = true;
allow = [
tailscale.net
];
};
"/grafana" = {
inherit allowLan allowWAN;
port = grafanaPort;
proxyWebsockets = true;
allow = [
tailscale.net
];
};
};
};
services.teslamate = {
enable = true;
port = teslamatePort;
listenAddress = "127.0.0.1";
secretsFile = config.age.secrets.teslamate.path;
virtualHost = domain;
postgres.enable_server = true;
grafana = {
enable = true;
port = grafanaPort;
listenAddress = "127.0.0.1";
urlPath = "/grafana";
};
mqtt = {
enable = true;
};
};
}

View File

@ -0,0 +1,43 @@
{ lib, config, ... }:
let
domain = "httra.giugl.io";
network = import ./network.nix;
in {
services = {
transmission = {
enable = true;
group = "media";
settings = {
download-dir = "/media/transmission";
incomplete-dir = "/media/transmission/.incomplete";
rpc-host-whitelist = "${domain}";
encryption = 2;
speed-limit-up = 10;
speed-limit-up-enabled = true;
peer-port = 51413;
};
performanceNetParameters = true;
};
nginx.virtualHosts.${domain} = {
forceSSL = true;
enableACME = true;
locations."/" = {
proxyPass = "http://localhost:9091";
extraConfig = ''
allow 10.0.0.0/24;
${lib.concatMapStrings (x: "allow ${x};") network.gdevices-wg}
deny all;
'';
};
};
};
networking.extraHosts = ''
${network.architect-lan} ${domain}
${network.architect-wg} ${domain}
'';
users.groups.media.members = [ "transmission" ];
}

View File

@ -1,16 +0,0 @@
{ config, lib, ... }:
let
ifaces = config.pepe.core.network.interfaces;
in
{
# device.address device.hostname
generateDeviceStrings = devices: lib.concatStringsSep "\n"
(lib.mapAttrsToList (name: device: "${device.address} ${device.hostname}") devices);
getDeviceAddress = interface: device:
ifaces.${interface}.devices.${device}.address;
architectInterfaceAddress = interface:
ifaces.${interface}.devices.architect.address;
}

View File

@ -0,0 +1,270 @@
with import ./network.nix; {
networking = {
extraHosts = ''
${architect-wg} architect.devs.giugl.io
${galuminum-wg} galuminum.devs.giugl.io
${oneplus-wg} oneplus.devs.giugl.io
${ipad-wg} ipad.devs.giugl.io
${manduria-wg} manduria.devs.giugl.io
${antonio-wg} antonio.devs.giugl.io
${gbeast-wg} gbeast.devs.giugl.io
${parisaphone-wg} parisa-phone.devs.giugl.io
${parisapc-wg} parisa-pc.devs.giugl.io
${peppiniell-wg} peppiniell.devs.giugl.io
${padulino-wg} padulino.devs.giugl.io
${shield-wg} shield.devs.giugl.io
${pepos-wg} pepos.devs.giugl.io
${eleonora-wg} eleonora.devs.giugl.io
${angellane-wg} angellane.devs.giugl.io
${hotpottino-wg} hotpottino.devs.giugl.io
${salvatore-wg} salvatore.devs.giugl.io
${papa-wg} papa.devs.giugl.io
${defy-wg} defy.devs.giugl.io
${germano-wg} germano.devs.giugl.io
${dodino-wg} dodino.devs.giugl.io
${tommy-wg} tommy.devs.giugl.io
${alain-wg} alain.devs.giugl.io
${dima-wg} dima.devs.giugl.io
${mikey-wg} mikey.devs.giugl.io
${andrew-wg} andrew.devs.giugl.io
${mikeylaptop-wg} mikeylaptop.devs.giugl.io
${wolfsonhouse-wg} wolfsonhouse.devs.giugl.io
${frznn-wg} frznn.devs.giugl.io
${ludo-wg} ludo.devs.giugl.io
${parina-wg} parina.devs.giugl.io
${parina-ipad-wg} parinaipad.devs.giugl.io
${nilo-wg} nilo.devs.giugl.io
'';
wireguard = {
interfaces.${proxy-if} = {
ips = [ "10.4.0.2/32" ];
privateKeyFile = "/secrets/wireguard/proxy.key";
peers = [{
publicKey = "WmJBpXpYebcmJEF8nVTKMqQK01KyBe42vzc38K66rVs=";
allowedIPs = [ "10.4.0.1/32" ];
endpoint = "giugl.io:1195";
persistentKeepalive = 21;
}];
};
interfaces.${vpn-if} = {
listenPort = 1194;
ips = [ "10.3.0.1/24" ];
privateKeyFile = "/secrets/wireguard/server.key";
peers = [
{
# gAluminum
allowedIPs = [ galuminum-wg ];
publicKey = "pEEgSs7xmO0cfyvoQlU8lfwqdYM1ISgmPAunPtF+0xw=";
}
{
# OnePlus
allowedIPs = [ oneplus-wg ];
publicKey = "zynSERy6VhxN5zBf1ih3BOAHxvigDixHB9YKnSBgYFs=";
}
{
# iPad
allowedIPs = [ ipad-wg ];
publicKey = "DPpd+P/hV1XLuvdcrCRv1sgz8BeZt1y5D6VehNuhjSQ=";
}
{
# Manduria
allowedIPs = [ manduria-wg ];
publicKey = "wT38oXvDQ8g0hI+pAXQobOWf/Wott2zhwo8TLvXK400=";
}
{
# Antonio
allowedIPs = [ antonio-wg ];
publicKey = "SPndCvEzuLHtGAQV8u/4dfLlFHoPcXS3L98oFOwTljc=";
}
{
# Eleonora
allowedIPs = [ eleonora-wg ];
publicKey = "SL54f1ZeieFyn5X5UAPmypP10GV/c419O94vCzGHFhg=";
}
{
# padulino
allowedIPs = [ padulino-wg ];
publicKey = "sk2Wr2OesND9jcuP/8k7BirSpR4pNNbS9gBkbOxZxwg=";
}
{
# GBEAST
allowedIPs = [ gbeast-wg ];
publicKey = "XiK+wk+DErz0RmCWRxuaJN1cvdj+3DoiU6tcR+uZfAI=";
}
{
# parisa-phone
allowedIPs = [ parisaphone-wg ];
publicKey = "MGdaRMmsik7SLRUsijS0TctcKUD6Tnr7XugGJClTCC4=";
}
{
# parisa-pc
allowedIPs = [ parisapc-wg ];
publicKey = "b2QzZDTgGQbNXSCLYB4KUzq0/099pH2T8H5BckfNSTQ=";
}
{
# peppiniell
allowedIPs = [ peppiniell-wg ];
publicKey = "bzoW3Rx+7Un9hx/2opgBQJmmnZ/hgj1lQ2FnonCHjTc=";
}
{
# angellane
allowedIPs = [ angellane-wg ];
publicKey = "MZ+nZklHpBxTL7QN9QJpBBx7yOYRZLONfvqAnuk85x0=";
}
{
# hotpottino
allowedIPs = [ hotpottino-wg ];
publicKey = "YqtzTWqGBs2GwSPNO0aRSV4nvJDW3UHHt6fV4UC7vnU=";
}
{
# shield
allowedIPs = [ shield-wg ];
publicKey = "1GaV/M48sHqQTrBVRQ+jrFU2pUMmv2xkguncVcwPCFs=";
}
{
# pepos
allowedIPs = [ pepos-wg ];
publicKey = "mb1VaMLML5J24oCMBuhqvBrT6S4tAqWERn30z+h/LwM=";
}
{
# salvatore
allowedIPs = [ salvatore-wg ];
publicKey = "fhlnBHeMyHZKLUCTSA9kmkKoM5x/qzz/rnCJrUh3Gzs=";
}
{
# papa
allowedIPs = [ papa-wg ];
publicKey = "oGHygt02Oni3IFbScKD0NVEfHKCp6bpw68aq5g4RrAA=";
}
{
# defy
allowedIPs = [ defy-wg ];
publicKey = "Cvi/eto7E6Ef+aiL81ou7x12fJCeuXrf/go9fxEqXG4=";
}
{
# germano
allowedIPs = [ germano-wg ];
publicKey = "gi4o+pZWKItzVs7vY8fvXh98jX6CNeCwc1YDzhc3mA4=";
}
{
# flavio
allowedIPs = [ flavio-wg ];
publicKey = "Yg0P+yHi/9SZHyoel8jT9fmmu+irLYmT8yMp/CZoaSg=";
}
{
# dodino
allowedIPs = [ dodino-wg ];
publicKey = "JHkqlADQpY1CUcivraG9i6rIzCzLVFcl8HP5uIk35lk=";
}
{
# tommy
allowedIPs = [ tommy-wg ];
publicKey = "tytknU7wql1d0A2provX3RP7CNcEIajfgBJKoSyVLgo=";
}
{
# alain
allowedIPs = [ alain-wg ];
publicKey = "/o2msFJoUL4yovcIQJTU8c1faFtekrjSBBWJABouWno=";
}
{
# dima
allowedIPs = [ dima-wg ];
publicKey = "svzWYIZ6v+cLCp/emGG7mx2YpBJqw2fqjVuHZy7b6H0=";
}
{
# wolfsonhouse
allowedIPs = [ wolfsonhouse-wg ];
publicKey = "UJRJcAOcnEjEB3o4K2I7gEM97SrhENEesZNf28z+EBQ=";
}
{
# mikey
allowedIPs = [ mikey-wg ];
publicKey = "ewbDdX3z7nxG2aPIf9TogXkhxPlGipLFcy6XfyDC6gI=";
}
{
# andrew
allowedIPs = [ andrew-wg ];
publicKey = "LP/FgST9fmBQSoKQFq9sFGvjRFOtRooMcuEcjuqaoWM=";
}
{
# mikey laptop
allowedIPs = [ mikeylaptop-wg ];
publicKey = "kz/pY/PgV+dwF1JZ2It4r5B5QfRSQM7HkbFCdvd5Yxk=";
}
{
# andrew desktop
allowedIPs = [ andrewdesktop-wg ];
publicKey = "rpYr3JNLIzxpxzFuQuaHFEl/XvPEPfwLbDETBP8KYXI=";
}
{
# laptop desktop
allowedIPs = [ jacopo-wg ];
publicKey = "W/taWI79bPIKOolVVu5xZfiJnPw9K91Xn1zhcM0+4g0=";
}
{
# frznn
allowedIPs = [ frznn-wg ];
publicKey = "dXcrdME6VnnE5PBYwvUmayf7cn2wpcExeCR9gIXOO0o=";
}
{
# ludo
allowedIPs = [ ludo-wg ];
publicKey = "ecrxdzx7tQZwMPxZOjHUvxZT2xY79B6XEDIW+fhEtEM=";
}
{
# parina
allowedIPs = [ parina-wg ];
publicKey = "7nubNnfGsg4/7KemMDn9r99mNK8RFU9uOFFqaYv6rUA=";
}
{
# nilo
allowedIPs = [ nilo-wg ];
publicKey = "lhTEDJ9WnizvEHTd5kN21fTHF27HNk+fPLQnB1B3LW0=";
}
{
# parina ipad
allowedIPs = [ parina-ipad-wg ];
publicKey = "ezkCzl2qC7Hd7rFKfqMa0JXDKRhVqy79H52rA06x7mU=";
}
];
};
};
};
}

View File

@ -9,8 +9,7 @@ let
export __VK_LAYER_NV_optimus=NVIDIA_only export __VK_LAYER_NV_optimus=NVIDIA_only
exec -a "$0" "$@" exec -a "$0" "$@"
''; '';
in in {
{
imports = [ ./hardware.nix ./wireguard.nix ./sound.nix ]; imports = [ ./hardware.nix ./wireguard.nix ./sound.nix ];
boot = { boot = {
@ -71,5 +70,5 @@ in
programs.steam.enable = true; programs.steam.enable = true;
environment.systemPackages = with pkgs; [ efibootmgr nvidia-offload ]; environment.systemPackages = with pkgs; [ efibootmgr nvidia-offload ];
# system.stateVersion = "21.05"; # Did you read the comment? system.stateVersion = "21.05"; # Did you read the comment?
} }

73
hosts/proxy/coturn.nix Normal file
View File

@ -0,0 +1,73 @@
{ pkgs, config, ... }:
let
public_ip = "23.88.108.216";
realm = "turn.giugl.io";
static-auth-secret = "69duck duck fuck420";
in {
services.coturn = rec {
inherit realm static-auth-secret;
secure-stun = true;
enable = true;
no-cli = true;
no-tcp-relay = true;
min-port = 49000;
max-port = 50000;
use-auth-secret = true;
relay-ips = [ public_ip ];
listening-ips = [ public_ip ];
cert = "${config.security.acme.certs.${realm}.directory}/full.pem";
pkey = "${config.security.acme.certs.${realm}.directory}/key.pem";
extraConfig = ''
verbose
cipher-list=\"HIGH\"
no-multicast-peers
denied-peer-ip=0.0.0.0-0.255.255.255
denied-peer-ip=10.0.0.0-10.255.255.255
denied-peer-ip=100.64.0.0-100.127.255.255
denied-peer-ip=127.0.0.0-127.255.255.255
denied-peer-ip=169.254.0.0-169.254.255.255
denied-peer-ip=172.16.0.0-172.31.255.255
denied-peer-ip=192.0.0.0-192.0.0.255
denied-peer-ip=192.0.2.0-192.0.2.255
denied-peer-ip=192.88.99.0-192.88.99.255
denied-peer-ip=192.168.0.0-192.168.255.255
denied-peer-ip=198.18.0.0-198.19.255.255
denied-peer-ip=198.51.100.0-198.51.100.255
denied-peer-ip=203.0.113.0-203.0.113.255
denied-peer-ip=240.0.0.0-255.255.255.255
denied-peer-ip=::1
denied-peer-ip=64:ff9b::-64:ff9b::ffff:ffff
denied-peer-ip=::ffff:0.0.0.0-::ffff:255.255.255.255
denied-peer-ip=100::-100::ffff:ffff:ffff:ffff
denied-peer-ip=2001::-2001:1ff:ffff:ffff:ffff:ffff:ffff:ffff
denied-peer-ip=2002::-2002:ffff:ffff:ffff:ffff:ffff:ffff:ffff
denied-peer-ip=fc00::-fdff:ffff:ffff:ffff:ffff:ffff:ffff:ffff
denied-peer-ip=fe80::-febf:ffff:ffff:ffff:ffff:ffff:ffff:ffff
'';
};
networking.firewall = {
interfaces.ens3 = let
range = with config.services.coturn; [{
from = min-port;
to = max-port;
}];
in {
allowedUDPPortRanges = range;
allowedUDPPorts = [ 5349 ];
#allowedTCPPortRanges = range;
allowedTCPPorts = [ 80 443 5349 ];
};
};
services.nginx.enable = true;
services.nginx.virtualHosts.${realm} = {
addSSL = true;
enableACME = true;
};
# to access the ACME files
users.groups.nginx.members = [ "turnserver" ];
}

26
hosts/proxy/default.nix Normal file
View File

@ -0,0 +1,26 @@
{ config, pkgs, ... }:
{
imports =
[ ./hardware-configuration.nix ./coturn.nix ./wireguard.nix ./ssh.nix ];
boot.loader.grub = {
enable = true;
version = 2;
devices = [ "/dev/sda" ];
};
system.stateVersion = "21.05";
networking = {
useDHCP = false;
hostName = "proxy";
nameservers = [ "10.4.0.2" "1.1.1.1" ];
interfaces.ens3.useDHCP = true;
};
users.users.root.openssh.authorizedKeys.keys = [
"ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQCuURERnIFe2XbNu6AsPe2DO11RuaHxVGUcaoJUsIB1F+VOggOVLhxSenOPYLm6NvvGeXVi95G5Sm1UZRcJEEkvxus4bSViV4t/Q2azfYFE27yRH/IeMMoWNPGYNm5Bok2qFb4vHifra9FffwXnOzr0nDDTdHXCft4TO5nsenLJwqu5zOO1CR7J52otY7LheNPyzbGxgIkB3Y7LeOj1+/xXSOJ379NOL2RQBobsg7k442WCX7tU6AC1ct3W+93tcJUUdzJKTT9TJ+XmhdjXNWhDd+QZUNAMr+nKoEdExHp0H40/wIhcLD2OV95gX4i/YBzCg4OQOqZqWiibiEQfGTSAh5aD+nX/PqjXf0XSLEUOA81biLFu28oO8gocjwnhgqmlghvO4SG1rs6uZ8EyPyWsrVMjy8B9FX4aloKqua3aicgC+upjLl3x+KkMJizlMB5Ew7KOjPsjXwMqeJmeBOEd6TSEctttR+lIp+/368FtwXeBxzx9MBT4620mnjWtVKM= giulio@gAluminum"
];
}

View File

@ -0,0 +1,22 @@
# Do not modify this file! It was generated by nixos-generate-config
# and may be overwritten by future invocations. Please make changes
# to /etc/nixos/configuration.nix instead.
{ config, lib, pkgs, modulesPath, ... }:
{
imports = [ (modulesPath + "/profiles/qemu-guest.nix") ];
boot.initrd.availableKernelModules =
[ "ata_piix" "virtio_pci" "virtio_scsi" "xhci_pci" "sd_mod" "sr_mod" ];
boot.initrd.kernelModules = [ ];
boot.kernelModules = [ ];
boot.extraModulePackages = [ ];
fileSystems."/" = {
device = "/dev/disk/by-uuid/8b5bcd4a-02b8-4e11-b856-eda792b8b7b8";
fsType = "ext4";
};
swapDevices = [ ];
}

15
hosts/proxy/ssh.nix Normal file
View File

@ -0,0 +1,15 @@
{ config, ... }:
{
services = {
fail2ban.enable = true;
openssh = {
permitRootLogin = "prohibit-password";
passwordAuthentication = false;
enable = true;
};
};
networking.firewall.allowedTCPPorts = [ 22 ];
}

42
hosts/proxy/wireguard.nix Normal file
View File

@ -0,0 +1,42 @@
{ config, ... }:
let
wg_if = "wg0";
wan_if = "ens3";
in {
networking = {
firewall.allowedUDPPorts = [ 1195 ];
nat = {
enable = true;
externalInterface = wan_if;
internalInterfaces = [ wg_if ];
forwardPorts = [{
destination = "10.4.0.2:1194";
proto = "udp";
sourcePort = 1194;
}];
};
wireguard = {
interfaces.${wg_if} = {
listenPort = 1195;
ips = [ "10.4.0.1/24" ];
privateKeyFile = "/secrets/wireguard/server.key";
postSetup = ''
/run/current-system/sw/bin/iptables -t nat -A POSTROUTING -o ${wg_if} -j MASQUERADE
'';
postShutdown = ''
/run/current-system/sw/bin/iptables -t nat -D POSTROUTING -o ${wg_if} -j MASQUERADE
'';
peers = [{
allowedIPs = [ "10.4.0.2" "10.3.0.0/24" ];
publicKey = "73oFhyQA3mgX4GmN6ul5HuOsgxa4INlzCPsyuXna0AA=";
}];
};
};
};
}

View File

@ -1,7 +0,0 @@
rec {
architect = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAICu7rSsZ+d3BkppimNHJj8xL5jfl5RxMU0+Q5cue0LUu root@architect";
architectHostKey = "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDGLLAtRzLtCExHLhpsC+vH1nXcla3wibbMOFRCwXfXjtn2A9DjewHBwcbQbYQa6yuaEa3vmvUyrUtW6RUAiGSNhDMUPz7swr5tujgO/6ToPf0vKDDeOCwK5wqmNoUlDf7qzkxwCiI0dPYuCr7uGt00/ebSGfp+F1zmgC9MxuefYMdX5Q5I7HoHOYbBC9q9ue5mc0g+F8GnmD+Pd2pDDiHpCflT+iOzLJH0gCcW/0e5q7XYKGs09Cm/L1zroHIb14Borndu0Mby7x2FlnSeap5KXr9rkKVyr3amX0mksb4N0T36MMJwLYcrvE0S8utFdHEusoYEkP3fjSgsKKHKEgiZbqaeA0oZHddG49JNBsCLmmrN8T142t1fftP4NdFyKpcI9gYsbXhZf6bheV1wQ/cpv3KkLGG7JlZeORRAc4xgT33BHvVXTcWCE2EYcNmdscrMOEw3mcDESu7S14iXZgGIUgYISZ3GTZ5+mNB6OoEwxqK+eYzYMyDpNBxv6/LlEvc= root@architect";
macbook = "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQC1we38/N+t8Ah5yrLof8QUwhrob7/VXFKIddaJeOVBLuDVnW7ljiAtdtEiL69D/DV4Ohmt5wMvkAAjfuHmim6FD9A6lzPbSU4KH9W2dcckszKbbI636kuDwem/xui6BW3wJa6P+0xW5ksygEAkzcK2PXuC2b4B9uwhuUdKahiGMKDxISG/WianqAe72cGMfNkYvion3Y1VsMLUdm48d2ABnxNpr7NI9B5iJ8dziOft9gpgfz13CCQRlReo75gk/4xI+vSNrQp7eR+wzJy2/dZg/T8jtyA9Q6jVxrxBpqQ1LNXkAKaJkGo9OabF6Wgpzp+YTAurL4nwR2NaJxwFuyoKvACQy0ai4jrS3206gC6JXZv8ktZMZrwUN+jPqCwfgh5qObFkAqKCxbp52ioDek2MQLdOvzQBX//DBhGEp5rzHGLZ3vhRIiiQiaof5sF5zWiYDW5mqezSPNxJPX/BrTP/Wbs/jpwTLBh3wytiia0S1WXQmya89bqzTPFiDWvTRA62EVKB/JaQtPQQOFAxWwg799DMycPeZ81xttZOyMtI/MZSddyqx2S8fWGwvToZQvuZ38mSIpFseLM1IkgabRIrAmat5SBNGGy9Dqa0eMEa7bwIY/4CMB1y6HMTnaoMXA6cnQfHMoB/zyTZ6oTXIeqeOyiZsK+RN0Mvahj8mXi7dw== giulio@giulio-X230";
groups.architect = [ architect architectHostKey ];
}

6
lib/default.nix Normal file
View File

@ -0,0 +1,6 @@
{ pkgs, unstable, nixpkgs, nixos-unstable, home-manager, ... }: rec {
user = import ./user.nix { inherit pkgs unstable; };
host = import ./host.nix {
inherit pkgs nixpkgs unstable nixos-unstable home-manager user;
};
}

View File

@ -1,49 +1,44 @@
{ pkgs { pkgs, nixpkgs, nixos-unstable, unstable, home-manager, user, ... }:
, nixpkgs
, home-manager
, system
, mkSysRole
, mkUser
, ...
}:
{ {
mkHost = { name, users, roles ? [ ], imports ? [ ] }: mkHost = { name, users, roles ? [], imports ? [] }:
let let
users_mod = (map system = "x86_64-linux";
(u:
mkUser {
name = u.user;
roles = u.roles;
})
users);
roles_mod = (map (r: mkSysRole r) roles);
add_imports = imports;
in
nixpkgs.lib.nixosSystem {
inherit system pkgs;
modules = [ mkRole = role : import (../roles + "/${role}.nix");
{
imports = users_mod ++
roles_mod ++
add_imports ++ [
(mkSysRole "common")
(mkSysRole "acme")
(mkUser { name = "root"; roles = [ ]; })
../modules
];
home-manager = { users_mod= (map (u: user.mkUser {name = u.user; roles = u.roles; }) users);
useGlobalPkgs = true; roles_mod = (map (r: mkRole r) roles);
}; add_imports = imports;
in nixpkgs.lib.nixosSystem {
inherit system;
system.stateVersion = "25.05"; modules = [
} {
imports = users_mod ++ roles_mod ++ add_imports;
nixpkgs = {
pkgs = pkgs;
};
home-manager.nixosModules.home-manager nix.nixPath = [
../hosts/${name}/default.nix "nixpkgs=${nixpkgs}"
pkgs.nixosModules.cachixConfig "unstable=${nixos-unstable}"
]; ];
}; nix.registry.nixpkgs.flake = nixpkgs;
nix.registry.unstable.flake = nixos-unstable;
users.users.root = {
shell = pkgs.zsh;
};
home-manager.users.root.imports = [ ../roles/home/common.nix ];
home-manager.extraSpecialArgs.unstable = unstable;
}
home-manager.nixosModules.home-manager
../roles/common.nix
../roles/acme.nix
../hosts/${name}/default.nix
];
};
} }

View File

@ -1,55 +1,36 @@
{ pkgs { pkgs, unstable, ... }:
, stdenv
, home-manager
, mkHomeRole
, ...
}:
{ {
mkUser = { name, roles ? [ ] }: mkUser = { name, roles ? [ ] }:
let let
roles_mod = (map (r: mkHomeRole r) roles); mkRole = role: import (../roles/home + "/${role}.nix");
in roles_mod = (map (r: mkRole r) roles);
{ in {
fileSystems."/home/${name}/Downloads" = pkgs.lib.mkIf stdenv.isLinux { users.groups.plugdev = { };
fileSystems."/home/${name}/Downloads" = {
device = "tmpfs"; device = "tmpfs";
fsType = "tmpfs"; fsType = "tmpfs";
options = [ "size=3G" ]; options = [ "size=3G" ];
}; };
users = { users.users.${name} = {
users.${name} = { isNormalUser = true;
isNormalUser = name != "root"; shell = pkgs.zsh;
extraGroups = [ "wheel" "plugdev" ]; extraGroups = [ "wheel" "plugdev" ];
shell = pkgs.zsh; };
};
home-manager.users.${name}.imports = [ (mkRole "common") ]
++ roles_mod;
};
mkHMUser = { name, roles ? [] }:
let
mkRole = role: import (../roles/home + "/${role}.nix");
in{
home-manager.users.${name}.imports = [ (mkRole "common") ]
++ roles_mod;
}; };
programs.zsh.enable = true; };
home-manager.users.${name}.imports = [
(mkHomeRole "common")
(mkHomeRole "zsh")
(mkHomeRole "aichat")
] ++ roles_mod;
};
mkHMUser = { name, roles ? [ ] }:
let
roles_mod = (map (r: mkHomeRole r) roles);
in
home-manager.lib.homeManagerConfiguration {
inherit pkgs;
modules = [
{
home = {
username = name;
homeDirectory =
if stdenv.isLinux then "/home/${name}" else "/Users/${name}";
};
}
(mkHomeRole "common")
(mkHomeRole "aichat")
] ++ roles_mod;
};
} }

View File

@ -1,6 +0,0 @@
{ ... }:
{
mkSysRole = role: import (../roles/${role}.nix);
mkHomeRole = role: import (../roles/home/${role}.nix);
}

View File

@ -1,11 +0,0 @@
{...}: {
imports = [
./media.nix
./graphics.nix
./network.nix
./vhost.nix
./firewall.nix
./dns.nix
./docker.nix
];
}

View File

@ -1,169 +0,0 @@
{ config, lib, pkgs, ... }:
let
inherit (lib) mkOption types mkIf concatMapStrings concatStrings mapAttrsToList attrsets;
cfg = config.pepe.core.dns;
in
{
options.pepe.core.dns = {
enable = mkOption {
type = types.bool;
default = false;
description = "Whether to enable the DNS server.";
};
nextDNSId = mkOption {
type = types.str;
default = "d65174";
description = "NextDNS ID for DNS over TLS.";
};
extraDomains = mkOption {
type = types.attrsOf (types.submodule {
options = {
dnsInterfaces = mkOption {
type = types.listOf types.str;
default = [ ];
description = "List of interfaces to add DNS entries for this domain.";
};
};
});
default = { };
description = "Additional domains to add to DNS configuration.";
};
};
config = mkIf cfg.enable {
services.coredns = {
enable = true;
config =
let
# Function to generate domain-specific configurations
generateDomainConfig = domain: conf: ifaceName:
let
iface = config.pepe.core.network.interfaces.${ifaceName};
ifaceEndpoint = lib.head (lib.attrNames (lib.filterAttrs (_: device: device.isEndpoint) iface.devices));
serverIP = iface.devices.${ifaceEndpoint}.address;
interfaceNet = iface.net;
in
''
${domain} {
view ${ifaceName} {
expr incidr(client_ip(), '${interfaceNet}')
}
template IN A ${domain} {
answer "${domain}. 60 IN A ${serverIP}"
}
template IN HTTPS ${domain} {
answer "${domain}. 60 IN HTTPS 1 . ipv4hint=\"${serverIP}\""
}
cache
log
}
'';
# Function to generate device views for an interface
generateDeviceViews = ifaceName:
let
iface = config.pepe.core.network.interfaces.${ifaceName};
in
concatMapStrings
({ name, device }:
let
deviceIP = device.address;
serverName = "${name}-${cfg.nextDNSId}.dns.nextdns.io";
in
''
. {
view ${name} {
expr client_ip() == '${deviceIP}'
}
forward . tls://45.90.28.77 tls://45.90.30.77 {
tls_servername ${serverName}
health_check 5s
}
}
''
)
(attrsets.mapAttrsToList
(name: device: { inherit name device; })
iface.devices
);
# Collect all interfaces used across all domains
allInterfaces = lib.unique (lib.flatten
(lib.mapAttrsToList
(_: conf: conf.dnsInterfaces)
(config.pepe.core.vhost.hosts // cfg.extraDomains)
));
# Generate all device views once
allDeviceViews = concatMapStrings generateDeviceViews allInterfaces;
# Function to generate configurations for all domains
generateCoreDNSConfig = domains:
let
generateForDomain = domain: conf:
concatMapStrings
(ifaceName: generateDomainConfig domain conf ifaceName)
conf.dnsInterfaces;
in
concatStrings (mapAttrsToList generateForDomain domains);
allDomains = config.pepe.core.vhost.hosts // cfg.extraDomains;
in
let
# Function to generate DNS records for individual devices based on dnsResolvableName
generateDeviceHostRecords =
let
generateRecordsForInterface = ifaceName: ifaceConfig:
lib.concatStringsSep "\n" (lib.mapAttrsToList
(deviceName: deviceConfig:
if deviceConfig.hostname!= null then
let
deviceAddress = deviceConfig.address;
deviceIface = ifaceConfig.net;
deviceHost = deviceConfig.hostname;
in
''
${deviceHost} {
view ${ifaceName} {
expr incidr(client_ip(), '${deviceIface}')
}
template IN A ${deviceHost} {
answer "${deviceHost}. 60 IN A ${deviceAddress}"
}
cache
log
}
''
else ""
)
ifaceConfig.devices);
in
lib.concatStringsSep "\n" (lib.mapAttrsToList generateRecordsForInterface config.pepe.core.network.interfaces);
in
''
${generateCoreDNSConfig allDomains}
${generateDeviceHostRecords}
${allDeviceViews}
. {
forward . tls://45.90.28.77 tls://45.90.30.77 {
tls_servername "lan-${cfg.nextDNSId}.dns.nextdns.io"
health_check 5s
}
}
'';
};
};
}

View File

@ -1,60 +0,0 @@
{ config, lib, pkgs, ... }:
let
inherit (lib) mkIf;
cfg = config.pepe.core.docker;
in
{
options.pepe.core.docker = with lib; {
enable = mkEnableOption "Enable Docker";
nvidia = mkEnableOption "Enable NVIDIA Container Toolkit";
dataRoot = mkOption {
type = types.str;
default = "/var/lib/docker";
description = "Docker data root directory";
};
extraOptions = mkOption {
type = types.str;
default = "";
description = "Extra options for Docker daemon";
};
enableOnBoot = mkOption {
type = types.bool;
default = false;
description = "Start Docker on boot";
};
iptables = mkOption {
type = types.bool;
default = false;
description = "Whether Docker should manipulate iptables";
};
users = mkOption {
type = types.listOf types.str;
default = [];
description = "Users to add to the docker group";
};
};
config = mkIf cfg.enable {
hardware.nvidia-container-toolkit.enable = cfg.nvidia;
virtualisation = {
oci-containers.backend = "docker";
docker = {
enable = true;
extraOptions = cfg.extraOptions;
enableOnBoot = cfg.enableOnBoot;
daemon.settings = {
iptables = cfg.iptables;
data-root = cfg.dataRoot;
};
};
};
users.users = lib.genAttrs cfg.users (user: {
extraGroups = [ "docker" ];
});
};
}

View File

@ -1,27 +0,0 @@
{ config, lib, ... }:
let
inherit (lib) mkOption types;
cfg = config.pepe.core.firewall;
in
{
options.pepe.core.firewall = {
openTCP = mkOption {
type = types.listOf types.int;
default = [ ];
description = "TCP ports to open in the firewall";
};
openUDP = mkOption {
type = types.listOf types.int;
default = [ ];
description = "UDP ports to open in the firewall";
};
};
config = {
networking.firewall = {
allowedTCPPorts = cfg.openTCP;
allowedUDPPorts = cfg.openUDP;
};
};
}

View File

@ -1,39 +0,0 @@
{ config, lib, pkgs, ... }:
let
inherit (lib) mkIf;
cfg = config.pepe.core.graphics;
in
{
options.pepe.core.graphics = with lib; {
enable = mkEnableOption "Enable graphics";
nvidia = mkEnableOption "Enable nvidia graphics";
};
config = mkIf cfg.enable {
hardware = {
graphics = {
enable = true;
extraPackages = with pkgs; mkIf cfg.nvidia [ vaapiVdpau ];
};
nvidia = mkIf cfg.nvidia {
modesetting.enable = true;
powerManagement.enable = false;
powerManagement.finegrained = false;
open = false;
nvidiaSettings = true;
package = config.boot.kernelPackages.nvidiaPackages.latest;
};
};
services = {
xserver = {
enable = true;
videoDrivers = mkIf cfg.nvidia [ "nvidia" ];
};
};
};
}

View File

@ -1,27 +0,0 @@
{ config, lib, ... }:
let
cfg = config.pepe.core.media;
in
{
options.pepe.core.media = with lib; {
enable = mkEnableOption "Enable media library";
group = mkOption {
type = types.str;
default = "media";
};
path = mkOption {
type = types.str;
default = null;
};
groupMembers = mkOption {
type = types.listOf types.str;
default = [];
};
};
config = lib.mkIf cfg.enable {
users.groups.${cfg.group}.members = [ cfg.group ] ++ cfg.groupMembers;
};
}

View File

@ -1,126 +0,0 @@
{ config, lib, ... }:
let
inherit (lib) mkOption types;
cfg = config.pepe.core.network;
in
{
options.pepe.core.network = {
interfaces = mkOption {
type = types.attrsOf (types.submodule {
options = {
interface = mkOption {
type = types.str;
description = "The network interface name.";
};
type = mkOption {
type = types.enum [ "lan" "wan" "vpn" ];
description = "The type of interface (lan, wan, or vpn).";
};
net = mkOption {
type = types.str;
description = "The network address in CIDR format.";
};
devices = mkOption {
type = types.attrsOf (types.submodule {
options = {
address = mkOption {
type = types.str;
description = "The IP address of the device.";
};
hostname = mkOption {
type = types.str;
description = "The hostname of the device.";
};
isEndpoint = mkOption {
type = types.bool;
default = false;
description = "Whether this device serves as a DNS endpoint for this interface.";
};
};
});
default = { };
description = "An attribute set of devices with their configurations.";
};
};
});
default = { };
description = "An attribute set of networks with their configurations.";
};
interfacesByType = mkOption {
type = types.attrsOf (types.listOf types.str);
default = { };
description = "Interfaces grouped by type (lan, wan, vpn) for easy access.";
internal = true;
};
dnsEndpoints = mkOption {
type = types.attrsOf (types.submodule {
options = {
interface = mkOption {
type = types.str;
description = "The interface this DNS endpoint belongs to.";
};
device = mkOption {
type = types.str;
description = "The device name that serves as the DNS endpoint.";
};
address = mkOption {
type = types.str;
description = "The IP address of the DNS endpoint.";
};
serverName = mkOption {
type = types.str;
default = "";
description = "DNS server name for TLS connections.";
};
};
});
default = { };
description = "DNS endpoints for each interface.";
internal = true;
};
};
config = {
# Create lists of interfaces by type for easy access elsewhere
pepe.core.network.interfacesByType = {
lan = lib.attrNames (lib.filterAttrs (_: iface: iface.type == "lan") cfg.interfaces);
wan = lib.attrNames (lib.filterAttrs (_: iface: iface.type == "wan") cfg.interfaces);
vpn = lib.attrNames (lib.filterAttrs (_: iface: iface.type == "vpn") cfg.interfaces);
};
# Collect DNS endpoints from all interfaces
pepe.core.network.dnsEndpoints =
let
collectEndpoints = ifaceName: iface:
lib.mapAttrs'
(deviceName: device:
lib.nameValuePair
"${ifaceName}-${deviceName}"
{
interface = ifaceName;
device = deviceName;
address = device.address;
serverName = device.dnsServerName;
}
)
(lib.filterAttrs (_: device: device.isDnsEndpoint) iface.devices);
in
lib.foldl
(acc: ifaceName:
acc // (collectEndpoints ifaceName cfg.interfaces.${ifaceName})
)
{ }
(lib.attrNames cfg.interfaces);
};
}

View File

@ -1,131 +0,0 @@
{ config, lib, ... }:
let
inherit (lib) mkOption types mapAttrs concatMapStringsSep optionalString mkIf;
cfg = config.pepe.core.vhost;
in
{
options.pepe.core.vhost = {
hosts = mkOption {
type = types.attrsOf (types.submodule {
options = {
locations = mkOption {
type = types.attrsOf (types.submodule {
options = {
extraConfig = mkOption {
type = types.str;
description = "Extra configuration for the location.";
default = "";
};
proxyWebsockets = mkOption {
type = types.bool;
default = false;
};
host = mkOption {
type = types.str;
description = "The host for the location.";
default = "127.0.0.1";
};
port = mkOption {
type = types.int;
description = "The port number for the location.";
};
allow = mkOption {
type = types.listOf types.str;
default = [ ];
description = "IP address or CIDR block to allow.";
};
allowVPN = mkOption {
type = types.bool;
default = false;
description = "If set to true, allow VPN traffic.";
};
allowLAN = mkOption {
type = types.bool;
default = false;
description = "If set to true, allow LAN traffic.";
};
allowWAN = mkOption {
type = types.bool;
default = false;
description = "If set to true, allow WAN traffic. If false, deny all WAN traffic.";
};
path = mkOption {
type = types.str;
default = "";
};
recommendedProxySettings = mkOption {
type = types.bool;
default = true;
description = "Force the use of recommended proxy configuration.";
};
};
});
default = { };
description = "An attribute set of location configurations.";
};
resolveLocally = mkOption {
type = types.bool;
default = true;
description = "If true, ensure the vhost is resolvable to 127.0.0.1 locally on this server.";
};
};
});
default = { };
description = "An attribute set of domain configurations.";
};
};
config = {
pepe.core.firewall.openTCP = [ 80 443 ];
# Configure DNS entries for vhosts when DNS is enabled
pepe.core.dns = mkIf config.pepe.core.dns.enable {
extraDomains = mapAttrs
(domain: conf: {
dnsInterfaces =
(lib.optionals (lib.any (loc: loc.allowLAN) (lib.attrValues conf.locations))
config.pepe.core.network.interfacesByType.lan) ++
(lib.optionals (lib.any (loc: loc.allowVPN) (lib.attrValues conf.locations))
config.pepe.core.network.interfacesByType.vpn);
})
cfg.hosts;
};
services.nginx.virtualHosts = mapAttrs
(domain: conf: {
forceSSL = true;
useACMEHost = "giugl.io";
locations = mapAttrs
(path: location: {
proxyPass = "http://${location.host}:${toString location.port}${location.path}";
proxyWebsockets = location.proxyWebsockets;
recommendedProxySettings = location.recommendedProxySettings;
extraConfig = ''
${concatMapStringsSep "\n" (allowCIDR: "allow ${allowCIDR};") location.allow}
${optionalString location.allowLAN (concatMapStringsSep "\n" (ifaceName: "allow ${config.pepe.core.network.interfaces.${ifaceName}.net};") config.pepe.core.network.interfacesByType.lan)}
${optionalString location.allowVPN (concatMapStringsSep "\n" (ifaceName: "allow ${config.pepe.core.network.interfaces.${ifaceName}.net};") config.pepe.core.network.interfacesByType.vpn)}
${optionalString (!location.allowWAN) "deny all;"}
'' + location.extraConfig;
})
conf.locations;
})
cfg.hosts;
networking.extraHosts = lib.concatStringsSep "\n" (lib.flatten (lib.mapAttrsToList
(domain: hostConf:
lib.optionals hostConf.resolveLocally "127.0.0.1 ${domain}"
)
cfg.hosts
));
};
}

View File

@ -1,7 +0,0 @@
{ ... }: {
imports = [
./services
./core
];
}

View File

@ -1,35 +0,0 @@
{ config, lib, pkgs, ... }:
let
inherit (lib) mkIf;
cfg = config.pepe.services.bazarr;
in
{
options.pepe.services.bazarr = with lib; {
enable = mkEnableOption "Enable bazarr";
package = mkPackageOption pkgs "bazarr" { };
domain = mkOption {
type = types.str;
default = null;
};
};
config = mkIf cfg.enable {
services.bazarr = {
enable = true;
package = cfg.package;
group = "media";
};
pepe.core.vhost.hosts.${cfg.domain} = {
locations."/" = {
port = 6767;
allowLAN = true;
allowVPN = true;
};
};
pepe.core.media.groupMembers = mkIf config.pepe.core.media.enable [ "bazarr" ];
};
}

View File

@ -1,20 +0,0 @@
{ ... }: {
imports = [
./bazarr
./gitea
./homeassistant
./immich
./jellyfin
./jellyseer
./lidarr
./llm
./minio
./navidrome
./nzbget
./prowlarr
./radarr
./redlib
./sonarr
./headscale
];
}

View File

@ -1,53 +0,0 @@
{ config, pkgs, lib, ... }:
let
inherit (lib) mkIf;
cfg = config.pepe.services.gitea;
in
{
options.pepe.services.gitea = with lib; {
enable = mkEnableOption "Enable gitea";
package = mkPackageOption pkgs "gitea" { };
domain = mkOption {
type = types.str;
default = null;
};
};
config = mkIf cfg.enable {
pepe.core = {
firewall.openTCP = [ config.services.gitea.settings.server.SSH_PORT ];
vhost.hosts.${cfg.domain} = {
locations."/" = {
port = config.services.gitea.settings.server.HTTP_PORT;
allowLAN = true;
allowVPN = true;
allowWAN = true;
};
};
};
services.gitea = {
enable = true;
package = cfg.package;
database.type = "sqlite3";
appName = "Gitea";
# https://github.com/NixOS/nixpkgs/issues/235442#issuecomment-1574329453
lfs.enable = true;
settings = {
server = {
DOMAIN = cfg.domain;
ROOT_URL = "https://${cfg.domain}";
SSH_PORT = 22;
HTTP_PORT = 3001;
};
service = {
DISABLE_REGISTRATION = true;
};
};
};
};
}

View File

@ -1,59 +0,0 @@
{ config, lib, pkgs, ... }:
let
inherit (lib) mkIf;
cfg = config.pepe.services.headscale;
in
{
options.pepe.services.headscale = with lib; {
enable = mkEnableOption "Enable Headscale";
package = mkPackageOption pkgs "headscale" { };
domain = mkOption {
type = types.str;
default = null;
description = "Domain for the Headscale service.";
};
host = mkOption {
type = types.str;
default = "127.0.0.1";
description = "Host for the Headscale service.";
};
port = mkOption {
type = types.int;
default = 1194;
description = "Port for the Headscale service.";
};
settings = mkOption {
type = types.attrsOf types.anything;
default = { };
description = "Arbitrary configuration settings for Headscale.";
};
};
config = mkIf cfg.enable {
environment.systemPackages = [ cfg.package ];
services.headscale = {
enable = true;
package = cfg.package;
port = cfg.port;
settings = cfg.settings;
};
pepe.core = {
firewall.openUDP = [ cfg.port ];
vhost.hosts.${cfg.domain} = {
locations."/" = {
host = cfg.host;
port = cfg.port;
allowWAN = true;
proxyWebsockets = true;
extraConfig = ''
add_header Strict-Transport-Security "max-age=15552000; includeSubDomains" always;
proxy_buffering off;
'';
};
};
};
};
}

View File

@ -1,69 +0,0 @@
{ config, lib, pkgs, ... }:
let
inherit (lib) mkIf;
cfg = config.pepe.services.homeassistant;
in
{
options.pepe.services.homeassistant = with lib; {
enable = mkEnableOption "Enable Home Assistant";
package = mkPackageOption pkgs "home-assistant" { };
domain = mkOption {
type = types.str;
default = null;
};
extraComponents = mkOption {
type = types.listOf types.str;
default = [];
description = "Additional components to enable in Home Assistant";
};
extraPackages = mkOption {
type = types.functionTo (types.listOf types.package);
default = _: [];
description = "Additional Python packages for Home Assistant";
};
config = mkOption {
type = types.attrs;
default = {};
description = "Home Assistant configuration";
};
};
config = mkIf cfg.enable {
services.home-assistant = {
enable = true;
package = cfg.package;
extraComponents = cfg.extraComponents;
extraPackages = cfg.extraPackages;
config = cfg.config;
};
services.mosquitto = {
enable = true;
listeners = [
{
address = "127.0.0.1";
acl = [ "pattern readwrite #" ];
omitPasswordAuth = true;
settings.allow_anonymous = true;
}
];
};
services.go2rtc = {
enable = true;
settings.api.listen = "127.0.0.1:1984";
};
pepe.core.vhost.hosts.${cfg.domain} = {
locations."/" = {
port = config.services.home-assistant.config.http.server_port or 8123;
allowLAN = true;
allowVPN = true;
allowWAN = true;
proxyWebsockets = true;
};
};
};
}

View File

@ -1,42 +0,0 @@
{ config, pkgs, lib, ... }:
let
cfg = config.pepe.services.immich;
in
{
options.pepe.services.immich = with lib; {
enable = mkEnableOption "Enable immich";
package = mkPackageOption pkgs "immich" { };
domain = mkOption {
type = types.str;
default = null;
};
};
config = lib.mkIf cfg.enable {
services = {
immich = {
enable = true;
package = cfg.package;
# accelerationDevices = null;
};
};
pepe.core.vhost.hosts.${cfg.domain} = {
locations."/" = {
host = "[::1]";
port = config.services.immich.port;
allowLAN = true;
allowVPN = true;
allowWAN = true;
proxyWebsockets = true;
extraConfig = ''
# allow large file uploads
client_max_body_size 50000M;
'';
};
};
users.users.immich.extraGroups = [ "video" "render" "media" "nextcloud" ];
};
}

View File

@ -1,63 +0,0 @@
{ config, lib, pkgs, ... }:
let
inherit (lib) mkIf mkForce;
cfg = config.pepe.services.jellyfin;
in
{
options.pepe.services.jellyfin = with lib; {
enable = mkEnableOption "Enable jellyfin";
package = mkPackageOption pkgs "jellyfin" { };
domain = mkOption {
type = types.str;
default = null;
};
tmpfsSize = mkOption {
type = types.str;
default = "20G";
description = "Size of the tmpfs mount for Jellyfin";
};
};
config = mkIf cfg.enable {
# needed since StateDirectory does not accept symlinks
systemd.services.jellyfin.serviceConfig.StateDirectory = mkForce "";
pepe.core.vhost.hosts.${cfg.domain} = {
locations = {
"/" = {
port = 8096;
allowLAN = true;
allowVPN = true;
};
"/socket" = {
port = 8096;
allowLAN = true;
allowVPN = true;
proxyWebsockets = true;
};
};
};
services.jellyfin = {
enable = true;
group = "media";
package = cfg.package;
};
users.groups = {
video.members = [ "jellyfin" ];
render.members = [ "jellyfin" ];
};
pepe.core.media.groupMembers = mkIf config.pepe.core.media.enable [ "jellyfin" ];
fileSystems."/tmp/jellyfin" = {
device = "none";
fsType = "tmpfs";
options = [ "defaults" "size=${cfg.tmpfsSize}" "uid=jellyfin" ];
};
};
}

View File

@ -1,32 +0,0 @@
{ config, lib, pkgs, ... }:
let
inherit (lib) mkIf;
cfg = config.pepe.services.jellyseer;
in
{
options.pepe.services.jellyseer = with lib; {
enable = mkEnableOption "Enable jellyseer";
package = mkPackageOption pkgs "jellyseerr" { };
domain = mkOption {
type = types.str;
default = null;
};
};
config = mkIf cfg.enable {
services.jellyseerr = {
enable = true;
package = cfg.package;
};
pepe.core.vhost.hosts.${cfg.domain} = {
locations."/" = {
port = config.services.jellyseerr.port;
allowLAN = true;
allowVPN = true;
};
};
};
}

View File

@ -1,36 +0,0 @@
{ config, lib, pkgs, ... }:
let
inherit (lib) mkIf;
cfg = config.pepe.services.lidarr;
in
{
options.pepe.services.lidarr = with lib; {
enable = mkEnableOption "Enable lidarr";
package = mkPackageOption pkgs "lidarr" { };
domain = mkOption {
type = types.str;
default = null;
};
};
config = mkIf cfg.enable {
services.lidarr = {
enable = true;
package = cfg.package;
group = "media";
};
pepe.core.vhost.hosts.${cfg.domain} = with config.pepe.core.network; {
dnsInterfaces = [ interfaceTypes.lan interfaceTypes.vpn ];
locations."/" = {
port = 8686;
allowLAN = true;
allowVPN = true;
};
};
pepe.core.media.groupMembers = mkIf config.pepe.core.media.enable [ "lidarr" ];
};
}

View File

@ -1,110 +0,0 @@
{ config, lib, pkgs, ... }:
let
inherit (lib) mkIf mkEnableOption mkPackageOption mkOption types optionalAttrs mkMerge;
cfg = config.pepe.services.llm;
in
{
options.pepe.services.llm = {
enable = mkEnableOption "Enable LLM backend service (Ollama)";
package = mkPackageOption pkgs "ollama" { };
backendDomain = mkOption {
type = types.nullOr types.str;
default = null;
description = "Domain for Ollama backend. If null, no vhost is created for the backend.";
};
acceleration = mkOption {
type = types.enum [ "none" "cuda" "rocm" ];
default = "none";
description = "Acceleration type for Ollama";
};
environmentVariables = mkOption {
type = types.attrsOf types.str;
default = {
OLLAMA_FLASH_ATTENTION = "1";
OLLAMA_NUM_PARALLEL = "2";
OLLAMA_KV_CACHE_TYPE = "q8_0";
};
description = "Environment variables for Ollama";
};
frontend = {
enable = mkEnableOption "Enable LLM frontend service (Open WebUI)"; # Defaults to false
uiPackage = mkPackageOption pkgs "open-webui" { };
tikaPackage = mkPackageOption pkgs "tika" { }; # Tika for document processing with Open WebUI
domain = mkOption {
type = types.nullOr types.str;
default = null;
description = "Domain for Open WebUI frontend. If null, no vhost is created for the frontend.";
};
};
};
config = mkMerge [
# Combined environment packages
{
environment.systemPackages =
(if cfg.enable then [ cfg.package ] else [ ]) ++
(if cfg.enable && cfg.frontend.enable then [ cfg.frontend.uiPackage cfg.frontend.tikaPackage ] else [ ]);
}
# Backend Ollama Service Configuration
(mkIf cfg.enable {
services.ollama = {
enable = true;
package = cfg.package;
acceleration = cfg.acceleration;
environmentVariables = cfg.environmentVariables;
};
pepe.core.vhost.hosts = optionalAttrs (cfg.backendDomain != null) {
"${cfg.backendDomain}" = {
locations."/" = {
host = config.services.ollama.host;
port = config.services.ollama.port;
allowLAN = true;
allowVPN = true;
allowWAN = true;
recommendedProxySettings = false;
extraConfig = ''
proxy_buffering off;
proxy_read_timeout 600s; # Ollama can take time to respond
proxy_set_header Host localhost:${toString config.services.ollama.port};
'';
};
};
};
})
# Frontend Open WebUI and Tika Service Configuration
(mkIf (cfg.enable && cfg.frontend.enable) {
services.open-webui = {
enable = true;
package = cfg.frontend.uiPackage;
};
services.tika = {
enable = true;
package = cfg.frontend.tikaPackage;
};
pepe.core.vhost.hosts = optionalAttrs (cfg.frontend.domain != null) {
"${cfg.frontend.domain}" = {
locations."/" = {
host = config.services.open-webui.host;
port = config.services.open-webui.port;
allowLAN = true;
allowVPN = true;
allowWAN = true;
proxyWebsockets = true;
};
};
};
})
];
}

View File

@ -1,35 +0,0 @@
{ config, lib, pkgs, ... }:
let
inherit (lib) mkIf;
cfg = config.pepe.services.minio;
in
{
options.pepe.services.minio = with lib; {
enable = mkEnableOption "Enable MinIO S3-compatible object storage";
package = mkPackageOption pkgs "minio" {};
domain = mkOption {
type = types.str;
default = null;
};
};
config = mkIf cfg.enable {
services.minio = {
enable = true;
package = cfg.package;
};
pepe.core.vhost.hosts.${cfg.domain} = {
locations."/" = {
port = 9000;
allowLAN = true;
allowVPN = true;
extraConfig = ''
client_max_body_size 500M;
'';
};
};
};
}

View File

@ -1,84 +0,0 @@
{ config, lib, pkgs, ... }:
let
inherit (lib) mkIf;
cfg = config.pepe.services.navidrome;
in
{
options.pepe.services.navidrome = with lib; {
enable = mkEnableOption "Enable navidrome";
package = mkPackageOption pkgs "navidrome" { };
domain = mkOption {
type = types.str;
default = null;
};
musicFolder = mkOption {
type = types.str;
default = "/media/Music";
description = "Path to the music library";
};
beetsConfig = mkOption {
type = types.str;
default = "/media/beets.conf";
description = "Path to the beets configuration file";
};
settings = mkOption {
type = types.attrs;
default = {};
description = "Additional settings for Navidrome";
};
};
config = mkIf cfg.enable {
services.navidrome = {
enable = true;
package = cfg.package;
settings = {
MusicFolder = cfg.musicFolder;
LastFM.enable = true;
LastFM.ApiKey = "5cef5cb5f9d31326b97d0f929ca9cf20";
LastFM.Secret = "d1296896126f4caae47407aecf080b25";
Spotify.ID = "3900c029b4f34f3fb61d554dda64794d";
Spotify.Secret = "d931ce5575a9401aa5ff8d37558cca0a";
EnableGravatar = true;
LogLevel = "WARN";
} // cfg.settings;
};
pepe.core.vhost.hosts.${cfg.domain} = with config.pepe.core.network; {
dnsInterfaces = [ interfaceTypes.lan interfaceTypes.vpn ];
locations."/" = {
port = 4533;
allowLAN = true;
allowVPN = true;
allowWAN = true;
};
};
systemd.services = {
"beets-update" = {
enable = true;
before = [ "beets-import.service" ];
serviceConfig = {
Type = "oneshot";
ExecStart = "${pkgs.beets}/bin/beet -c ${cfg.beetsConfig} update";
};
};
"beets-import" = {
enable = true;
path = [ pkgs.imagemagick ];
requires = [ "beets-update.service" ];
after = [ "beets-update.service" ];
serviceConfig = {
Type = "oneshot";
ExecStart = "${pkgs.beets}/bin/beet -c ${cfg.beetsConfig} import --flat -q ${cfg.musicFolder}";
};
startAt = "weekly";
};
};
pepe.core.media.groupMembers = mkIf config.pepe.core.media.enable [ "navidrome" ];
};
}

View File

@ -1,35 +0,0 @@
{ config, lib, pkgs, ... }:
let
inherit (lib) mkIf;
cfg = config.pepe.services.nzbget;
in
{
options.pepe.services.nzbget = with lib; {
enable = mkEnableOption "Enable nzbget";
package = mkPackageOption pkgs "nzbget" { };
domain = mkOption {
type = types.str;
default = null;
};
};
config = mkIf cfg.enable {
services.nzbget = {
enable = true;
package = cfg.package;
group = "media";
};
pepe.core.vhost.hosts.${cfg.domain} = {
locations."/" = {
port = 6789;
allowLAN = true;
allowVPN = true;
};
};
pepe.core.media.groupMembers = mkIf config.pepe.core.media.enable [ "nzbget" ];
};
}

View File

@ -1,35 +0,0 @@
{ config, lib, pkgs, ... }:
let
inherit (lib) mkIf;
cfg = config.pepe.services.prowlarr;
in
{
options.pepe.services.prowlarr = with lib; {
enable = mkEnableOption "Enable prowlarr";
package = mkPackageOption pkgs "prowlarr" { };
domain = mkOption {
type = types.str;
default = null;
};
};
config = mkIf cfg.enable {
services.prowlarr = {
enable = true;
package = cfg.package;
};
pepe.core.vhost.hosts.${cfg.domain} = {
locations."/" = {
port = 9696;
allowLAN = true;
allowVPN = true;
proxyWebsockets = true;
};
};
pepe.core.media.groupMembers = mkIf config.pepe.core.media.enable [ "prowlarr" ];
};
}

View File

@ -1,34 +0,0 @@
{ config, lib, pkgs, ... }:
let
inherit (lib) mkIf;
cfg = config.pepe.services.radarr;
in
{
options.pepe.services.radarr = with lib; {
enable = mkEnableOption "Enable radarr";
package = mkPackageOption pkgs "radarr" { };
domain = mkOption {
type = types.str;
default = null;
};
};
config = mkIf cfg.enable {
services.radarr = {
enable = true;
package = cfg.package;
};
pepe.core.vhost.hosts.${cfg.domain} = {
locations."/" = {
port = 7878;
allowLAN = true;
allowVPN = true;
};
};
pepe.core.media.groupMembers = mkIf config.pepe.core.media.enable [ config.services.radarr.group ];
};
}

View File

@ -1,48 +0,0 @@
{ config, lib, pkgs, ... }:
let
inherit (lib) mkIf;
cfg = config.pepe.services.redlib;
in
{
options.pepe.services.redlib = with lib; {
enable = mkEnableOption "Enable Redlib";
package = mkPackageOption pkgs "redlib" { };
domain = mkOption {
type = types.str;
default = null;
};
settings = mkOption {
type = types.attrsOf types.str;
default = {
REDLIB_ROBOTS_DISABLE_INDEXING = "on";
REDLIB_DEFAULT_THEME = "dracula";
REDLIB_DEFAULT_SHOW_NSFW = "on";
REDLIB_DEFAULT_BLUR_NSFW = "off";
REDLIB_DEFAULT_USE_HLS = "on";
REDLIB_DEFAULT_HIDE_HLS_NOTIFICATION = "on";
};
description = "Environment variables for Redlib configuration";
};
};
config = mkIf cfg.enable {
services.redlib = {
enable = true;
port = 9090;
package = cfg.package;
};
systemd.services.redlib.environment = cfg.settings;
pepe.core.vhost.hosts.${cfg.domain} = {
locations."/" = {
port = config.services.redlib.port;
allowLAN = true;
allowVPN = true;
allowWAN = true;
};
};
};
}

View File

@ -1,34 +0,0 @@
{ config, lib, pkgs, ... }:
let
inherit (lib) mkIf;
cfg = config.pepe.services.sonarr;
in
{
options.pepe.services.sonarr = with lib; {
enable = mkEnableOption "Enable sonarr";
package = mkPackageOption pkgs "sonarr" { };
domain = mkOption {
type = types.str;
default = null;
};
};
config = mkIf cfg.enable {
services.sonarr = {
enable = true;
package = cfg.package;
};
pepe.core.vhost.hosts.${cfg.domain} = {
locations."/" = {
port = 8989;
allowLAN = true;
allowVPN = true;
};
};
pepe.core.media.groupMembers = mkIf config.pepe.core.media.enable [ config.services.sonarr.group ];
};
}

View File

@ -1,25 +1,4 @@
{ config, ... }: { ... }: {
security.acme.acceptTerms = true;
let security.acme.email = "sysadmin@giugl.io";
giuglioDomain = "giugl.io";
in
{
age.secrets.cloudflare = {
file = ../secrets/cloudflare.age;
owner = "acme";
};
security.acme = {
acceptTerms = true;
certs.${giuglioDomain} =
{
dnsProvider = "cloudflare";
environmentFile = config.age.secrets.cloudflare.path;
extraDomainNames = [ "*.${giuglioDomain}" ];
};
defaults = {
email = "letsencrypt@depasquale.giugl.io";
dnsProvider = "cloudflare";
environmentFile = config.age.secrets.cloudflare.path;
};
};
} }

View File

@ -1,9 +1,7 @@
{ config, pkgs, lib, ... }: { config, pkgs, variables, lib, ... }:
{ {
boot.tmp = { boot.tmpOnTmpfs = true;
useTmpfs = true;
};
console = { console = {
keyMap = "us"; keyMap = "us";
@ -13,7 +11,8 @@
i18n.defaultLocale = "en_US.UTF-8"; i18n.defaultLocale = "en_US.UTF-8";
nix = { nix = {
settings.auto-optimise-store = true; autoOptimiseStore = true;
package = pkgs.nixUnstable;
extraOptions = '' extraOptions = ''
experimental-features = nix-command flakes experimental-features = nix-command flakes
''; '';
@ -25,11 +24,10 @@
}; };
}; };
nixpkgs = { config = { allowUnfree = true; }; };
fonts = { fonts.fontconfig.enable = true;
fontconfig.enable = true; fonts.fonts = with pkgs; [ cascadia-code victor-mono ];
packages = with pkgs; [ cascadia-code victor-mono ];
};
environment.systemPackages = with pkgs; [ environment.systemPackages = with pkgs; [
file file
@ -42,6 +40,7 @@
glances glances
tcpdump tcpdump
restic restic
neovim
tmux tmux
parted parted
unzip unzip
@ -50,7 +49,5 @@
nmap nmap
ripgrep ripgrep
jq jq
helix
poetry
]; ];
} }

View File

@ -22,7 +22,7 @@
environment.systemPackages = with pkgs; [ environment.systemPackages = with pkgs; [
gnomeExtensions.appindicator gnomeExtensions.appindicator
gnomeExtensions.sound-output-device-chooser gnomeExtensions.sound-output-device-chooser
pkgs.unstablePkgs.gnomeExtensions.pop-shell pkgs.unstable.gnomeExtensions.pop-shell
]; ];
security.pam.services.gdm.enableGnomeKeyring = true; security.pam.services.gdm.enableGnomeKeyring = true;
} }

View File

@ -1,17 +0,0 @@
{ pkgs, ... }:
let
lib = pkgs.lib;
configDir = "$HOME/.config/aichat";
in
{
home = {
sessionVariables = {
AICHAT_CONFIG_DIR = configDir;
};
packages = [ pkgs.aichat ];
file.".config/aichat/config.yaml".text = lib.readFile ./aichat/config.yaml;
file.".config/aichat/roles/commitmessage.md".text = lib.readFile ./aichat/roles/commitmessage.md;
file.".config/aichat/roles/createpr.md".text = lib.readFile ./aichat/roles/createpr.md;
};
}

View File

@ -1,20 +0,0 @@
clients:
- type: openai-compatible
name: ollama
api_base: https://ollama.giugl.io/v1
models:
- name: pino
max_input_tokens: 8192
max_output_tokens: 16000
- name: pino-coder
max_input_tokens: 16000
max_output_tokens: 16000
- name: pino-embed
type: embedding
default_chunk_size: 512
max_batch_size: 100
rag_embedding_model: ollama:pino-embed
rag_top_k: 5

View File

@ -1,204 +0,0 @@
---
model: ollama:pino-coder
temperature: 0
---
You are a panel of three expert developers specializing in commit message generation:
- A (Version Control Specialist): Expert in Git workflows and commit conventions
- B (Code Review Expert): Specializes in code change analysis and impact assessment
- C (Technical Writer): Focuses on clarity, consistency, and documentation standards
Commit Convention Format:
<type>(<scope>): <description>
[body description]
Types:
- feat: New feature
- fix: Bug fix
- docs: Documentation changes
- style: Code style changes (non-functional)
- refactor: Code restructuring (non-functional)
- test: Test-related changes
- chore: Build process or tool changes
- perf: Performance improvements
Panel Analysis Process:
1. Initial Assessment:
- Alex: Analyzes commit convention compliance and change scope
- Blake: Reviews technical changes and their impact
- Casey: Evaluates message clarity and completeness
2. Message Components:
- Type Selection: Panel agrees on the most appropriate type
- Scope Definition: Identify affected components/systems
- Description: Craft clear, concise summary, bullet points only
3. Quality Criteria:
- Conventional commits compliance
- Technical accuracy
- Clear and concise language
- Meaningful context
- Future maintainer consideration
- Breaking change identification
Guidelines:
- Exclude trivial changes (imports, formatting)
- Focus on functional and behavioral changes
- Include breaking changes prominently
- Reference relevant issue numbers
- Keep first line under 72 characters
- Use imperative mood ("add" not "added")
### INPUT:
diff --git a/src/utils/date-formatter.js b/src/utils/date-formatter.js
index 2345678..3456789 100644
--- a/src/utils/date-formatter.js
+++ b/src/utils/date-formatter.js
@@ -5,7 +5,7 @@ export function formatDate(date) {
const month = String(date.getMonth() + 1).padStart(2, '0');
const day = String(date.getDate()).padStart(2, '0');
- return `$${year}-$${month}-$${day}`;
+ return `$${year}/$${month}/$${day}`;
}
### OUTPUT:
fix(date-formatter): modified `formatDate()` to use '/' instead of '-' as the separator
### INPUT:
diff --git a/src/app.js b/src/app.js
index 83d2e7a..b6a1c3f 100644
--- a/src/app.js
+++ b/src/app.js
@@ -10,6 +10,10 @@ function initialize() {
setupEventListeners();
}
+// TODO: add other listeners
+// https://github.com/user/project/issue/123
+function setupEventListeners() {
+ document.getElementById('submit').addEventListener('click', handleSubmit);
+ document.getElementById('reset').addEventListener('click', handleReset);
+}
+
function handleSubmit(event) {
event.preventDefault();
const data = new FormData(event.target);
@@ -20,6 +24,10 @@ function handleSubmit(event) {
console.log('Form submitted:', data);
}
+function handleReset(event) {
+ event.preventDefault();
+ event.target.form.reset();
+ console.log('Form reset');
}
### OUTPUT:
feat(app): implement form event listeners
- Added `setupEventListeners()` to handle form interactions
- Implemented `handleReset()` for form reset functionality
- Added event listeners for submit and reset buttons
- Track TODO comment for future listener additions (https://github.com/user/project/issue/123)
### INPUT:
diff --git a/pkg/database/client.go b/pkg/database/client.go
index 003740f..6fc4861 100644
--- a/pkg/database/client.go
+++ b/pkg/database/client.go
@@ -24,9 +24,12 @@ var ErrNilDatabaseClient = errors.New("database client is nil after setup")
// InitDB initializes the database with the given application name and optional dbpath for SQLite.
func InitDB(appName string, dbpath ...string) error {
- cfg := config.New()
+ var (
+ psqlReadReplica string
+ err error
+ )
- var err error
+ cfg := config.New()
// Set up a new logger with your required configuration.
newLogger := logger.New(
@@ -38,9 +41,8 @@ func InitDB(appName string, dbpath ...string) error {
},
)
- // Load PostgreSQL configurations
- var psqlReadReplica string
psqlSource, err := cfg.Load(config.PSQL.String())
+
if err != nil {
log.Println("PSQL not set, using SQLite instead.")
} else {
### OUTPUT:
style(database/client): group together `psqlReadReplica` and `err` in function's prologue
### INPUT:
diff --git a/pkg/khttp/client.go b/pkg/khttp/client.go
index a53064c..3aff938 100644
--- a/pkg/khttp/client.go
+++ b/pkg/khttp/client.go
@@ -11,14 +11,17 @@ import (
"github.pie.apple.com/kerosene/Core/structs"
)
+// TODO: https://github.pie.apple.com/Kerosene/Core/issues/43
+// feat: Centralise and remove over use of os.Environment
const (
- // Environment variables and file names.
authFilesDirEnvVar = "WHISPER_AUTH_FILES_DIR"
keyName = "decrypted_key.pem"
certName = "cert.pem"
)
// Error for missing environment variable.
+// TODO: refactor: move errors into centralized errors.go files
+// https://github.pie.apple.com/Kerosene/Core/issues/57
var errMissingEnvironmentVariable = fmt.Errorf("%s environment variable is not set", authFilesDirEnvVar)
// AuthConfig holds authentication file paths.
@@ -31,9 +34,11 @@ type AuthConfig struct {
// NewAuthConfig creates an AuthConfig from environment variables.
func NewAuthConfig() (*AuthConfig, error) {
dir := os.Getenv(authFilesDirEnvVar)
+
if dir == "" {
return nil, errMissingEnvironmentVariable
}
+
return &AuthConfig{
Dir: dir,
CertFile: filepath.Join(dir, certName),
@@ -211,7 +216,7 @@ func setupMTLSOnlyTransport(certData string, keyData string) (*http.Transport, e
// Make scheme and Auth Type separate and load from DB.
func parseProxyURL(scheme string, routing structs.Routing) (*url.URL, error) {
- return url.Parse(fmt.Sprintf("%s://%s:%s", scheme, routing.Proxy, routing.Port))
+ return url.Parse(fmt.Sprintf("%s://%s:%d", scheme, routing.Proxy, routing.Port))
}
// loadX509KeyPair loads an X509 key pair from the specified cert and key files.
### OUTPUT:
fix/refactor(khttp/client): use correct format specifier for and add TODOs
- Parsed proxy URL using `fmt.Sprintf()` with correct format specifier for port
- Added TODOs to centralize errors and remove overuse of `os.Environment` (#43, #57)

View File

@ -1,626 +0,0 @@
You are a technical documentation assistant specializing in creating clear, concise PR messages. Given a git diff, you will:
1. Analyze the structural changes to identify:
- New components/interfaces
- Architectural changes
- Behavioral modifications
- Impact on existing systems
2. Create a PR message following this format:
<type>(<scope>): <description>
[PROSE DESCRIPTION explaining the changes and their purpose]
---
[OPTIONAL SECTIONS like "Structs:", "Interfaces:", "Methods:", etc. when relevant]
Each section should:
- Use bullet points for clarity
- Bold key terms
- Include brief descriptions
- Focus on significant changes
- Exclude trivial details (imports, formatting)
### INPUT:
```
diff --git a/pkg/cli/cli.go b/pkg/cli/cli.go
index 9aee19c..9863f0b 100644
--- a/pkg/cli/cli.go
+++ b/pkg/cli/cli.go
@@ -1,21 +1,13 @@
package cli
import (
- "context"
- "net/http"
"strings"
- "time"
"github.com/sanity-io/litter"
"github.com/spf13/cobra"
- "github.com/org/Core/pkg/logging"
"github.com/org/Core/pkg/structs"
- "github.com/org/example/internal/storage"
- "github.com/org/example/pkg/browser"
"github.com/org/example/pkg/config"
- "github.com/org/example/pkg/crawler"
"github.com/org/example/pkg/errors"
- "github.com/org/example/pkg/models"
cookieflags "github.com/org/example/pkg/plugins/cookieflags"
cookiescope "github.com/org/example/pkg/plugins/cookiescope"
corsmisconfig "github.com/org/example/pkg/plugins/cors"
@@ -124,7 +116,18 @@ func NewRootCmd() (*cobra.Command, error) {
logger.Tracef("Crawler Configuration:
=============
%s
=============
", litter.Sdump(crawlConfig))
logger.Tracef("Vulnerability Scan Configuration:
=============
%s
=============
", litter.Sdump(pluginsConfig))
- return StartScan(sysConfig, crawlConfig, pluginsConfig)
+ manager, err := NewManagerBuilder(sysConfig, crawlConfig, pluginsConfig).Build()
+
+ if err != nil {
+ return err
+ }
+
+ err = manager.Start()
+ if err != nil {
+ return err
+ }
+
+ return manager.Stop()
},
}
@@ -186,113 +189,6 @@ func parseConfigFiles(crawlConfPath, systemConfPath, pluginConfPath string) (*co
return crawlConfig, sysConfig, vulnConfig, nil
}
-func StartScan(sysCfg *config.SystemConfiguration, //nolint:cyclop
- crawlCfg *config.CrawlConfiguration,
- pluginCfg *config.PluginsConfiguration,
-) error {
- // Initialize shared components
- logger := logging.NewLoggerBuilder().Build()
- metrics := &models.Metrics{}
- // Initialize the browser session
- browserSession, err := browser.New(logger, crawlCfg)
- if err != nil {
- return errors.ErrFailedExecution.WrapWithNoMessage(err)
- }
- defer browserSession.Close()
- if err := browserSession.Start(context.Background()); err != nil {
- return errors.ErrFailedExecution.WrapWithNoMessage(err)
- }
- // Create custom HTTP client if needed
- // TODO: Change and centralise
- // see https://github.com/org/example/issues/436
- customClient := &http.Client{
- Timeout: time.Second * 30,
- // Add other custom configurations...
- }
- // Iniialize storage once
- scanStorage, err := storage.NewScanStorage(sysCfg, metrics, logger)
- if err != nil {
- return errors.ErrStorageSetup.WrapWithNoMessage(err)
- }
- // Start batch session before any operations
- err = scanStorage.BatchSession.Start()
- if err != nil {
- return errors.ErrStorageSetup.WrapWithNoMessage(err)
- }
- // Initialize crawler with all options
- crawler, err := crawler.New(
- crawlCfg,
- sysCfg,
- crawler.WithStorage(scanStorage.Database, scanStorage.BatchSession),
- crawler.WithLogger(logger),
- crawler.WithMetrics(metrics),
- crawler.WithHTTPClient(customClient),
- crawler.WithBrowser(browserSession),
- )
- if err != nil {
- return errors.ErrFailedExecution.WrapWithNoMessage(err)
- }
- // Initialize scanner with shared components
- scan, err := scanner.NewScanner(
- sysCfg,
- crawlCfg,
- pluginCfg,
- browserSession,
- scanner.WithStorage(scanStorage),
- scanner.WithLogger(logger),
- scanner.WithHTTPClient(customClient),
- )
- if err != nil {
- return errors.ErrFailedExecution.WrapWithNoMessage(err)
- }
- err = initializePluginsFromConfig(scan, pluginCfg)
- if err != nil {
- return errors.ErrInitialisePlugin.WrapWithNoMessage(err)
- }
- output, err := crawler.Start()
- if err != nil {
- crawler.Close()
- return errors.ErrFailedExecution.WrapWithNoMessage(err)
- }
- // Add this: Stop scanner before waiting for batch operations
- scan.Stop()
- logger.Debugf("Crawl completed with metrics: %+v", output.Metrics)
- if sysCfg.ShouldExportMetrics() {
- err := output.Metrics.ToJSONFile()
- if err != nil {
- return errors.ErrJSONMarshalling.WrapWithNoMessage(err)
- }
- }
- if output.BenchmarkResults != nil {
- logger.Debugf("Benchmark results: %+v", output.BenchmarkResults)
- }
- scanStorage.BatchSession.Wait()
- err = scanStorage.BatchSession.Stop()
- if err != nil {
- return errors.ErrStorageSetup.WrapWithNoMessage(err)
- }
- crawler.Close()
- return nil
-}
// BuildPluginsFromConfig creates plugin instances based on configuration
func BuildPluginsFromConfig(config *config.PluginsConfiguration) []*structs.Plugin {
enabledPlugins := []*structs.Plugin{}
diff --git a/pkg/cli/interface.go b/pkg/cli/interface.go
new file mode 100644
index 0000000..4d68a45
--- /dev/null
+++ b/pkg/cli/interface.go
@@ -0,0 +1,10 @@
+package cli
+
+// Scanner represents the core scanning operations lifecycle
+type ScanManagerInterface interface {
+ // Start initializes and begins the scanning process
+ Start() error
+
+ // Stop gracefully terminates all scanning operations
+ Stop() error
+}
diff --git a/pkg/cli/manager.go b/pkg/cli/manager.go
new file mode 100644
index 0000000..3f2a8fc
--- /dev/null
+++ b/pkg/cli/manager.go
@@ -0,0 +1,277 @@
+package cli
+
+import (
+ "context"
+ "net/http"
+
+ "github.com/org/Core/pkg/logging"
+ "github.com/org/example/internal/storage"
+ "github.com/org/example/pkg/browser"
+ "github.com/org/example/pkg/config"
+ "github.com/org/example/pkg/crawler"
+ "github.com/org/example/pkg/errors"
+ "github.com/org/example/pkg/models"
+ "github.com/org/example/pkg/scanner"
+)
+
+var _ ScanManagerInterface = (*ScanManager)(nil)
+
+type ScanManager struct {
+ browser *browser.Session
+ crawler *crawler.Crawler
+ scanner *scanner.Scanner
+ storage *storage.ScanStorage
+ logger *logging.Logger
+ metrics *models.Metrics
+ httpClient *http.Client
+
+ sysCfg *config.SystemConfiguration
+ crawlCfg *config.CrawlConfiguration
+ pluginCfg *config.PluginsConfiguration
+}
+
+// ScanManagerBuilder handles the construction of a ScanManager
+type ScanManagerBuilder struct {
+ manager *ScanManager
+ err error
+}
+
+// NewManagerBuilder creates a new builder for ScanManager
+func NewManagerBuilder(sysCfg *config.SystemConfiguration,
+ crawlCfg *config.CrawlConfiguration,
+ pluginCfg *config.PluginsConfiguration,
+) *ScanManagerBuilder {
+ builder := &ScanManagerBuilder{
+ manager: &ScanManager{
+ logger: logging.NewLoggerBuilder().Build(),
+ metrics: &models.Metrics{},
+ httpClient: &http.Client{},
+ },
+ }
+
+ if sysCfg == nil || crawlCfg == nil || pluginCfg == nil {
+ builder.err = errors.ErrInvalidArgument.New("configurations cannot be nil")
+
+ return builder
+ }
+
+ builder.manager.sysCfg = sysCfg
+ builder.manager.crawlCfg = crawlCfg
+ builder.manager.pluginCfg = pluginCfg
+
+ return builder
+}
+
+// WithLogger sets a custom logger
+func (b *ScanManagerBuilder) WithLogger(logger *logging.Logger) *ScanManagerBuilder {
+ if b.err != nil {
+ return b
+ }
+
+ if logger == nil {
+ b.err = errors.ErrInvalidArgument.New("logger cannot be nil")
+
+ return b
+ }
+
+ b.manager.logger = logger
+
+ return b
+}
+
+// WithMetrics sets custom metrics
+func (b *ScanManagerBuilder) WithMetrics(metrics *models.Metrics) *ScanManagerBuilder {
+ if b.err != nil {
+ return b
+ }
+
+ if metrics == nil {
+ b.err = errors.ErrInvalidArgument.New("metrics cannot be nil")
+
+ return b
+ }
+
+ b.manager.metrics = metrics
+
+ return b
+}
+
+// Build creates the ScanManager instance
+func (b *ScanManagerBuilder) Build() (*ScanManager, error) {
+ if b.err != nil {
+ return nil, b.err
+ }
+
+ return b.manager, nil
+}
+
+func (sm *ScanManager) initBrowser(crawlCfg *config.CrawlConfiguration) error {
+ var err error
+
+ sm.browser, err = browser.New(sm.logger, crawlCfg)
+ if err != nil {
+ return errors.ErrFailedExecution.WrapWithNoMessage(err)
+ }
+
+ if err := sm.browser.Start(context.Background()); err != nil {
+ sm.browser.Close()
+
+ return errors.ErrFailedExecution.WrapWithNoMessage(err)
+ }
+
+ return nil
+}
+
+func (sm *ScanManager) initStorage(sysCfg *config.SystemConfiguration) error {
+ var err error
+
+ storage, err := storage.NewScanStorage(sysCfg, sm.metrics, sm.logger)
+ if err != nil {
+ return errors.ErrStorageSetup.WrapWithNoMessage(err)
+ }
+
+ sm.storage = &storage
+
+ err = sm.storage.BatchSession.Start()
+ if err != nil {
+ return errors.ErrStorageSetup.WrapWithNoMessage(err)
+ }
+
+ return nil
+}
+
+func (sm *ScanManager) initCrawler(sysCfg *config.SystemConfiguration, crawlCfg *config.CrawlConfiguration) error {
+ var err error
+
+ sm.crawler, err = crawler.New(
+ crawlCfg,
+ sysCfg,
+ crawler.WithStorage(sm.storage.Database, sm.storage.BatchSession),
+ crawler.WithLogger(sm.logger),
+ crawler.WithMetrics(sm.metrics),
+ crawler.WithHTTPClient(sm.httpClient),
+ crawler.WithBrowser(sm.browser),
+ )
+
+ if err != nil {
+ return errors.ErrFailedExecution.WrapWithNoMessage(err)
+ }
+
+ return nil
+}
+
+func (sm *ScanManager) initScanner() error {
+ var err error
+
+ sm.scanner, err = scanner.NewScanner(
+ sm.sysCfg,
+ sm.crawlCfg,
+ sm.pluginCfg,
+ sm.browser,
+ scanner.WithStorage(*sm.storage),
+ scanner.WithLogger(sm.logger),
+ scanner.WithHTTPClient(sm.httpClient),
+ )
+
+ if err != nil {
+ return errors.ErrFailedExecution.WrapWithNoMessage(err)
+ }
+
+ err = initializePluginsFromConfig(sm.scanner, sm.pluginCfg)
+ if err != nil {
+ return errors.ErrInitialisePlugin.WrapWithNoMessage(err)
+ }
+
+ return nil
+}
+
+func (sm *ScanManager) cleanup() error {
+ var errs []error
+
+ if sm.crawler != nil {
+ sm.crawler.Close()
+ }
+
+ if sm.scanner != nil {
+ sm.scanner.Stop()
+ }
+
+ if sm.storage != nil && sm.storage.BatchSession != nil {
+ sm.storage.BatchSession.Wait()
+
+ err := sm.storage.BatchSession.Stop()
+ if err != nil {
+ errs = append(errs, errors.ErrStorageSetup.WrapWithNoMessage(err))
+ }
+ }
+
+ if sm.browser != nil {
+ sm.browser.Close()
+ }
+
+ if len(errs) > 0 {
+ return errors.ErrFailedExecution.New("multiple cleanup errors occurred: %v", errs)
+ }
+
+ return nil
+}
+
+func (sm *ScanManager) Start() error {
+ err := sm.initBrowser(sm.crawlCfg)
+ if err != nil {
+ return err
+ }
+
+ err = sm.initStorage(sm.sysCfg)
+ if err != nil {
+ _ = sm.cleanup()
+
+ return err
+ }
+
+ err = sm.initCrawler(sm.sysCfg, sm.crawlCfg)
+ if err != nil {
+ _ = sm.cleanup()
+
+ return err
+ }
+
+ err = sm.initScanner()
+ if err != nil {
+ _ = sm.cleanup()
+
+ return err
+ }
+
+ // Start the crawl
+ output, err := sm.crawler.Start()
+ if err != nil {
+ _ = sm.cleanup()
+
+ return errors.ErrFailedExecution.WrapWithNoMessage(err)
+ }
+
+ sm.logger.Debugf("Crawl completed with metrics: %+v", output.Metrics)
+
+ if sm.sysCfg.ShouldExportMetrics() {
+ err := output.Metrics.ToJSONFile()
+
+ if err != nil {
+ return errors.ErrJSONMarshalling.WrapWithNoMessage(err)
+ }
+ }
+
+ if output.BenchmarkResults != nil {
+ sm.logger.Debugf("Benchmark results: %+v", output.BenchmarkResults)
+ }
+
+ return nil
+}
+
+func (sm *ScanManager) Stop() error {
+ if sm == nil {
+ return nil
+ }
+
+ return sm.cleanup()
+}
diff --git a/pkg/scanner/scanner.go b/pkg/scanner/scanner.go
index c0a104f..6ef620a 100644
--- a/pkg/scanner/scanner.go
+++ b/pkg/scanner/scanner.go
@@ -676,6 +676,8 @@ func (s *Scanner) Stop() {
s.wg.Wait()
close(s.initialRequestQueue)
close(s.processedRequestQueue)
+
+ instance = nil
}
// Wait blocks until all workers have finished processing.
diff --git a/tests/e2e/wallace_test.go b/tests/e2e/wallace_test.go
index 0e899e9..b8de5c8 100644
--- a/tests/e2e/wallace_test.go
+++ b/tests/e2e/wallace_test.go
@@ -550,31 +550,26 @@ type scanResults struct {
// }
// TestPlugins verifies that each plugin detects at least one vulnerability for each test path
-func TestPlugins(t *testing.T) { //nolint: paralleltest
- // Retrieve all available plugins without specific configurations
+func TestPlugins(t *testing.T) {
plugins := cli.BuildAllPlugins()
if len(plugins) == 0 {
t.Fatal("No plugins available to test")
}
- // Ensure that there are test paths to scan
if len(SiteURLs) == 0 {
t.Fatal("No site URLs available for scanning")
}
- // Iterate over each plugin and perform the test
- for _, plugin := range plugins { //nolint: paralleltest
+ for _, plugin := range plugins {
pluginName := plugin.Name()
t.Run(pluginName, func(t *testing.T) {
- // t.Parallel()
// Setup test environment for the current plugin
resultDir, dbPath, cleanup := setupTestEnvironment(t, pluginName)
defer cleanup()
- // Initialize plugin-specific configuration if needed
- // Currently, using default configurations
+ // Initialize plugin-specific configuration
pluginConfig := coreStructs.NewPluginConfigBuilder(pluginName).
WithCustomConfiguration(coreStructs.CustomPluginConfig{"name": pluginName}).
Build()
@@ -595,7 +590,6 @@ func TestPlugins(t *testing.T) { //nolint: paralleltest
// Collect test URLs from initialized plugins' metadata
testURLs := collectTestURLs(pluginInstances)
- // Skip the test if no test URLs are defined for the plugin
if len(testURLs) == 0 {
t.Skipf("No test URLs defined for plugin '%s'. Skipping test.", pluginName)
}
@@ -606,11 +600,26 @@ func TestPlugins(t *testing.T) { //nolint: paralleltest
t.Fatalf("Failed to get default test config: %v", err)
}
- // Run the scan
- if err := cli.StartScan(sysConfig, crawlConfig, vulnConfig); err != nil {
+ // Create and start scanner using the new interface
+ scanner, err := cli.NewManagerBuilder(sysConfig, crawlConfig, vulnConfig).
+ Build()
+ if err != nil {
+ t.Fatalf("Failed to create scanner: %v", err)
+ }
+
+ // Start the scan
+ if err := scanner.Start(); err != nil {
t.Fatalf("Failed to start scan: %v", err)
}
+ // Ensure scanner is stopped after test
+ defer func() {
+ if err := scanner.Stop(); err != nil {
+ t.Errorf("Failed to stop scanner: %v", err)
+ }
+ }()
+
+ // Allow time for processing
time.Sleep(2 * time.Second)
// Verify results in the database
@@ -621,11 +630,11 @@ func TestPlugins(t *testing.T) { //nolint: paralleltest
// Assert that at least one vulnerability was found per test path
expectedVulns := len(testURLs)
- assert.GreaterOrEqual(t, vulnCount, expectedVulns, "Expected at least %d vulnerabilities", expectedVulns)
- time.Sleep(5 * time.Second)
+ assert.GreaterOrEqual(t, vulnCount, expectedVulns,
+ "Expected at least %d vulnerabilities", expectedVulns)
+
// Verify that result files are present
results := readScanResults(sysConfig.GetOutputDirectory())
assert.True(t, results.resultsFound, "Expected at least one result file")
})
}
```
### OUTPUT:
```
refactor(cli): introduce ScanManager for scan lifecycle management
Introduced `ScanManager` which simplifies the scan initiation process and makes the scan modular (instead of a monolithic function that bubbles everything). The manager implements the `ScanManagerInterface`:
- **Start():** Initializes and starts the scan process.
- **Stop():** Gracefully terminates the scan process.
---
Structs:
- **ScanManager:** A new struct that manages the lifecycle of scanning components such as browser, crawler, scanner, storage, logger, and metrics.
- **ScanManagerBuilder:** A builder pattern implementation to construct a `ScanManager` with custom configurations.
```
Guidelines:
- Title should follow commit convention
- Description should be clear and business-focused
- Technical details should be organized in sections
- Use markdown formatting for readability
- Focus on architectural and behavioral changes
- Exclude trivial changes
- Keep descriptions concise but complete

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