Compare commits
223 Commits
cd4c727255
...
dev
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d9e7775afc | ||
|
|
424f12f5f7 | ||
|
|
7ca8362d39 | ||
|
|
f54977fe42 | ||
|
|
1b05194939 | ||
|
|
4e31a9f54a | ||
|
|
7cd78511e7 | ||
|
|
88d100dd77 | ||
|
|
775e3e93bb | ||
|
|
bfec529d88 | ||
|
|
2afcbf6d99 | ||
|
|
2cd45ef7de | ||
|
|
4d743836ca | ||
|
|
9a6dda390b | ||
|
|
dcd998830c | ||
|
|
57bcf4d33c | ||
|
|
7cc516a0be | ||
|
|
37143eff2d | ||
|
|
c3edd3c9fa | ||
|
|
775b0b4823 | ||
|
|
ce0797b73b | ||
|
|
a8bbbdc518 | ||
|
|
742760afa7 | ||
|
|
8b9187b17a | ||
|
|
8d50d4ecaf | ||
|
|
7ee341ee06 | ||
|
|
5288f83c2e | ||
|
|
dd70ef6499 | ||
|
|
beaed878f8 | ||
|
|
09ca162eed | ||
|
|
4f5e6f210d | ||
|
|
4a61f43eb9 | ||
|
|
a257a3153d | ||
|
|
7da9acfcdc | ||
|
|
f838eb9850 | ||
|
|
302f9ae51b | ||
|
|
90b5828663 | ||
|
|
1d9c5cdcd2 | ||
|
|
b59eecd26a | ||
|
|
6f8c8c92f1 | ||
|
|
f24102d752 | ||
|
|
23147ca625 | ||
|
|
40cf001ffa | ||
|
|
46fae29477 | ||
|
|
deea98b2de | ||
|
|
b7aa160baa | ||
|
|
b91e9cacfd | ||
|
|
46b6b4db4f | ||
|
|
5dcb3a7d4a | ||
|
|
43780f80aa | ||
|
|
23cd521445 | ||
|
|
51b6d88c64 | ||
|
|
bc9b06f3ae | ||
|
|
220aee72ef | ||
|
|
5dac3d02ce | ||
|
|
58825913e0 | ||
|
|
20103fe83c | ||
|
|
271502f1c9 | ||
|
|
33da5d9f1b | ||
|
|
771e6029b6 | ||
|
|
2cf5dcedbd | ||
|
|
f27ec01b2f | ||
|
|
9b6359fe86 | ||
|
|
31b23ae4a6 | ||
|
|
af36497035 | ||
|
|
3f57b606a0
|
||
|
|
f6498b3177 | ||
|
|
44d9ae0faf | ||
|
|
d5cedb017e | ||
|
|
3e3a108707 | ||
|
|
2b555b1a78 | ||
|
|
04ac376ea9
|
||
|
|
b20763fa86
|
||
|
|
de92ad0f12
|
||
|
|
c3b2468ff3
|
||
|
|
6ed1514f31
|
||
|
|
94a27bb403
|
||
|
|
1d656dc4ae
|
||
|
|
b3014bce85
|
||
|
|
ef845edd12
|
||
|
|
c456e4cad8
|
||
|
|
39b12966f1
|
||
|
|
cbef43ae83
|
||
|
|
765f18d5a0
|
||
|
|
76302840d0
|
||
|
|
e02aca85bd
|
||
|
|
0378ce7dff
|
||
|
|
0fd8286331
|
||
|
|
e4aac05b6a
|
||
|
|
fd7b95e12e
|
||
|
|
a94e8beb37
|
||
|
|
3e05dfbc07
|
||
|
|
a94574a53d | ||
|
|
03bec133ba | ||
|
|
b6de2d2ccf | ||
|
|
334a484ad4 | ||
|
|
11f7e95d95 | ||
|
|
24a0fb6a93 | ||
|
|
892e271719 | ||
|
|
8769b6da9d | ||
|
|
00bdef4307 | ||
|
|
6ed72c00ff | ||
|
|
6d5cd82e72 | ||
|
|
3e921ef2ab | ||
|
|
09cc16bc40 | ||
|
|
425722e2c6 | ||
|
|
0fb6aa0047 | ||
|
|
07283e1f26 | ||
|
|
362afd3d4e | ||
|
|
1154cbb3bd | ||
|
|
25c7823f38 | ||
|
|
d57fb32f67 | ||
|
|
08a7ed2469 | ||
|
|
45e375168e | ||
|
|
56252474d9 | ||
|
|
0ee26c817c | ||
|
|
51bd495981 | ||
|
|
d9a59e9593 | ||
|
|
8557df0199 | ||
|
|
84d9b0ade0 | ||
|
|
7e3d86e37e | ||
|
|
f5c16775c4 | ||
|
|
83dec697d1 | ||
|
|
dd47977cbd | ||
|
|
601999180b | ||
|
|
97004b4b75 | ||
|
|
d15895d8e3 | ||
|
|
983c19eaa5 | ||
|
|
4e7a348461 | ||
|
|
4ccb941766 | ||
|
|
e19fe6a973 | ||
|
|
d9e07543ba | ||
|
|
5d4aaeb49f | ||
|
|
14540f043d | ||
|
|
2c29f8a41b | ||
|
|
cd994d6359 | ||
|
|
a1da14f9fb | ||
|
|
143ea35dc1 | ||
|
|
c23ad28f85 | ||
|
|
c60123ca3c | ||
|
|
c069079a3a | ||
|
|
e777a56816 | ||
|
|
9933d12183 | ||
|
|
2e6c044b89 | ||
|
|
252373f956 | ||
|
|
511837f0a5 | ||
|
|
2882889eae | ||
|
|
c174fe20ae | ||
|
|
31a972d55b | ||
|
|
9de6cedf33 | ||
|
|
f80107efab | ||
|
|
ea21907e5d | ||
|
|
d86c3b76b5 | ||
|
|
66e878b902 | ||
|
|
6a3327386f | ||
|
|
860955f555 | ||
|
|
f5fd711636 | ||
|
|
677c9bc7d4 | ||
|
|
2cb8d6c24e | ||
|
|
3b4e7d07a4 | ||
|
|
8ecef91c92 | ||
|
|
95c1bb126e | ||
|
|
de18ad8127 | ||
|
|
8e57822c10 | ||
|
|
f2883aa33d | ||
|
|
7bd5ceacd9 | ||
|
|
e940bd0ec1 | ||
|
|
44813226c2 | ||
|
|
7381a17f87 | ||
|
|
19cec13a43 | ||
|
|
9117530393 | ||
|
|
868ba53208 | ||
|
|
f7c55f3a5a | ||
| 7cfd1bb245 | |||
| 85a6517609 | |||
| d55756f8f8 | |||
| 18beb41cd8 | |||
| 78f01cf111 | |||
| c582d89715 | |||
| 4270b15f9d | |||
| a62bc660c5 | |||
| 973fd78c1b | |||
| 7e62883e66 | |||
| feccc2c3e0 | |||
| 0b02de0957 | |||
| 721838df2b | |||
|
|
8c800ad0d3 | ||
|
|
f7af832d8e | ||
|
|
6ddf4a07cc | ||
|
|
0df674647c | ||
|
|
7bead6b3ac | ||
|
|
f0befa87be | ||
|
|
6d665ee59c | ||
|
|
befe46c085 | ||
|
|
ba6d057600 | ||
|
|
db5230bd69 | ||
|
|
e1a80bb7ce | ||
| 7d95ba04a9 | |||
| 9169205357 | |||
| 74721f6b09 | |||
| 668c0107f9 | |||
| 331291c54d | |||
| d10f53e485 | |||
| 82aea8268f | |||
| 050eaedca2 | |||
| be9cb270aa | |||
| a5e0e96b52 | |||
| 4366232f18 | |||
| 4398b1d888 | |||
| c4b5c47aa4 | |||
| ff64e6c231 | |||
| e7d656141a | |||
| cf3c2428fb | |||
| a2dc050b1c | |||
| 8bf332caf2 | |||
| 20d3786547 | |||
| 79422c180a | |||
| 65fc9c6df2 | |||
| a59cbd13a3 | |||
| 5f04ef7ae5 | |||
| 0aff508cda | |||
| 30df106b94 | |||
| 3abdb6d637 |
104
flake.lock
generated
104
flake.lock
generated
@@ -1,27 +1,5 @@
|
||||
{
|
||||
"nodes": {
|
||||
"arion": {
|
||||
"inputs": {
|
||||
"flake-parts": "flake-parts",
|
||||
"haskell-flake": "haskell-flake",
|
||||
"nixpkgs": [
|
||||
"nixpkgs"
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1770259557,
|
||||
"narHash": "sha256-EvZ09k9+mzXAngPzU2K7oLLUDlKoT1numb4bDb3Gtl4=",
|
||||
"owner": "hercules-ci",
|
||||
"repo": "arion",
|
||||
"rev": "9b24cf65c72cb0e9616e437d55e1ac8e5c6bc715",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "hercules-ci",
|
||||
"repo": "arion",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"base16-schemes": {
|
||||
"flake": false,
|
||||
"locked": {
|
||||
@@ -45,11 +23,11 @@
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1777780666,
|
||||
"narHash": "sha256-8wURyQMdDkGUarSTKOGdCuFfYiwa3HbzwscUfn3STDE=",
|
||||
"lastModified": 1779036909,
|
||||
"narHash": "sha256-zXcwYQGCT6pzinK+1dBB2ekTVtfxGZAapb3Evdcu4fY=",
|
||||
"owner": "lnl7",
|
||||
"repo": "nix-darwin",
|
||||
"rev": "8c62fba0854ba15c8917aed18894dbccb48a3777",
|
||||
"rev": "56c666e108467d87d13508936aade6d567f2a501",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -60,27 +38,6 @@
|
||||
}
|
||||
},
|
||||
"flake-parts": {
|
||||
"inputs": {
|
||||
"nixpkgs-lib": [
|
||||
"arion",
|
||||
"nixpkgs"
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1769996383,
|
||||
"narHash": "sha256-AnYjnFWgS49RlqX7LrC4uA+sCCDBj0Ry/WOJ5XWAsa0=",
|
||||
"owner": "hercules-ci",
|
||||
"repo": "flake-parts",
|
||||
"rev": "57928607ea566b5db3ad13af0e57e921e6b12381",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "hercules-ci",
|
||||
"repo": "flake-parts",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"flake-parts_2": {
|
||||
"inputs": {
|
||||
"nixpkgs-lib": [
|
||||
"nur",
|
||||
@@ -103,11 +60,11 @@
|
||||
},
|
||||
"hardware": {
|
||||
"locked": {
|
||||
"lastModified": 1778143761,
|
||||
"narHash": "sha256-lkesY6x2X2qxlqLM7CT2iM/0rP2JB7fruPN3h8POXmI=",
|
||||
"lastModified": 1779258371,
|
||||
"narHash": "sha256-j1iZsLy6oFApqR1oiDmHhvkwxXqcNi0aoSJj643LuwU=",
|
||||
"owner": "nixos",
|
||||
"repo": "nixos-hardware",
|
||||
"rev": "3bcaa367d4c550d687a17ac792fd5cda214ee871",
|
||||
"rev": "c97bc4d15bd3473dd095e8e8ba57330ab1943a77",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -116,22 +73,6 @@
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"haskell-flake": {
|
||||
"locked": {
|
||||
"lastModified": 1675296942,
|
||||
"narHash": "sha256-u1X1sblozi5qYEcLp1hxcyo8FfDHnRUVX3dJ/tW19jY=",
|
||||
"owner": "srid",
|
||||
"repo": "haskell-flake",
|
||||
"rev": "c2cafce9d57bfca41794dc3b99c593155006c71e",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "srid",
|
||||
"ref": "0.1.0",
|
||||
"repo": "haskell-flake",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"home-manager": {
|
||||
"inputs": {
|
||||
"nixpkgs": [
|
||||
@@ -139,11 +80,11 @@
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1777851538,
|
||||
"narHash": "sha256-Gp8qwTEYNoy2yvmErVGlvLOQvrtEECCAKbonW7VJef8=",
|
||||
"lastModified": 1778905220,
|
||||
"narHash": "sha256-ox/5IHc8uwy6UTw6N7Shp6uCHIgu/S2PsWeuXsOHSo8=",
|
||||
"owner": "nix-community",
|
||||
"repo": "home-manager",
|
||||
"rev": "cc09c0f9b7eaa95c2d9827338a5eb03d32505ca5",
|
||||
"rev": "d1686dc7d36cbd1234cb226ad6ef97e882716acb",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -174,11 +115,11 @@
|
||||
},
|
||||
"nixUnstable": {
|
||||
"locked": {
|
||||
"lastModified": 1778274207,
|
||||
"narHash": "sha256-I4puXmX1iovcCHZlRmztO3vW0mAbbRvq4F8wgIMQ1MM=",
|
||||
"lastModified": 1779259093,
|
||||
"narHash": "sha256-7DKWmH23hL2eYdkxCKeqj2i+yljTKuU+3Nk1UPHOnxc=",
|
||||
"owner": "nixos",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "b3da656039dc7a6240f27b2ef8cc6a3ef3bccae7",
|
||||
"rev": "d99b013d5d1931ad77fe3912ed218170dec5d9a4",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -190,11 +131,11 @@
|
||||
},
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1778003029,
|
||||
"narHash": "sha256-q/nkKLDtHIyLjZpKhWk3cSK5IYsFqtMd6UtXF3ddjgA=",
|
||||
"lastModified": 1779102034,
|
||||
"narHash": "sha256-vZJZjLo513IeI8hjzHFc6TDezUd4uCE2Eq4SNO3DNNg=",
|
||||
"owner": "nixos",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "0c88e1f2bdb93d5999019e99cb0e61e1fe2af4c5",
|
||||
"rev": "687f05a9184cad4eaf905c48b63649e3a86f5433",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -221,11 +162,11 @@
|
||||
},
|
||||
"nixpkgs_2": {
|
||||
"locked": {
|
||||
"lastModified": 1777954456,
|
||||
"narHash": "sha256-hGdgeU2Nk87RAuZyYjyDjFL6LK7dAZN5RE9+hrDTkDU=",
|
||||
"lastModified": 1778869304,
|
||||
"narHash": "sha256-30sZNZoA1cqF5JNO9fVX+wgiQYjB7HJqqJ4ztCDeBZE=",
|
||||
"owner": "nixos",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "549bd84d6279f9852cae6225e372cc67fb91a4c1",
|
||||
"rev": "d233902339c02a9c334e7e593de68855ad26c4cb",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -237,15 +178,15 @@
|
||||
},
|
||||
"nur": {
|
||||
"inputs": {
|
||||
"flake-parts": "flake-parts_2",
|
||||
"flake-parts": "flake-parts",
|
||||
"nixpkgs": "nixpkgs_2"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1778376280,
|
||||
"narHash": "sha256-pL2F2FF2FN7zWr5o/vG7GiYOSjp+DUNyPIYqNaLQFFs=",
|
||||
"lastModified": 1779398206,
|
||||
"narHash": "sha256-jX+5X7dr4ZLA/LgCZUkKngFsA6MGHBMReOspOiE1mYI=",
|
||||
"owner": "nix-community",
|
||||
"repo": "nur",
|
||||
"rev": "828688994167eb57628c98fd1d7e1223b079cda1",
|
||||
"rev": "a592294d7840cea33f00c2a9f1efd396710f75af",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -256,7 +197,6 @@
|
||||
},
|
||||
"root": {
|
||||
"inputs": {
|
||||
"arion": "arion",
|
||||
"darwin": "darwin",
|
||||
"hardware": "hardware",
|
||||
"home-manager": "home-manager",
|
||||
|
||||
@@ -1,3 +1,9 @@
|
||||
{ ... }: {
|
||||
imports = [ ./dbus ./fonts ./hw ./locale ./network ./nix ./security ./xdg ];
|
||||
|
||||
# services.journald.extraConfig = ''
|
||||
# LineMax=128K
|
||||
# SystemMaxUse=512M
|
||||
# SystemMaxFileSize=128M
|
||||
# '';
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
|
||||
allowedTCPPorts =
|
||||
(if (config.syscfg.server != false && config.syscfg.server.web) then [ 80 443 22 ] else [ ]) ++
|
||||
(if (config.syscfg.server != false) then [ 5432 6379 ] else [ ]) ++
|
||||
(if (config.syscfg.server != false) then [ 5432 6379 8181 ] else [ ]) ++
|
||||
[ ];
|
||||
};
|
||||
};
|
||||
|
||||
46
modules/server/containers/apps/.template.nix
Normal file
46
modules/server/containers/apps/.template.nix
Normal file
@@ -0,0 +1,46 @@
|
||||
{ config, containerCfg, pkgs, lib, builder, name,... }:
|
||||
let
|
||||
serverCfg = config.syscfg.server;
|
||||
image = pkgs.dockerTools.streamLayeredImage {
|
||||
name = "EXAMPLE";
|
||||
tag = "0.0.0";
|
||||
contents = [ pkgs.bashInteractive ];
|
||||
config = {
|
||||
Entrypoint = [ "echo 1" ];
|
||||
ExposedPorts = { };
|
||||
};
|
||||
};
|
||||
templateData = builder.mkData { name = "template"; dir = "template"; vars = {
|
||||
_ARGUMENT = "template";
|
||||
};
|
||||
};
|
||||
in {
|
||||
sops = false;
|
||||
db = false;
|
||||
paths = [{
|
||||
path="${serverCfg.configPath}/example/";
|
||||
mode = "0444";
|
||||
}];
|
||||
|
||||
containers = {
|
||||
server = builder.mkContainer {
|
||||
subdomain = containerCfg.subdomain;
|
||||
imageStream = image;
|
||||
port = 8080;
|
||||
secret = name;
|
||||
extraEnv = { };
|
||||
overrides = {
|
||||
cmd = [ ];
|
||||
volumes = [ ];
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
setup = {
|
||||
trigger = "server";
|
||||
envFile = config.sops.secrets."EXAMPLE".path;
|
||||
script = pkgs.writeShellScript "setup" ''
|
||||
...
|
||||
'';
|
||||
};
|
||||
}
|
||||
@@ -2,3 +2,7 @@
|
||||
|
||||
RSS: TTRSS / FreshRSS
|
||||
Monitoring: Telegraf + InfluxDB
|
||||
https://github.com/tarampampam/error-pages ?
|
||||
kavita + mylar ? kapowarr ?
|
||||
|
||||
- Transmission Cfg and API/Token handling
|
||||
|
||||
@@ -4,12 +4,18 @@ let
|
||||
serverCfg = config.syscfg.server;
|
||||
authentikData = builder.mkData {
|
||||
name = "authentik"; dir = "authentik"; vars = {
|
||||
NEXTCLOUD_DOMAIN = "${serverCfg.containers.nextcloud.subdomain or "nextcloud"}.${serverCfg.hostDomain}";
|
||||
AUTHENTIK_DOMAIN = "${containerCfg.subdomain}.${serverCfg.hostDomain}";
|
||||
COOKIE_DOMAIN = "${serverCfg.hostDomain}";
|
||||
};
|
||||
AUTHENTIK_DOMAIN = "${containerCfg.subdomain}.${serverCfg.domain}";
|
||||
COOKIE_DOMAIN = "${serverCfg.domain}";
|
||||
AUTHENTIK_LDAP_DC_DOMAIN = "dc=ldap," + (lib.concatMapStringsSep "," (x: "dc=${x}") (lib.splitString "." serverCfg.domain));
|
||||
}
|
||||
// (if serverCfg.containers?jellyfin then { JELLYFIN_DOMAIN = "${serverCfg.containers.jellyfin.subdomain}.${serverCfg.domain}";} else {})
|
||||
// (if serverCfg.containers?gitea then { GITEA_DOMAIN = "${serverCfg.containers.gitea.subdomain}.${serverCfg.domain}";} else {})
|
||||
// (if serverCfg.containers?immich then { IMMICH_DOMAIN = "${serverCfg.containers.immich.subdomain}.${serverCfg.domain}";} else {})
|
||||
// (if serverCfg.containers?nextcloud then { NEXTCLOUD_DOMAIN = "${serverCfg.containers.nextcloud.subdomain}.${serverCfg.domain}";} else {});
|
||||
};
|
||||
in {
|
||||
sops = true;
|
||||
db = true;
|
||||
paths = [{
|
||||
path="${serverCfg.configPath}/authentik/media";
|
||||
owner = "1000:1000";
|
||||
@@ -25,26 +31,25 @@ in {
|
||||
subdomain = containerCfg.subdomain;
|
||||
image = "ghcr.io/goauthentik/server:${version}";
|
||||
port = 9000;
|
||||
ip = containerCfg.ip;
|
||||
secret = name;
|
||||
extraEnv = {
|
||||
"AUTHENTIK_REDIS__HOST" = builder.host;
|
||||
"AUTHENTIK_POSTGRESQL__HOST" = builder.host;
|
||||
"AUTHENTIK_POSTGRESQL__USER" = "authentik_user";
|
||||
"AUTHENTIK_POSTGRESQL__NAME" = "authentik_db";
|
||||
"AUTHENTIK_EMAIL__HOST" = serverCfg.mailDomain;
|
||||
"AUTHENTIK_EMAIL__PORT" = "587";
|
||||
"AUTHENTIK_EMAIL__USERNAME" = "noreply@${serverCfg.hostDomain}";
|
||||
"AUTHENTIK_EMAIL__USE_TLS" = "true";
|
||||
"AUTHENTIK_EMAIL__USE_SSL" = "false";
|
||||
"AUTHENTIK_EMAIL__TIMEOUT" = "10";
|
||||
"AUTHENTIK_EMAIL__FROM" = "sso@noreply.${serverCfg.hostDomain}";
|
||||
"AUTHENTIK_DISABLE_UPDATE_CHECK" = "true";
|
||||
"AUTHENTIK_POSTGRESQL__SSLMODE" = "disable";
|
||||
AUTHENTIK_REDIS__HOST = builder.host;
|
||||
AUTHENTIK_POSTGRESQL__HOST = builder.host;
|
||||
AUTHENTIK_POSTGRESQL__USER = "authentik_user";
|
||||
AUTHENTIK_POSTGRESQL__NAME = "authentik_db";
|
||||
AUTHENTIK_POSAUTHENTIK_POSTGRESQL__SSLMODE = "false";
|
||||
AUTHENTIK_EMAIL__HOST = serverCfg.mailDomain;
|
||||
AUTHENTIK_EMAIL__PORT = "587";
|
||||
AUTHENTIK_EMAIL__USERNAME = "noreply@${serverCfg.domain}";
|
||||
AUTHENTIK_EMAIL__USE_TLS = "true";
|
||||
AUTHENTIK_EMAIL__USE_SSL = "false";
|
||||
AUTHENTIK_EMAIL__TIMEOUT = "10";
|
||||
AUTHENTIK_EMAIL__FROM = "sso@noreply.${serverCfg.domain}";
|
||||
AUTHENTIK_DISABLE_UPDATE_CHECK = "true";
|
||||
AUTHENTIK_POSTGRESQL__SSLMODE = "disable";
|
||||
};
|
||||
overrides = {
|
||||
cmd = [ "server" ];
|
||||
ports = if containerCfg.port!=null then [ "${toString containerCfg.port}:9000" ] else [];
|
||||
volumes = [
|
||||
"${serverCfg.configPath}/authentik/media:/media"
|
||||
"${serverCfg.configPath}/authentik/templates:/templates"
|
||||
@@ -55,14 +60,15 @@ in {
|
||||
|
||||
worker = builder.mkContainer {
|
||||
image = "ghcr.io/goauthentik/server:${version}";
|
||||
secret = "authentik";
|
||||
secret = name;
|
||||
extraEnv = {
|
||||
"AUTHENTIK_REDIS__HOST" = builder.host;
|
||||
"AUTHENTIK_POSTGRESQL__HOST" = builder.host;
|
||||
"AUTHENTIK_POSTGRESQL__USER" = "authentik_user";
|
||||
"AUTHENTIK_POSTGRESQL__NAME" = "authentik_db";
|
||||
"AUTHENTIK_DISABLE_UPDATE_CHECK" = "true";
|
||||
"AUTHENTIK_POSTGRESQL__SSLMODE" = "disable";
|
||||
AUTHENTIK_REDIS__HOST = builder.host;
|
||||
AUTHENTIK_POSTGRESQL__HOST = builder.host;
|
||||
AUTHENTIK_POSTGRESQL__USER = "authentik_user";
|
||||
AUTHENTIK_POSTGRESQL__NAME = "authentik_db";
|
||||
AUTHENTIK_POSAUTHENTIK_POSTGRESQL__SSLMODE = "false";
|
||||
AUTHENTIK_DISABLE_UPDATE_CHECK = "true";
|
||||
AUTHENTIK_POSTGRESQL__SSLMODE = "disable";
|
||||
};
|
||||
overrides = {
|
||||
cmd = [ "worker" ];
|
||||
@@ -73,18 +79,31 @@ in {
|
||||
];
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
ldap = builder.mkContainer {
|
||||
image = "ghcr.io/goauthentik/ldap:${version}";
|
||||
secret = name;
|
||||
extraEnv = {
|
||||
AUTHENTIK_HOST = "https://${containerCfg.subdomain}.${serverCfg.domain}";
|
||||
AUTHENTIK_INSECURE = "false";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
setup = {
|
||||
trigger = "worker";
|
||||
script = pkgs.writeShellScript "setup" ''
|
||||
# Define the command wrapper
|
||||
AK="${pkgs.podman}/bin/podman --events-backend=none exec --env-file ${config.sops.secrets."CUSTOM".path} -e DOMAIN=${serverCfg.hostDomain} -u root authentik-worker ak"
|
||||
AK="${pkgs.podman}/bin/podman --events-backend=none exec --env-file ${config.sops.secrets."CUSTOM".path} -e DOMAIN=${serverCfg.domain} -u root authentik-worker ak"
|
||||
|
||||
$AK apply_blueprint /blueprints/custom/authentik.yaml
|
||||
$AK apply_blueprint /blueprints/custom/traefik.yaml
|
||||
$AK apply_blueprint /blueprints/custom/ldap.yaml
|
||||
|
||||
${lib.optionalString (serverCfg.containers ? gitea) ''$AK apply_blueprint /blueprints/custom/gitea.yaml''}
|
||||
${lib.optionalString (serverCfg.containers ? jellyfin) ''$AK apply_blueprint /blueprints/custom/jellyfin.yaml''}
|
||||
${lib.optionalString (serverCfg.containers ? nextcloud) ''$AK apply_blueprint /blueprints/custom/nextcloud.yaml''}
|
||||
${lib.optionalString (serverCfg.containers ? immich) ''$AK apply_blueprint /blueprints/custom/immich.yaml''}
|
||||
|
||||
echo "Completed Authentik Setup"
|
||||
'';
|
||||
|
||||
@@ -3,18 +3,18 @@ let
|
||||
version = "latest";
|
||||
serverCfg = config.syscfg.server;
|
||||
in {
|
||||
sops = true;
|
||||
containers = {
|
||||
server = builder.mkContainer {
|
||||
subdomain = containerCfg.subdomain;
|
||||
image = "collabora/code:${version}";
|
||||
port = 9980;
|
||||
ip = containerCfg.ip;
|
||||
secret = name;
|
||||
extraEnv = {
|
||||
"aliasgroup1" = "https://${serverCfg.containers.nextcloud.subdomain}.${serverCfg.hostDomain}";
|
||||
"server_name" = "${containerCfg.subdomain}.${serverCfg.hostDomain}";
|
||||
"aliasgroup1" = "https://${serverCfg.containers.nextcloud.subdomain}.${serverCfg.domain}";
|
||||
"server_name" = "${containerCfg.subdomain}.${serverCfg.domain}";
|
||||
"username" = "collabora_user";
|
||||
"VIRTUAL_HOST" = "${containerCfg.subdomain}.${serverCfg.hostDomain}";
|
||||
"VIRTUAL_HOST" = "${containerCfg.subdomain}.${serverCfg.domain}";
|
||||
"VIRTUAL_PORT" = "9980";
|
||||
"VIRTUAL_PROTO" = "http";
|
||||
"DONT_GEN_SSL_CERT" = "true";
|
||||
|
||||
@@ -13,6 +13,7 @@ let
|
||||
};
|
||||
};
|
||||
in {
|
||||
sops = true;
|
||||
paths = [{
|
||||
path="${serverCfg.dataPath}/ethercalc/";
|
||||
mode = "0666";
|
||||
@@ -23,7 +24,6 @@ in {
|
||||
subdomain = containerCfg.subdomain;
|
||||
imageStream = image;
|
||||
port = 8080;
|
||||
ip = containerCfg.ip;
|
||||
secret = name;
|
||||
extraEnv = {
|
||||
ETHERCALC_PORT = "8080";
|
||||
|
||||
@@ -76,6 +76,8 @@ let
|
||||
};
|
||||
};
|
||||
in {
|
||||
sops = true;
|
||||
db = true;
|
||||
paths = [{
|
||||
path="${serverCfg.configPath}/etherpad/";
|
||||
mode = "0444";
|
||||
@@ -86,7 +88,6 @@ in {
|
||||
subdomain = containerCfg.subdomain;
|
||||
imageStream = image;
|
||||
port = 8080;
|
||||
ip = containerCfg.ip;
|
||||
secret = name;
|
||||
extraEnv = {
|
||||
TITLE = "Pad";
|
||||
|
||||
@@ -1,3 +1,95 @@
|
||||
{...}:{
|
||||
|
||||
}
|
||||
{ config, containerCfg, pkgs, lib, builder, name, ... }:
|
||||
let
|
||||
serverCfg = config.syscfg.server;
|
||||
|
||||
# Ensure the package is available (Nixpkgs includes frigate)
|
||||
frigatePkg = pkgs.frigate;
|
||||
|
||||
image = pkgs.dockerTools.streamLayeredImage {
|
||||
name = "frigate";
|
||||
tag = frigatePkg.version;
|
||||
contents = [
|
||||
pkgs.bashInteractive
|
||||
frigatePkg
|
||||
pkgs.ffmpeg # Explicitly included for video stream processing
|
||||
];
|
||||
config = {
|
||||
Entrypoint = [ "${frigatePkg}/bin/frigate" ];
|
||||
Cmd = [ "start" ];
|
||||
ExposedPorts = {
|
||||
"5000/tcp" = {}; # Web UI / API
|
||||
"8554/tcp" = {}; # RTSP Feeds
|
||||
"8555/tcp" = {}; # WebRTC
|
||||
};
|
||||
Env = [
|
||||
"FRIGATE_RTSP_PASSWORD=secret" # Base fallback, overridden by envFile/sops
|
||||
];
|
||||
};
|
||||
};
|
||||
in {
|
||||
sops = true; # Enabled to safeguard sensitive camera RTSP stream credentials
|
||||
db = false; # Internal SQLite is used by default in Frigate
|
||||
|
||||
paths = [
|
||||
{
|
||||
path = "${serverCfg.configPath}/frigate/";
|
||||
mode = "0755";
|
||||
}
|
||||
{
|
||||
path = "/var/lib/frigate/storage/";
|
||||
mode = "0755"; # Dedicated path for heavy video recordings and media
|
||||
}
|
||||
];
|
||||
|
||||
containers = {
|
||||
server = builder.mkContainer {
|
||||
subdomain = containerCfg.subdomain;
|
||||
imageStream = image;
|
||||
port = 5000;
|
||||
secret = name;
|
||||
extraEnv = {
|
||||
PLUS_API_KEY = ""; # Optional: For Frigate Plus users
|
||||
};
|
||||
overrides = {
|
||||
cmd = [ ];
|
||||
volumes = [
|
||||
"${serverCfg.configPath}/frigate:/config"
|
||||
"/var/lib/frigate/storage:/media/frigate"
|
||||
"/dev/bus/usb:/dev/bus/usb" # Passes Google Coral USB TPU to the container
|
||||
"/dev/dri:/dev/dri" # Passes Intel/AMD GPU for hardware video decoding
|
||||
];
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
setup = {
|
||||
trigger = "server";
|
||||
envFile = config.sops.secrets."FRIGATE_ENV".path;
|
||||
script = pkgs.writeShellScript "setup-frigate" ''
|
||||
mkdir -p "${serverCfg.configPath}/frigate"
|
||||
mkdir -p "/var/lib/frigate/storage"
|
||||
|
||||
# Bootstrap a standard configuration layout if missing
|
||||
if [ ! -f "${serverCfg.configPath}/frigate/config.yml" ]; then
|
||||
cat <<EOF > "${serverCfg.configPath}/frigate/config.yml"
|
||||
mqtt:
|
||||
enabled: False # Set to True and define host if connecting to Home Assistant
|
||||
|
||||
database:
|
||||
path: /config/frigate.db
|
||||
|
||||
cameras:
|
||||
dummy_camera: # Replace with your actual RTSP stream details
|
||||
enabled: false
|
||||
ffmpeg:
|
||||
inputs:
|
||||
- path: rtsp://127.0.0.1:554/live
|
||||
roles:
|
||||
- detect
|
||||
detect:
|
||||
enabled: false
|
||||
EOF
|
||||
fi
|
||||
'';
|
||||
};
|
||||
}
|
||||
|
||||
@@ -2,8 +2,11 @@
|
||||
let
|
||||
version = "latest";
|
||||
serverCfg = config.syscfg.server;
|
||||
|
||||
LDAP_DC_DOMAIN = "dc=ldap," + (lib.concatMapStringsSep "," (x: "dc=${x}") (lib.splitString "." serverCfg.domain));
|
||||
in {
|
||||
|
||||
sops = true;
|
||||
db = true;
|
||||
paths = [{
|
||||
path="${serverCfg.dataPath}/gitea/data";
|
||||
owner = "1000:1000";
|
||||
@@ -18,7 +21,6 @@ in {
|
||||
subdomain = containerCfg.subdomain;
|
||||
image = "gitea/gitea:${version}";
|
||||
port = 8080;
|
||||
ip = containerCfg.ip;
|
||||
secret = name;
|
||||
|
||||
extraEnv = { # app.ini -> GITEA__<section>__<KEY> = "<VALUE>";
|
||||
@@ -47,8 +49,8 @@ in {
|
||||
GITEA__mailer__SMTP_PORT = "";
|
||||
GITEA__mailer__USER= "";
|
||||
|
||||
GITEA__server__DOMAIN = "${containerCfg.subdomain}.${serverCfg.hostDomain}";
|
||||
GITEA__server__ROOT_URL = "https://${containerCfg.subdomain}.${serverCfg.hostDomain}/";
|
||||
GITEA__server__DOMAIN = "${containerCfg.subdomain}.${serverCfg.domain}";
|
||||
GITEA__server__ROOT_URL = "https://${containerCfg.subdomain}.${serverCfg.domain}/";
|
||||
GITEA__server__PROTOCOL = "http";
|
||||
GITEA__server__HTTP_PORT = "8080";
|
||||
GITEA__server__LFS_START_SERVER = "true";
|
||||
@@ -62,7 +64,7 @@ in {
|
||||
GITEA__service__ENABLE_REVERSE_PROXY_EMAIL = "true";
|
||||
GITEA__service__ENABLE_REVERSE_PROXY_FULL_NAME = "true";
|
||||
GITEA__service__ALLOW_ONLY_EXTERNAL_REGISTRATION = "true";
|
||||
GITEA__security__REVERSE_PROXY_LOGOUT_REDIRECT = "https://${serverCfg.containers.authentik.subdomain}.${serverCfg.hostDomain}/outpost.goauthentik.io/sign_out";
|
||||
GITEA__security__REVERSE_PROXY_LOGOUT_REDIRECT = "https://${serverCfg.containers.authentik.subdomain}.${serverCfg.domain}/outpost.goauthentik.io/sign_out";
|
||||
GITEA__security__REVERSE_PROXY_AUTHENTICATION_USER = "X-authentik-username";
|
||||
GITEA__security__REVERSE_PROXY_AUTHENTICATION_EMAIL = "X-authentik-email";
|
||||
GITEA__security__REVERSE_PROXY_AUTHENTICATION_FULL_NAME = "X-authentik-name";
|
||||
@@ -70,8 +72,8 @@ in {
|
||||
GITEA__security__REVERSE_PROXY_TRUSTED_PROXIES = "127.0.0.0/8,::1/128,10.0.0.0/8";
|
||||
} else {});
|
||||
extraLabels = {
|
||||
"traefik.http.routers.${containerCfg.subdomain}-login.rule" = "Host(`${containerCfg.subdomain}.${serverCfg.hostDomain}`) && Path(`/user/login`) ";
|
||||
"traefik.http.routers.${containerCfg.subdomain}-login.middlewares" = if serverCfg.containers?authentik then "authentik" else "";
|
||||
"traefik.http.routers.${containerCfg.subdomain}-login.rule" = "Host(`${containerCfg.subdomain}.${serverCfg.domain}`) && Path(`/user/login`) ";
|
||||
"traefik.http.routers.${containerCfg.subdomain}-login.middlewares" = if (serverCfg.containers?authentik && false) then "authentik" else "";
|
||||
"traefik.http.routers.${containerCfg.subdomain}-login.priority" = "100";
|
||||
"traefik.http.routers.${containerCfg.subdomain}-login.entrypoints" = "web-secure";
|
||||
"traefik.http.routers.${containerCfg.subdomain}-login.tls" = "true";
|
||||
@@ -90,8 +92,8 @@ in {
|
||||
secret = name;
|
||||
extraEnv = {
|
||||
CONFIG_FILE="/data/config.yml";
|
||||
GITEA_INSTANCE_URL="https://${containerCfg.subdomain}.${serverCfg.hostDomain}";
|
||||
GITHUB_INSTANCE_URL="https://${containerCfg.subdomain}.${serverCfg.hostDomain}";
|
||||
GITEA_INSTANCE_URL="https://${containerCfg.subdomain}.${serverCfg.domain}";
|
||||
GITHUB_INSTANCE_URL="https://${containerCfg.subdomain}.${serverCfg.domain}";
|
||||
};
|
||||
|
||||
overrides = {
|
||||
@@ -115,15 +117,28 @@ in {
|
||||
|
||||
$GT admin user create --username "$DEFAULT_ADMIN_USERNAME" --password "$DEFAULT_ADMIN_PASSWORD" --email "$DEFAULT_ADMIN_EMAIL" --admin || true
|
||||
|
||||
touch ${serverCfg.dataPath}/gitea/data-runner/config.yml
|
||||
|
||||
RUNNER_TOKEN=$($GT actions generate-runner-token)
|
||||
$GTR register \
|
||||
--instance "https://${containerCfg.subdomain}.${serverCfg.hostDomain}" \
|
||||
--instance "https://${containerCfg.subdomain}.${serverCfg.domain}" \
|
||||
--token "$RUNNER_TOKEN" \
|
||||
--name "Runner" \
|
||||
--labels "ubuntu-latest:docker://catthehacker/ubuntu:act-latest" \
|
||||
--no-interactive
|
||||
|
||||
|
||||
${lib.optionalString (serverCfg.containers ? authentik) ''
|
||||
$GT admin auth add-ldap --name Authentik --host authentik-ldap --port 6636 --security-protocol ldaps --skip-tls-verify \
|
||||
--bind-dn "cn=ldap-service,ou=users,${LDAP_DC_DOMAIN}" --bind-password $DEFAULT_LDAP_PASSWORD \
|
||||
--user-search-base "ou=users,${LDAP_DC_DOMAIN}" \
|
||||
--user-filter "(&(objectClass=user)(|(uid=%[1]s)(mail=%[1]s)))" \
|
||||
--admin-filter "(memberOf=cn=admin,ou=groups,${LDAP_DC_DOMAIN})" \
|
||||
--username-attribute "username" --firstname-attribute "givenName" --surname-attribute "sn" --email-attribute "mail" \
|
||||
--synchronize-users
|
||||
''}
|
||||
|
||||
|
||||
echo "Completed Gitea Setup"
|
||||
'';
|
||||
};
|
||||
|
||||
@@ -1,3 +1,59 @@
|
||||
{...}:{
|
||||
{ config, containerCfg, pkgs, lib, builder, name,... }:
|
||||
let
|
||||
serverCfg = config.syscfg.server;
|
||||
version = "latest";
|
||||
|
||||
routerName = if containerCfg.subpath != null
|
||||
then "${containerCfg.subdomain}-${lib.strings.sanitizeDerivationName containerCfg.subpath}"
|
||||
else containerCfg.subdomain;
|
||||
in {
|
||||
|
||||
paths = [{
|
||||
path = "${serverCfg.configPath}/handbrake/config";
|
||||
mode = "0755";
|
||||
} {
|
||||
path = "${serverCfg.dataPath}/handbrake/";
|
||||
mode = "0755";
|
||||
}];
|
||||
|
||||
containers = {
|
||||
server = builder.mkContainer {
|
||||
subdomain = containerCfg.subdomain;
|
||||
subpath = containerCfg.subpath;
|
||||
image = "ghcr.io/jlesage/handbrake:${version}";
|
||||
port = 5800;
|
||||
|
||||
extraEnv = {
|
||||
USER_ID = "1000";
|
||||
GROUP_ID = "1000";
|
||||
AUTOMATED_CONVERSION_PRESET = "Custom/AV1 MKV 1080p30";
|
||||
AUTOMATED_CONVERSION_FORMAT = "mkv";
|
||||
AUTOMATED_CONVERSION_OUTPUT_SUBDIR = "SAME_AS_SRC";
|
||||
};
|
||||
extraLabels = { } // (if serverCfg.containers ? authentik then {
|
||||
"traefik.http.routers.${routerName}.middlewares" = "authentik";
|
||||
} else {});
|
||||
extraOptions = [
|
||||
"--tmpfs=/tmp:rw,noexec,nosuid,size=512m"
|
||||
];
|
||||
|
||||
overrides = {
|
||||
volumes = [
|
||||
"${serverCfg.configPath}/handbrake/config:/config:rw"
|
||||
"${serverCfg.dataPath}/handbrake/watch:/watch:rw"
|
||||
"${serverCfg.dataPath}/handbrake/output:/output:rw"
|
||||
];
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
setup = {
|
||||
trigger = "server";
|
||||
script = pkgs.writeShellScript "setup" ''
|
||||
mkdir -p ${serverCfg.dataPath}/handbrake/{watch,output}
|
||||
|
||||
'';
|
||||
};
|
||||
|
||||
}
|
||||
@@ -1,3 +1,101 @@
|
||||
{...}:{
|
||||
|
||||
{ config, containerCfg, pkgs, lib, builder, name, ... }:
|
||||
let
|
||||
version = "latest";
|
||||
serverCfg = config.syscfg.server;
|
||||
|
||||
in {
|
||||
vm = {
|
||||
portForward = [ 8123 ];
|
||||
cfg = {cfg,...}:{
|
||||
services.home-assistant = {
|
||||
enable = true;
|
||||
openFirewall = true;
|
||||
|
||||
extraComponents = [
|
||||
"matter" "thread" "cast" "zha"
|
||||
"default_config" "met" "esphome" "radio_browser"
|
||||
"telegram_bot" "swiss_public_transport" "nextcloud" "jellyfin"
|
||||
] ++ (if containerCfg.extra ? components then containerCfg.extra.components else []);
|
||||
|
||||
|
||||
extraPackages = pp: with pp; [
|
||||
python-telegram gtts
|
||||
];
|
||||
lovelaceConfig = {};
|
||||
|
||||
config = {
|
||||
homeassistant = {
|
||||
name = "Home";
|
||||
latitude = "${if containerCfg.extra ? latitude then toString containerCfg.extra.latitude else toString 0}";
|
||||
longitude = "${if containerCfg.extra ? longitude then toString containerCfg.extra.longitude else toString 0}";
|
||||
elevation = "${if containerCfg.extra ? elevation then toString containerCfg.extra.elevation else toString 0}";
|
||||
unit_system = "metric";
|
||||
time_zone = config.time.timeZone;
|
||||
};
|
||||
lovelace = { mode = "yaml"; };
|
||||
customLovelaceModules = [];
|
||||
|
||||
# default_config = {};
|
||||
http = {
|
||||
use_x_forwarded_for = true;
|
||||
trusted_proxies = [ "10.0.0.0/8" "127.0.0.1" ];
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
containers = {
|
||||
dummy = builder.mkContainer {
|
||||
subdomain = containerCfg.subdomain;
|
||||
image = "alpine:latest";
|
||||
extraLabels = {
|
||||
"traefik.http.services.${containerCfg.subdomain}.loadbalancer.server.url" = "http://${builder.hostIp}:8123";
|
||||
};
|
||||
overrides = {cmd = [ "sleep" "infinity" ];};
|
||||
};
|
||||
};
|
||||
|
||||
setup = {
|
||||
trigger = "dummy";
|
||||
envFile = config.sops.secrets."CUSTOM".path;
|
||||
script = pkgs.writeShellScript "setup" ''
|
||||
|
||||
HASS_URL="https://${containerCfg.subdomain}.${serverCfg.domain}"
|
||||
until [[ "$(${pkgs.curl}/bin/curl -s -o /dev/null -w "%{http_code}" "$HASS_URL/manifest.json")" =~ (200|301|302) ]]; do
|
||||
sleep 5
|
||||
done
|
||||
sleep 5
|
||||
|
||||
ONBOARDING_STATUS=$(${pkgs.curl}/bin/curl -s -o /dev/null -w "%{http_code}" "$HASS_URL/api/onboarding" 2>/dev/null || echo "000")
|
||||
|
||||
if [ "$ONBOARDING_STATUS" = "200" ]; then
|
||||
AUTH_CODE=$( ${pkgs.curl}/bin/curl -s -X POST "$HASS_URL/api/onboarding/users" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"client_id":"'"$HASS_URL"'","name":"'"$DEFAULT_ADMIN_USERNAME"'","username":"'"$DEFAULT_ADMIN_USERNAME"'","password":"'"$DEFAULT_ADMIN_PASSWORD"'","language":"en"}' \
|
||||
| ${pkgs.jq}/bin/jq -r '.auth_code' )
|
||||
|
||||
ACCESS_TOKEN=$(${pkgs.curl}/bin/curl -s -X POST "$HASS_URL/auth/token" \
|
||||
-H "Content-Type: application/x-www-form-urlencoded" \
|
||||
-d "grant_type=authorization_code&code=$AUTH_CODE&client_id=$HASS_URL" \
|
||||
| ${pkgs.jq}/bin/jq -r '.access_token' )
|
||||
|
||||
${pkgs.curl} -s -X POST "$HASS_URL/api/onboarding/core_config" \
|
||||
-H "Authorization: Bearer $ACCESS_TOKEN" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"time_zone":"${config.time.timeZone}"}' > /dev/null 2>&1 || true
|
||||
# We can configure many more things above !
|
||||
|
||||
${pkgs.curl} -s -X POST "$HASS_URL/api/onboarding/analytics" \
|
||||
-H "Authorization: Bearer $ACCESS_TOKEN" \
|
||||
-H "Content-Type: application/json" -d '{}' > /dev/null 2>&1 || true
|
||||
|
||||
${pkgs.curl} -s -X POST "$HA_URL/api/onboarding/integration" \
|
||||
-H "Authorization: Bearer $ACCESS_TOKEN" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"client_id":"'"$HASS_URL"'","redirect_uri":"'"$HASS_URL"'/?auth_callback=1"}' > /dev/null 2>&1 || true
|
||||
fi
|
||||
'';
|
||||
};
|
||||
|
||||
}
|
||||
97
modules/server/containers/apps/immich.nix
Normal file
97
modules/server/containers/apps/immich.nix
Normal file
@@ -0,0 +1,97 @@
|
||||
{ config, containerCfg, pkgs, lib, builder, name,... }:
|
||||
let
|
||||
version = "v2";
|
||||
serverCfg = config.syscfg.server;
|
||||
|
||||
in {
|
||||
sops = true;
|
||||
db = true;
|
||||
|
||||
paths = [{
|
||||
path = "${serverCfg.configPath}/immich/cache";
|
||||
mode = "0750";
|
||||
}{
|
||||
path = "${serverCfg.dataPath}/immich/";
|
||||
mode = "0755";
|
||||
}];
|
||||
|
||||
containers = {
|
||||
server = builder.mkContainer {
|
||||
subdomain = containerCfg.subdomain;
|
||||
image = "ghcr.io/immich-app/immich-server:${version}";
|
||||
port = 2283;
|
||||
secret = name;
|
||||
extraEnv = {
|
||||
DB_HOSTNAME = builder.host;
|
||||
REDIS_HOSTNAME = builder.host;
|
||||
DB_USERNAME = "immich_user";
|
||||
DB_DATABASE_NAME = "immich_db";
|
||||
IMMICH_TRUSTED_PROXIES = "10.0.0.0/8";
|
||||
IMMICH_MACHINE_LEARNING_URL = "http://immich-ml:3003";
|
||||
# IMMICH_ALLOW_SETUP = "false";
|
||||
# IMMICH_IGNORE_MOUNT_CHECK_ERRORS = "true";
|
||||
};
|
||||
overrides = {
|
||||
volumes = [
|
||||
"${serverCfg.dataPath}/immich:/data"
|
||||
];
|
||||
};
|
||||
};
|
||||
|
||||
ml = builder.mkContainer {
|
||||
image = "ghcr.io/immich-app/immich-machine-learning:${version}";
|
||||
port = 3003;
|
||||
overrides = {
|
||||
volumes = [
|
||||
"${serverCfg.configPath}/immich/cache:/cache"
|
||||
];
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
setup = {
|
||||
trigger = "server";
|
||||
envFile = config.sops.secrets."CUSTOM".path;
|
||||
script = pkgs.writeShellScript "setup" ''
|
||||
PSQL="${pkgs.postgresql}/bin/psql -U postgres"
|
||||
$PSQL -d "immich_db" -tAc "CREATE EXTENSION IF NOT EXISTS vchord CASCADE;"
|
||||
$PSQL -d "immich_db" -tAc "CREATE EXTENSION IF NOT EXISTS earthdistance CASCADE;"
|
||||
|
||||
mkdir -p ${serverCfg.dataPath}/immich/{upload,library,thumbs,encoded-video,profile,backups}
|
||||
|
||||
IMMICH_URL="https://${containerCfg.subdomain}.${serverCfg.domain}"
|
||||
until [[ "$(${pkgs.curl}/bin/curl -s -o /dev/null -w "%{http_code}" "$IMMICH_URL")" =~ (200|301|302) ]]; do
|
||||
sleep 5
|
||||
done
|
||||
${pkgs.curl}/bin/curl -X POST "$IMMICH_URL/api/auth/admin-sign-up" \
|
||||
-H "Content-Type: application/json" -H "Accept: application/json" \
|
||||
-d '{ "email": "'"$DEFAULT_ADMIN_EMAIL"'", "password": "'"$DEFAULT_ADMIN_PASSWORD"'", "name": "'"$DEFAULT_ADMIN_USERNAME"'" }'
|
||||
|
||||
IMMICH_TOKEN=$(${pkgs.curl}/bin/curl -sSf -X POST "$IMMICH_URL/api/auth/login" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{ "email": "'"$DEFAULT_ADMIN_EMAIL"'", "password": "'"$DEFAULT_ADMIN_PASSWORD"'"}' \
|
||||
| ${pkgs.jq}/bin/jq -r '.accessToken')
|
||||
|
||||
${lib.optionalString (serverCfg.containers ? authentik) ''
|
||||
${pkgs.curl}/bin/curl -s -X GET "$IMMICH_URL/api/system-config" -H "Cookie: immich_access_token=$IMMICH_TOKEN; immich_auth_type=password; immich_is_authenticated=true" | \
|
||||
${pkgs.jq}/bin/jq '.oauth.enabled = true |
|
||||
.oauth.autoRegister = true |
|
||||
.oauth.autoLaunch = true |
|
||||
.oauth.signingAlgorithm = "RS256" |
|
||||
.oauth.profileSigningAlgorithm = "RS256" |
|
||||
.oauth.clientId = "immich" |
|
||||
.oauth.clientSecret = "'"$IMMICH_OAUTH_SECRET"'" |
|
||||
.oauth.issuerUrl = "https://${serverCfg.containers.authentik.subdomain}.${serverCfg.domain}/application/o/immich/" |
|
||||
.oauth.scope = "openid profile email" |
|
||||
.oauth.buttonText = "Login with SSO"' | \
|
||||
${pkgs.curl}/bin/curl -s -X PUT "$IMMICH_URL/api/system-config" -H "Cookie: immich_access_token=$IMMICH_TOKEN; immich_auth_type=password; immich_is_authenticated=true" -H "Content-Type: application/json" -d @-
|
||||
''}
|
||||
|
||||
${pkgs.curl}/bin/curl -s -X GET "$IMMICH_URL/api/system-config" -H "Cookie: immich_access_token=$IMMICH_TOKEN; immich_auth_type=password; immich_is_authenticated=true" | \
|
||||
${pkgs.jq}/bin/jq '.storageTemplate.enable = true |
|
||||
.storageTemplate.template = "{{y}}/{{#if album}}{{album}}{{else}}{{MM}}{{/if}}/{{filename}}"' | \
|
||||
${pkgs.curl}/bin/curl -s -X PUT "$IMMICH_URL/api/system-config" -H "Cookie: immich_access_token=$IMMICH_TOKEN; immich_auth_type=password; immich_is_authenticated=true" -H "Content-Type: application/json" -d @-
|
||||
|
||||
'';
|
||||
};
|
||||
}
|
||||
58
modules/server/containers/apps/influx.nix
Normal file
58
modules/server/containers/apps/influx.nix
Normal file
@@ -0,0 +1,58 @@
|
||||
{ config, containerCfg, pkgs, lib, builder, name, ... }:
|
||||
let
|
||||
serverCfg = config.syscfg.server;
|
||||
version = "latest";
|
||||
in {
|
||||
sops = true;
|
||||
db = true;
|
||||
|
||||
paths = [{
|
||||
path = "${serverCfg.configPath}/influxdb/";
|
||||
mode = "0700";
|
||||
}{
|
||||
path = "${serverCfg.dataPath}/influxdb/";
|
||||
owner = "1500:1500";
|
||||
mode = "0700";
|
||||
}];
|
||||
|
||||
containers = {
|
||||
server = builder.mkContainer {
|
||||
subdomain = containerCfg.subdomain;
|
||||
image = "influxdata/influxdb3-ui:${version}";
|
||||
port = 8080;
|
||||
secret = name;
|
||||
extraEnv = {
|
||||
SESSION_SECRET_KEY = "7b0024c13ae770000f797c201e2f210b9932a689c04d34de04379faa44e88e97";
|
||||
DATABASE_URL = "/db/sqlite.db";
|
||||
};
|
||||
extraOptions = [
|
||||
"--tmpfs=/tmp:rw,noexec,nosuid,size=512m"
|
||||
];
|
||||
overrides = {
|
||||
cmd = [ "--mode=admin" ];
|
||||
volumes = [
|
||||
"${serverCfg.dataPath}/influxdb:/db:rw"
|
||||
"${serverCfg.configPath}/influxdb/:/app-root/config:ro"
|
||||
];
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
setup = {
|
||||
trigger = "server";
|
||||
script = pkgs.writeShellScript "setup" ''
|
||||
|
||||
cat > ${serverCfg.configPath}/influxdb/config.json << 'EOF'
|
||||
{
|
||||
"DEFAULT_INFLUX_SERVER": "http://${builder.host}:8181",
|
||||
"DEFAULT_INFLUX_DATABASE": "main",
|
||||
"DEFAULT_API_TOKEN": "b27686e85a883437666f61586e084f7deb763958497739479ca48bc913ee90afd1a920332156133c89fb8674cb197ced17706074e6a42fc7ce6b2d54ac6119c9",
|
||||
"DEFAULT_SERVER_NAME": "${serverCfg.domain}"
|
||||
}
|
||||
EOF
|
||||
|
||||
chown 1500:1500 ${serverCfg.configPath}/influxdb/config.json
|
||||
'';
|
||||
};
|
||||
|
||||
}
|
||||
@@ -1,3 +1,78 @@
|
||||
{...}:{
|
||||
|
||||
{ config, containerCfg, pkgs, lib, builder, name, ... }:
|
||||
let
|
||||
serverCfg = config.syscfg.server;
|
||||
|
||||
patchedInvidious = pkgs.invidious.overrideAttrs (oldAttrs: {
|
||||
postPatch = (oldAttrs.postPatch or "") + ''
|
||||
cp ${../data/invidious/login.cr} src/invidious/routes/login.cr
|
||||
'';
|
||||
});
|
||||
|
||||
image = pkgs.dockerTools.streamLayeredImage {
|
||||
name = pkgs.invidious.name;
|
||||
tag = pkgs.invidious.version;
|
||||
|
||||
contents = [ pkgs.cacert patchedInvidious ];
|
||||
config = {
|
||||
Entrypoint = [ "${patchedInvidious}/bin/invidious" ];
|
||||
ExposedPorts = { "3000/tcp" = {}; };
|
||||
Env = [
|
||||
"SSL_CERT_FILE=${pkgs.cacert}/etc/ssl/certs/ca-bundle.crt"
|
||||
"NIX_SSL_CERT_FILE=${pkgs.cacert}/etc/ssl/certs/ca-bundle.crt"
|
||||
];
|
||||
};
|
||||
};
|
||||
|
||||
in {
|
||||
sops = true;
|
||||
db = true;
|
||||
paths = [{
|
||||
path="${serverCfg.configPath}/invidious";
|
||||
mode = "0755";
|
||||
}];
|
||||
|
||||
containers = {
|
||||
server = builder.mkContainer {
|
||||
subdomain = containerCfg.subdomain;
|
||||
imageStream = image;
|
||||
port = 3000;
|
||||
secret = name;
|
||||
extraLabels = {
|
||||
"traefik.http.routers.${containerCfg.subdomain}-login.rule" = "Host(`${containerCfg.subdomain}.${serverCfg.domain}`) && Path(`/login`) ";
|
||||
"traefik.http.routers.${containerCfg.subdomain}-login.middlewares" = if serverCfg.containers?authentik then "authentik" else "";
|
||||
"traefik.http.routers.${containerCfg.subdomain}-login.priority" = "100";
|
||||
"traefik.http.routers.${containerCfg.subdomain}-login.entrypoints" = "web-secure";
|
||||
"traefik.http.routers.${containerCfg.subdomain}-login.tls" = "true";
|
||||
};
|
||||
extraEnv = {
|
||||
INVIDIOUS_CONFIG_FILE = "/data/config.yml";
|
||||
};
|
||||
overrides = {
|
||||
volumes = [
|
||||
"${serverCfg.configPath}/invidious:/data:ro"
|
||||
];
|
||||
};
|
||||
};
|
||||
|
||||
companion = builder.mkContainer {
|
||||
image = "quay.io/invidious/invidious-companion:latest";
|
||||
port = 8282;
|
||||
secret = name; #SERVER_SECRET_KEY = INVIDIOUS_COMPANION_KEY
|
||||
extraOptions = [
|
||||
"--cap-drop=all"
|
||||
"--security-opt=no-new-privileges"
|
||||
];
|
||||
};
|
||||
};
|
||||
|
||||
setup = {
|
||||
trigger = "server";
|
||||
envFile = [ config.sops.secrets."INVIDIOUS".path config.sops.secrets."CUSTOM".path ];
|
||||
script = pkgs.writeShellScript "setup" ''
|
||||
export DB_HOST=${builder.host}
|
||||
export INVIDIOUS_DOMAIN=${containerCfg.subdomain}.${serverCfg.domain}
|
||||
|
||||
${pkgs.gettext}/bin/envsubst < "${../data/invidious/config.yml}" > "${serverCfg.configPath}/invidious/config.yml"
|
||||
'';
|
||||
};
|
||||
}
|
||||
@@ -1,3 +1,177 @@
|
||||
{...}:{
|
||||
|
||||
{ config, containerCfg, pkgs, lib, builder, name, ... }:
|
||||
let
|
||||
serverCfg = config.syscfg.server;
|
||||
LDAP_DC_DOMAIN = "dc=ldap," + (lib.concatMapStringsSep "," (x: "dc=${x}") (lib.splitString "." serverCfg.domain));
|
||||
nss = pkgs.dockerTools.fakeNss.override {
|
||||
extraPasswdLines = [
|
||||
"jellyfin:x:1000:1000:Jellyfin Daemon:/config/data:/bin/false"
|
||||
];
|
||||
extraGroupLines = [
|
||||
"jellyfin:x:1000:"
|
||||
];
|
||||
};
|
||||
image = pkgs.dockerTools.streamLayeredImage { # pkgs.dockerTools.buildImage{#
|
||||
name = pkgs.jellyfin.name;
|
||||
tag = pkgs.jellyfin.version;
|
||||
contents = [ pkgs.cacert nss pkgs.jellyfin pkgs.bashInteractive ];
|
||||
config = {
|
||||
User = "jellyfin:jellyfin";
|
||||
Entrypoint = [ "${pkgs.jellyfin}/bin/jellyfin" ];
|
||||
ExposedPorts = { "8096/tcp" = { }; };
|
||||
Env = [
|
||||
"SSL_CERT_FILE=${pkgs.cacert}/etc/ssl/certs/ca-bundle.crt"
|
||||
"NIX_SSL_CERT_FILE=${pkgs.cacert}/etc/ssl/certs/ca-bundle.crt"
|
||||
];
|
||||
};
|
||||
};
|
||||
in {
|
||||
paths = [
|
||||
{
|
||||
path = "${serverCfg.dataPath}/media/";
|
||||
owner = "1000:1000";
|
||||
mode = "0755";
|
||||
}
|
||||
{
|
||||
path = "${serverCfg.configPath}/jellyfin/";
|
||||
owner = "1000:1000";
|
||||
mode = "0755";
|
||||
}
|
||||
];
|
||||
|
||||
containers = {
|
||||
server = builder.mkContainer {
|
||||
subdomain = containerCfg.subdomain;
|
||||
imageStream = image;
|
||||
port = 8096;
|
||||
extraEnv = {
|
||||
HOME = "/config/data";
|
||||
DOTNET_SYSTEM_GLOBALIZATION_INVARIANT = "1";
|
||||
JELLYFIN_HttpListenerHost__BindAddress= "0.0.0.0"; #we can use settings.xml override
|
||||
JELLYFIN_ServerName = if containerCfg.extra?name then containerCfg.extra.name else "Flix";
|
||||
};
|
||||
extraOptions = [
|
||||
"--tmpfs=/tmp:rw,noexec,nosuid,size=512m"
|
||||
];
|
||||
overrides = {
|
||||
cmd = [
|
||||
"--datadir" "/config/data"
|
||||
"--cachedir" "/config/cache"
|
||||
"--configdir" "/config/config"
|
||||
"--logdir" "/config/log"
|
||||
];
|
||||
volumes = [
|
||||
"${serverCfg.dataPath}/media:/media:ro"
|
||||
"${serverCfg.configPath}/jellyfin:/config"
|
||||
];
|
||||
# If you have an Intel/AMD GPU for transcoding, add the device:
|
||||
devices = lib.optionals (builtins.pathExists "/dev/dri") [ "/dev/dri:/dev/dri" ];
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
setup = {
|
||||
trigger = "server";
|
||||
envFile = config.sops.secrets."CUSTOM".path;
|
||||
script = pkgs.writeShellScript "setup" ''
|
||||
JELLYFIN_URL="https://${containerCfg.subdomain}.${serverCfg.domain}"
|
||||
until [ "$(${pkgs.curl}/bin/curl -sf "$JELLYFIN_URL/health")" = "Healthy" ]; do
|
||||
sleep 5
|
||||
done
|
||||
echo "Jellyfin is up. Sleeping for 20 seconds..."
|
||||
sleep 20
|
||||
WIZARD_COMPLETE=$(${pkgs.curl}/bin/curl -sSf "$JELLYFIN_URL/System/Info/Public" 2>/dev/null | \
|
||||
${pkgs.jq}/bin/jq -r '.StartupWizardCompleted // false')
|
||||
if [ "$WIZARD_COMPLETE" = "false" ]; then
|
||||
if ! ${pkgs.curl}/bin/curl -sSf -X POST "$JELLYFIN_URL/Startup/Configuration" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"ServerName":"Flix","UICulture":"en-US","MetadataCountryCode":"US","PreferredMetadataLanguage":"en"}'; then
|
||||
echo "ERROR: Failed to set startup configuration."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if ! ${pkgs.curl}/bin/curl -sSf -X GET "$JELLYFIN_URL/Startup/User"; then
|
||||
echo "ERROR: Failed to get base user."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if ! ${pkgs.curl}/bin/curl -sSf -X POST "$JELLYFIN_URL/Startup/User" \
|
||||
-H 'accept: */*' -H "Content-Type: application/json" \
|
||||
-d '{"Name": "'"$DEFAULT_ADMIN_USERNAME"'", "Password": "'"$DEFAULT_ADMIN_PASSWORD"'"}'; then
|
||||
echo "ERROR: Failed to set admin user."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if ! ${pkgs.curl}/bin/curl -sSf -X POST "$JELLYFIN_URL/Startup/RemoteAccess" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"EnableRemoteAccess":true,"EnableAutomaticPortMapping":false}'; then
|
||||
echo "ERROR: Failed to configure remote access."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if ! ${pkgs.curl}/bin/curl -sSf -X POST "''$JELLYFIN_URL/Startup/Complete"; then
|
||||
echo "ERROR: Failed to complete wizard."
|
||||
exit 1
|
||||
fi
|
||||
echo "Jellyfin initialization successfully completed!"
|
||||
fi
|
||||
|
||||
${lib.optionalString (serverCfg.containers ? authentik) ''
|
||||
JELLYFIN_TOKEN=$(${pkgs.curl}/bin/curl -sSf -X POST "$JELLYFIN_URL/Users/AuthenticateByName" \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "Authorization: MediaBrowser Client=\"Bash Script\", Device=\"Server Terminal\", DeviceId=\"script-12345\", Version=\"1.0.0\"" \
|
||||
-d "{\"Username\": \"$DEFAULT_ADMIN_USERNAME\", \"Pw\": \"$DEFAULT_ADMIN_PASSWORD\"}" \
|
||||
| ${pkgs.jq}/bin/jq -r '.AccessToken')
|
||||
|
||||
# Verify we got a token
|
||||
if [ "$JELLYFIN_TOKEN" = "null" ] || [ -z "$JELLYFIN_TOKEN" ]; then
|
||||
echo "ERROR: Authentication failed."
|
||||
exit 1
|
||||
fi
|
||||
if ${pkgs.curl}/bin/curl -sSf -H "Authorization: MediaBrowser Token=\"$JELLYFIN_TOKEN\"" \
|
||||
"$JELLYFIN_URL/Plugins" | ${pkgs.gnugrep}/bin/grep -q "958aad6637844d2ab89aa7b6fab6e25c"; then
|
||||
echo "LDAP Plugin is already installed. Skipping setup."
|
||||
else
|
||||
if ! ${pkgs.curl}/bin/curl -sSf -X POST "$JELLYFIN_URL/Packages/Installed/LDAP%20Authentication?assemblyGuid=958aad6637844d2ab89aa7b6fab6e25c" \
|
||||
-H "Authorization: MediaBrowser Token=\"$JELLYFIN_TOKEN\"" \
|
||||
-H "Content-Length: 0"; then
|
||||
echo "ERROR: LDAP Plugin Setup Failed."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if ! ${pkgs.curl}/bin/curl -sSf -X POST "$JELLYFIN_URL/System/Restart" \
|
||||
-H "Authorization: MediaBrowser Token=\"$JELLYFIN_TOKEN\"" \
|
||||
-H "Content-Length: 0"; then
|
||||
echo "ERROR: Server failed to accept restart command."
|
||||
exit 1
|
||||
fi
|
||||
sleep 1-
|
||||
until [ "$(${pkgs.curl}/bin/curl -sf "$JELLYFIN_URL/health")" = "Healthy" ]; do
|
||||
sleep 5
|
||||
done
|
||||
echo "Jellyfin is up. Sleeping for 20 seconds..."
|
||||
sleep 20
|
||||
fi
|
||||
|
||||
if ! ${pkgs.curl}/bin/curl -sSf -X POST "$JELLYFIN_URL/Plugins/958aad66-3784-4d2a-b89a-a7b6fab6e25c/Configuration" \
|
||||
-H "Authorization: MediaBrowser Token=\"$JELLYFIN_TOKEN\"" \
|
||||
-H "Content-Type: application/json" -H 'accept: */*' \
|
||||
-d '{"LdapUsers":[],"LdapServer":"authentik-ldap","LdapPort":6636,"UseSsl":true,"UseStartTls":false,"SkipSslVerify":true,
|
||||
"LdapBindUser":"cn=ldap-service,ou=users,${LDAP_DC_DOMAIN}","LdapBindPassword": "'"$DEFAULT_LDAP_PASSWORD"'",
|
||||
"LdapBaseDn":"${LDAP_DC_DOMAIN}","LdapSearchFilter":"(memberOf=cn=flix,ou=groups,${LDAP_DC_DOMAIN})",
|
||||
"LdapSearchAttributes":"uid, cn, mail, displayName",
|
||||
"LdapAdminBaseDn":"","LdapAdminFilter":"(memberOf=cn=admin,ou=groups,${LDAP_DC_DOMAIN})",
|
||||
"EnableLdapAdminFilterMemberUid":false,"LdapUidAttribute":"uid","LdapUsernameAttribute":"cn","LdapPasswordAttribute":"userPassword",
|
||||
"EnableLdapProfileImageSync":false,"RemoveImagesNotInLdap":false,"LdapProfileImageAttribute":"jpegphoto","LdapProfileImageFormat":"Default",
|
||||
"LdapClientCertPath":"","LdapClientKeyPath":"","LdapRootCaPath":"","CreateUsersFromLdap":true,"AllowPassChange":false,
|
||||
"EnableAllFolders":true,"EnabledFolders":[],"PasswordResetUrl":""}'; then
|
||||
echo "ERROR: LDAP Plugin Setup Failed."
|
||||
exit 1
|
||||
fi
|
||||
''}
|
||||
|
||||
echo "Completed Setup"
|
||||
|
||||
'';
|
||||
};
|
||||
|
||||
}
|
||||
0
modules/server/containers/apps/miniflux.nix
Normal file
0
modules/server/containers/apps/miniflux.nix
Normal file
@@ -3,6 +3,8 @@ let
|
||||
version = "31";
|
||||
serverCfg = config.syscfg.server;
|
||||
in {
|
||||
sops = true;
|
||||
db = true;
|
||||
paths = [{
|
||||
path="${serverCfg.dataPath}/nextcloud/www";
|
||||
owner = "33:33";
|
||||
@@ -19,7 +21,6 @@ in {
|
||||
subdomain = containerCfg.subdomain;
|
||||
image = "nextcloud:${version}";
|
||||
port = 80;
|
||||
ip = containerCfg.ip;
|
||||
secret = name;
|
||||
extraEnv = {
|
||||
REDIS_HOST = builder.host;
|
||||
@@ -27,11 +28,11 @@ in {
|
||||
POSTGRES_USER = "nextcloud_user";
|
||||
POSTGRES_DB = "nextcloud_db";
|
||||
AUTHENTIK_POSTGRESQL__SSLMODE = "disable";
|
||||
"NEXTCLOUD_TRUSTED_DOMAINS " = "${containerCfg.subdomain}.${serverCfg.hostDomain}";
|
||||
"NEXTCLOUD_TRUSTED_DOMAINS " = "${containerCfg.subdomain}.${serverCfg.domain}";
|
||||
"SMTP_HOST" = serverCfg.mailServer;
|
||||
"SMTP_NAME" = "mail_user";
|
||||
"SMTP_PASSWORD" = "mail_password";
|
||||
"MAIL_FROM_ADDRESS" = "${containerCfg.subdomain}@${serverCfg.hostDomain}";
|
||||
"MAIL_FROM_ADDRESS" = "${containerCfg.subdomain}@${serverCfg.domain}";
|
||||
"MAIL_DOMAIN" = serverCfg.mailDomain;
|
||||
"TRUSTED_PROXIES" = "10.10.0.0/16 192.168.0.0/16";
|
||||
};
|
||||
@@ -60,7 +61,7 @@ in {
|
||||
trigger = "server";
|
||||
script = pkgs.writeShellScript "setup" ''
|
||||
# Define the command wrapper
|
||||
OCC="${pkgs.podman}/bin/podman --events-backend=none exec --env-file ${config.sops.secrets."CUSTOM".path} -e DOMAIN=${serverCfg.hostDomain} -u www-data nextcloud-server php occ"
|
||||
OCC="${pkgs.podman}/bin/podman --events-backend=none exec --env-file ${config.sops.secrets."CUSTOM".path} -e DOMAIN=${serverCfg.domain} -u www-data nextcloud-server php occ"
|
||||
|
||||
echo "Waiting for Nextcloud container to start..."
|
||||
until $OCC status > /dev/null 2>&1; do
|
||||
@@ -101,7 +102,6 @@ in {
|
||||
$OCC app:disable updatenotification || true
|
||||
$OCC app:disable user_status || true
|
||||
|
||||
$OCC app:install calendar || true
|
||||
$OCC app:install calendar || true
|
||||
$OCC app:install contacts || true
|
||||
$OCC app:install camerarawpreviews || true
|
||||
@@ -125,23 +125,23 @@ in {
|
||||
|
||||
${lib.optionalString (serverCfg.containers ? ethercalc) ''
|
||||
$OCC config:app:set ownpad ownpad_ethercalc_enable --value="yes"
|
||||
$OCC config:app:set ownpad ownpad_ethercalc_host --value="https://${serverCfg.containers.ethercalc.subdomain}.${serverCfg.hostDomain}"
|
||||
$OCC config:app:set ownpad ownpad_ethercalc_host --value="https://${serverCfg.containers.ethercalc.subdomain}.${serverCfg.domain}"
|
||||
''}
|
||||
${lib.optionalString (serverCfg.containers ? etherpad) ''
|
||||
$OCC config:app:set ownpad ownpad_etherpad_enable --value="yes"
|
||||
$OCC config:app:set ownpad ownpad_etherpad_host --value="https://${serverCfg.containers.etherpad.subdomain}.${serverCfg.hostDomain}"
|
||||
$OCC config:app:set ownpad ownpad_etherpad_host --value="https://${serverCfg.containers.etherpad.subdomain}.${serverCfg.domain}"
|
||||
''}
|
||||
${lib.optionalString (serverCfg.containers ? collabora) ''
|
||||
$OCC config:app:set richdocuments wopi_url --value="https://${serverCfg.containers.collabora.subdomain}.${serverCfg.hostDomain}/"
|
||||
$OCC config:app:set richdocuments public_wopi_url --value="https://${serverCfg.containers.collabora.subdomain}.${serverCfg.hostDomain}"
|
||||
$OCC config:app:set richdocuments wopi_url --value="https://${serverCfg.containers.collabora.subdomain}.${serverCfg.domain}/"
|
||||
$OCC config:app:set richdocuments public_wopi_url --value="https://${serverCfg.containers.collabora.subdomain}.${serverCfg.domain}"
|
||||
$OCC config:app:set richdocuments wopi_allowlist --value="10.0.0.0/8"
|
||||
''}
|
||||
${lib.optionalString (serverCfg.containers ? authentik) ''
|
||||
$OCC saml:config:set 1 --general-idp0_display_name="authentik"
|
||||
$OCC saml:config:set 1 --general-uid_mapping="http://schemas.goauthentik.io/2021/02/saml/username"
|
||||
$OCC saml:config:set 1 --idp-entityId="https://${serverCfg.containers.authentik.subdomain}.${serverCfg.hostDomain}"
|
||||
$OCC saml:config:set 1 --idp-singleSignOnService.url="https://${serverCfg.containers.authentik.subdomain}.${serverCfg.hostDomain}/application/saml/nextcloud/sso/binding/redirect/"
|
||||
$OCC saml:config:set 1 --idp-singleLogoutService.url="https://${serverCfg.containers.authentik.subdomain}.${serverCfg.hostDomain}/application/saml/nextcloud/slo/binding/redirect/"
|
||||
$OCC saml:config:set 1 --idp-entityId="https://${serverCfg.containers.authentik.subdomain}.${serverCfg.domain}"
|
||||
$OCC saml:config:set 1 --idp-singleSignOnService.url="https://${serverCfg.containers.authentik.subdomain}.${serverCfg.domain}/application/saml/nextcloud/sso/binding/redirect/"
|
||||
$OCC saml:config:set 1 --idp-singleLogoutService.url="https://${serverCfg.containers.authentik.subdomain}.${serverCfg.domain}/application/saml/nextcloud/slo/binding/redirect/"
|
||||
AUTHENTIK_CERT=$(${pkgs.postgresql}/bin/psql -h localhost -U authentik_user -d authentik_db -At -c "SELECT certificate_data FROM authentik_crypto_certificatekeypair WHERE name = 'authentik Self-signed Certificate';")
|
||||
$OCC saml:config:set 1 --idp-x509cert="$AUTHENTIK_CERT"
|
||||
|
||||
@@ -171,7 +171,7 @@ in {
|
||||
$OCC config:app:set systemtags allow_user_creating --value="no"
|
||||
|
||||
echo "Applying Theme..."
|
||||
$OCC config:app:set theming url --value="https://${containerCfg.subdomain}.${serverCfg.hostDomain}"
|
||||
$OCC config:app:set theming url --value="https://${containerCfg.subdomain}.${serverCfg.domain}"
|
||||
${lib.optionalString (containerCfg.extra ? name) ''$OCC config:app:set theming name --value="${containerCfg.extra.name}"''}
|
||||
${lib.optionalString (containerCfg.extra ? slogan) ''$OCC config:app:set theming slogan --value="${containerCfg.extra.slogan}"''}
|
||||
$OCC config:app:set theming background_color --value="${serverCfg.colorScheme.palette.base02}"
|
||||
|
||||
77
modules/server/containers/apps/openhab.nix
Normal file
77
modules/server/containers/apps/openhab.nix
Normal file
@@ -0,0 +1,77 @@
|
||||
{ config, containerCfg, pkgs, lib, builder, name,... }:
|
||||
let
|
||||
serverCfg = config.syscfg.server;
|
||||
version = "5.1.4";
|
||||
|
||||
in {
|
||||
sops = false;
|
||||
db = false;
|
||||
paths = [
|
||||
{ path="${serverCfg.configPath}/openhab/conf"; owner="1000:1000"; mode = "0755"; }
|
||||
{ path="${serverCfg.configPath}/openhab/userdata"; owner="1000:1000"; mode = "0755"; }
|
||||
{ path="${serverCfg.configPath}/openhab/addons"; owner="1000:1000"; mode = "0755"; }
|
||||
];
|
||||
|
||||
containers = {
|
||||
server = builder.mkContainer {
|
||||
subdomain = containerCfg.subdomain;
|
||||
image = "openhab/openhab:${version}";
|
||||
port = 8080;
|
||||
extraEnv = {
|
||||
USER_ID = "1000";
|
||||
GROUP_ID = "1000";
|
||||
CRYPTO_POLICY = "unlimited";
|
||||
OPENHAB_HTTP_PORT = "8080";
|
||||
};
|
||||
extraOptions = [
|
||||
"--network=host"
|
||||
"--cap-add=NET_ADMIN"
|
||||
"--cap-add=NET_RAW"
|
||||
"--no-healthcheck"
|
||||
];
|
||||
overrides = {
|
||||
volumes = [
|
||||
"${serverCfg.configPath}/openhab/conf:/openhab/conf"
|
||||
"${serverCfg.configPath}/openhab/userdata:/openhab/userdata"
|
||||
"${serverCfg.configPath}/openhab/addons:/opt/openhab/addons"
|
||||
"/var/run/dbus/system_bus_socket:/var/run/dbus/system_bus_socket:ro"
|
||||
];
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
setup = {
|
||||
trigger = "server";
|
||||
envFile = [ config.sops.secrets."CUSTOM".path ];
|
||||
script = pkgs.writeShellScript "setup" ''
|
||||
# Pre-generate openHAB directories on the host
|
||||
OHAB="${pkgs.podman}/bin/podman --events-backend=none exec openhab-server /openhab/runtime/bin/client -u openhab -p habopen"
|
||||
sleep 20
|
||||
exit 0
|
||||
$OHAB openhab:users add $DEFAULT_ADMIN_USERNAME $DEFAULT_ADMIN_PASSWORD administrator
|
||||
$OHAB feature:list
|
||||
$OHAB openhab:addons install persistance-mapdb
|
||||
$OHAB openhab:addons install persistance-influxdb
|
||||
|
||||
$OHAB openhab:addons install ui-basic
|
||||
$OHAB openhab:addons install automation-jsscripting
|
||||
|
||||
$OHAB openhab:addons install binding-telegram
|
||||
$OHAB openhab:addons install binding-matter
|
||||
$OHAB openhab:addons install binding-mqtt
|
||||
$OHAB openhab:addons install binding-bluetooth
|
||||
$OHAB openhab:addons install binding-zigbee
|
||||
$OHAB openhab:addons install binding-chromecast
|
||||
$OHAB openhab:addons install binding-astro
|
||||
$OHAB openhab:addons install binding-meteoblue
|
||||
$OHAB openhab:addons install binding-publictransportswitzerland
|
||||
|
||||
#IF APPLE DEVICE: HomeKit (siri/apple bridge)
|
||||
#IF UBIQUITY NET: Unifi + UnifiProtect (net/cam bridge)
|
||||
#IF YAMAHA+EPSON: EpsonProjector + Yamaha (projector and sound)
|
||||
#IF BAMBULAB DEVICE: BambuLab (notify print state)
|
||||
#IF GARDENA DEVICE: Gardena (smart watering)
|
||||
#Extra: AndroidTV/Jellyfin (Bind with lights + more)
|
||||
'';
|
||||
};
|
||||
}
|
||||
@@ -1,3 +1,92 @@
|
||||
{...}:{
|
||||
|
||||
{ config, containerCfg, pkgs, lib, builder, name,... }:
|
||||
let
|
||||
version= "latest";
|
||||
serverCfg = config.syscfg.server;
|
||||
settings = pkgs.writeText"settings.yml" (pkgs.lib.generators.toYAML {}{
|
||||
use_default_settings = true;
|
||||
brand = {
|
||||
issue_url = "";
|
||||
docs_url = "";
|
||||
public_instances = "";
|
||||
wiki_url = "";
|
||||
custom = {
|
||||
links = {
|
||||
"Home" = "https://${serverCfg.domain}";
|
||||
# "Status" = "https://status.${serverCfg.domain}";
|
||||
};
|
||||
};
|
||||
pwa_colors = {
|
||||
theme_color_light = "${serverCfg.colorScheme.palette.base0C}";
|
||||
background_color_light = "${serverCfg.colorScheme.palette.base07}";
|
||||
theme_color_dark = "${serverCfg.colorScheme.palette.base0C}";
|
||||
background_color_dark = "${serverCfg.colorScheme.palette.base02}";
|
||||
theme_color_black = "${serverCfg.colorScheme.palette.base0C}";
|
||||
background_color_black = "${serverCfg.colorScheme.palette.base01}";
|
||||
};
|
||||
};
|
||||
general = {
|
||||
debug = false;
|
||||
instance_name = if containerCfg.extra ? instanceName then containerCfg.extra.instanceName else "SearXNG";
|
||||
privacypolicy_url = false;
|
||||
donation_url = false;
|
||||
contact_url = false;
|
||||
enable_metrics = false;
|
||||
};
|
||||
search = {
|
||||
safe_search = 0;
|
||||
autocomplete = if containerCfg.extra ? autocomplete then containerCfg.extra.autocomplete else "";
|
||||
languages = [ "all" "en" "en-US" "ja" "de-CH" "fr-CH" "nb" ];
|
||||
};
|
||||
server = {
|
||||
# secret_key = ""; SET BY ENV VAR
|
||||
};
|
||||
ui = {
|
||||
default_locale = if containerCfg.extra ? defaultLocale then containerCfg.extra.defaultLocale else "en";
|
||||
# query_in_title = "true";
|
||||
#default_theme = "custom";
|
||||
custom_css = "footer { display: none !important; }";
|
||||
};
|
||||
# categories_as_tabs = {
|
||||
# general = {};
|
||||
# images ={};
|
||||
# videos = {};
|
||||
# news = {};
|
||||
# files = {};
|
||||
# };
|
||||
plugins = {
|
||||
"searx.plugins.infinite_scroll.SXNGPlugin".active = true;
|
||||
"searx.plugins.tracker_url_remover.SXNGPlugin".active = true;
|
||||
};
|
||||
});
|
||||
in {
|
||||
sops = true;
|
||||
# paths = [{
|
||||
# path="${serverCfg.dataPath}/searxng/";
|
||||
# mode = "0444";
|
||||
# }];
|
||||
|
||||
containers = {
|
||||
server = builder.mkContainer {
|
||||
subdomain = containerCfg.subdomain;
|
||||
image = "searxng/searxng:${version}";
|
||||
port = 8080;
|
||||
secret = name;
|
||||
extraEnv = {
|
||||
SEARXNG_BASE_URL = "https://${containerCfg.subdomain}.${serverCfg.domain}";
|
||||
SEARXNG_PORT = "8080";
|
||||
SEARXNG_BIND_ADDRESS = "[::]";
|
||||
SEARXNG_PUBLIC_INSTANCE = "false";
|
||||
SEARXNG_SETTINGS_PATH = "/etc/searxng/settings.yml";
|
||||
#SEARXNG_VALKEY_URL = "valkey://user:password@${builder.host}:6379/0}";
|
||||
};
|
||||
overrides = {
|
||||
cmd = [ ];
|
||||
volumes = [
|
||||
"${settings}:/etc/searxng/settings.yml"
|
||||
# "/path/to/your/logo.png:/usr/local/searxng/searx/static/themes/simple/img/searxng.png
|
||||
# "${serverCfg.dataPath}/searxng:/var/cache/searxng/"
|
||||
];
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
File diff suppressed because one or more lines are too long
@@ -11,6 +11,7 @@ let
|
||||
};
|
||||
};
|
||||
in {
|
||||
sops = true;
|
||||
paths = [{
|
||||
path="${serverCfg.configPath}/traefik";
|
||||
owner = "1000:1000";
|
||||
@@ -21,7 +22,6 @@ in {
|
||||
server = builder.mkContainer {
|
||||
imageStream = image;
|
||||
subdomain = containerCfg.subdomain;
|
||||
ip = containerCfg.ip;
|
||||
port = 8080;
|
||||
secret = name;
|
||||
extraLabels = {
|
||||
@@ -29,14 +29,20 @@ in {
|
||||
"traefik.http.routers.${containerCfg.subdomain}.service" = "api@internal";
|
||||
|
||||
"traefik.http.routers.${containerCfg.subdomain}.middlewares" = if serverCfg.containers?authentik then "authentik" else "";
|
||||
} // (if serverCfg.containers?authentik then {
|
||||
"traefik.http.middlewares.authentik.forwardauth.maxResponseBodySize" = "10485760";
|
||||
"traefik.http.middlewares.authentik.forwardauth.address" = "http://authentik-server:9000/outpost.goauthentik.io/auth/traefik";
|
||||
"traefik.http.middlewares.authentik.forwardauth.trustForwardHeader" = "true";
|
||||
"traefik.http.middlewares.authentik.forwardauth.authResponseHeaders" = "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";
|
||||
} // (if containerCfg.extra ? provider || serverCfg.hostDomain != "localhost" then {
|
||||
} else {}) // (if serverCfg.containers?umami then {
|
||||
"traefik.http.middlewares.umami-global.plugin.umami-feeder.umamiHost" = "http://umami-server:3000";
|
||||
"traefik.http.middlewares.umami-global.plugin.umami-feeder.umamiUsername" = "admin";
|
||||
"traefik.http.middlewares.umami-global.plugin.umami-feeder.umamiPassword" = "umami";
|
||||
"traefik.http.middlewares.umami-global.plugin.umami-feeder.createNewWebsites" = "true";
|
||||
} else {}) // (if containerCfg.extra ? provider || serverCfg.domain != "localhost" then {
|
||||
"traefik.http.routers.${containerCfg.subdomain}.tls.certresolver" = "default";
|
||||
"traefik.http.routers.${containerCfg.subdomain}.tls.domains[0].main" = "${serverCfg.hostDomain}";
|
||||
"traefik.http.routers.${containerCfg.subdomain}.tls.domains[0].sans" = "*.${serverCfg.hostDomain}";
|
||||
"traefik.http.routers.${containerCfg.subdomain}.tls.domains[0].main" = "${serverCfg.domain}";
|
||||
"traefik.http.routers.${containerCfg.subdomain}.tls.domains[0].sans" = "*.${serverCfg.domain}";
|
||||
} else {});
|
||||
extraEnv = { };
|
||||
overrides = {
|
||||
@@ -55,15 +61,19 @@ in {
|
||||
"--entrypoints.web.http.redirections.entrypoint.scheme=https"
|
||||
"--entrypoints.web-secure.transport.respondingtimeouts.readtimeout=0s"
|
||||
"--entrypoints.web-secure.proxyprotocol.trustedips=127.0.0.1/32,192.168.1.1/16,10.10.0.0/16"
|
||||
] ++ (if containerCfg.extra ? provider then [
|
||||
"--certificatesresolvers.default.acme.email=acme@${serverCfg.hostDomain}"
|
||||
] ++ (if serverCfg.containers ? umami then [
|
||||
"--experimental.plugins.umami-feeder.moduleName=github.com/astappiev/traefik-umami-feeder"
|
||||
"--experimental.plugins.umami-feeder.version=v1.4.1"
|
||||
"--entrypoints.web-secure.http.middlewares=umami-global@docker"
|
||||
] else []) ++ (if containerCfg.extra ? provider then [
|
||||
"--certificatesresolvers.default.acme.email=acme@${serverCfg.domain}"
|
||||
"--certificatesresolvers.default.acme.dnschallenge=true"
|
||||
"--certificatesresolvers.default.acme.dnschallenge.provider=${containerCfg.extra.provider}"
|
||||
"--certificatesresolvers.default.acme.storage=/custom/acme.json"
|
||||
] else (if serverCfg.hostDomain != "localhost" then [
|
||||
] else []) ++ (if serverCfg.domain != "localhost" then [
|
||||
"--certificatesresolvers.default.acme.httpchallenge=false"
|
||||
"--certificatesresolvers.default.acme.tlschallenge=true"
|
||||
] else [ ]));
|
||||
] else []);
|
||||
ports = [ "443:443" "80:80" ] ++ (if containerCfg.port!=null then [ "${toString containerCfg.port}:8080" ] else []);
|
||||
volumes = [
|
||||
"/var/run/podman/podman.sock:/var/run/docker.sock"
|
||||
|
||||
@@ -1,3 +1,73 @@
|
||||
{...}:{
|
||||
|
||||
{ config, containerCfg, pkgs, lib, builder, name, ... }:
|
||||
let
|
||||
serverCfg = config.syscfg.server;
|
||||
image = pkgs.dockerTools.streamLayeredImage {
|
||||
name = pkgs.transmission_4.name;
|
||||
tag = pkgs.transmission_4.version;
|
||||
contents = [ pkgs.cacert ];
|
||||
config = {
|
||||
Cmd = [ "${pkgs.transmission_4}/bin/transmission-daemon" "--foreground" "--config-dir" "/config" ];
|
||||
ExposedPorts = {
|
||||
"9091/tcp" = {};
|
||||
"51413/tcp" = {}; "51413/udp" = {};
|
||||
};
|
||||
};
|
||||
};
|
||||
routerName = if containerCfg.subpath != null
|
||||
then "${containerCfg.subdomain}-${lib.strings.sanitizeDerivationName containerCfg.subpath}"
|
||||
else containerCfg.subdomain;
|
||||
in {
|
||||
paths = [{
|
||||
path = "${serverCfg.dataPath}/transmission/complete";
|
||||
owner = "1000:1000";
|
||||
mode = "0755";
|
||||
}{
|
||||
path = "${serverCfg.dataPath}/transmission/incomplete";
|
||||
owner = "1000:1000";
|
||||
mode = "0755";
|
||||
}{
|
||||
path = "${serverCfg.configPath}/transmission/config";
|
||||
owner = "1000:1000";
|
||||
mode = "0755";
|
||||
}];
|
||||
|
||||
containers = {
|
||||
server = builder.mkContainer {
|
||||
subdomain = containerCfg.subdomain;
|
||||
subpath = containerCfg.subpath;
|
||||
imageStream = image;
|
||||
port = 9091;
|
||||
|
||||
extraEnv = {
|
||||
PUID = "1000";
|
||||
PGID = "1000";
|
||||
WHITELIST = "";# 127.0.0.1,::1,10.*";
|
||||
# HOST_WHITELIST = "traefik-server,authentik-server,authentik-worker";
|
||||
};
|
||||
extraLabels = {
|
||||
} // (if serverCfg.containers ? authentik then {
|
||||
"traefik.http.routers.${routerName}.middlewares" = "authentik";
|
||||
} else {});
|
||||
|
||||
overrides = {
|
||||
cmd = [ ];
|
||||
volumes = [
|
||||
"${serverCfg.dataPath}/transmission/complete:/downloads/complete"
|
||||
"${serverCfg.dataPath}/transmission/incomplete:/downloads/incomplete"
|
||||
"${serverCfg.configPath}/transmission/config:/config"
|
||||
];
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
setup = {
|
||||
trigger = "server";
|
||||
envFile = [ config.sops.secrets."CUSTOM".path ];
|
||||
script = pkgs.writeShellScript "setup" ''
|
||||
|
||||
${pkgs.gettext}/bin/envsubst < "${../data/transmission/settings.json}" > "${serverCfg.configPath}/transmission/config/settings.json"
|
||||
'';
|
||||
};
|
||||
|
||||
}
|
||||
@@ -1,3 +1,54 @@
|
||||
{...}:{
|
||||
|
||||
{ config, containerCfg, pkgs, lib, builder, name,... }:
|
||||
let
|
||||
serverCfg = config.syscfg.server;
|
||||
|
||||
# Umami image built from nixpkgs
|
||||
image = pkgs.dockerTools.streamLayeredImage {
|
||||
name = pkgs.umami.name;
|
||||
tag = pkgs.umami.version;
|
||||
contents = with pkgs; [ cacert openssl ];
|
||||
config = {
|
||||
# Umami in nixpkgs typically provides a binary or script to start the server
|
||||
Entrypoint = [ "${pkgs.umami}/bin/umami-server" ];
|
||||
ExposedPorts = { "3000/tcp" = {}; };
|
||||
Env = [ "NODE_ENV=production" ];
|
||||
};
|
||||
};
|
||||
in {
|
||||
sops = true;
|
||||
db = true;
|
||||
paths = [{
|
||||
path = "${serverCfg.configPath}/umami/";
|
||||
mode = "0444";
|
||||
}];
|
||||
|
||||
containers = {
|
||||
server = builder.mkContainer {
|
||||
subdomain = containerCfg.subdomain;
|
||||
image = "${pkgs.umami.name}:${pkgs.umami.version}";
|
||||
imageStream = image;
|
||||
port = 3000;
|
||||
secret = name;
|
||||
extraEnv = {
|
||||
PORT = "3000";
|
||||
# HOSTNAME = "${containerCfg.subdomain}.${serverCfg.domain}";
|
||||
DATABASE_TYPE = "postgresql";
|
||||
REDIS_URL = "redis://${builder.host}";
|
||||
CLIENT_IP_HEADER = "X-Forwarded-For";
|
||||
BASE_PATH = lib.optionalString (containerCfg.subpath or null != null) "/${containerCfg.subpath}";
|
||||
# DISABLE_LOGIN = "1";#(if serverCfg.containers?authentik then "1" else "0");
|
||||
|
||||
};
|
||||
extraLabels = { } // ( if serverCfg.containers?authentik then {
|
||||
"traefik.http.routers.${containerCfg.subdomain}.middlewares" = if serverCfg.containers?authentik then "authentik" else "";
|
||||
} else {});
|
||||
extraOptions = [
|
||||
"--tmpfs=/tmp:rw,noexec,nosuid,size=512m"
|
||||
];
|
||||
overrides = {
|
||||
cmd = [ "start" ]; # Specific command for the umami binary
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
}
|
||||
@@ -1,38 +1,74 @@
|
||||
{ config, lib, pkgs, serverCfg }:
|
||||
let
|
||||
builder =
|
||||
{ image ? null, imageStream ? null
|
||||
, secret ? null
|
||||
, subdomain ? null, ip ? null, port ? 0
|
||||
, extraEnv ? { }, extraLabels ? { }, extraOptions ? [ ]
|
||||
, overrides ? { }
|
||||
}:
|
||||
let base = {
|
||||
image = if imageStream != null then "${imageStream.imageName}:${imageStream.imageTag}"
|
||||
else image;
|
||||
imageStream = imageStream;
|
||||
contBuilder =
|
||||
{ image ? null, imageStream ? null, imageFile ? null
|
||||
, secret ? null
|
||||
, subdomain ? null, subpath?null, port ? null
|
||||
, extraEnv ? { }, extraLabels ? { }, extraOptions ? [ ]
|
||||
, overrides ? { }
|
||||
}:
|
||||
let
|
||||
routerName = if subpath != null
|
||||
then "${subdomain}-${lib.strings.sanitizeDerivationName subpath}"
|
||||
else subdomain;
|
||||
base = {
|
||||
image = if imageStream != null then "${imageStream.imageName}:${imageStream.imageTag}"
|
||||
else if imageFile != null then "${imageFile.imageName}:${imageFile.imageTag}" else image;
|
||||
imageStream = imageStream;
|
||||
imageFile = imageFile;
|
||||
|
||||
environmentFiles = if secret!=null then [ config.sops.secrets."${lib.toUpper secret}".path ] else [];
|
||||
environment = {} // extraEnv;
|
||||
environmentFiles = if secret!=null then [ config.sops.secrets."${lib.toUpper secret}".path ] else [];
|
||||
environment = {
|
||||
TZ = config.time.timeZone;
|
||||
} // extraEnv;
|
||||
|
||||
labels = (if subdomain!=null then ({
|
||||
"traefik.enable" = "true";
|
||||
"traefik.http.routers.${subdomain}.entrypoints" = "web-secure";
|
||||
"traefik.http.routers.${subdomain}.rule" = "Host(`${subdomain}.${serverCfg.hostDomain}`)";
|
||||
"traefik.http.routers.${subdomain}.tls" = "true";
|
||||
} // lib.optionalAttrs (port!=null) {
|
||||
"traefik.http.services.${subdomain}.loadbalancer.server.port" = toString port;
|
||||
}) else {
|
||||
"traefik.enable" = "false";
|
||||
}) // extraLabels;
|
||||
labels = (if subdomain!=null then ({
|
||||
"traefik.enable" = "true";
|
||||
"traefik.http.routers.${routerName}.entrypoints" = "web-secure";
|
||||
"traefik.http.routers.${routerName}.rule" = if subpath != null
|
||||
then "Host(`${subdomain}.${serverCfg.domain}`) && PathPrefix(`/${subpath}`)"
|
||||
else "Host(`${subdomain}.${serverCfg.domain}`)";
|
||||
"traefik.http.routers.${routerName}.tls" = "true";
|
||||
} // lib.optionalAttrs (port!=null) {
|
||||
"traefik.http.services.${routerName}.loadbalancer.server.port" = toString port;
|
||||
}) else {
|
||||
"traefik.enable" = "false";
|
||||
}) // extraLabels;
|
||||
|
||||
extraOptions = extraOptions ++ [
|
||||
"--add-host=host.containers.internal:host-gateway"
|
||||
] ++ lib.optional (ip!=null) "--ip=${ip}";
|
||||
};
|
||||
in lib.recursiveUpdate base overrides;
|
||||
extraOptions = [
|
||||
"--add-host=host.containers.internal:host-gateway"
|
||||
] ++ extraOptions;
|
||||
};
|
||||
in lib.recursiveUpdate base overrides;
|
||||
vmBuilder = { name, vm }: ((import "${pkgs.path}/nixos/lib/eval-config.nix" {
|
||||
system = "x86_64-linux";
|
||||
modules = [ vm.cfg
|
||||
({ config, lib, modulesPath, ... }: {
|
||||
imports = [
|
||||
"${modulesPath}/profiles/qemu-guest.nix"
|
||||
"${modulesPath}/virtualisation/qemu-vm.nix"
|
||||
];
|
||||
networking.hostName = name;
|
||||
networking.useDHCP = true;
|
||||
networking.firewall.enable = false;
|
||||
services.qemuGuest.enable = true;
|
||||
system.stateVersion = "25.11";
|
||||
virtualisation = {
|
||||
memorySize = vm.memory or 2048;
|
||||
cores = vm.cores or 2;
|
||||
forwardPorts = let
|
||||
parsePortString = port: {
|
||||
from = "host";
|
||||
host.port = port;
|
||||
guest.port = port;
|
||||
};
|
||||
in if (vm ? portForward && vm.portForward != null) then map parsePortString vm.portForward else [];
|
||||
};})
|
||||
];
|
||||
}).config.system.build.vm);
|
||||
in {
|
||||
mkContainer = builder;
|
||||
mkContainer = contBuilder;
|
||||
mkVm = vmBuilder;
|
||||
mkData = { name, dir, vars?{} }: pkgs.runCommand name vars ''
|
||||
mkdir -p $out
|
||||
cp -r ${./data + "/${dir}"}/. $out/
|
||||
@@ -43,4 +79,7 @@ in {
|
||||
done
|
||||
'';
|
||||
host = "host.containers.internal";
|
||||
hostIp = if (config.virtualisation.podman.defaultNetwork.settings ? subnets)
|
||||
then (builtins.elemAt config.virtualisation.podman.defaultNetwork.settings.subnets 0).gateway
|
||||
else "10.88.0.1";
|
||||
}
|
||||
11
modules/server/containers/data/authentik/gitea.yaml
Normal file
11
modules/server/containers/data/authentik/gitea.yaml
Normal file
@@ -0,0 +1,11 @@
|
||||
version: 1
|
||||
metadata:
|
||||
name: gitea-ldap-setup
|
||||
entries:
|
||||
- model: authentik_core.application
|
||||
id: gitea-app
|
||||
identifiers:
|
||||
slug: gitea
|
||||
attrs:
|
||||
name: Gitea
|
||||
launch_url: "@GITEA_DOMAIN@"
|
||||
62
modules/server/containers/data/authentik/immich.yaml
Normal file
62
modules/server/containers/data/authentik/immich.yaml
Normal file
@@ -0,0 +1,62 @@
|
||||
version: 1
|
||||
metadata:
|
||||
name: "Immich OAuth2 Provisioning"
|
||||
labels:
|
||||
app: immich
|
||||
entries:
|
||||
- model: authentik_providers_oauth2.oauth2provider
|
||||
identifiers:
|
||||
name: "Immich Provider"
|
||||
attrs:
|
||||
authorization_flow:
|
||||
!Find [
|
||||
authentik_flows.flow,
|
||||
[slug, default-provider-authorization-implicit-consent],
|
||||
]
|
||||
authentication_flow:
|
||||
!Find [authentik_flows.flow, [slug, default-authentication-flow]]
|
||||
invalidation_flow:
|
||||
!Find [authentik_flows.flow, [slug, default-provider-invalidation-flow]]
|
||||
client_type: "confidential"
|
||||
client_id: "immich"
|
||||
|
||||
client_secret: !Env IMMICH_OAUTH_SECRET
|
||||
access_code_validity: "minutes=5"
|
||||
token_validity: "days=30"
|
||||
signing_key:
|
||||
!Find [
|
||||
authentik_crypto.certificatekeypair,
|
||||
[name, "authentik Self-signed Certificate"],
|
||||
]
|
||||
redirect_uris:
|
||||
- url: "app.immich:///oauth-callback"
|
||||
matching_mode: "strict"
|
||||
- url: "https://@IMMICH_DOMAIN@/auth/login"
|
||||
matching_mode: "regex"
|
||||
- url: "https://@IMMICH_DOMAIN@/user-settings"
|
||||
matching_mode: "regex"
|
||||
property_mappings:
|
||||
- !Find [
|
||||
authentik_providers_oauth2.scopemapping,
|
||||
[name, "authentik default OAuth Mapping: OpenID 'openid'"],
|
||||
]
|
||||
- !Find [
|
||||
authentik_providers_oauth2.scopemapping,
|
||||
[name, "authentik default OAuth Mapping: OpenID 'email'"],
|
||||
]
|
||||
- !Find [
|
||||
authentik_providers_oauth2.scopemapping,
|
||||
[name, "authentik default OAuth Mapping: OpenID 'profile'"],
|
||||
]
|
||||
|
||||
- model: authentik_core.application
|
||||
identifiers:
|
||||
slug: "immich"
|
||||
attrs:
|
||||
name: "Immich"
|
||||
launch_url: "@IMMICH_DOMAIN@"
|
||||
provider:
|
||||
!Find [
|
||||
authentik_providers_oauth2.oauth2provider,
|
||||
[name, "Immich Provider"],
|
||||
]
|
||||
11
modules/server/containers/data/authentik/jellyfin.yaml
Normal file
11
modules/server/containers/data/authentik/jellyfin.yaml
Normal file
@@ -0,0 +1,11 @@
|
||||
version: 1
|
||||
metadata:
|
||||
name: jellyfin-ldap-setup
|
||||
entries:
|
||||
- model: authentik_core.application
|
||||
id: jellyfin-app
|
||||
identifiers:
|
||||
slug: jellyfin
|
||||
attrs:
|
||||
name: Jellyfin
|
||||
launch_url: "@JELLYFIN_DOMAIN@"
|
||||
78
modules/server/containers/data/authentik/ldap.yaml
Normal file
78
modules/server/containers/data/authentik/ldap.yaml
Normal file
@@ -0,0 +1,78 @@
|
||||
version: 1
|
||||
metadata:
|
||||
name: Pre-configured LDAP Outpost
|
||||
entries:
|
||||
- model: authentik_providers_ldap.ldapprovider
|
||||
identifiers:
|
||||
name: ldap-provider
|
||||
attrs:
|
||||
base_dn: "@AUTHENTIK_LDAP_DC_DOMAIN@"
|
||||
search_group: null
|
||||
authorization_flow:
|
||||
!Find [authentik_flows.flow, [slug, default-authentication-flow]]
|
||||
invalidation_flow:
|
||||
!Find [authentik_flows.flow, [slug, default-provider-invalidation-flow]]
|
||||
|
||||
- model: authentik_core.user
|
||||
state: present
|
||||
identifiers:
|
||||
username: "ldap-service"
|
||||
attrs:
|
||||
name: "LDAP Bind Service Account"
|
||||
type: "service_account"
|
||||
path: "goauthentik.io"
|
||||
is_active: true
|
||||
password: !Env DEFAULT_LDAP_PASSWORD
|
||||
attributes:
|
||||
ak_recovery_immutable: true
|
||||
|
||||
- model: authentik_core.token
|
||||
identifiers:
|
||||
identifier: ldap-outpost-static-token
|
||||
attrs:
|
||||
intent: api
|
||||
key: !Env AUTHENTIK_TOKEN
|
||||
user: !Find [authentik_core.user, [username, "ldap-service"]]
|
||||
|
||||
- model: authentik_outposts.outpost
|
||||
identifiers:
|
||||
name: LDAP Outpost
|
||||
attrs:
|
||||
type: ldap
|
||||
providers:
|
||||
- !Find [authentik_providers_ldap.ldapprovider, [name, ldap-provider]]
|
||||
token:
|
||||
!Find [authentik_core.token, [identifier, ldap-outpost-static-token]]
|
||||
config:
|
||||
log_level: info
|
||||
authentik_host: https://sso.test.helcel.net/
|
||||
refresh_interval: minutes=5
|
||||
authentik_host_insecure: false
|
||||
|
||||
- model: authentik_rbac.role
|
||||
state: present
|
||||
identifiers:
|
||||
name: "LDAP Search Role"
|
||||
attrs:
|
||||
permissions:
|
||||
- "authentik_providers_ldap.search_full_directory"
|
||||
|
||||
- model: authentik_core.group
|
||||
state: present
|
||||
identifiers:
|
||||
name: "LDAP Search Group"
|
||||
attrs:
|
||||
users:
|
||||
- !Find [authentik_core.user, [username, "ldap-service"]]
|
||||
roles:
|
||||
- !Find [authentik_rbac.role, [name, "LDAP Search Role"]]
|
||||
|
||||
- model: authentik_core.application
|
||||
id: ldap-placeholder
|
||||
identifiers:
|
||||
slug: ldap
|
||||
attrs:
|
||||
name: ldap
|
||||
group: _
|
||||
provider:
|
||||
!Find [authentik_providers_ldap.ldapprovider, [name, ldap-provider]]
|
||||
@@ -2,7 +2,6 @@ version: 1
|
||||
metadata:
|
||||
name: nextcloud-saml-setup
|
||||
entries:
|
||||
# 1. Create the SAML Provider
|
||||
- model: authentik_providers_saml.samlprovider
|
||||
identifiers:
|
||||
name: Nextcloud SAML
|
||||
@@ -15,12 +14,10 @@ entries:
|
||||
invalidation_flow:
|
||||
!Find [authentik_flows.flow, [slug, default-provider-invalidation-flow]]
|
||||
|
||||
# Adjust these URLs to match your Nextcloud domain
|
||||
acs_url: https://@NEXTCLOUD_DOMAIN@/apps/user_saml/saml/acs
|
||||
audience: https://@NEXTCLOUD_DOMAIN@/apps/user_saml/saml/metadata
|
||||
issuer: https://@AUTHENTIK_DOMAIN@
|
||||
sp_binding: post
|
||||
# Map the attributes for Name, Email, and Groups
|
||||
property_mappings:
|
||||
- !Find [
|
||||
authentik_core.propertymapping,
|
||||
@@ -43,32 +40,6 @@ entries:
|
||||
[name, "authentik default SAML Mapping: User ID"],
|
||||
]
|
||||
|
||||
# - !Find [
|
||||
# authentik_providers_saml.samlpropertymapping,
|
||||
# [managed, "goauthentik.io/providers/saml/ms-name"],
|
||||
# ]
|
||||
# - !Find [
|
||||
# authentik_providers_saml.samlpropertymapping,
|
||||
# [managed, "goauthentik.io/providers/saml/ms-email"],
|
||||
# ]
|
||||
# - !Find [
|
||||
# authentik_providers_saml.samlpropertymapping,
|
||||
# [managed, "goauthentik.io/providers/saml/ms-groups"],
|
||||
# ]
|
||||
|
||||
# - !Find [
|
||||
# authentik_core.propertymapping,
|
||||
# [managed, goauthentik.io/providers/saml/ms-name],
|
||||
# ]
|
||||
# - !Find [
|
||||
# authentik_core.propertymapping,
|
||||
# [managed, goauthentik.io/providers/saml/ms-email],
|
||||
# ]
|
||||
# - !Find [
|
||||
# authentik_core.propertymapping,
|
||||
# [managed, goauthentik.io/providers/saml/ms-groups],
|
||||
# ]
|
||||
# Select your signing certificate (default is usually self-signed)
|
||||
signing_kp:
|
||||
!Find [
|
||||
authentik_crypto.certificatekeypair,
|
||||
@@ -77,7 +48,6 @@ entries:
|
||||
sign_assertion: true
|
||||
sign_response: false
|
||||
|
||||
# 2. Create the Application
|
||||
- model: authentik_core.application
|
||||
identifiers:
|
||||
slug: nextcloud
|
||||
@@ -85,4 +55,4 @@ entries:
|
||||
name: Nextcloud
|
||||
provider:
|
||||
!Find [authentik_providers_saml.samlprovider, [name, Nextcloud SAML]]
|
||||
group: "Cloud Services"
|
||||
launch_url: "@NEXTCLOUD_DOMAIN@"
|
||||
|
||||
@@ -27,6 +27,7 @@ entries:
|
||||
slug: authentik-proxy
|
||||
attrs:
|
||||
name: "Domain Auth Provider"
|
||||
group: _
|
||||
provider:
|
||||
!Find [
|
||||
authentik_providers_proxy.proxyprovider,
|
||||
|
||||
137
modules/server/containers/data/invidious/config.yml
Normal file
137
modules/server/containers/data/invidious/config.yml
Normal file
@@ -0,0 +1,137 @@
|
||||
db:
|
||||
user: invidious_user
|
||||
password: $DB_PASSWORD
|
||||
host: $DB_HOST
|
||||
port: 5432
|
||||
dbname: invidious_db
|
||||
|
||||
check_tables: true
|
||||
invidious_companion:
|
||||
- private_url: "http://invidious-companion:8282/companion"
|
||||
|
||||
invidious_companion_key: $SERVER_SECRET_KEY
|
||||
port: 3000
|
||||
|
||||
external_port: 443
|
||||
host_binding: 0.0.0.0
|
||||
domain: $INVIDIOUS_DOMAIN
|
||||
https_only: false
|
||||
#hsts: true
|
||||
|
||||
## Accepted values: true, false, dash, livestreams, downloads, local
|
||||
#disable_proxy: false
|
||||
# use_innertube_for_captions: false
|
||||
|
||||
# -----------------------------
|
||||
# Features
|
||||
# -----------------------------
|
||||
|
||||
popular_enabled: false
|
||||
statistics_enabled: true
|
||||
registration_enabled: true
|
||||
login_enabled: true
|
||||
captcha_enabled: false
|
||||
admins: ["$DEFAULT_ADMIN_EMAIL"]
|
||||
enable_user_notifications: false
|
||||
|
||||
# -----------------------------
|
||||
# Background jobs
|
||||
# -----------------------------
|
||||
|
||||
channel_threads: 1
|
||||
#channel_refresh_interval: 30m
|
||||
|
||||
full_refresh: false
|
||||
feed_threads: 1
|
||||
|
||||
jobs:
|
||||
clear_expired_items:
|
||||
enable: true
|
||||
refresh_channels:
|
||||
enable: true
|
||||
refresh_feeds:
|
||||
enable: true
|
||||
|
||||
# -----------------------------
|
||||
# Miscellaneous
|
||||
# -----------------------------
|
||||
|
||||
#banner:
|
||||
# use_pubsub_feeds: true
|
||||
|
||||
hmac_key: $HMAC_KEY
|
||||
#dmca_content:
|
||||
#cache_annotations: false
|
||||
#modified_source_code_url: ""
|
||||
#playlist_length_limit: 500
|
||||
|
||||
#########################################
|
||||
#
|
||||
# Default user preferences
|
||||
#
|
||||
#########################################
|
||||
|
||||
default_user_preferences:
|
||||
# -----------------------------
|
||||
# Internationalization
|
||||
# -----------------------------
|
||||
|
||||
#locale: en-US
|
||||
#region: US
|
||||
## Top 3 preferred languages for video captions.
|
||||
#captions: ["", "", ""]
|
||||
|
||||
# -----------------------------
|
||||
# Interface
|
||||
# -----------------------------
|
||||
|
||||
dark_mode: "auto"
|
||||
#thin_mode: false
|
||||
feed_menu: ["Subscriptions", "Playlists"]
|
||||
default_home: Subscriptions
|
||||
#max_results: 40
|
||||
#annotations: false
|
||||
#annotations_subscribed: false
|
||||
#comments: ["youtube", ""]
|
||||
#player_style: invidious
|
||||
#related_videos: true
|
||||
|
||||
# -----------------------------
|
||||
# Video player behavior
|
||||
# -----------------------------
|
||||
|
||||
#preload: true
|
||||
#autoplay: false
|
||||
#continue: false
|
||||
#continue_autoplay: true
|
||||
#listen: false
|
||||
#video_loop: false
|
||||
|
||||
# -----------------------------
|
||||
# Video playback settings
|
||||
# -----------------------------
|
||||
|
||||
#quality: dash
|
||||
#quality_dash: auto
|
||||
#speed: 1.0
|
||||
#volume: 100
|
||||
#vr_mode: true
|
||||
save_player_pos: true
|
||||
|
||||
# -----------------------------
|
||||
# Subscription feed
|
||||
# -----------------------------
|
||||
|
||||
#latest_only: false
|
||||
#notifications_only: false
|
||||
unseen_only: true
|
||||
#sort: published
|
||||
|
||||
# -----------------------------
|
||||
# Miscellaneous
|
||||
# -----------------------------
|
||||
|
||||
#local: false
|
||||
show_nick: false
|
||||
#automatic_instance_redirect: false
|
||||
#extend_desc: false
|
||||
101
modules/server/containers/data/invidious/login.cr
Normal file
101
modules/server/containers/data/invidious/login.cr
Normal file
@@ -0,0 +1,101 @@
|
||||
{% skip_file if flag?(:api_only) %}
|
||||
|
||||
module Invidious::Routes::Login
|
||||
def self.login_page(env)
|
||||
locale = env.get("preferences").as(Preferences).locale
|
||||
|
||||
user = env.get? "user"
|
||||
referer = get_referer(env, "/feed/subscriptions")
|
||||
return env.redirect referer if user
|
||||
return error_template(400, "Login has been disabled by administrator.") if !CONFIG.login_enabled
|
||||
|
||||
|
||||
if forwarded_user = env.request.headers["X-authentik-email"]?
|
||||
begin
|
||||
email = forwarded_user.try &.downcase.byte_slice(0, 254)
|
||||
|
||||
return error_template(401, "User ID is a required field") if email.nil? || email.empty?
|
||||
|
||||
user = Invidious::Database::Users.select(email: email)
|
||||
if user
|
||||
sid = Base64.urlsafe_encode(Random::Secure.random_bytes(32))
|
||||
Invidious::Database::SessionIDs.insert(sid, email)
|
||||
env.response.cookies["SID"] = Invidious::User::Cookies.sid(CONFIG.domain, sid)
|
||||
|
||||
if env.request.cookies["PREFS"]?
|
||||
cookie = env.request.cookies["PREFS"]
|
||||
cookie.expires = Time.utc(1990, 1, 1)
|
||||
env.response.cookies << cookie
|
||||
end
|
||||
else
|
||||
return error_template(400, "Registration has been disabled by administrator.") if !CONFIG.registration_enabled
|
||||
|
||||
sid = Base64.urlsafe_encode(Random::Secure.random_bytes(32))
|
||||
user, sid = create_user(sid, email, "")
|
||||
|
||||
if language_header = env.request.headers["Accept-Language"]?
|
||||
if language = ANG.language_negotiator.best(language_header, LOCALES.keys)
|
||||
user.preferences.locale = language.header
|
||||
end
|
||||
end
|
||||
|
||||
Invidious::Database::Users.insert(user)
|
||||
Invidious::Database::SessionIDs.insert(sid, email)
|
||||
|
||||
view_name = "subscriptions_#{sha256(user.email)}"
|
||||
PG_DB.exec("CREATE MATERIALIZED VIEW #{view_name} AS #{MATERIALIZED_VIEW_SQL.call(user.email)}")
|
||||
env.response.cookies["SID"] = Invidious::User::Cookies.sid(CONFIG.domain, sid)
|
||||
|
||||
if env.request.cookies["PREFS"]?
|
||||
user.preferences = env.get("preferences").as(Preferences)
|
||||
Invidious::Database::Users.update_preferences(user)
|
||||
|
||||
cookie = env.request.cookies["PREFS"]
|
||||
cookie.expires = Time.utc(1990, 1, 1)
|
||||
env.response.cookies << cookie
|
||||
end
|
||||
end
|
||||
|
||||
return env.redirect referer
|
||||
rescue ex
|
||||
return error_template(500, "Authentication error: #{ex.message}")
|
||||
end
|
||||
end
|
||||
env.redirect referer
|
||||
end
|
||||
|
||||
def self.login(env)
|
||||
referer = get_referer(env, "/feed/subscriptions")
|
||||
env.redirect referer
|
||||
return error_template(403, "Login post is not supported.")
|
||||
end
|
||||
|
||||
def self.signout(env)
|
||||
locale = env.get("preferences").as(Preferences).locale
|
||||
|
||||
user = env.get? "user"
|
||||
sid = env.get? "sid"
|
||||
referer = get_referer(env)
|
||||
|
||||
return env.redirect referer if !user
|
||||
|
||||
user = user.as(User)
|
||||
sid = sid.as(String)
|
||||
token = env.params.body["csrf_token"]?
|
||||
|
||||
begin
|
||||
validate_request(token, sid, env.request, HMAC_KEY, locale)
|
||||
rescue ex
|
||||
return error_template(400, ex)
|
||||
end
|
||||
|
||||
Invidious::Database::SessionIDs.delete(sid: sid)
|
||||
|
||||
env.request.cookies.each do |cookie|
|
||||
cookie.expires = Time.utc(1990, 1, 1)
|
||||
env.response.cookies << cookie
|
||||
end
|
||||
|
||||
env.redirect referer
|
||||
end
|
||||
end
|
||||
70
modules/server/containers/data/transmission/settings.json
Normal file
70
modules/server/containers/data/transmission/settings.json
Normal file
@@ -0,0 +1,70 @@
|
||||
{
|
||||
"alt-speed-down": 50,
|
||||
"alt-speed-enabled": false,
|
||||
"alt-speed-time-begin": 540,
|
||||
"alt-speed-time-day": 127,
|
||||
"alt-speed-time-enabled": false,
|
||||
"alt-speed-time-end": 1020,
|
||||
"alt-speed-up": 50,
|
||||
"bind-address-ipv4": "0.0.0.0",
|
||||
"bind-address-ipv6": "::",
|
||||
"blocklist-enabled": false,
|
||||
"blocklist-url": "http://www.example.com/blocklist",
|
||||
"cache-size-mb": 4,
|
||||
"dht-enabled": true,
|
||||
"download-dir": "/downloads/complete",
|
||||
"download-queue-enabled": true,
|
||||
"download-queue-size": 5,
|
||||
"encryption": 1,
|
||||
"idle-seeding-limit": 30,
|
||||
"idle-seeding-limit-enabled": false,
|
||||
"incomplete-dir": "/downloads/incomplete",
|
||||
"incomplete-dir-enabled": true,
|
||||
"lpd-enabled": false,
|
||||
"message-level": 2,
|
||||
"peer-congestion-algorithm": "",
|
||||
"peer-id-ttl-hours": 6,
|
||||
"peer-limit-global": 200,
|
||||
"peer-limit-per-torrent": 50,
|
||||
"peer-port": 51413,
|
||||
"peer-port-random-high": 65535,
|
||||
"peer-port-random-low": 49152,
|
||||
"peer-port-random-on-start": false,
|
||||
"peer-socket-tos": "default",
|
||||
"pex-enabled": true,
|
||||
"port-forwarding-enabled": true,
|
||||
"preallocation": 1,
|
||||
"prefetch-enabled": 1,
|
||||
"queue-stalled-enabled": true,
|
||||
"queue-stalled-minutes": 30,
|
||||
"ratio-limit": 2,
|
||||
"ratio-limit-enabled": false,
|
||||
"rename-partial-files": true,
|
||||
"rpc-authentication-required": false,
|
||||
"rpc-bind-address": "0.0.0.0",
|
||||
"rpc-enabled": true,
|
||||
"rpc-password": "$TRANSMISSION_RPC_PASSWORD",
|
||||
"rpc-port": 9091,
|
||||
"rpc-url": "/transmission/",
|
||||
"rpc-username": "",
|
||||
"rpc-host-whitelist": "127.0.0.1",
|
||||
"rpc-host-whitelist-enabled": false,
|
||||
"rpc-whitelist": "127.0.0.1",
|
||||
"rpc-whitelist-enabled": false,
|
||||
"scrape-paused-torrents-enabled": true,
|
||||
"script-torrent-done-enabled": false,
|
||||
"script-torrent-done-filename": "",
|
||||
"seed-queue-enabled": false,
|
||||
"seed-queue-size": 10,
|
||||
"speed-limit-down": 100,
|
||||
"speed-limit-down-enabled": false,
|
||||
"speed-limit-up": 100,
|
||||
"speed-limit-up-enabled": false,
|
||||
"start-added-torrents": true,
|
||||
"trash-original-torrent-files": false,
|
||||
"umask": 2,
|
||||
"upload-slots-per-torrent": 14,
|
||||
"utp-enabled": false,
|
||||
"watch-dir": "/watch",
|
||||
"watch-dir-enabled": true
|
||||
}
|
||||
@@ -2,80 +2,98 @@
|
||||
let
|
||||
serverCfg = config.syscfg.server;
|
||||
builder = import ./builder.nix { inherit config lib pkgs serverCfg; };
|
||||
enabledConfigs = lib.filterAttrs (name: c: c.enable) serverCfg.containers;
|
||||
containerSetsList = lib.mapAttrsToList (name: containerCfg:
|
||||
let apps = import (./apps + "/${name}.nix") {inherit config pkgs lib containerCfg builder name;};
|
||||
in{
|
||||
name = name;
|
||||
containers = lib.mapAttrs' (cName: cValue:
|
||||
lib.nameValuePair "${name}-${cName}" cValue
|
||||
) apps.containers;
|
||||
paths = apps.paths or [];
|
||||
setup = apps.setup or null;
|
||||
cron = apps.cron or [];
|
||||
}
|
||||
) enabledConfigs;
|
||||
mergedContainers = lib.attrsets.mergeAttrsList (lib.map(e: e.containers) containerSetsList);
|
||||
allPathConfigs = lib.flatten (lib.map (e: e.paths) containerSetsList);
|
||||
allCronsConfigs = lib.flatten (lib.map (e: e.cron or []) containerSetsList);
|
||||
in
|
||||
{
|
||||
config = lib.mkIf ( enabledConfigs != {} ) {
|
||||
|
||||
virtualisation.oci-containers = {
|
||||
backend = "podman";
|
||||
containers = mergedContainers;
|
||||
};
|
||||
|
||||
system.activationScripts.container-setup-dirs = {
|
||||
deps = [ "users" "groups" ];
|
||||
text = lib.concatStringsSep "\n" (map (cfg:
|
||||
let
|
||||
effectiveCfg = {
|
||||
owner = "root:root";
|
||||
mode = "0400";
|
||||
} // cfg;
|
||||
in ''
|
||||
${pkgs.coreutils}/bin/mkdir -p "${effectiveCfg.path}"
|
||||
${pkgs.coreutils}/bin/chown ${effectiveCfg.owner} "${effectiveCfg.path}"
|
||||
${pkgs.coreutils}/bin/chmod ${effectiveCfg.mode} "${effectiveCfg.path}"
|
||||
'') allPathConfigs);
|
||||
};
|
||||
|
||||
systemd.services = {
|
||||
podman-gc = {
|
||||
description = "Podman garbage collection";
|
||||
serviceConfig.Type = "oneshot";
|
||||
script = ''
|
||||
${pkgs.podman}/bin/podman container prune -f
|
||||
${pkgs.podman}/bin/podman image prune -f
|
||||
'';
|
||||
startAt = "weekly";
|
||||
in{
|
||||
config = lib.mkMerge [{
|
||||
syscfg.server.loadedContainers = lib.mapAttrs (name: containerCfg:
|
||||
(import (./apps + "/${name}.nix")) { inherit config pkgs lib containerCfg builder name; }
|
||||
) config.syscfg.server.containers;
|
||||
} (lib.mkIf ( serverCfg.containers != {} ) (
|
||||
let
|
||||
appsList = builtins.attrValues config.syscfg.server.loadedContainers;
|
||||
mergedContainers = lib.concatMapAttrs (appName: app:
|
||||
lib.mapAttrs' (cName: cCfg: lib.nameValuePair "${appName}-${cName}" cCfg) app.containers
|
||||
) config.syscfg.server.loadedContainers;
|
||||
allPathConfigs = lib.concatMap (app: app.paths) appsList;
|
||||
allSetupConfigs = lib.concatMap (app: if app.setup?script then [({name = app.name; envFile="";} // app.setup)] else []) appsList;
|
||||
allCronsConfigs = lib.concatMap (app: app.cron) appsList;
|
||||
allVMConfigs = builtins.filter (app: app.vm != null) appsList;
|
||||
in{
|
||||
virtualisation.oci-containers = {
|
||||
backend = "podman";
|
||||
containers = mergedContainers;
|
||||
};
|
||||
} // lib.listToAttrs (lib.concatMap (containerSet:
|
||||
if containerSet.setup != null then [{
|
||||
name = "${containerSet.name}-setup";
|
||||
system.activationScripts.container-setup-dirs = {
|
||||
deps = [ "users" "groups" ];
|
||||
text = lib.concatStringsSep "\n" (map (cfg:
|
||||
let
|
||||
effectiveCfg = {
|
||||
owner = "root:root";
|
||||
mode = "0400";
|
||||
} // cfg;
|
||||
in ''
|
||||
${pkgs.coreutils}/bin/mkdir -p "${effectiveCfg.path}"
|
||||
${pkgs.coreutils}/bin/chown ${effectiveCfg.owner} "${effectiveCfg.path}"
|
||||
${pkgs.coreutils}/bin/chmod ${effectiveCfg.mode} "${effectiveCfg.path}"
|
||||
'') allPathConfigs);
|
||||
};
|
||||
|
||||
systemd.services = {
|
||||
podman-gc = {
|
||||
description = "Podman garbage collection";
|
||||
serviceConfig.Type = "oneshot";
|
||||
script = ''
|
||||
${pkgs.podman}/bin/podman container prune -f
|
||||
${pkgs.podman}/bin/podman image prune -f
|
||||
'';
|
||||
startAt = "weekly";
|
||||
};
|
||||
}
|
||||
// lib.listToAttrs (lib.concatMap (e: [{
|
||||
name = "${e.name}-vm";
|
||||
value = {
|
||||
description = "Run ${containerSet.name} setup";
|
||||
after = [ "podman-${containerSet.name}-${containerSet.setup.trigger}.service" ];
|
||||
wants = [ "podman-${containerSet.name}-${containerSet.setup.trigger}.service" ];
|
||||
description = "Isolated NixOS Guest VM for ${e.name}";
|
||||
after = [ "network-online.target" ];
|
||||
wants = [ "network-online.target" ];
|
||||
wantedBy = [ "multi-user.target" ];
|
||||
environment = {
|
||||
QEMU_VM_REG_SND = "0";
|
||||
NIX_DISK_IMAGE = "/media/data/kvm/${e.name}-guest.qcw2";
|
||||
};
|
||||
serviceConfig = {
|
||||
Type = "simple";
|
||||
Restart = "always";
|
||||
RestartSec = "10s";
|
||||
ExecStartPre = "${pkgs.coreutils}/bin/mkdir -p /media/data/kvm";
|
||||
ExecStart = ''
|
||||
${builder.mkVm { name = e.name; vm = e.vm; }}/bin/run-${e.name}-vm -nographic
|
||||
'';
|
||||
};
|
||||
};
|
||||
}]) allVMConfigs)
|
||||
// lib.listToAttrs (lib.concatMap (e: [{
|
||||
name = "${e.name}-setup";
|
||||
value = {
|
||||
description = "Run ${e.name} setup";
|
||||
after = [ "podman-${e.name}-${e.trigger}.service" ];
|
||||
wants = [ "podman-${e.name}-${e.trigger}.service" ];
|
||||
wantedBy = [ "multi-user.target" ];
|
||||
serviceConfig = {
|
||||
Type = "oneshot";
|
||||
TimeoutStartSec = "360s";
|
||||
EnvironmentFile = if (containerSet.setup ? envFile) then containerSet.setup.envFile else [ ];
|
||||
ExecStart = "${containerSet.setup.script}";
|
||||
EnvironmentFile = e.envFile;
|
||||
ExecStart = e.script;
|
||||
RemainAfterExit = true;
|
||||
User = "root";
|
||||
};
|
||||
};
|
||||
}] else []
|
||||
) containerSetsList);
|
||||
}]) allSetupConfigs );
|
||||
|
||||
services.cron = {
|
||||
enable = true;
|
||||
systemCronJobs = allCronsConfigs;
|
||||
};
|
||||
services.cron = {
|
||||
enable = true;
|
||||
systemCronJobs = allCronsConfigs;
|
||||
};
|
||||
|
||||
}))];
|
||||
|
||||
};
|
||||
}
|
||||
@@ -1,21 +1,19 @@
|
||||
|
||||
{ config, lib, pkgs, ... }:
|
||||
let
|
||||
|
||||
listNames = config.syscfg.server.db;
|
||||
|
||||
containerNames = lib.mapAttrsToList
|
||||
(name: cfg: name)
|
||||
(lib.filterAttrs (name: cfg: cfg.db or false) config.syscfg.server.containers);
|
||||
|
||||
containerNames = builtins.attrNames (lib.filterAttrs (appName: app: app.db) config.syscfg.server.loadedContainers);
|
||||
allApps = lib.unique (listNames ++ containerNames);
|
||||
|
||||
in {
|
||||
config = lib.mkIf ( builtins.length allApps > 0) {
|
||||
services.postgresql = {
|
||||
enable = true;
|
||||
enableTCPIP = true; # Required to listen on network interfaces
|
||||
enableTCPIP = true;
|
||||
extensions = ps: with ps; [ vectorchord pgvector ];
|
||||
settings = {
|
||||
listen_addresses = lib.mkForce "*";
|
||||
shared_preload_libraries = "vchord";
|
||||
};
|
||||
authentication = pkgs.lib.mkOverride 10 ''
|
||||
# TYPE DATABASE USER ADDRESS METHOD
|
||||
@@ -41,6 +39,28 @@ in {
|
||||
bind = "*";
|
||||
settings.protected-mode = "no";
|
||||
};
|
||||
|
||||
systemd.services.influxdb3 = {
|
||||
description = "InfluxDB 3 Time Series Database Engine";
|
||||
after = [ "network.target" ];
|
||||
wantedBy = [ "multi-user.target" ];
|
||||
environment = {
|
||||
INFLUXDB3_NODE_IDENTIFIER_PREFIX = "node0";
|
||||
INFLUXDB3_OBJECT_STORE = "file";
|
||||
INFLUXDB3_DATA_DIR = "${config.syscfg.server.dataPath}/influxdb";
|
||||
INFLUXDB3_DB_DIR = "${config.syscfg.server.dataPath}/influxdb";
|
||||
INFLUXDB3_ENABLE_INTERNAL_DB = "true";
|
||||
INFLUXDB3_INITIAL_ADMIN_TOKEN = "b27686e85a883437666f61586e084f7deb763958497739479ca48bc913ee90afd1a920332156133c89fb8674cb197ced17706074e6a42fc7ce6b2d54ac6119c9";
|
||||
};
|
||||
serviceConfig = {
|
||||
Type = "simple";
|
||||
ExecStart = "${pkgs.influxdb3}/bin/influxdb3 serve";
|
||||
Restart = "on-failure";
|
||||
StateDirectory = "influxdb3";
|
||||
PrivateTmp = true;
|
||||
NoNewPrivileges = true;
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
systemd.services.postgresql-init = {
|
||||
@@ -55,7 +75,7 @@ in {
|
||||
};
|
||||
|
||||
script = ''
|
||||
${pkgs.coreutils}/bin/sleep 2
|
||||
${pkgs.coreutils}/bin/sleep 20
|
||||
PSQL="${pkgs.postgresql}/bin/psql"
|
||||
${lib.concatMapStringsSep "\n" (name: ''
|
||||
$PSQL -tAc "ALTER DATABASE ${name}_db OWNER TO ${name}_user;"
|
||||
|
||||
@@ -1,130 +0,0 @@
|
||||
{ config, lib, pkgs, ... }:
|
||||
let
|
||||
cfg = config.syscfg.server;
|
||||
containers = cfg.containers;
|
||||
faviconOverride = {
|
||||
" ~* /favicon\.(ico|png|svg|jpg)$" = {
|
||||
extraConfig = ''
|
||||
add_header Content-Type image/svg+xml;
|
||||
return 200 '<svg xmlns="http://www.w3.org/2000/svg" id="Layer_1" data-name="Layer 1" viewBox="0 0 50 50"><defs><style>.cls-1{fill:#fd4b2d;}</style></defs><path class="cls-1" d="M30.83,5A23.23,23.23,0,0,0,10.41,67.13h10.8C26,63,32.94,61.8,38,67.13H49.39C44.93,61.09,38.24,55,30.83,55Z"/><path class="cls-1" d="M46.25,28.11c-14.89,31.15-41,4.6-25-11H10.41c-8.47,14.76,3.24,34.68,20.42,34.23,13.28,0,24.24-19.72,24.24-23.21,0-1.54-2.14-6.25-5.68-11H38A40.52,40.52,0,0,1,46.25,78.11Zm.4-.91Z"/></svg>';
|
||||
'';
|
||||
# proxyPass = "http://127.0.0.1:9000";
|
||||
};
|
||||
};
|
||||
# Function to convert your container config into an NGINX vhost
|
||||
mkVhost = container: {
|
||||
forceSSL = true;
|
||||
# quic = true;
|
||||
# http3 = true;
|
||||
useACMEHost = "${cfg.hostDomain}";
|
||||
locations = faviconOverride // {
|
||||
"/" = {
|
||||
proxyPass = "http://${container.ip}:${toString container.port}";
|
||||
proxyWebsockets = true; # Recommended for modern apps
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
in {
|
||||
config = lib.mkIf ( config.syscfg.server.web) {
|
||||
security.acme = {
|
||||
acceptTerms = true;
|
||||
defaults.email = "admin@domain.org";
|
||||
|
||||
certs."${cfg.hostDomain}" = {
|
||||
domain = "*.${cfg.hostDomain}";
|
||||
extraDomainNames = [ "${cfg.hostDomain}" ]; # Adds the root too
|
||||
dnsProvider = "infomaniak";
|
||||
credentialsFile = config.sops.secrets."INFOMANIAK_API_KEY".path; # File containing your API token (e.g. CLOUDFLARE_DNS_API_TOKEN=...)
|
||||
group = "nginx";
|
||||
};
|
||||
};
|
||||
|
||||
services.nginx = {
|
||||
enable = true;
|
||||
recommendedProxySettings = true;
|
||||
recommendedTlsSettings = true;
|
||||
recommendedGzipSettings = true;
|
||||
|
||||
# appendHttpConfig = ''
|
||||
# add_header Alt-Svc 'h3=":443"; ma=86400';
|
||||
# '';
|
||||
commonHttpConfig = ''
|
||||
proxy_buffer_size 32k;
|
||||
proxy_buffers 8 16k;
|
||||
proxy_busy_buffers_size 48k;
|
||||
'';
|
||||
|
||||
virtualHosts = {
|
||||
"_" = {
|
||||
default = true;
|
||||
forceSSL = true;
|
||||
# quic = true;
|
||||
# http3 = true;
|
||||
useACMEHost = "${cfg.hostDomain}";
|
||||
|
||||
locations = {
|
||||
"/" = {
|
||||
extraConfig = ''
|
||||
return 404;
|
||||
'';
|
||||
};
|
||||
};
|
||||
};
|
||||
"sec.${cfg.hostDomain}" = {
|
||||
forceSSL = true;
|
||||
useACMEHost = "${cfg.hostDomain}";
|
||||
|
||||
locations = {
|
||||
"/" = {
|
||||
proxyWebsockets = true;
|
||||
proxyPass= "http://${cfg.containers.authentik.subdomain}.${cfg.hostDomain}";
|
||||
|
||||
extraConfig = ''
|
||||
auth_request /outpost.goauthentik.io/auth/nginx;
|
||||
error_page 401 = @goauthentik_proxy_signin;
|
||||
auth_request_set $auth_cookie $upstream_http_set_cookie;
|
||||
add_header Set-Cookie $auth_cookie;
|
||||
|
||||
auth_request_set $authentik_username $upstream_http_x_authentik_username;
|
||||
auth_request_set $authentik_groups $upstream_http_x_authentik_groups;
|
||||
auth_request_set $authentik_entitlements $upstream_http_x_authentik_entitlements;
|
||||
auth_request_set $authentik_email $upstream_http_x_authentik_email;
|
||||
auth_request_set $authentik_name $upstream_http_x_authentik_name;
|
||||
auth_request_set $authentik_uid $upstream_http_x_authentik_uid;
|
||||
|
||||
proxy_set_header X-authentik-username $authentik_username;
|
||||
proxy_set_header X-authentik-groups $authentik_groups;
|
||||
proxy_set_header X-authentik-entitlements $authentik_entitlements;
|
||||
proxy_set_header X-authentik-email $authentik_email;
|
||||
proxy_set_header X-authentik-name $authentik_name;
|
||||
proxy_set_header X-authentik-uid $authentik_uid;
|
||||
'';
|
||||
};
|
||||
|
||||
"/outpost.goauthentik.io" = {
|
||||
proxyWebsockets = true;
|
||||
proxyPass = "http://${config.syscfg.server.containers.authentik.ip}:${toString config.syscfg.server.containers.authentik.port}/outpost.goauthentik.io";
|
||||
extraConfig = ''
|
||||
proxy_set_header X-Original-URL $scheme://$http_host$request_uri;
|
||||
add_header Set-Cookie $auth_cookie;
|
||||
auth_request_set $auth_cookie $upstream_http_set_cookie;
|
||||
proxy_pass_request_body off;
|
||||
proxy_set_header Content-Length "";
|
||||
'';
|
||||
};
|
||||
};
|
||||
extraConfig = ''
|
||||
location @goauthentik_proxy_signin {
|
||||
internal;
|
||||
add_header Set-Cookie $auth_cookie;
|
||||
return 302 https://${cfg.containers.authentik.subdomain}.${cfg.hostDomain}/outpost.goauthentik.io/start?rd=$scheme://$http_host$request_uri;
|
||||
}
|
||||
'';
|
||||
};
|
||||
} //lib.mapAttrs' (name: v:
|
||||
lib.nameValuePair "${v.subdomain}.${cfg.hostDomain}" (mkVhost v)
|
||||
) containers;
|
||||
};
|
||||
};
|
||||
}
|
||||
@@ -1,8 +1,7 @@
|
||||
{ config, lib, pkgs, ... }:
|
||||
let
|
||||
listNames = config.syscfg.server.db;
|
||||
containerNames = lib.mapAttrsToList (name: cfg: name)
|
||||
(lib.filterAttrs (name: cfg: ((cfg.db or false) || (cfg.sops or false))) config.syscfg.server.containers);
|
||||
containerNames = builtins.attrNames (lib.filterAttrs (appName: app: app.sops) config.syscfg.server.loadedContainers);
|
||||
allApps = lib.unique (listNames ++ containerNames);
|
||||
in{
|
||||
sops.secrets = {
|
||||
|
||||
@@ -2,6 +2,7 @@ CUSTOM: |
|
||||
DEFAULT_ADMIN_USERNAME=...
|
||||
DEFAULT_ADMIN_PASSWORD=...
|
||||
DEFAULT_ADMIN_EMAIL=...
|
||||
DEFAULT_LDAP_PASSWORD=...
|
||||
TRAEFIK: |
|
||||
INFOMANIAK_ACCESS_TOKEN=...
|
||||
AUTHENTIK: |
|
||||
@@ -9,6 +10,7 @@ AUTHENTIK: |
|
||||
POSTGRES_PASSWORD=...
|
||||
AUTHENTIK_SECRET_KEY=...
|
||||
AUTHENTIK_EMAIL__PASSWORD=...
|
||||
AUTHENTIK_TOKEN=...
|
||||
NEXTCLOUD: |
|
||||
DB_PASSWORD=...
|
||||
POSTGRES_PASSWORD=...
|
||||
@@ -26,3 +28,14 @@ GITEA: |
|
||||
GITEA__database__PASSWD=...
|
||||
GITEA__security__SECRET_KEY=...
|
||||
GITEA__security__INTERNAL_TOKEN=...
|
||||
SEARXNG: |
|
||||
SEARXNG_SECRET=...
|
||||
UMAMI: |
|
||||
DB_PASSWORD=...
|
||||
DATABASE_URL=postgresql://username:mypassword@localhost:5432/mydb
|
||||
APP_SECRET=...
|
||||
IMMICH: |
|
||||
DB_URL = "postgresql://immich_user:...@localhost:5432/immich_db";
|
||||
SERVARR: |
|
||||
SONARR__AUTH__APIKEY=...
|
||||
RADARR__AUTH__APIKEY=...
|
||||
|
||||
@@ -1,11 +1,17 @@
|
||||
CUSTOM: ENC[AES256_GCM,data:OVhE99dmudlV31Re2/fyFurXnRSM3RjbdVDxYp6oF4kazaseISlI4QjgIyyUNEAjeAST17Prv/t5GdyTUvoUICoVKmhQdRv5xFeB7ngTCdi7XoYW1r6HIXwz9wOf/UvPWLafSxSM,iv:/ikpvHH5sLZpTnNABUFjZoVLS+tBZSUYIUxxdXMCCcc=,tag:mS9uW33M355KErY1rQtvqQ==,type:str]
|
||||
CUSTOM: ENC[AES256_GCM,data:JP+FhvZVtGfrQNT4/qQkdTGJpIRT2mG35U5gwzWVjJ5tkv3ZqdYW1oUZluWTNQw1ayC82o9o2HKK6GiahMQUYvTnaJhVjwibE9fXHwQlExW5bKdeh43IYBRnWgejvlC6/LF9GK/23u8CzgsLIeQglrZcpITppgShuUt11TZRHY4r8lUIR+xDOiovIkIE+ZXXYBYg6e0/IS6C0SgEO6lIKR9WShVZ3Avm5fmPEGC2ipuQKDhNIc8T7e9CVc1ieKj58gXo6Xd6SH2XDDdkOR8wGkV/uZmYgaMMKo2a4CASgXCdwPfQxa/GdU8/05KgVP7ihCZn4ufXIA+BwMAdoGdLkO2KOQqyOuVVT/qXHUaWbBZQ3lDvqC8MaOuNxKWEhTQoJjX5eir7fe/GOqC8/yofu1+OpVwRedZmUdx3lIVd/Tv9A1LVrp0aldLuWdUJgW3994xK+K1hOXbruFWAkUznOMZiRvFnCCGc4CNo3TIO1krkRMQKffPjvF8OCoU9ojaGHw96AYdPQywnOLCPI2wy+tCITExLgGItDz0ewNE=,iv:/H03Fer5ZF6mpoO9pEfHVLxuULuWy5sDhCjR4eWsSXk=,tag:Ato0XXHZ0fo0xLKD+uGcSg==,type:str]
|
||||
TRAEFIK: ENC[AES256_GCM,data:Ei+/OL7xwNaOEg3rSaz95N78nvp51lC63XCplNzeD+bBMGcK9G7HoyQxfpaJ7S0MkuMW0ZXT2nJ4GES40GoJCZIrnEiSBm2tpjDfNjlS/rFwxx0wVfM1nsEuBf3pL5dqiCNa9+Lad2Cd,iv:d1MH0ive+E8xuUK0CIOXZeEigHJKVGlFaq0iH4KSbZA=,tag:VTARuNeotr2I0+fdOk+iqA==,type:str]
|
||||
AUTHENTIK: ENC[AES256_GCM,data:dZ+Kf85ZjaZ82coYNeNOXe5zfD2M9rEeOB6jDNoaKmo3jMABhnha+iBvYJTI2NltkGzymPJQI+JV8F6GdT1l6cqcR8p0nNjQjS1BMk0rR7n8RCp6MazUTJuIjbEq6zEUrA4SXquw5gZDEp4FLo010PhoLaLinHg8OoqzjDsTxdcKevbQWmZeefDBrwXWpz6BlkRIQA3KazVb0w7l1jDTIkozUIWbvtvtk5ccGjzx3b+wCC36QYFcHHtPvFZwMDHzFPVBd90hWc/BwFfvCExONmH0S7GLFTp7I5NsBnWpT0AHUHHc5PlSR2dUy9H2DZ3IkORdNVzOaqESbYKymuWTQBDQuyI9IJdt4Cac0CV9i6p8rFXL6fQyQKZ9djHX8orpyCUeJXqFs8I6et+IzpTeZcmdv/76Q9tomBBi4k4PRMXpeff8Bn02bOSb7RSaj5NVeWxIhZkh3sEXUeva5/yrAYT30mrLpbwzWoCaKrPCPLIcFxvNrYxPUo6kVVz1jSlBurvcKefbreJGqA==,iv:Hj7aBfDLSqRBzueN8b9F9TutpjMESFloqrnirSmnH9U=,tag:1ikt1JvuhIZCx68nh/VzMA==,type:str]
|
||||
AUTHENTIK: ENC[AES256_GCM,data:HlUFb7JjzSMTM345miSLlUE4SEXgaRAx7SkDDQzaJzs9VuifJKtOE2M4PCKc35VjVt9xIFH+YoIE93re10Rwbe+QEaUphPOgb/G7jRhaaPV/roBYuv6uO5xy68jaVJZpobxajOSVUmJa1JANCh1qrX0+Imr6udYULvK6wQzAnu2tEDkElQ3eZtezUa4E5ia1j7RCYTTPW9oie+YEVJl5Aws2HzPK5q0wKojZOmHanbnKzij3KnSgtsMc3ftL1Fam3wlSk2n3Tw0nz8aBag9IPwYje5zdBkDJY6qiBwYKcBPQUIW+Na0xX2JHymwJSzMdKmW8cEV9b1fXCPsnYVXulb4VMVkTk4MibZ3YT57wlFhqhSy7D39ZTySllIZg8sOrj8cKhpJ3HlSbceD1GnPJatVzZkDkDeyICLu9sYX3B+KrCDlL5sUMPagUFc3g3HUAPxLVPltoP69ro69acUoz5w8gkAwHlE45I3biC/jLz4telEcW8GkF868j3gsHiayE3f87T5MOPvuvhAFdSMl3SF1ND3mWjJq7+FmA6BhxgESg4m+vPnYyVumcbXJnbgfW69BgPYcL1CWZcA+SP6OWg9GOYT5SuWixkaGn2TgRAUj3nlCcAja8,iv:uXAyOIBl9lGYBvALMdvp2hf6cj6QGWRcyUvEsjIDr1I=,tag:iLxO/qYT2zafXhFGVVUYkA==,type:str]
|
||||
NEXTCLOUD: ENC[AES256_GCM,data:IWitzubILQ5SrGdO3UQZboisqAECt5lXOqHVg4yAKxedG7ZLOgVp6jPV+4VVDC13KEkxIsiYjjNvjqnOXCdYWQIC13YZ+o2IBDI9PgavBB3nmjfi0Q7BVki6C8qCtbM5H9uFlQ3h7rkPyEbE3pHa3dY5uwgdtmvw3qKf2UAZGIJCU7dKamjuTCucGitOEG434jFQik9duHZs7EV3AZrkLXqOfdvftvdpciDb/4/K7h/4uEYSXJ94Lf0b16/NRUcR,iv:1UvcbqC3hJEHU9t6Z+N226DTJEcgM315ynYkxPKpYSM=,tag:FGkXlUw+7LRu1/cpMys7OA==,type:str]
|
||||
COLLABORA: ENC[AES256_GCM,data:cLGEziks5dyxTF1jugfpQE0l0nSkDP7MpROzCxCM94jv49sguA+d/SnY1olE8ZP9iCBnlvbMZyNR7uYo88B92Pmv8wVWfeuhHiHFIXh5aaOxntpt80UMg3Jy,iv:gmFG7C893QPuZ4rEqllAlUpNIXMcGsf9+/QCPLhWLTM=,tag:WpKHCUk6zhQRfFX2d6OPbQ==,type:str]
|
||||
ETHERPAD: ENC[AES256_GCM,data:PSr06GyOgY0HDNC4Hr2XUjbNUszGlfBjxDbrrKNQOqSMSVfZj4iFIGamrS72WO0un4U7IENx0T6CTBN/ELoq7J/+W9zf879uzKWuNaAulLVtBqrUbbqA7hTJpidnveZXzdwZRvlz/bU8kWAmXyhiDb2Q42Sz3BDb6duM3PO1AgG8Ko1pi2IemCPjO3uzudeT8FAlO8NnCUxKgwIKSz8CodOXFVGk66NX4xJd4ycfdNYXvKBNlzt1+WuWsZeZzeWmF7WD2dt4wWA9fWxB90fnth6ZV5LdeXjyYnzwkFOWoyNazgqV4jBv+aXKVwX4fYvspu13cVdrak3gc698bS2N1guDss4A/sfXMbtaYPGm98xXkqz1LP7sXQzKUdZf9sAS9gtOVv2tmg==,iv:uQ0Roe+XefzMjZCF3It+U2D1MWPMT5f6CPwlz0gQ5W0=,tag:wSgp0CVr6Y6M3eqcoTy8cw==,type:str]
|
||||
ETHERCALC: ENC[AES256_GCM,data:0ScnDsUNBt6wYJC4hTXn8huuTptBTDKZV4yFVQ4fuBWc6auWNWhDQlTc0ImJoK6efr2uyp3sVu3o+KlCNvUGhDOJ1you6socyTgRP0q7oLPC+Ln+bFP8gWG8v2nyEFY=,iv:YqvVjBFG/WZg1l4aMAiioOruWZ9zcTMr74DVW+1+2DQ=,tag:ePBXd4ddipJtxhFE1amfMg==,type:str]
|
||||
GITEA: ENC[AES256_GCM,data:TGsye52g8DVOk51o1dWfF6x3m6BFPe0MtUbOayxYejaYSZ4cDCfx02EPAhiL3FJGLfidZt7+WpjhVqJvFeCvJ1OXdYMQyuL3akPBCmEjmBgBhJvEjVtVgV3aNMS21gy3o0RG/MXDXOLpjHeaXn3G/XWKsv5bw+gg94bDirOguLC5eaFxddn5/UGFvfwXzDmkoNmc9zufGvVpUkRy8rju/TjOz4Q5GZk1gXWtJWdkTGhuVYVBDDHIQq9DBS0qXi8tP3FvY8prOi/c5ZbyyZjTOgNpWB9uU4HkHz4aUTMFIrPFwUeWmNMIdyUmarSQ7tDDdhBfIiLhb94jaMATHq+PfwwpCcmiTfBEG3OS/3SXPMWg6jhfUxvxsKNT2HQca31B9IRqlitaio3r6KEPBTYh2nWtFO9d5Is7TUWkGsOaV+0JSiNPNh5uD7kNyBxsKUKcND7JXMxW/TZOqARY64x6IwDrbcypsyMW5i+4Er1/0HCvZ7FBH1XkTul8vBGF5ZGD/tYp/m6Ld26GKYkj967eYIwlNFcOMGNue5PyJfFCq0qWbLWO9dFA9c2e9r81DCD8y/0S3Ts7X7TkXVGShm7JoMqb8dzg9i6CoNlmbPAM5GwoNV3ftQugvqEGMk5UIvf05YsgSNvq4UJbhsT4bZqpV0plnxYD5TswCGF/XQ3nwwFTudmf1OLcgYwM2NckbVui128o1Rw=,iv:vo6l0QirLIUvwLN675LYkffkXejJecvBesLJvoW/bjY=,tag:zyLyiCskF84A3QVoq5X3iw==,type:str]
|
||||
SEARXNG: ENC[AES256_GCM,data:gtKhEmMemzLRl4c3cYhMAQ+5vUth1IhWQeLvW1YtaG5TbhQHBR4PDREQOlGt+tlfGQrft+FeNhMSN/SKOp8gmScVWa+9qmltzxRGRpLm3m/VuBZvOlGdeUcKAX8zEH6A,iv:B2UEtjTRIjT6W+tH2gtcl6XMvZNgbvZUXTiBePGOu24=,tag:SHIF6eaWBLwy9RrEy1N9kg==,type:str]
|
||||
UMAMI: ENC[AES256_GCM,data:onB/uXLajaRLmeQMGNHFsjREzPih9ha+cogGRw+nRomERSRrbBv+6gCqEr8F3Dcm818JB4jGRYKoIYG8Jl6gMDaz5QQiA4qAnbG19LuzVeVUgz4NGEgXBULoT/0sQacnyAPIfPEp+ESWRQH81nO6Qcs+rICpS2Xfeye5hb+8rSAxmLpY991AJ3+avGyMwPcpfNCkixWt68KuG5ZN/IGDksM/sSLGgyMisClbEdhigq4mwibOxpiWjcKk/17xYgY6Xz93h/yloHKZIZZpnyA+85YC6oNWgCPhkGIAVu3dGshp10a0nk1A2INm6vxNPbfUjYLkt3zDAPZtoBRCqUs+43Eh62hYgajgWCQJhjJkDgF4Y1ifGfDerIXs/cDpIKLt2+7VqM6/ouqIDPJ7khSAr+8bcHU4CKDtsDagob5PpCG4ABt44cg9cGw=,iv:HD450JZuWn2+V0pvOsDHy9oVAanFMf1el9LA1z0PULY=,tag:p7Vl7dtM8UdAUNgmdG+7cg==,type:str]
|
||||
IMMICH: ENC[AES256_GCM,data:1y78yeawkRjUXLWPyFdMB5HCDQhb1PoxEMfHmKSZfv0CWloOrQWT735dlH+W9yC6ljZjqVD9Fwq/9GqqKQMTFMCpr8wVRwSHEuqmaG3UgKzbLA3aWZ1SIB0AiJi+eUunzHj2vikUJx9dMRjC+iNXrsVWh2HqMrOyFCWetZoIfxNiAgsgNKPgYYsHLv6OAZs9XT7V3veqe0zc0nyw7ghWSXne/yNhQESyyGlMAdagrJRNimvXIp/AoAUKl2WUJm2MBl7lb6K1YeJ1XW8OjAHzV8isBiUwU8ZD81VJog0fgTGjbUa+HO7jEo+9YwmDIMx3f5z9N4A=,iv:pboITW2rr7+w8VNZM6uYMMEFZ1S/JtqjNOVthpYJ2tQ=,tag:0dgrJ191sB4MLJHMoQBlCg==,type:str]
|
||||
INVIDIOUS: ENC[AES256_GCM,data:ZfgU5UFMmG9Cx9UaR0xnKr9VPebG3kut0difTFZmoqOSs+stG6YJfV82OOhj1RQLVJlPr/scydYy1+3LytwvP1BT7tLe0jII7XupbkL0w3n79KBaiIzAPdicqLxeqjKH45I0NjHra4djdnO2Ff4T8CTiFDlPn1rMuiw=,iv:UaDmOKJ4bFPGCaIePLXkWot9E6sTu2nhaVs83sI38G0=,tag:spTjxWEmLfPc8BZl2GglBA==,type:str]
|
||||
SERVARR: ENC[AES256_GCM,data:757WdthmToCGr2boph7iW1ycs3tQyGgD3lhYOcX/X3hjs9dLLPCWGI2zt5axp72IGJ/sVYEop2rqsRLxdPn3VIyQLvQ+3MYdo8Z/yOuMy7DAlnITQQQUI2ylZKHVmFAt39/xBpwsVjh3m/hBQvn/LbCDtR2s4qa+8fQDfeZXksTtnf7YZbVygTF7jWZ+0oVvkvNO1ZUejvP+uHL+jHwgMEwQnR22hOYWEKZ1s7PI+EZHujqyOhnwXB4jRG+XD7R4N6AhC5Z+nmkFpy3ffszCJ0/H,iv:NrNbkL6GWN4r+uzxNYrhoECD1APbRsRBcMBbVHD3DwM=,tag:YK1O50wV+lHAQa6TX9huUw==,type:str]
|
||||
INFLUX: ENC[AES256_GCM,data:woaZ/8zWRFtuO6U3HQ==,iv:iLo33tykE6oQW2jUNVGtR/JwrZbs9Fe1fFQ5/LnvaRY=,tag:6UDOU0JJgR7eyQ0hL8j5Qw==,type:str]
|
||||
sops:
|
||||
age:
|
||||
- recipient: age1sxzuhh2fcd4pmaz4mdqq95t683d32ft22w9t2r7pk258u0s8wymsqdj7lg
|
||||
@@ -26,8 +32,8 @@ sops:
|
||||
S1NaTVFTL0FCdm1EQmRsUnlhclZNZlEKEgIe60qkvY8+UocjQU+WM2dTL/1y3Kqk
|
||||
d4RrlLP9NSozwVsPYI4ntygvMSApbT4v0YvoO7gV90lkGWEvW1YDfA==
|
||||
-----END AGE ENCRYPTED FILE-----
|
||||
lastmodified: "2026-05-11T22:33:41Z"
|
||||
mac: ENC[AES256_GCM,data:276HHpEW56HOvKKbNPM79QrEBYDM590bOLfsgssSb79jm+LzrgLlYk2QImmXArADWby4Ai4jBPL4EahNm+a3aBazMEbwAu+EorvORE2P12W5C1ztskx5XUI3yDKY96jlZvmpXsqefa2pOQc1USk8ai/Obd5MLK06kMr2w3a7P9s=,iv:NJoe1lvw1hrWNL79Ux065UkSEDEEc0+NqlqB4tk3mAw=,tag:YTjIvEP1BO69Pa0qispMLQ==,type:str]
|
||||
lastmodified: "2026-05-21T21:42:55Z"
|
||||
mac: ENC[AES256_GCM,data:6FCnBXQ1ueiNECTZDo2AGw8CXCimGWKXQYbI+4in0tCfI477Ip9OISwodK4cFhZPKEYFOB4z5KU5vXJ014jErYDylaPlHYj7Bj/4ugkYt/ywDg3VEYZVVZfBI7bhe1x8/kbkg+Y6ZwURf74xPyg7X+7IH5VGQA4eKck5rTPwGZQ=,iv:04MI9S51azQLEvDtbxL0cf6NUNtV4hRALBcsMmrAFKg=,tag:5oxAZwCfWmatWnghZ97W6g==,type:str]
|
||||
pgp:
|
||||
- created_at: "2026-05-05T23:46:27Z"
|
||||
enc: |-
|
||||
|
||||
@@ -5,161 +5,9 @@ let
|
||||
(name: type: type == "directory" && builtins.pathExists (systemsDir + "/${name}/cfg.nix"))
|
||||
(builtins.readDir systemsDir));
|
||||
|
||||
userOpt = with lib; {
|
||||
username = mkOption { type = types.str; };
|
||||
pubssh = mkOption { type = types.str; default=""; };
|
||||
wm = mkOption {
|
||||
type = types.enum [ "Wayland" "X11" "-" ];
|
||||
default = "-";
|
||||
};
|
||||
git = {
|
||||
username = mkOption { type = types.str; default = "Anonymous";};
|
||||
email = mkOption { type = types.str; default = "anonymous@domain"; };
|
||||
key = mkOption { type = types.nullOr types.str; default=null; };
|
||||
};
|
||||
};
|
||||
netOpt = with lib; {
|
||||
ble = {
|
||||
enable = mkOption {
|
||||
type = types.bool;
|
||||
default = false;
|
||||
};
|
||||
};
|
||||
wlp = {
|
||||
enable = mkOption {
|
||||
type = types.bool;
|
||||
default = false;
|
||||
};
|
||||
nif = mkOption {
|
||||
type = types.str;
|
||||
default = "";
|
||||
};
|
||||
};
|
||||
wg = {
|
||||
enable = mkOption {
|
||||
type = types.bool;
|
||||
default = false;
|
||||
};
|
||||
ip4 = mkOption {
|
||||
type = types.str;
|
||||
default = "";
|
||||
};
|
||||
ip6 = mkOption {
|
||||
type = types.str;
|
||||
default = "";
|
||||
};
|
||||
pubkey = mkOption {
|
||||
type = types.str;
|
||||
default = "";
|
||||
};
|
||||
};
|
||||
};
|
||||
makeOpt = with lib; {
|
||||
cli = mkOption {
|
||||
type = types.bool;
|
||||
default = true;
|
||||
};
|
||||
gui = mkOption {
|
||||
type = types.bool;
|
||||
default = false;
|
||||
};
|
||||
virt = mkOption {
|
||||
type = types.bool;
|
||||
default = false;
|
||||
};
|
||||
power = mkOption {
|
||||
type = types.bool;
|
||||
default = false;
|
||||
};
|
||||
game = mkOption {
|
||||
type = types.bool;
|
||||
default = false;
|
||||
};
|
||||
develop = mkOption {
|
||||
type = types.bool;
|
||||
default = false;
|
||||
};
|
||||
};
|
||||
serverOpt = with lib; {
|
||||
hostDomain = mkOption { type = types.str; };
|
||||
mailDomain = mkOption { type = types.str; };
|
||||
mailServer = mkOption { type = types.str; };
|
||||
|
||||
configPath = mkOption {
|
||||
type = types.str;
|
||||
default = "/media/config";
|
||||
};
|
||||
dataPath = mkOption {
|
||||
type = types.str;
|
||||
default = "/media/data";
|
||||
};
|
||||
|
||||
colorScheme = mkOption {
|
||||
#type = types.submodule {
|
||||
# options = {
|
||||
# slug = mkOption { type = types.str; };
|
||||
# name = mkOption { type = types.str; };
|
||||
# palette = mkOption {
|
||||
type = types.attrs; #default = {};# };
|
||||
#};
|
||||
# };
|
||||
default = (lib.evalModules { modules =[ { freeformType = with lib.types; attrsOf anything; } ../colors ];}).config.colorScheme ;
|
||||
};
|
||||
containers = mkOption {
|
||||
type = types.attrsOf (types.submodule {
|
||||
options = {
|
||||
enable = mkOption { type = types.bool;default = false; };
|
||||
db = mkOption { type = types.bool;default = false; };
|
||||
sops = mkOption { type = types.bool;default = false; };
|
||||
ip = mkOption { type = types.nullOr types.str; default = null;};
|
||||
subdomain = mkOption { type = types.nullOr types.str; default=null;};
|
||||
port = mkOption { type = types.nullOr types.port; default = null; };
|
||||
extra = mkOption { type = types.attrs; default = {}; };
|
||||
};
|
||||
});
|
||||
default = {};
|
||||
};
|
||||
openssh = mkOption {
|
||||
type = types.bool;
|
||||
default = false;
|
||||
};
|
||||
wireguard = mkOption {
|
||||
type = types.bool;
|
||||
default = false;
|
||||
};
|
||||
web = mkOption {
|
||||
type = types.bool;
|
||||
default = false;
|
||||
};
|
||||
ipfw = {
|
||||
enable = mkOption {
|
||||
type = types.bool;
|
||||
default = false;
|
||||
};
|
||||
ifs = mkOption {
|
||||
type = types.listOf types.str;
|
||||
default = [ ];
|
||||
};
|
||||
ports = mkOption {
|
||||
type = types.listOf (types.listOf (types.oneOf [ types.str types.int ]));
|
||||
default = [];
|
||||
description = "Forwarding rules: [ [srcInterface dstAddr srcPort dstPort] ... ]";
|
||||
example = [
|
||||
[ "ens3" "10.10.1.2" "IPV6" 22 2222 ]
|
||||
[ "ens3" "10.10.1.2" "IPV6" 80 80 ]
|
||||
[ "ens3" "10.10.1.2" "IPV6" 443 443 ]
|
||||
];
|
||||
};
|
||||
};
|
||||
|
||||
db = mkOption {
|
||||
type = types.listOf (types.str);
|
||||
default = [ ];
|
||||
};
|
||||
|
||||
};
|
||||
in with lib; {
|
||||
options.usercfg = userOpt;
|
||||
options.usercfg = import ./user.nix {inherit lib;};
|
||||
options.syscfg = {
|
||||
hostname = mkOption { type = types.str; };
|
||||
type = mkOption {
|
||||
@@ -171,20 +19,17 @@ in with lib; {
|
||||
default = "x86_64-linux";
|
||||
};
|
||||
defaultUser = mkOption { type = types.str; };
|
||||
make = makeOpt;
|
||||
net = netOpt;
|
||||
make = import ./make.nix {inherit lib;};
|
||||
net = import ./net.nix {inherit lib;};
|
||||
users = mkOption {
|
||||
type = types.listOf (types.submodule { options = userOpt; });
|
||||
type = types.listOf (types.submodule { options = import ./user.nix {inherit lib;}; });
|
||||
default = [ ];
|
||||
};
|
||||
peers = mkOption {
|
||||
default = map (name: import (systemsDir + "/${name}/cfg.nix")) systemNames;
|
||||
};
|
||||
server = mkOption {
|
||||
type = types.oneOf [
|
||||
types.bool
|
||||
(types.submodule { options = serverOpt; })
|
||||
];
|
||||
type = types.oneOf [ types.bool (types.submodule { options = import ./server.nix {inherit lib;}; }) ];
|
||||
default = false;
|
||||
};
|
||||
};
|
||||
|
||||
9
modules/shared/syscfg/make.nix
Normal file
9
modules/shared/syscfg/make.nix
Normal file
@@ -0,0 +1,9 @@
|
||||
{ lib,... }:
|
||||
with lib; {
|
||||
cli = mkOption { type = types.bool; default = true; };
|
||||
gui = mkOption { type = types.bool; default = false; };
|
||||
virt = mkOption { type = types.bool; default = false; };
|
||||
power = mkOption { type = types.bool; default = false; };
|
||||
game = mkOption { type = types.bool; default = false; };
|
||||
develop = mkOption { type = types.bool; default = false; };
|
||||
}
|
||||
14
modules/shared/syscfg/net.nix
Normal file
14
modules/shared/syscfg/net.nix
Normal file
@@ -0,0 +1,14 @@
|
||||
{ lib,... }:
|
||||
with lib; {
|
||||
ble.enable = mkOption { type = types.bool; default = false; };
|
||||
wlp = {
|
||||
enable = mkOption { type = types.bool; default = false; };
|
||||
nif = mkOption { type = types.str; default = ""; };
|
||||
};
|
||||
wg = {
|
||||
enable = mkOption { type = types.bool; default = false; };
|
||||
ip4 = mkOption { type = types.str; default = ""; };
|
||||
ip6 = mkOption { type = types.str; default = ""; };
|
||||
pubkey = mkOption { type = types.str; default = ""; };
|
||||
};
|
||||
}
|
||||
93
modules/shared/syscfg/server.nix
Normal file
93
modules/shared/syscfg/server.nix
Normal file
@@ -0,0 +1,93 @@
|
||||
{ lib,... }:
|
||||
let
|
||||
|
||||
in with lib; {
|
||||
domain = mkOption { type = types.str; };
|
||||
mailDomain = mkOption { type = types.str; };
|
||||
mailServer = mkOption { type = types.str; };
|
||||
|
||||
configPath = mkOption {
|
||||
type = types.str;
|
||||
default = "/media/config";
|
||||
};
|
||||
dataPath = mkOption {
|
||||
type = types.str;
|
||||
default = "/media/data";
|
||||
};
|
||||
|
||||
colorScheme = mkOption {
|
||||
type = types.attrs;
|
||||
default = (lib.evalModules { modules =[ { freeformType = with lib.types; attrsOf anything; } ../colors ];}).config.colorScheme ;
|
||||
};
|
||||
loadedContainers = lib.mkOption {
|
||||
readOnly = true;
|
||||
type = lib.types.attrsOf (lib.types.submodule ({ name, ... }: {
|
||||
options = {
|
||||
name = lib.mkOption {type = lib.types.str; default = name;};
|
||||
sops = lib.mkOption {type = lib.types.bool; default = false;};
|
||||
db = lib.mkOption {type = lib.types.bool; default = false;};
|
||||
|
||||
paths = lib.mkOption {type = lib.types.listOf lib.types.attrs; default = [ ];};
|
||||
containers = lib.mkOption {type = lib.types.attrsOf lib.types.attrs; default = { };};
|
||||
vm = lib.mkOption {type = lib.types.nullOr lib.types.attrs; default = null;};
|
||||
cron = lib.mkOption {type = lib.types.listOf lib.types.str; default = [ ];};
|
||||
|
||||
setup = {
|
||||
trigger = lib.mkOption {type = lib.types.str; default = "";};
|
||||
script = lib.mkOption {type = lib.types.nullOr lib.types.package; default = null;};
|
||||
envFile = lib.mkOption {type = with lib.types; coercedTo str (x: [x]) (listOf str); default = [];};
|
||||
};
|
||||
};
|
||||
}));
|
||||
|
||||
};
|
||||
containers = mkOption {
|
||||
type = types.attrsOf (types.submodule {
|
||||
options = {
|
||||
subdomain = mkOption { type = types.nullOr types.str; default=null;};
|
||||
subpath = mkOption { type = types.nullOr types.str; default=null;};
|
||||
port = mkOption { type = types.nullOr types.port; default = null; };
|
||||
extra = mkOption { type = types.attrs; default = {}; };
|
||||
};
|
||||
});
|
||||
default = {};
|
||||
};
|
||||
openssh = mkOption {
|
||||
type = types.bool;
|
||||
default = false;
|
||||
};
|
||||
wireguard = mkOption {
|
||||
type = types.bool;
|
||||
default = false;
|
||||
};
|
||||
web = mkOption {
|
||||
type = types.bool;
|
||||
default = false;
|
||||
};
|
||||
ipfw = {
|
||||
enable = mkOption {
|
||||
type = types.bool;
|
||||
default = false;
|
||||
};
|
||||
ifs = mkOption {
|
||||
type = types.listOf types.str;
|
||||
default = [ ];
|
||||
};
|
||||
ports = mkOption {
|
||||
type = types.listOf (types.listOf (types.oneOf [ types.str types.int ]));
|
||||
default = [];
|
||||
description = "Forwarding rules: [ [srcInterface dstAddr srcPort dstPort] ... ]";
|
||||
example = [
|
||||
[ "ens3" "10.10.1.2" "IPV6" 22 2222 ]
|
||||
[ "ens3" "10.10.1.2" "IPV6" 80 80 ]
|
||||
[ "ens3" "10.10.1.2" "IPV6" 443 443 ]
|
||||
];
|
||||
};
|
||||
};
|
||||
|
||||
db = mkOption {
|
||||
type = types.listOf (types.str);
|
||||
default = [ ];
|
||||
};
|
||||
|
||||
}
|
||||
14
modules/shared/syscfg/user.nix
Normal file
14
modules/shared/syscfg/user.nix
Normal file
@@ -0,0 +1,14 @@
|
||||
{ lib,... }:
|
||||
with lib; {
|
||||
username = mkOption { type = types.str; };
|
||||
pubssh = mkOption { type = types.str; default=""; };
|
||||
wm = mkOption {
|
||||
type = types.enum [ "Wayland" "X11" "-" ];
|
||||
default = "-";
|
||||
};
|
||||
git = {
|
||||
username = mkOption { type = types.str; default = "Anonymous";};
|
||||
email = mkOption { type = types.str; default = "anonymous@domain"; };
|
||||
key = mkOption { type = types.nullOr types.str; default=null; };
|
||||
};
|
||||
}
|
||||
@@ -19,47 +19,43 @@
|
||||
virt = true;
|
||||
};
|
||||
server = {
|
||||
domain = "test.helcel.net";
|
||||
openssh = true;
|
||||
web = true;
|
||||
|
||||
hostDomain = "test.helcel.net";
|
||||
# mail = {
|
||||
# server = ...
|
||||
# user = ...
|
||||
# ...
|
||||
# };
|
||||
mailDomain = "test@helcel";
|
||||
mailServer = "infomaniak.ch";
|
||||
|
||||
containers = {
|
||||
|
||||
traefik = {
|
||||
enable = true;
|
||||
sops = true;
|
||||
subdomain = "traefik";
|
||||
extra={provider="infomaniak";};
|
||||
};
|
||||
authentik = {
|
||||
enable = true;
|
||||
db = true;
|
||||
subdomain = "sso";
|
||||
port = 9000;
|
||||
};
|
||||
nextcloud = {
|
||||
enable = true;
|
||||
db = true;
|
||||
subdomain = "cloud";
|
||||
};
|
||||
collabora = {
|
||||
enable = true;
|
||||
sops = true;
|
||||
subdomain = "office";
|
||||
};
|
||||
etherpad = {
|
||||
enable = true;
|
||||
db = true;
|
||||
subdomain = "pad";
|
||||
};
|
||||
gitea = {
|
||||
enable = true;
|
||||
db = true;
|
||||
subdomain = "git";
|
||||
};
|
||||
# ===== BASE =====
|
||||
traefik.subdomain = "traefik";
|
||||
traefik.extra={provider="infomaniak";};
|
||||
umami.subdomain = "umami";
|
||||
authentik.subdomain = "sso";
|
||||
searxng.subdomain = "searx";
|
||||
# ===== CLOUD =====
|
||||
# nextcloud.subdomain = "cloud";
|
||||
# collabora.subdomain = "office";
|
||||
# etherpad.subdomain = "pad";
|
||||
# ethercalc.subdomain = "calc";
|
||||
# immich.subdomain = "pic";
|
||||
# ===== FLIX =====
|
||||
# invidious.subdomain = "yt";
|
||||
jellyfin.subdomain = "flix";
|
||||
servarr.subdomain = "arr";
|
||||
transmission = { subdomain = "arr"; subpath = "transmission"; };
|
||||
handbrake = { subdomain = "arr"; subpath = "hb"; };
|
||||
# ===== DEV =====
|
||||
gitea.subdomain = "git";
|
||||
# ===== HOME =====
|
||||
openhab.subdomain = "hab";
|
||||
# trmnl = { subdomain = "hass"; subpath = "trmnl"; };
|
||||
influx.subdomain = "metrum";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user