Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 123dfd7605 |
96
flake.lock
generated
96
flake.lock
generated
@@ -153,11 +153,11 @@
|
||||
"rust-overlay": "rust-overlay"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1759235653,
|
||||
"narHash": "sha256-sKFehUxXCzM6E1LcmnRa/O6HKsRI/TGtciG5ulAJt08=",
|
||||
"lastModified": 1759348172,
|
||||
"narHash": "sha256-ZPUJX2ZA0ndcHndIA/S/nRESIJV0rifPr91SUpzJtEM=",
|
||||
"owner": "chaotic-cx",
|
||||
"repo": "nyx",
|
||||
"rev": "2bf7f138e42fa8b2133761edab64263505cb83bf",
|
||||
"rev": "dd1af56ad79c965ee20c236ba6adbb2135ac02af",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -212,11 +212,11 @@
|
||||
"rust-analyzer-src": "rust-analyzer-src"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1759301100,
|
||||
"narHash": "sha256-hmiTEoVAqLnn80UkreCNunnRKPucKvcg5T4/CELEtbw=",
|
||||
"lastModified": 1759473677,
|
||||
"narHash": "sha256-9AFDP5AhXe06aXXPsV7bDGH7KA9Wo4uUtK7pRrsxuFQ=",
|
||||
"owner": "nix-community",
|
||||
"repo": "fenix",
|
||||
"rev": "0956bc5d1df2ea800010172c6bc4470d9a22cb81",
|
||||
"rev": "8532b07ced6a95d57650124c79534a3c770431ab",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -332,11 +332,11 @@
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1756770412,
|
||||
"narHash": "sha256-+uWLQZccFHwqpGqr2Yt5VsW/PbeJVTn9Dk6SHWhNRPw=",
|
||||
"lastModified": 1759362264,
|
||||
"narHash": "sha256-wfG0S7pltlYyZTM+qqlhJ7GMw2fTF4mLKCIVhLii/4M=",
|
||||
"owner": "hercules-ci",
|
||||
"repo": "flake-parts",
|
||||
"rev": "4524271976b625a4a605beefd893f270620fd751",
|
||||
"rev": "758cf7296bee11f1706a574c77d072b8a7baa881",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -392,11 +392,11 @@
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1756770412,
|
||||
"narHash": "sha256-+uWLQZccFHwqpGqr2Yt5VsW/PbeJVTn9Dk6SHWhNRPw=",
|
||||
"lastModified": 1759362264,
|
||||
"narHash": "sha256-wfG0S7pltlYyZTM+qqlhJ7GMw2fTF4mLKCIVhLii/4M=",
|
||||
"owner": "hercules-ci",
|
||||
"repo": "flake-parts",
|
||||
"rev": "4524271976b625a4a605beefd893f270620fd751",
|
||||
"rev": "758cf7296bee11f1706a574c77d072b8a7baa881",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -610,11 +610,11 @@
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1759331616,
|
||||
"narHash": "sha256-LVpodobJvJM5rmfh2sFBHPNX0PYpNbbHzx/gprlKGGg=",
|
||||
"lastModified": 1759337100,
|
||||
"narHash": "sha256-CcT3QvZ74NGfM+lSOILcCEeU+SnqXRvl1XCRHenZ0Us=",
|
||||
"owner": "nix-community",
|
||||
"repo": "home-manager",
|
||||
"rev": "5890176f856dcaf55f3ab56b25d4138657531cbd",
|
||||
"rev": "004753ae6b04c4b18aa07192c1106800aaacf6c3",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -652,11 +652,11 @@
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1759172751,
|
||||
"narHash": "sha256-E8W8sRXfrvkFW26GuuiWq6QfReU7m5+cngwHuRo/3jc=",
|
||||
"lastModified": 1759261733,
|
||||
"narHash": "sha256-G104PUPKBgJmcu4NWs0LUaPpSOTD4jiq4mamLWu3Oc0=",
|
||||
"owner": "nix-community",
|
||||
"repo": "home-manager",
|
||||
"rev": "12fa8548feefa9a10266ba65152fd1a787cdde8f",
|
||||
"rev": "5a21f4819ee1be645f46d6b255d49f4271ef6723",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -803,11 +803,11 @@
|
||||
"xdph": "xdph"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1759318697,
|
||||
"narHash": "sha256-iCL/F+rlgzgBfG4QURfjBrxVBMPsXCzZKHXn1SNBshc=",
|
||||
"lastModified": 1759399554,
|
||||
"narHash": "sha256-FsFugHj7He5siEcmoRUdMKHB8uMzyneK/fynPS57W4E=",
|
||||
"owner": "hyprwm",
|
||||
"repo": "hyprland",
|
||||
"rev": "e0c96276df75accc853a30186ae5de580b2c725f",
|
||||
"rev": "3bcfa94ee4189faaa4daf661949e88cf28c00d94",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -1006,11 +1006,11 @@
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1758124489,
|
||||
"narHash": "sha256-YiVF/8Me3vVKJBEgGpQhn0HF09EWfXZGaWLzAaJBrO4=",
|
||||
"lastModified": 1759492718,
|
||||
"narHash": "sha256-Mxi/LyyHE9VKUnBs4y1hXO+wRqukZJjbx/igqKQxkQk=",
|
||||
"owner": "hyprwm",
|
||||
"repo": "hyprlock",
|
||||
"rev": "7f769fa993cb492982d7bf25676c68ddbcc0268e",
|
||||
"rev": "3cb799b1842016c85cca2db66fa502b8179cf0fe",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -1191,11 +1191,11 @@
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1757230583,
|
||||
"narHash": "sha256-4uqu7sFPOaVTCogsxaGMgbzZ2vK40GVGMfUmrvK3/LY=",
|
||||
"lastModified": 1759217228,
|
||||
"narHash": "sha256-P13ExJlhMVkrc5LxZLNkIJZhjNYo3LLXnxDsUNrdnMQ=",
|
||||
"owner": "Jovian-Experiments",
|
||||
"repo": "Jovian-NixOS",
|
||||
"rev": "fc3960e6c32c9d4f95fff2ef84444284d24d3bea",
|
||||
"rev": "e52c15ab25f7dc68dde527c8df5bfa9d80d8e64f",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -1229,11 +1229,11 @@
|
||||
},
|
||||
"mnw": {
|
||||
"locked": {
|
||||
"lastModified": 1756659871,
|
||||
"narHash": "sha256-v6Rh4aQ6RKjM2N02kK9Usn0Ix7+OY66vNpeklc1MnGE=",
|
||||
"lastModified": 1758834834,
|
||||
"narHash": "sha256-Y7IvY4F8vajZyp3WGf+KaiIVwondEkMFkt92Cr9NZmg=",
|
||||
"owner": "Gerg-L",
|
||||
"repo": "mnw",
|
||||
"rev": "ed6cc3e48557ba18266e598a5ebb6602499ada16",
|
||||
"rev": "cfbc7d1cc832e318d0863a5fc91d940a96034001",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -1278,11 +1278,11 @@
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1759110900,
|
||||
"narHash": "sha256-fcu/r0ijvaYT2VHGkZGr0wq9uBMNFkiftVBy43/2oig=",
|
||||
"lastModified": 1759455985,
|
||||
"narHash": "sha256-8qDv7NXH3fj1CDXed7c7vJLtrRKDZSo0x6TaWSfelVg=",
|
||||
"owner": "fufexan",
|
||||
"repo": "nix-gaming",
|
||||
"rev": "2ac6a49266e9159ccb001b4c8cb1f50f67d502ae",
|
||||
"rev": "eb5ab503cbd3cb386e8d85a55a9faed73ec7dc37",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -1443,11 +1443,11 @@
|
||||
},
|
||||
"nixpkgs_8": {
|
||||
"locked": {
|
||||
"lastModified": 1759036355,
|
||||
"narHash": "sha256-0m27AKv6ka+q270dw48KflE0LwQYrO7Fm4/2//KCVWg=",
|
||||
"lastModified": 1759381078,
|
||||
"narHash": "sha256-gTrEEp5gEspIcCOx9PD8kMaF1iEmfBcTbO0Jag2QhQs=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "e9f00bd893984bc8ce46c895c3bf7cac95331127",
|
||||
"rev": "7df7ff7d8e00218376575f0acdcc5d66741351ee",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -1459,11 +1459,11 @@
|
||||
},
|
||||
"nixpkgs_9": {
|
||||
"locked": {
|
||||
"lastModified": 1756696532,
|
||||
"narHash": "sha256-6FWagzm0b7I/IGigOv9pr6LL7NQ86mextfE8g8Q6HBg=",
|
||||
"lastModified": 1759386674,
|
||||
"narHash": "sha256-wg1Lz/1FC5Q13R+mM5a2oTV9TA9L/CHHTm3/PiLayfA=",
|
||||
"owner": "nixos",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "58dcbf1ec551914c3756c267b8b9c8c86baa1b2f",
|
||||
"rev": "625ad6366178f03acd79f9e3822606dd7985b657",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -1482,11 +1482,11 @@
|
||||
"systems": "systems_5"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1758271661,
|
||||
"narHash": "sha256-ENqd2/33uP5vB44ClDjjAV+J78oF8q1er4QUZuT8Z7g=",
|
||||
"lastModified": 1759469269,
|
||||
"narHash": "sha256-DP833ejGUNRRHsJOB3WRTaWWXLNucaDga2ju/fGe+sc=",
|
||||
"owner": "notashelf",
|
||||
"repo": "nvf",
|
||||
"rev": "b7571df4d6e9ac08506a738ddceeec0b141751b0",
|
||||
"rev": "e48638aef3a95377689de0ef940443c64f870a09",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -1626,11 +1626,11 @@
|
||||
"rust-analyzer-src": {
|
||||
"flake": false,
|
||||
"locked": {
|
||||
"lastModified": 1759245522,
|
||||
"narHash": "sha256-H4Hx/EuMJ9qi1WzPV4UG2bbZiDCdREtrtDvYcHr0kmk=",
|
||||
"lastModified": 1759301569,
|
||||
"narHash": "sha256-7StxDed3v2fAWLkl+Hse9FlpjT7Dk7Cn/4vxTFyEhIg=",
|
||||
"owner": "rust-lang",
|
||||
"repo": "rust-analyzer",
|
||||
"rev": "a6bc4a4bbe6a65b71cbf76a0cf528c47a8d9f97f",
|
||||
"rev": "472037b789cf593172d6adf3b8d9f7a429f6cd9b",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -1648,11 +1648,11 @@
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1759113356,
|
||||
"narHash": "sha256-xm4kEUcV2jk6u15aHazFP4YsMwhq+PczA+Ul/4FDKWI=",
|
||||
"lastModified": 1759286284,
|
||||
"narHash": "sha256-JLdGGc4XDutzSD1L65Ni6Ye+oTm8kWfm0KTPMcyl7Y4=",
|
||||
"owner": "oxalica",
|
||||
"repo": "rust-overlay",
|
||||
"rev": "be3b8843a2be2411500f6c052876119485e957a2",
|
||||
"rev": "f6f2da475176bb7cff51faae8b3fe879cd393545",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
uid = 994;
|
||||
gid = 993;
|
||||
|
||||
traefik = {
|
||||
nginx = {
|
||||
enable = true;
|
||||
};
|
||||
gitea = {
|
||||
@@ -42,7 +42,11 @@
|
||||
enable = true;
|
||||
};
|
||||
jellyfin = {
|
||||
enable = true;
|
||||
enable = false;
|
||||
cloudflared = {
|
||||
tunnelId = "234811e2-bc86-44b2-9abd-493686e25704";
|
||||
credentialsFile = config.age.secrets.jellyfinCloudflared.path;
|
||||
};
|
||||
};
|
||||
uptime-kuma = {
|
||||
enable = true;
|
||||
@@ -56,7 +60,7 @@
|
||||
};
|
||||
};
|
||||
www = {
|
||||
enable = true;
|
||||
enable = false;
|
||||
url = "cnst.dev";
|
||||
cloudflared = {
|
||||
tunnelId = "e5076186-efb7-405a-998c-6155af7fb221";
|
||||
@@ -88,14 +92,14 @@
|
||||
enable = true;
|
||||
gluetun.enable = true;
|
||||
qbittorrent = {
|
||||
enable = true;
|
||||
port = 8080;
|
||||
enable = false;
|
||||
port = 8387;
|
||||
};
|
||||
slskd = {
|
||||
enable = true;
|
||||
};
|
||||
pihole = {
|
||||
enable = true;
|
||||
enable = false;
|
||||
port = 8053;
|
||||
};
|
||||
};
|
||||
|
||||
@@ -141,7 +141,7 @@
|
||||
./server/keepalived
|
||||
./server/gitea
|
||||
./server/postgres
|
||||
./server/traefik
|
||||
./server/nginx
|
||||
./server/www
|
||||
./server/authentik
|
||||
];
|
||||
|
||||
@@ -3,17 +3,16 @@
|
||||
config,
|
||||
lib,
|
||||
...
|
||||
}:
|
||||
let
|
||||
inherit (lib)
|
||||
}: let
|
||||
inherit
|
||||
(lib)
|
||||
mkIf
|
||||
mkOption
|
||||
mkMerge
|
||||
types
|
||||
;
|
||||
cfg = config.nixos.programs.pkgs;
|
||||
in
|
||||
{
|
||||
in {
|
||||
options = {
|
||||
nixos.programs.pkgs = {
|
||||
enable = mkOption {
|
||||
@@ -51,8 +50,7 @@ in
|
||||
};
|
||||
|
||||
config = mkIf cfg.enable {
|
||||
environment.systemPackages =
|
||||
with pkgs;
|
||||
environment.systemPackages = with pkgs;
|
||||
mkMerge [
|
||||
[
|
||||
pciutils
|
||||
@@ -114,6 +112,8 @@ in
|
||||
helix
|
||||
zfs
|
||||
zfstools
|
||||
cron
|
||||
acme-sh
|
||||
])
|
||||
|
||||
(mkIf cfg.dev.enable [
|
||||
|
||||
@@ -70,7 +70,7 @@ in {
|
||||
secrets = {
|
||||
cloudflareFirewallApiKey.file = "${self}/secrets/cloudflareFirewallApiKey.age";
|
||||
cloudflareDnsApiToken.file = "${self}/secrets/cloudflareDnsApiToken.age";
|
||||
cloudflareDnsCredentials.file = "${self}/secrets/cloudflareDnsCredentials.age";
|
||||
# cloudflareDnsCredentials.file = "${self}/secrets/cloudflareDnsCredentials.age";
|
||||
wgCredentials.file = "${self}/secrets/wgCredentials.age";
|
||||
wgSobotkaPrivateKey.file = "${self}/secrets/wgSobotkaPrivateKey.age";
|
||||
gluetunEnvironment.file = "${self}/secrets/gluetunEnvironment.age";
|
||||
|
||||
@@ -53,15 +53,9 @@ in {
|
||||
age.secrets = {
|
||||
authentikEnv = {
|
||||
file = "${self}/secrets/authentikEnv.age";
|
||||
owner = "authentik";
|
||||
group = "authentik";
|
||||
mode = "0400";
|
||||
};
|
||||
authentikCloudflared = {
|
||||
file = "${self}/secrets/authentikCloudflared.age";
|
||||
owner = "authentik";
|
||||
group = "authentik";
|
||||
mode = "0400";
|
||||
};
|
||||
};
|
||||
|
||||
@@ -110,15 +104,9 @@ in {
|
||||
"X-authentik-username"
|
||||
"X-authentik-groups"
|
||||
"X-authentik-email"
|
||||
"X-authentik-name"
|
||||
"X-authentik-uid"
|
||||
"X-authentik-jwt"
|
||||
"X-authentik-meta-jwks"
|
||||
"X-authentik-meta-outpost"
|
||||
"X-authentik-meta-provider"
|
||||
"X-authentik-meta-app"
|
||||
"X-authentik-meta-version"
|
||||
];
|
||||
timeout = "10s";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
@@ -17,14 +17,14 @@ in {
|
||||
default = "";
|
||||
type = types.str;
|
||||
description = ''
|
||||
Email name to be used to access the server services via Caddy reverse proxy
|
||||
Email name to be used to access the server services via NGINX reverse proxy
|
||||
'';
|
||||
};
|
||||
domain = mkOption {
|
||||
default = "";
|
||||
type = types.str;
|
||||
description = ''
|
||||
Domain name to be used to access the server services via Caddy reverse proxy
|
||||
Domain name to be used to access the server services via NGINX reverse proxy
|
||||
'';
|
||||
};
|
||||
user = lib.mkOption {
|
||||
@@ -65,6 +65,11 @@ in {
|
||||
};
|
||||
|
||||
config = lib.mkIf cfg.enable {
|
||||
_module.args = {
|
||||
myLib = import ./lib.nix {
|
||||
inherit lib config pkgs;
|
||||
};
|
||||
};
|
||||
users = {
|
||||
groups.${cfg.group} = {
|
||||
gid = cfg.gid;
|
||||
@@ -93,7 +98,7 @@ in {
|
||||
"render"
|
||||
"input"
|
||||
"authentik"
|
||||
"traefik"
|
||||
"nginx"
|
||||
];
|
||||
};
|
||||
};
|
||||
|
||||
@@ -58,14 +58,14 @@ in {
|
||||
jails =
|
||||
lib.attrsets.mapAttrs (name: value: {
|
||||
settings = {
|
||||
bantime = "24h";
|
||||
findtime = "10m";
|
||||
bantime = "168h";
|
||||
findtime = "30m";
|
||||
enabled = true;
|
||||
backend = "systemd";
|
||||
journalmatch = "_SYSTEMD_UNIT=${value.serviceName}.service";
|
||||
port = "http,https";
|
||||
filter = "${name}";
|
||||
maxretry = 3;
|
||||
maxretry = value.maxRetry or 5;
|
||||
action = "cloudflare-token-agenix";
|
||||
};
|
||||
})
|
||||
|
||||
@@ -99,23 +99,6 @@ in {
|
||||
};
|
||||
};
|
||||
|
||||
services.traefik = {
|
||||
dynamicConfigOptions = {
|
||||
http = {
|
||||
services.gitea.loadBalancer.servers = [{url = "http://127.0.0.1:5003";}];
|
||||
routers = {
|
||||
gitea = {
|
||||
entryPoints = ["websecure"];
|
||||
rule = "Host(`${cfg.url}`)";
|
||||
service = "gitea";
|
||||
tls.certResolver = "letsencrypt";
|
||||
# middlewares = ["authentik"];
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
server.postgresql.databases = [
|
||||
{
|
||||
database = "gitea";
|
||||
|
||||
@@ -212,25 +212,6 @@ in {
|
||||
}
|
||||
];
|
||||
};
|
||||
|
||||
traefik = {
|
||||
dynamicConfigOptions = {
|
||||
http = {
|
||||
services.homepage.loadBalancer.servers = [
|
||||
{url = "http://127.0.0.1:${toString config.services.${unit}.listenPort}";}
|
||||
];
|
||||
routers = {
|
||||
homepage = {
|
||||
entryPoints = ["websecure"];
|
||||
rule = "Host(`cnix.dev`)";
|
||||
service = "homepage";
|
||||
tls.certResolver = "letsencrypt";
|
||||
# middlewares = ["authentik"];
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
@@ -2,23 +2,38 @@
|
||||
config,
|
||||
lib,
|
||||
pkgs,
|
||||
self,
|
||||
...
|
||||
}: let
|
||||
service = "jellyfin";
|
||||
cfg = config.server.${service};
|
||||
unit = "jellyfin";
|
||||
cfg = config.server.${unit};
|
||||
srv = config.server;
|
||||
in {
|
||||
options.server.${service} = {
|
||||
options.server.${unit} = {
|
||||
enable = lib.mkEnableOption {
|
||||
description = "Enable ${service}";
|
||||
description = "Enable ${unit}";
|
||||
};
|
||||
configDir = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
default = "/var/lib/${service}";
|
||||
default = "/var/lib/${unit}";
|
||||
};
|
||||
url = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
default = "jellyfin.${srv.domain}";
|
||||
default = "jellyfin.${srv.www.url}";
|
||||
};
|
||||
cloudflared = {
|
||||
credentialsFile = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
example = lib.literalExpression ''
|
||||
pkgs.writeText "cloudflare-credentials.json" '''
|
||||
{"AccountTag":"secret"."TunnelSecret":"secret","TunnelID":"secret"}
|
||||
'''
|
||||
'';
|
||||
};
|
||||
tunnelId = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
example = "00000000-0000-0000-0000-000000000000";
|
||||
};
|
||||
};
|
||||
homepage.name = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
@@ -38,17 +53,62 @@ in {
|
||||
};
|
||||
};
|
||||
config = lib.mkIf cfg.enable {
|
||||
services.${service} = {
|
||||
enable = true;
|
||||
user = srv.user;
|
||||
group = srv.group;
|
||||
age.secrets = {
|
||||
jellyfinCloudflared = {
|
||||
file = "${self}/secrets/jellyfinCloudflared.age";
|
||||
owner = "${srv.user}";
|
||||
group = "${srv.group}";
|
||||
mode = "0400";
|
||||
};
|
||||
};
|
||||
|
||||
services = {
|
||||
cloudflared = {
|
||||
enable = true;
|
||||
tunnels.${cfg.cloudflared.tunnelId} = {
|
||||
credentialsFile = cfg.cloudflared.credentialsFile;
|
||||
default = "http_status:404";
|
||||
ingress."${cfg.url}".service = "http://127.0.0.1:8096";
|
||||
};
|
||||
};
|
||||
|
||||
${unit} = {
|
||||
enable = true;
|
||||
user = srv.user;
|
||||
group = srv.group;
|
||||
};
|
||||
};
|
||||
|
||||
environment.systemPackages = with pkgs; [
|
||||
jellyfin-ffmpeg
|
||||
];
|
||||
services.traefik = {
|
||||
dynamicConfigOptions = {
|
||||
http = {
|
||||
middlewares = {
|
||||
secureHeaders = {
|
||||
headers = {
|
||||
stsSeconds = 31536000;
|
||||
forceSTSHeader = true;
|
||||
stsIncludeSubdomains = true;
|
||||
stsPreload = true;
|
||||
browserXssFilter = true;
|
||||
frameDeny = true;
|
||||
referrerPolicy = "no-referrer";
|
||||
contentTypeNosniff = true;
|
||||
customResponseHeaders = {
|
||||
"Content-Security-Policy" = "default-src 'self'; img-src 'self' data:; script-src 'self' 'unsafe-inline' 'unsafe-eval'; style-src 'self' 'unsafe-inline';";
|
||||
};
|
||||
};
|
||||
};
|
||||
ratelimit = {
|
||||
rateLimit = {
|
||||
average = 10;
|
||||
burst = 20;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
services.jellyfin.loadBalancer.servers = [{url = "http://127.0.0.1:8096";}];
|
||||
routers = {
|
||||
jellyfin = {
|
||||
@@ -56,7 +116,7 @@ in {
|
||||
rule = "Host(`${cfg.url}`)";
|
||||
service = "jellyfin";
|
||||
tls.certResolver = "letsencrypt";
|
||||
# middlewares = ["authentik"];
|
||||
middlewares = ["authentik" "secureHeaders" "ratelimit"];
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
148
modules/server/lib.nix
Normal file
148
modules/server/lib.nix
Normal file
@@ -0,0 +1,148 @@
|
||||
# from @jtojnar
|
||||
{
|
||||
config,
|
||||
lib,
|
||||
pkgs,
|
||||
...
|
||||
}: {
|
||||
mkVirtualHost = {
|
||||
path ? null,
|
||||
config ? "",
|
||||
acme ? null,
|
||||
redirect ? null,
|
||||
...
|
||||
} @ args:
|
||||
(
|
||||
if lib.isString acme
|
||||
then {
|
||||
useACMEHost = acme;
|
||||
forceSSL = true;
|
||||
}
|
||||
else {}
|
||||
)
|
||||
// (
|
||||
if lib.isBool acme
|
||||
then {
|
||||
enableACME = acme;
|
||||
forceSSL = true;
|
||||
}
|
||||
else {}
|
||||
)
|
||||
// (
|
||||
if redirect != null
|
||||
then {
|
||||
globalRedirect = redirect;
|
||||
}
|
||||
else {}
|
||||
)
|
||||
// (
|
||||
if path != null
|
||||
then {
|
||||
root = "/var/www/" + path;
|
||||
}
|
||||
else {}
|
||||
)
|
||||
// {
|
||||
extraConfig = config;
|
||||
}
|
||||
// builtins.removeAttrs args [
|
||||
"path"
|
||||
"config"
|
||||
"acme"
|
||||
"redirect"
|
||||
];
|
||||
mkPhpPool = {
|
||||
user,
|
||||
debug ? false,
|
||||
settings ? {},
|
||||
...
|
||||
} @ args:
|
||||
{
|
||||
inherit user;
|
||||
settings =
|
||||
{
|
||||
"listen.owner" = "nginx";
|
||||
"listen.group" = "root";
|
||||
"pm" = "dynamic";
|
||||
"pm.max_children" = 5;
|
||||
"pm.start_servers" = 2;
|
||||
"pm.min_spare_servers" = 1;
|
||||
"pm.max_spare_servers" = 3;
|
||||
}
|
||||
// (
|
||||
lib.optionalAttrs debug {
|
||||
# log worker's stdout, but this has a performance hit
|
||||
"catch_workers_output" = true;
|
||||
}
|
||||
// settings
|
||||
);
|
||||
}
|
||||
// builtins.removeAttrs args [
|
||||
"user"
|
||||
"debug"
|
||||
"settings"
|
||||
];
|
||||
enablePHP = sockName: ''
|
||||
fastcgi_pass unix:${config.services.phpfpm.pools.${sockName}.socket};
|
||||
include ${config.services.nginx.package}/conf/fastcgi.conf;
|
||||
fastcgi_param PATH_INFO $fastcgi_path_info;
|
||||
fastcgi_param PATH_TRANSLATED $document_root$fastcgi_path_info;
|
||||
'';
|
||||
|
||||
/*
|
||||
Adds extra options to ssh key that will only allow it to be used for rsync.
|
||||
See sshd(8) manual page for details.
|
||||
*/
|
||||
restrictToRsync = directory: key: ''command="${pkgs.rrsync}/bin/rrsync -wo ${directory}",restrict ${key}'';
|
||||
|
||||
/*
|
||||
Emulate systemd credentials.
|
||||
Those will only be available to the user the service is running under,
|
||||
not being aware of dropped euid.
|
||||
http://systemd.io/CREDENTIALS/
|
||||
*/
|
||||
emulateCredentials = let
|
||||
parseCredential = credential: let
|
||||
matches = builtins.match "(.+):(.+)" credential;
|
||||
in
|
||||
assert lib.assertMsg (matches != null) "A credential needs to match “id:value” format"; {
|
||||
id = builtins.elemAt matches 0;
|
||||
value = builtins.elemAt matches 1;
|
||||
};
|
||||
|
||||
parseCredentials = credentials:
|
||||
builtins.map parseCredential (
|
||||
if builtins.isList credentials
|
||||
then credentials
|
||||
else lib.splitString "," credentials
|
||||
);
|
||||
in
|
||||
serviceConfig:
|
||||
lib.mkMerge [
|
||||
(builtins.removeAttrs serviceConfig [
|
||||
"SetCredential"
|
||||
"LoadCredential"
|
||||
])
|
||||
{
|
||||
Environment = [
|
||||
"CREDENTIALS_DIRECTORY=${
|
||||
pkgs.runCommand "credentials" {} ''
|
||||
mkdir "$out"
|
||||
${lib.concatMapStringsSep "\n" ({
|
||||
id,
|
||||
value,
|
||||
}: ''ln -s "${value}" "$out/${id}"'') (
|
||||
parseCredentials serviceConfig.LoadCredential or []
|
||||
)}
|
||||
${lib.concatMapStringsSep "\n" ({
|
||||
id,
|
||||
value,
|
||||
}: ''echo -n "${value}" > "$out/${id}"'') (
|
||||
parseCredentials serviceConfig.SetCredential or []
|
||||
)}
|
||||
''
|
||||
}"
|
||||
];
|
||||
}
|
||||
];
|
||||
}
|
||||
79
modules/server/nginx/default.nix
Normal file
79
modules/server/nginx/default.nix
Normal file
@@ -0,0 +1,79 @@
|
||||
{
|
||||
lib,
|
||||
config,
|
||||
pkgs,
|
||||
self,
|
||||
myLib,
|
||||
...
|
||||
}: let
|
||||
inherit (myLib) mkVirtualHost;
|
||||
inherit (lib) mkEnableOption mkIf types;
|
||||
|
||||
unit = "nginx";
|
||||
cfg = config.server.nginx;
|
||||
srv = config.server;
|
||||
in {
|
||||
options.server.nginx = {
|
||||
enable = mkEnableOption "Enable global NGINX reverse proxy with ACME";
|
||||
};
|
||||
|
||||
config = mkIf cfg.enable {
|
||||
age.secrets = {
|
||||
nginxEnv = {
|
||||
file = "${self}/secrets/nginxEnv.age";
|
||||
};
|
||||
};
|
||||
|
||||
networking.firewall.allowedTCPPorts = [80 443];
|
||||
|
||||
security = {
|
||||
acme = {
|
||||
acceptTerms = true;
|
||||
defaults.email = config.server.email;
|
||||
certs.${srv.domain} = {
|
||||
reloadServices = ["nginx.service"];
|
||||
domain = "${srv.domain}";
|
||||
extraDomainNames = ["*.${srv.domain}"];
|
||||
dnsProvider = "cloudflare";
|
||||
dnsPropagationCheck = true;
|
||||
group = config.services.nginx.group;
|
||||
environmentFile = config.age.secrets.nginxEnv.path;
|
||||
};
|
||||
};
|
||||
dhparams = {
|
||||
enable = true;
|
||||
params.nginx = {};
|
||||
};
|
||||
};
|
||||
|
||||
services.nginx = {
|
||||
enable = true;
|
||||
recommendedGzipSettings = true;
|
||||
recommendedOptimisation = true;
|
||||
recommendedProxySettings = true;
|
||||
recommendedTlsSettings = true;
|
||||
resolver.addresses = config.networking.nameservers;
|
||||
sslDhparam = config.security.dhparams.params.nginx.path;
|
||||
appendHttpConfig = ''
|
||||
proxy_headers_hash_max_size 512;
|
||||
proxy_headers_hash_bucket_size 128;
|
||||
'';
|
||||
|
||||
virtualHosts = let
|
||||
labDomain = "cnix.dev";
|
||||
labCert = {
|
||||
useACMEHost = "cnix.dev";
|
||||
forceSSL = true;
|
||||
};
|
||||
in {
|
||||
# proxies on domain with https, only accessible within local network
|
||||
"${labDomain}" =
|
||||
labCert
|
||||
// {
|
||||
locations."/".proxyPass = "http://127.0.0.1:${toString config.services.homepage-dashboard.listenPort}";
|
||||
};
|
||||
};
|
||||
};
|
||||
users.users.nginx.extraGroups = ["acme"];
|
||||
};
|
||||
}
|
||||
@@ -34,7 +34,7 @@ in {
|
||||
};
|
||||
port = lib.mkOption {
|
||||
type = lib.types.int;
|
||||
default = 8080;
|
||||
default = 8387;
|
||||
description = "The port to host qBittorrent on.";
|
||||
};
|
||||
homepage.name = lib.mkOption {
|
||||
@@ -143,62 +143,6 @@ in {
|
||||
];
|
||||
};
|
||||
|
||||
services.traefik = lib.mkMerge [
|
||||
(lib.mkIf cfg.pihole.enable {
|
||||
dynamicConfigOptions = {
|
||||
http = {
|
||||
services = {
|
||||
pihole.loadBalancer.servers = [{url = "http://localhost:${toString cfg.pihole.port}";}];
|
||||
};
|
||||
routers = {
|
||||
pihole = {
|
||||
entryPoints = ["websecure"];
|
||||
rule = "Host(`${cfg.pihole.url}`)";
|
||||
service = "pihole";
|
||||
tls.certResolver = "letsencrypt";
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
})
|
||||
|
||||
(lib.mkIf cfg.qbittorrent.enable {
|
||||
dynamicConfigOptions = {
|
||||
http = {
|
||||
services = {
|
||||
qbittorrent.loadBalancer.servers = [{url = "http://localhost:${toString cfg.qbittorrent.port}";}];
|
||||
};
|
||||
routers = {
|
||||
qbittorrent = {
|
||||
entryPoints = ["websecure"];
|
||||
rule = "Host(`${cfg.qbittorrent.url}`)";
|
||||
service = "qbittorrent";
|
||||
tls.certResolver = "letsencrypt";
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
})
|
||||
|
||||
(lib.mkIf cfg.slskd.enable {
|
||||
dynamicConfigOptions = {
|
||||
http = {
|
||||
services = {
|
||||
slskd.loadBalancer.servers = [{url = "http://localhost:${toString cfg.slskd.port}";}];
|
||||
};
|
||||
routers = {
|
||||
slskd = {
|
||||
entryPoints = ["websecure"];
|
||||
rule = "Host(`${cfg.slskd.url}`)";
|
||||
service = "slskd";
|
||||
tls.certResolver = "letsencrypt";
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
})
|
||||
];
|
||||
|
||||
virtualisation.oci-containers.containers = lib.mkMerge [
|
||||
(lib.mkIf cfg.gluetun.enable {
|
||||
gluetun = {
|
||||
@@ -206,7 +150,7 @@ in {
|
||||
ports = [
|
||||
"8388:8388"
|
||||
"58846:58846"
|
||||
"8080:8080"
|
||||
"8387:8387"
|
||||
"5030:5030"
|
||||
"5031:5031"
|
||||
"50300:50300"
|
||||
@@ -235,7 +179,7 @@ in {
|
||||
autoStart = true;
|
||||
dependsOn = ["gluetun"];
|
||||
ports = [
|
||||
"8080:8080"
|
||||
"8387:8387"
|
||||
"58846:58846"
|
||||
];
|
||||
extraOptions = [
|
||||
|
||||
@@ -43,15 +43,16 @@ in {
|
||||
group = srv.group;
|
||||
};
|
||||
services.traefik = {
|
||||
# staticConfigOptions.entryPoints.${unit}.address = "127.0.0.1:8989";
|
||||
dynamicConfigOptions = {
|
||||
http = {
|
||||
services.sonarr.loadBalancer.servers = [{url = "http://127.0.0.1:8989";}];
|
||||
# services.sonarr.loadBalancer.servers = [{url = "http://127.0.0.1:8989";}];
|
||||
routers = {
|
||||
sonarr = {
|
||||
entryPoints = ["websecure"];
|
||||
rule = "Host(`${cfg.url}`)";
|
||||
service = "sonarr";
|
||||
tls.certResolver = "letsencrypt";
|
||||
# tls.certResolver = "letsencrypt";
|
||||
# middlewares = ["authentik"];
|
||||
};
|
||||
};
|
||||
|
||||
@@ -1,100 +0,0 @@
|
||||
{
|
||||
lib,
|
||||
config,
|
||||
pkgs,
|
||||
self,
|
||||
...
|
||||
}: let
|
||||
inherit (lib) mkEnableOption mkIf types;
|
||||
|
||||
cfg = config.server.traefik;
|
||||
getCloudflareCredentials = hostname:
|
||||
if hostname == "ziggy"
|
||||
then config.age.secrets.cloudflareDnsCredentialsZiggy.path
|
||||
else if hostname == "sobotka"
|
||||
then config.age.secrets.cloudflareDnsCredentials.path
|
||||
else throw "Unknown hostname: ${hostname}";
|
||||
in {
|
||||
options.server.traefik = {
|
||||
enable = mkEnableOption "Enable global Traefik reverse proxy with ACME";
|
||||
};
|
||||
|
||||
config = mkIf cfg.enable {
|
||||
age.secrets.traefikEnv = {
|
||||
file = "${self}/secrets/traefikEnv.age";
|
||||
mode = "640";
|
||||
owner = "root";
|
||||
group = "traefik";
|
||||
};
|
||||
|
||||
systemd.services.traefik = {
|
||||
serviceConfig = {
|
||||
EnvironmentFile = [config.age.secrets.traefikEnv.path];
|
||||
};
|
||||
};
|
||||
networking.firewall.allowedTCPPorts = [80 443];
|
||||
|
||||
services = {
|
||||
tailscale.permitCertUid = "traefik";
|
||||
traefik = {
|
||||
enable = true;
|
||||
|
||||
staticConfigOptions = {
|
||||
log = {
|
||||
level = "DEBUG";
|
||||
};
|
||||
|
||||
tracing = {};
|
||||
api = {
|
||||
dashboard = true;
|
||||
};
|
||||
certificatesResolvers = {
|
||||
tailscale.tailscale = {};
|
||||
letsencrypt = {
|
||||
acme = {
|
||||
email = "adam@cnst.dev";
|
||||
storage = "/var/lib/traefik/cert.json";
|
||||
dnsChallenge = {
|
||||
provider = "cloudflare";
|
||||
resolvers = [
|
||||
"1.1.1.1:53"
|
||||
"1.0.0.1:53"
|
||||
];
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
entryPoints = {
|
||||
redis = {
|
||||
address = "0.0.0.0:6381";
|
||||
};
|
||||
postgres = {
|
||||
address = "0.0.0.0:5433";
|
||||
};
|
||||
web = {
|
||||
address = "0.0.0.0:80";
|
||||
http.redirections.entryPoint = {
|
||||
to = "websecure";
|
||||
scheme = "https";
|
||||
permanent = true;
|
||||
};
|
||||
};
|
||||
websecure = {
|
||||
address = "0.0.0.0:443";
|
||||
http.tls = {
|
||||
certResolver = "letsencrypt";
|
||||
domains = [
|
||||
{
|
||||
main = "cnix.dev";
|
||||
sans = ["*.cnix.dev"];
|
||||
}
|
||||
];
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
@@ -114,18 +114,5 @@ in {
|
||||
Disallow: /
|
||||
'';
|
||||
};
|
||||
|
||||
services.traefik.dynamicConfigOptions.http = {
|
||||
routers.webfinger = {
|
||||
entryPoints = ["websecure"];
|
||||
rule = "Host(`${cfg.url}`) && Path(`/.well-known/webfinger`)";
|
||||
service = "webfinger";
|
||||
tls.certResolver = "letsencrypt";
|
||||
};
|
||||
|
||||
services.webfinger.loadBalancer.servers = [
|
||||
{url = "http://127.0.0.1:8283";}
|
||||
];
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
11
secrets/jellyfinCloudflared.age
Normal file
11
secrets/jellyfinCloudflared.age
Normal file
@@ -0,0 +1,11 @@
|
||||
age-encryption.org/v1
|
||||
-> ssh-ed25519 t9iOEg dULaZVrpkeN1HqwM+HSU46XJjQznBOxa+wUDSSciK1w
|
||||
FxqlulfViojLLoskKu56Utiq57rkDC6TocoaFQOgahc
|
||||
-> ssh-ed25519 KUYMFA QYnb24Av6Vy17Lb5gC6dzQszOxa5bRP2PgFetmqhfCA
|
||||
MaIes8V0NkyTAzFzbRuCMzMLwhYljYHwhXPKEZEdlC4
|
||||
-> ssh-ed25519 76RhUQ NLoyNp/z5MXE3Hrmv7CCDYB1X8R1OORm2MZVMbgNDQs
|
||||
6U8r3wpXNtQdkIH+lo7cDrnjPw9B6eF/jMeb+iW9T1I
|
||||
-> ssh-ed25519 Jf8sqw GOWsvhSzrHJGJFOgsXr+O1jq7jnjyzKc82Mu7ntrI2Q
|
||||
P3n32btGFtUxB28/a0iz6lBTM6UP3EYUrq0y8BR49IM
|
||||
--- geE2uS2TMspihaNwgzH4FQKYoUFFRhjTS6V7F/cdTE0
|
||||
%<25>^<5E>E3<33><F2BF88BE><EFBFBD>gg<67><67>ٸk<D9B8><6B>;<3B>lE+b<>x<EFBFBD>Ҝ-<2D>X<>Et'&^<5E>=c<><63>9<EFBFBD>v*<2A>
|
||||
12
secrets/nginxEnv.age
Normal file
12
secrets/nginxEnv.age
Normal file
@@ -0,0 +1,12 @@
|
||||
age-encryption.org/v1
|
||||
-> ssh-ed25519 t9iOEg SCmn/7gQOD3fRn0PKPXxNBDKNSqUcnejG8tPwjoNT1I
|
||||
vd57xXCQ16PPPMlNysw6BWSIP/EgB3HrWlesBiQUVA0
|
||||
-> ssh-ed25519 KUYMFA WVn1OhFDxMMEHFa1yHpBmWskbcRgHyBiDWZjPv8NfX8
|
||||
8BCp/iUFkRsAGnc+0MEfpRXo1/Xla4WO3qYZMSl7LGw
|
||||
-> ssh-ed25519 76RhUQ ZfdS1EWtBOwd7+hVnJ/JqxUCDTYmfEs1OwyzXajFMmg
|
||||
eE0ahMgW76RFu9y9Z+PE6IzaLX3ydhhJvjk5vu2EgXo
|
||||
-> ssh-ed25519 Jf8sqw Pr/LkT5RQysShzjqEZlXFRHim3WpAav04c/GGftDTiA
|
||||
TWWRURLPnkpsxxSdSQem9sAoUoLt5wQe0CYl1xl1RaA
|
||||
--- cfGjcdOvwxLDlANhDe44U5JXGAyRYPZMVtC48PuhLNo
|
||||
<EFBFBD>Zyr<>#*K<><4B><EFBFBD><EFBFBD><0B><1F>
|
||||
$<24><>FB<46>$<24>^<5E>BT<42><54>#D<>v<EFBFBD><76>3<EFBFBD><13><f<>dG$<24><><EFBFBD><~<7E><10><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>m<EFBFBD><6D>8^<1A><><EFBFBD>\# Z<>-8<><38><EFBFBD>D<EFBFBD>dM<64><4D>
|
||||
@@ -58,9 +58,10 @@ in {
|
||||
"sobotkaPihole.age".publicKeys = kima ++ sobotka;
|
||||
"slskd.age".publicKeys = kima ++ sobotka;
|
||||
"authentikEnv.age".publicKeys = kima ++ sobotka;
|
||||
"traefikEnv.age".publicKeys = kima ++ sobotka;
|
||||
"nginxEnv.age".publicKeys = kima ++ sobotka;
|
||||
"wwwCloudflared.age".publicKeys = kima ++ sobotka;
|
||||
"authentikCloudflared.age".publicKeys = kima ++ sobotka;
|
||||
"jellyfinCloudflared.age".publicKeys = kima ++ sobotka;
|
||||
|
||||
# Ziggy-specific
|
||||
"cloudflareDnsCredentialsZiggy.age".publicKeys = kima ++ ziggy;
|
||||
|
||||
Binary file not shown.
Reference in New Issue
Block a user