Compare commits
186 Commits
8c800ad0d3
...
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 |
104
flake.lock
generated
104
flake.lock
generated
@@ -1,27 +1,5 @@
|
|||||||
{
|
{
|
||||||
"nodes": {
|
"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": {
|
"base16-schemes": {
|
||||||
"flake": false,
|
"flake": false,
|
||||||
"locked": {
|
"locked": {
|
||||||
@@ -45,11 +23,11 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1777780666,
|
"lastModified": 1779036909,
|
||||||
"narHash": "sha256-8wURyQMdDkGUarSTKOGdCuFfYiwa3HbzwscUfn3STDE=",
|
"narHash": "sha256-zXcwYQGCT6pzinK+1dBB2ekTVtfxGZAapb3Evdcu4fY=",
|
||||||
"owner": "lnl7",
|
"owner": "lnl7",
|
||||||
"repo": "nix-darwin",
|
"repo": "nix-darwin",
|
||||||
"rev": "8c62fba0854ba15c8917aed18894dbccb48a3777",
|
"rev": "56c666e108467d87d13508936aade6d567f2a501",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -60,27 +38,6 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"flake-parts": {
|
"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": {
|
"inputs": {
|
||||||
"nixpkgs-lib": [
|
"nixpkgs-lib": [
|
||||||
"nur",
|
"nur",
|
||||||
@@ -103,11 +60,11 @@
|
|||||||
},
|
},
|
||||||
"hardware": {
|
"hardware": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1778143761,
|
"lastModified": 1779258371,
|
||||||
"narHash": "sha256-lkesY6x2X2qxlqLM7CT2iM/0rP2JB7fruPN3h8POXmI=",
|
"narHash": "sha256-j1iZsLy6oFApqR1oiDmHhvkwxXqcNi0aoSJj643LuwU=",
|
||||||
"owner": "nixos",
|
"owner": "nixos",
|
||||||
"repo": "nixos-hardware",
|
"repo": "nixos-hardware",
|
||||||
"rev": "3bcaa367d4c550d687a17ac792fd5cda214ee871",
|
"rev": "c97bc4d15bd3473dd095e8e8ba57330ab1943a77",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -116,22 +73,6 @@
|
|||||||
"type": "github"
|
"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": {
|
"home-manager": {
|
||||||
"inputs": {
|
"inputs": {
|
||||||
"nixpkgs": [
|
"nixpkgs": [
|
||||||
@@ -139,11 +80,11 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1777851538,
|
"lastModified": 1778905220,
|
||||||
"narHash": "sha256-Gp8qwTEYNoy2yvmErVGlvLOQvrtEECCAKbonW7VJef8=",
|
"narHash": "sha256-ox/5IHc8uwy6UTw6N7Shp6uCHIgu/S2PsWeuXsOHSo8=",
|
||||||
"owner": "nix-community",
|
"owner": "nix-community",
|
||||||
"repo": "home-manager",
|
"repo": "home-manager",
|
||||||
"rev": "cc09c0f9b7eaa95c2d9827338a5eb03d32505ca5",
|
"rev": "d1686dc7d36cbd1234cb226ad6ef97e882716acb",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -174,11 +115,11 @@
|
|||||||
},
|
},
|
||||||
"nixUnstable": {
|
"nixUnstable": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1778274207,
|
"lastModified": 1779259093,
|
||||||
"narHash": "sha256-I4puXmX1iovcCHZlRmztO3vW0mAbbRvq4F8wgIMQ1MM=",
|
"narHash": "sha256-7DKWmH23hL2eYdkxCKeqj2i+yljTKuU+3Nk1UPHOnxc=",
|
||||||
"owner": "nixos",
|
"owner": "nixos",
|
||||||
"repo": "nixpkgs",
|
"repo": "nixpkgs",
|
||||||
"rev": "b3da656039dc7a6240f27b2ef8cc6a3ef3bccae7",
|
"rev": "d99b013d5d1931ad77fe3912ed218170dec5d9a4",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -190,11 +131,11 @@
|
|||||||
},
|
},
|
||||||
"nixpkgs": {
|
"nixpkgs": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1778003029,
|
"lastModified": 1779102034,
|
||||||
"narHash": "sha256-q/nkKLDtHIyLjZpKhWk3cSK5IYsFqtMd6UtXF3ddjgA=",
|
"narHash": "sha256-vZJZjLo513IeI8hjzHFc6TDezUd4uCE2Eq4SNO3DNNg=",
|
||||||
"owner": "nixos",
|
"owner": "nixos",
|
||||||
"repo": "nixpkgs",
|
"repo": "nixpkgs",
|
||||||
"rev": "0c88e1f2bdb93d5999019e99cb0e61e1fe2af4c5",
|
"rev": "687f05a9184cad4eaf905c48b63649e3a86f5433",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -221,11 +162,11 @@
|
|||||||
},
|
},
|
||||||
"nixpkgs_2": {
|
"nixpkgs_2": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1777954456,
|
"lastModified": 1778869304,
|
||||||
"narHash": "sha256-hGdgeU2Nk87RAuZyYjyDjFL6LK7dAZN5RE9+hrDTkDU=",
|
"narHash": "sha256-30sZNZoA1cqF5JNO9fVX+wgiQYjB7HJqqJ4ztCDeBZE=",
|
||||||
"owner": "nixos",
|
"owner": "nixos",
|
||||||
"repo": "nixpkgs",
|
"repo": "nixpkgs",
|
||||||
"rev": "549bd84d6279f9852cae6225e372cc67fb91a4c1",
|
"rev": "d233902339c02a9c334e7e593de68855ad26c4cb",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -237,15 +178,15 @@
|
|||||||
},
|
},
|
||||||
"nur": {
|
"nur": {
|
||||||
"inputs": {
|
"inputs": {
|
||||||
"flake-parts": "flake-parts_2",
|
"flake-parts": "flake-parts",
|
||||||
"nixpkgs": "nixpkgs_2"
|
"nixpkgs": "nixpkgs_2"
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1778376280,
|
"lastModified": 1779398206,
|
||||||
"narHash": "sha256-pL2F2FF2FN7zWr5o/vG7GiYOSjp+DUNyPIYqNaLQFFs=",
|
"narHash": "sha256-jX+5X7dr4ZLA/LgCZUkKngFsA6MGHBMReOspOiE1mYI=",
|
||||||
"owner": "nix-community",
|
"owner": "nix-community",
|
||||||
"repo": "nur",
|
"repo": "nur",
|
||||||
"rev": "828688994167eb57628c98fd1d7e1223b079cda1",
|
"rev": "a592294d7840cea33f00c2a9f1efd396710f75af",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -256,7 +197,6 @@
|
|||||||
},
|
},
|
||||||
"root": {
|
"root": {
|
||||||
"inputs": {
|
"inputs": {
|
||||||
"arion": "arion",
|
|
||||||
"darwin": "darwin",
|
"darwin": "darwin",
|
||||||
"hardware": "hardware",
|
"hardware": "hardware",
|
||||||
"home-manager": "home-manager",
|
"home-manager": "home-manager",
|
||||||
|
|||||||
@@ -1,3 +1,9 @@
|
|||||||
{ ... }: {
|
{ ... }: {
|
||||||
imports = [ ./dbus ./fonts ./hw ./locale ./network ./nix ./security ./xdg ];
|
imports = [ ./dbus ./fonts ./hw ./locale ./network ./nix ./security ./xdg ];
|
||||||
|
|
||||||
|
# services.journald.extraConfig = ''
|
||||||
|
# LineMax=128K
|
||||||
|
# SystemMaxUse=512M
|
||||||
|
# SystemMaxFileSize=128M
|
||||||
|
# '';
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,7 +13,7 @@
|
|||||||
|
|
||||||
allowedTCPPorts =
|
allowedTCPPorts =
|
||||||
(if (config.syscfg.server != false && config.syscfg.server.web) then [ 80 443 22 ] else [ ]) ++
|
(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 [ ]) ++
|
||||||
[ ];
|
[ ];
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -15,6 +15,8 @@ let
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
in {
|
in {
|
||||||
|
sops = false;
|
||||||
|
db = false;
|
||||||
paths = [{
|
paths = [{
|
||||||
path="${serverCfg.configPath}/example/";
|
path="${serverCfg.configPath}/example/";
|
||||||
mode = "0444";
|
mode = "0444";
|
||||||
|
|||||||
@@ -3,6 +3,6 @@
|
|||||||
RSS: TTRSS / FreshRSS
|
RSS: TTRSS / FreshRSS
|
||||||
Monitoring: Telegraf + InfluxDB
|
Monitoring: Telegraf + InfluxDB
|
||||||
https://github.com/tarampampam/error-pages ?
|
https://github.com/tarampampam/error-pages ?
|
||||||
|
kavita + mylar ? kapowarr ?
|
||||||
|
|
||||||
- JellyFin external mkData for config (system.xml)
|
|
||||||
- Transmission Cfg and API/Token handling
|
- Transmission Cfg and API/Token handling
|
||||||
|
|||||||
@@ -4,10 +4,14 @@ let
|
|||||||
serverCfg = config.syscfg.server;
|
serverCfg = config.syscfg.server;
|
||||||
authentikData = builder.mkData {
|
authentikData = builder.mkData {
|
||||||
name = "authentik"; dir = "authentik"; vars = {
|
name = "authentik"; dir = "authentik"; vars = {
|
||||||
NEXTCLOUD_DOMAIN = "${serverCfg.containers.nextcloud.subdomain or "nextcloud"}.${serverCfg.hostDomain}";
|
AUTHENTIK_DOMAIN = "${containerCfg.subdomain}.${serverCfg.domain}";
|
||||||
AUTHENTIK_DOMAIN = "${containerCfg.subdomain}.${serverCfg.hostDomain}";
|
COOKIE_DOMAIN = "${serverCfg.domain}";
|
||||||
COOKIE_DOMAIN = "${serverCfg.hostDomain}";
|
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 {
|
in {
|
||||||
sops = true;
|
sops = true;
|
||||||
@@ -29,23 +33,23 @@ in {
|
|||||||
port = 9000;
|
port = 9000;
|
||||||
secret = name;
|
secret = name;
|
||||||
extraEnv = {
|
extraEnv = {
|
||||||
"AUTHENTIK_REDIS__HOST" = builder.host;
|
AUTHENTIK_REDIS__HOST = builder.host;
|
||||||
"AUTHENTIK_POSTGRESQL__HOST" = builder.host;
|
AUTHENTIK_POSTGRESQL__HOST = builder.host;
|
||||||
"AUTHENTIK_POSTGRESQL__USER" = "authentik_user";
|
AUTHENTIK_POSTGRESQL__USER = "authentik_user";
|
||||||
"AUTHENTIK_POSTGRESQL__NAME" = "authentik_db";
|
AUTHENTIK_POSTGRESQL__NAME = "authentik_db";
|
||||||
"AUTHENTIK_EMAIL__HOST" = serverCfg.mailDomain;
|
AUTHENTIK_POSAUTHENTIK_POSTGRESQL__SSLMODE = "false";
|
||||||
"AUTHENTIK_EMAIL__PORT" = "587";
|
AUTHENTIK_EMAIL__HOST = serverCfg.mailDomain;
|
||||||
"AUTHENTIK_EMAIL__USERNAME" = "noreply@${serverCfg.hostDomain}";
|
AUTHENTIK_EMAIL__PORT = "587";
|
||||||
"AUTHENTIK_EMAIL__USE_TLS" = "true";
|
AUTHENTIK_EMAIL__USERNAME = "noreply@${serverCfg.domain}";
|
||||||
"AUTHENTIK_EMAIL__USE_SSL" = "false";
|
AUTHENTIK_EMAIL__USE_TLS = "true";
|
||||||
"AUTHENTIK_EMAIL__TIMEOUT" = "10";
|
AUTHENTIK_EMAIL__USE_SSL = "false";
|
||||||
"AUTHENTIK_EMAIL__FROM" = "sso@noreply.${serverCfg.hostDomain}";
|
AUTHENTIK_EMAIL__TIMEOUT = "10";
|
||||||
"AUTHENTIK_DISABLE_UPDATE_CHECK" = "true";
|
AUTHENTIK_EMAIL__FROM = "sso@noreply.${serverCfg.domain}";
|
||||||
"AUTHENTIK_POSTGRESQL__SSLMODE" = "disable";
|
AUTHENTIK_DISABLE_UPDATE_CHECK = "true";
|
||||||
|
AUTHENTIK_POSTGRESQL__SSLMODE = "disable";
|
||||||
};
|
};
|
||||||
overrides = {
|
overrides = {
|
||||||
cmd = [ "server" ];
|
cmd = [ "server" ];
|
||||||
ports = if containerCfg.port!=null then [ "${toString containerCfg.port}:9000" ] else [];
|
|
||||||
volumes = [
|
volumes = [
|
||||||
"${serverCfg.configPath}/authentik/media:/media"
|
"${serverCfg.configPath}/authentik/media:/media"
|
||||||
"${serverCfg.configPath}/authentik/templates:/templates"
|
"${serverCfg.configPath}/authentik/templates:/templates"
|
||||||
@@ -56,14 +60,15 @@ in {
|
|||||||
|
|
||||||
worker = builder.mkContainer {
|
worker = builder.mkContainer {
|
||||||
image = "ghcr.io/goauthentik/server:${version}";
|
image = "ghcr.io/goauthentik/server:${version}";
|
||||||
secret = "authentik";
|
secret = name;
|
||||||
extraEnv = {
|
extraEnv = {
|
||||||
"AUTHENTIK_REDIS__HOST" = builder.host;
|
AUTHENTIK_REDIS__HOST = builder.host;
|
||||||
"AUTHENTIK_POSTGRESQL__HOST" = builder.host;
|
AUTHENTIK_POSTGRESQL__HOST = builder.host;
|
||||||
"AUTHENTIK_POSTGRESQL__USER" = "authentik_user";
|
AUTHENTIK_POSTGRESQL__USER = "authentik_user";
|
||||||
"AUTHENTIK_POSTGRESQL__NAME" = "authentik_db";
|
AUTHENTIK_POSTGRESQL__NAME = "authentik_db";
|
||||||
"AUTHENTIK_DISABLE_UPDATE_CHECK" = "true";
|
AUTHENTIK_POSAUTHENTIK_POSTGRESQL__SSLMODE = "false";
|
||||||
"AUTHENTIK_POSTGRESQL__SSLMODE" = "disable";
|
AUTHENTIK_DISABLE_UPDATE_CHECK = "true";
|
||||||
|
AUTHENTIK_POSTGRESQL__SSLMODE = "disable";
|
||||||
};
|
};
|
||||||
overrides = {
|
overrides = {
|
||||||
cmd = [ "worker" ];
|
cmd = [ "worker" ];
|
||||||
@@ -74,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 = {
|
setup = {
|
||||||
trigger = "worker";
|
trigger = "worker";
|
||||||
script = pkgs.writeShellScript "setup" ''
|
script = pkgs.writeShellScript "setup" ''
|
||||||
# Define the command wrapper
|
# 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/authentik.yaml
|
||||||
$AK apply_blueprint /blueprints/custom/traefik.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 ? nextcloud) ''$AK apply_blueprint /blueprints/custom/nextcloud.yaml''}
|
||||||
|
${lib.optionalString (serverCfg.containers ? immich) ''$AK apply_blueprint /blueprints/custom/immich.yaml''}
|
||||||
|
|
||||||
echo "Completed Authentik Setup"
|
echo "Completed Authentik Setup"
|
||||||
'';
|
'';
|
||||||
|
|||||||
@@ -11,10 +11,10 @@ in {
|
|||||||
port = 9980;
|
port = 9980;
|
||||||
secret = name;
|
secret = name;
|
||||||
extraEnv = {
|
extraEnv = {
|
||||||
"aliasgroup1" = "https://${serverCfg.containers.nextcloud.subdomain}.${serverCfg.hostDomain}";
|
"aliasgroup1" = "https://${serverCfg.containers.nextcloud.subdomain}.${serverCfg.domain}";
|
||||||
"server_name" = "${containerCfg.subdomain}.${serverCfg.hostDomain}";
|
"server_name" = "${containerCfg.subdomain}.${serverCfg.domain}";
|
||||||
"username" = "collabora_user";
|
"username" = "collabora_user";
|
||||||
"VIRTUAL_HOST" = "${containerCfg.subdomain}.${serverCfg.hostDomain}";
|
"VIRTUAL_HOST" = "${containerCfg.subdomain}.${serverCfg.domain}";
|
||||||
"VIRTUAL_PORT" = "9980";
|
"VIRTUAL_PORT" = "9980";
|
||||||
"VIRTUAL_PROTO" = "http";
|
"VIRTUAL_PROTO" = "http";
|
||||||
"DONT_GEN_SSL_CERT" = "true";
|
"DONT_GEN_SSL_CERT" = "true";
|
||||||
|
|||||||
@@ -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,6 +2,8 @@
|
|||||||
let
|
let
|
||||||
version = "latest";
|
version = "latest";
|
||||||
serverCfg = config.syscfg.server;
|
serverCfg = config.syscfg.server;
|
||||||
|
|
||||||
|
LDAP_DC_DOMAIN = "dc=ldap," + (lib.concatMapStringsSep "," (x: "dc=${x}") (lib.splitString "." serverCfg.domain));
|
||||||
in {
|
in {
|
||||||
sops = true;
|
sops = true;
|
||||||
db = true;
|
db = true;
|
||||||
@@ -47,8 +49,8 @@ in {
|
|||||||
GITEA__mailer__SMTP_PORT = "";
|
GITEA__mailer__SMTP_PORT = "";
|
||||||
GITEA__mailer__USER= "";
|
GITEA__mailer__USER= "";
|
||||||
|
|
||||||
GITEA__server__DOMAIN = "${containerCfg.subdomain}.${serverCfg.hostDomain}";
|
GITEA__server__DOMAIN = "${containerCfg.subdomain}.${serverCfg.domain}";
|
||||||
GITEA__server__ROOT_URL = "https://${containerCfg.subdomain}.${serverCfg.hostDomain}/";
|
GITEA__server__ROOT_URL = "https://${containerCfg.subdomain}.${serverCfg.domain}/";
|
||||||
GITEA__server__PROTOCOL = "http";
|
GITEA__server__PROTOCOL = "http";
|
||||||
GITEA__server__HTTP_PORT = "8080";
|
GITEA__server__HTTP_PORT = "8080";
|
||||||
GITEA__server__LFS_START_SERVER = "true";
|
GITEA__server__LFS_START_SERVER = "true";
|
||||||
@@ -62,7 +64,7 @@ in {
|
|||||||
GITEA__service__ENABLE_REVERSE_PROXY_EMAIL = "true";
|
GITEA__service__ENABLE_REVERSE_PROXY_EMAIL = "true";
|
||||||
GITEA__service__ENABLE_REVERSE_PROXY_FULL_NAME = "true";
|
GITEA__service__ENABLE_REVERSE_PROXY_FULL_NAME = "true";
|
||||||
GITEA__service__ALLOW_ONLY_EXTERNAL_REGISTRATION = "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_USER = "X-authentik-username";
|
||||||
GITEA__security__REVERSE_PROXY_AUTHENTICATION_EMAIL = "X-authentik-email";
|
GITEA__security__REVERSE_PROXY_AUTHENTICATION_EMAIL = "X-authentik-email";
|
||||||
GITEA__security__REVERSE_PROXY_AUTHENTICATION_FULL_NAME = "X-authentik-name";
|
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";
|
GITEA__security__REVERSE_PROXY_TRUSTED_PROXIES = "127.0.0.0/8,::1/128,10.0.0.0/8";
|
||||||
} else {});
|
} else {});
|
||||||
extraLabels = {
|
extraLabels = {
|
||||||
"traefik.http.routers.${containerCfg.subdomain}-login.rule" = "Host(`${containerCfg.subdomain}.${serverCfg.hostDomain}`) && Path(`/user/login`) ";
|
"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 then "authentik" else "";
|
"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.priority" = "100";
|
||||||
"traefik.http.routers.${containerCfg.subdomain}-login.entrypoints" = "web-secure";
|
"traefik.http.routers.${containerCfg.subdomain}-login.entrypoints" = "web-secure";
|
||||||
"traefik.http.routers.${containerCfg.subdomain}-login.tls" = "true";
|
"traefik.http.routers.${containerCfg.subdomain}-login.tls" = "true";
|
||||||
@@ -90,8 +92,8 @@ in {
|
|||||||
secret = name;
|
secret = name;
|
||||||
extraEnv = {
|
extraEnv = {
|
||||||
CONFIG_FILE="/data/config.yml";
|
CONFIG_FILE="/data/config.yml";
|
||||||
GITEA_INSTANCE_URL="https://${containerCfg.subdomain}.${serverCfg.hostDomain}";
|
GITEA_INSTANCE_URL="https://${containerCfg.subdomain}.${serverCfg.domain}";
|
||||||
GITHUB_INSTANCE_URL="https://${containerCfg.subdomain}.${serverCfg.hostDomain}";
|
GITHUB_INSTANCE_URL="https://${containerCfg.subdomain}.${serverCfg.domain}";
|
||||||
};
|
};
|
||||||
|
|
||||||
overrides = {
|
overrides = {
|
||||||
@@ -115,15 +117,28 @@ in {
|
|||||||
|
|
||||||
$GT admin user create --username "$DEFAULT_ADMIN_USERNAME" --password "$DEFAULT_ADMIN_PASSWORD" --email "$DEFAULT_ADMIN_EMAIL" --admin || true
|
$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)
|
RUNNER_TOKEN=$($GT actions generate-runner-token)
|
||||||
$GTR register \
|
$GTR register \
|
||||||
--instance "https://${containerCfg.subdomain}.${serverCfg.hostDomain}" \
|
--instance "https://${containerCfg.subdomain}.${serverCfg.domain}" \
|
||||||
--token "$RUNNER_TOKEN" \
|
--token "$RUNNER_TOKEN" \
|
||||||
--name "Runner" \
|
--name "Runner" \
|
||||||
--labels "ubuntu-latest:docker://catthehacker/ubuntu:act-latest" \
|
--labels "ubuntu-latest:docker://catthehacker/ubuntu:act-latest" \
|
||||||
--no-interactive
|
--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"
|
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
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -1,3 +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,31 +1,39 @@
|
|||||||
{ config, containerCfg, pkgs, lib, builder, name, ... }:
|
{ config, containerCfg, pkgs, lib, builder, name, ... }:
|
||||||
let
|
let
|
||||||
serverCfg = config.syscfg.server;
|
serverCfg = config.syscfg.server;
|
||||||
image = pkgs.dockerTools.streamLayeredImage {
|
LDAP_DC_DOMAIN = "dc=ldap," + (lib.concatMapStringsSep "," (x: "dc=${x}") (lib.splitString "." serverCfg.domain));
|
||||||
name = pkgs.jellyfin.name;
|
nss = pkgs.dockerTools.fakeNss.override {
|
||||||
tag = pkgs.jellyfin.version;
|
extraPasswdLines = [
|
||||||
contents = [
|
"jellyfin:x:1000:1000:Jellyfin Daemon:/config/data:/bin/false"
|
||||||
pkgs.cacert
|
|
||||||
];
|
];
|
||||||
|
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 = {
|
config = {
|
||||||
|
User = "jellyfin:jellyfin";
|
||||||
Entrypoint = [ "${pkgs.jellyfin}/bin/jellyfin" ];
|
Entrypoint = [ "${pkgs.jellyfin}/bin/jellyfin" ];
|
||||||
ExposedPorts = { "8096/tcp" = { }; };
|
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"
|
||||||
|
];
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
#LDAP_DC_DOMAIN = "dc=ldap,dc=helcel,dc=net"
|
|
||||||
#HOST=...
|
|
||||||
#LDAP_BIND_USER=ldap-sa
|
|
||||||
#LDAP_BIND_PASSWORD=...
|
|
||||||
#LDAP_GROUP=flix
|
|
||||||
#LDAP_ADMIN=admin
|
|
||||||
in {
|
in {
|
||||||
paths = [
|
paths = [
|
||||||
{
|
{
|
||||||
path = "${serverCfg.dataPath}/media/";
|
path = "${serverCfg.dataPath}/media/";
|
||||||
|
owner = "1000:1000";
|
||||||
mode = "0755";
|
mode = "0755";
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
path = "${serverCfg.configPath}/jellyfin/";
|
path = "${serverCfg.configPath}/jellyfin/";
|
||||||
|
owner = "1000:1000";
|
||||||
mode = "0755";
|
mode = "0755";
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
@@ -35,11 +43,11 @@ in {
|
|||||||
subdomain = containerCfg.subdomain;
|
subdomain = containerCfg.subdomain;
|
||||||
imageStream = image;
|
imageStream = image;
|
||||||
port = 8096;
|
port = 8096;
|
||||||
# secret = name;
|
|
||||||
extraEnv = {
|
extraEnv = {
|
||||||
|
HOME = "/config/data";
|
||||||
DOTNET_SYSTEM_GLOBALIZATION_INVARIANT = "1";
|
DOTNET_SYSTEM_GLOBALIZATION_INVARIANT = "1";
|
||||||
# JELLYFIN_WEB_DIR = "${pkgs.jellyfin-web}/share/jellyfin-web";
|
|
||||||
JELLYFIN_HttpListenerHost__BindAddress= "0.0.0.0"; #we can use settings.xml override
|
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 = [
|
extraOptions = [
|
||||||
"--tmpfs=/tmp:rw,noexec,nosuid,size=512m"
|
"--tmpfs=/tmp:rw,noexec,nosuid,size=512m"
|
||||||
@@ -52,7 +60,7 @@ in {
|
|||||||
"--logdir" "/config/log"
|
"--logdir" "/config/log"
|
||||||
];
|
];
|
||||||
volumes = [
|
volumes = [
|
||||||
"${serverCfg.dataPath}:/media:ro"
|
"${serverCfg.dataPath}/media:/media:ro"
|
||||||
"${serverCfg.configPath}/jellyfin:/config"
|
"${serverCfg.configPath}/jellyfin:/config"
|
||||||
];
|
];
|
||||||
# If you have an Intel/AMD GPU for transcoding, add the device:
|
# If you have an Intel/AMD GPU for transcoding, add the device:
|
||||||
@@ -60,4 +68,110 @@ in {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
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
@@ -28,11 +28,11 @@ in {
|
|||||||
POSTGRES_USER = "nextcloud_user";
|
POSTGRES_USER = "nextcloud_user";
|
||||||
POSTGRES_DB = "nextcloud_db";
|
POSTGRES_DB = "nextcloud_db";
|
||||||
AUTHENTIK_POSTGRESQL__SSLMODE = "disable";
|
AUTHENTIK_POSTGRESQL__SSLMODE = "disable";
|
||||||
"NEXTCLOUD_TRUSTED_DOMAINS " = "${containerCfg.subdomain}.${serverCfg.hostDomain}";
|
"NEXTCLOUD_TRUSTED_DOMAINS " = "${containerCfg.subdomain}.${serverCfg.domain}";
|
||||||
"SMTP_HOST" = serverCfg.mailServer;
|
"SMTP_HOST" = serverCfg.mailServer;
|
||||||
"SMTP_NAME" = "mail_user";
|
"SMTP_NAME" = "mail_user";
|
||||||
"SMTP_PASSWORD" = "mail_password";
|
"SMTP_PASSWORD" = "mail_password";
|
||||||
"MAIL_FROM_ADDRESS" = "${containerCfg.subdomain}@${serverCfg.hostDomain}";
|
"MAIL_FROM_ADDRESS" = "${containerCfg.subdomain}@${serverCfg.domain}";
|
||||||
"MAIL_DOMAIN" = serverCfg.mailDomain;
|
"MAIL_DOMAIN" = serverCfg.mailDomain;
|
||||||
"TRUSTED_PROXIES" = "10.10.0.0/16 192.168.0.0/16";
|
"TRUSTED_PROXIES" = "10.10.0.0/16 192.168.0.0/16";
|
||||||
};
|
};
|
||||||
@@ -61,7 +61,7 @@ in {
|
|||||||
trigger = "server";
|
trigger = "server";
|
||||||
script = pkgs.writeShellScript "setup" ''
|
script = pkgs.writeShellScript "setup" ''
|
||||||
# Define the command wrapper
|
# 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..."
|
echo "Waiting for Nextcloud container to start..."
|
||||||
until $OCC status > /dev/null 2>&1; do
|
until $OCC status > /dev/null 2>&1; do
|
||||||
@@ -102,7 +102,6 @@ in {
|
|||||||
$OCC app:disable updatenotification || true
|
$OCC app:disable updatenotification || true
|
||||||
$OCC app:disable user_status || true
|
$OCC app:disable user_status || true
|
||||||
|
|
||||||
$OCC app:install calendar || true
|
|
||||||
$OCC app:install calendar || true
|
$OCC app:install calendar || true
|
||||||
$OCC app:install contacts || true
|
$OCC app:install contacts || true
|
||||||
$OCC app:install camerarawpreviews || true
|
$OCC app:install camerarawpreviews || true
|
||||||
@@ -126,23 +125,23 @@ in {
|
|||||||
|
|
||||||
${lib.optionalString (serverCfg.containers ? ethercalc) ''
|
${lib.optionalString (serverCfg.containers ? ethercalc) ''
|
||||||
$OCC config:app:set ownpad ownpad_ethercalc_enable --value="yes"
|
$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) ''
|
${lib.optionalString (serverCfg.containers ? etherpad) ''
|
||||||
$OCC config:app:set ownpad ownpad_etherpad_enable --value="yes"
|
$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) ''
|
${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 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.hostDomain}"
|
$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"
|
$OCC config:app:set richdocuments wopi_allowlist --value="10.0.0.0/8"
|
||||||
''}
|
''}
|
||||||
${lib.optionalString (serverCfg.containers ? authentik) ''
|
${lib.optionalString (serverCfg.containers ? authentik) ''
|
||||||
$OCC saml:config:set 1 --general-idp0_display_name="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 --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-entityId="https://${serverCfg.containers.authentik.subdomain}.${serverCfg.domain}"
|
||||||
$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-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.hostDomain}/application/saml/nextcloud/slo/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';")
|
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"
|
$OCC saml:config:set 1 --idp-x509cert="$AUTHENTIK_CERT"
|
||||||
|
|
||||||
@@ -172,7 +171,7 @@ in {
|
|||||||
$OCC config:app:set systemtags allow_user_creating --value="no"
|
$OCC config:app:set systemtags allow_user_creating --value="no"
|
||||||
|
|
||||||
echo "Applying Theme..."
|
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 ? 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}"''}
|
${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}"
|
$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)
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -11,8 +11,8 @@ let
|
|||||||
wiki_url = "";
|
wiki_url = "";
|
||||||
custom = {
|
custom = {
|
||||||
links = {
|
links = {
|
||||||
"Home" = "https://${serverCfg.hostDomain}";
|
"Home" = "https://${serverCfg.domain}";
|
||||||
# "Status" = "https://status.${serverCfg.hostDomain}";
|
# "Status" = "https://status.${serverCfg.domain}";
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
pwa_colors = {
|
pwa_colors = {
|
||||||
@@ -72,7 +72,7 @@ in {
|
|||||||
port = 8080;
|
port = 8080;
|
||||||
secret = name;
|
secret = name;
|
||||||
extraEnv = {
|
extraEnv = {
|
||||||
SEARXNG_BASE_URL = "https://${containerCfg.subdomain}.${serverCfg.hostDomain}";
|
SEARXNG_BASE_URL = "https://${containerCfg.subdomain}.${serverCfg.domain}";
|
||||||
SEARXNG_PORT = "8080";
|
SEARXNG_PORT = "8080";
|
||||||
SEARXNG_BIND_ADDRESS = "[::]";
|
SEARXNG_BIND_ADDRESS = "[::]";
|
||||||
SEARXNG_PUBLIC_INSTANCE = "false";
|
SEARXNG_PUBLIC_INSTANCE = "false";
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
@@ -29,14 +29,20 @@ in {
|
|||||||
"traefik.http.routers.${containerCfg.subdomain}.service" = "api@internal";
|
"traefik.http.routers.${containerCfg.subdomain}.service" = "api@internal";
|
||||||
|
|
||||||
"traefik.http.routers.${containerCfg.subdomain}.middlewares" = if serverCfg.containers?authentik then "authentik" else "";
|
"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.maxResponseBodySize" = "10485760";
|
||||||
"traefik.http.middlewares.authentik.forwardauth.address" = "http://authentik-server:9000/outpost.goauthentik.io/auth/traefik";
|
"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.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";
|
"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.certresolver" = "default";
|
||||||
"traefik.http.routers.${containerCfg.subdomain}.tls.domains[0].main" = "${serverCfg.hostDomain}";
|
"traefik.http.routers.${containerCfg.subdomain}.tls.domains[0].main" = "${serverCfg.domain}";
|
||||||
"traefik.http.routers.${containerCfg.subdomain}.tls.domains[0].sans" = "*.${serverCfg.hostDomain}";
|
"traefik.http.routers.${containerCfg.subdomain}.tls.domains[0].sans" = "*.${serverCfg.domain}";
|
||||||
} else {});
|
} else {});
|
||||||
extraEnv = { };
|
extraEnv = { };
|
||||||
overrides = {
|
overrides = {
|
||||||
@@ -60,11 +66,11 @@ in {
|
|||||||
"--experimental.plugins.umami-feeder.version=v1.4.1"
|
"--experimental.plugins.umami-feeder.version=v1.4.1"
|
||||||
"--entrypoints.web-secure.http.middlewares=umami-global@docker"
|
"--entrypoints.web-secure.http.middlewares=umami-global@docker"
|
||||||
] else []) ++ (if containerCfg.extra ? provider then [
|
] else []) ++ (if containerCfg.extra ? provider then [
|
||||||
"--certificatesresolvers.default.acme.email=acme@${serverCfg.hostDomain}"
|
"--certificatesresolvers.default.acme.email=acme@${serverCfg.domain}"
|
||||||
"--certificatesresolvers.default.acme.dnschallenge=true"
|
"--certificatesresolvers.default.acme.dnschallenge=true"
|
||||||
"--certificatesresolvers.default.acme.dnschallenge.provider=${containerCfg.extra.provider}"
|
"--certificatesresolvers.default.acme.dnschallenge.provider=${containerCfg.extra.provider}"
|
||||||
"--certificatesresolvers.default.acme.storage=/custom/acme.json"
|
"--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.httpchallenge=false"
|
||||||
"--certificatesresolvers.default.acme.tlschallenge=true"
|
"--certificatesresolvers.default.acme.tlschallenge=true"
|
||||||
] else []);
|
] else []);
|
||||||
|
|||||||
@@ -13,6 +13,9 @@ let
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
routerName = if containerCfg.subpath != null
|
||||||
|
then "${containerCfg.subdomain}-${lib.strings.sanitizeDerivationName containerCfg.subpath}"
|
||||||
|
else containerCfg.subdomain;
|
||||||
in {
|
in {
|
||||||
paths = [{
|
paths = [{
|
||||||
path = "${serverCfg.dataPath}/transmission/complete";
|
path = "${serverCfg.dataPath}/transmission/complete";
|
||||||
@@ -23,7 +26,7 @@ in {
|
|||||||
owner = "1000:1000";
|
owner = "1000:1000";
|
||||||
mode = "0755";
|
mode = "0755";
|
||||||
}{
|
}{
|
||||||
path = "${serverCfg.dataPath}/transmission/config";
|
path = "${serverCfg.configPath}/transmission/config";
|
||||||
owner = "1000:1000";
|
owner = "1000:1000";
|
||||||
mode = "0755";
|
mode = "0755";
|
||||||
}];
|
}];
|
||||||
@@ -31,16 +34,19 @@ in {
|
|||||||
containers = {
|
containers = {
|
||||||
server = builder.mkContainer {
|
server = builder.mkContainer {
|
||||||
subdomain = containerCfg.subdomain;
|
subdomain = containerCfg.subdomain;
|
||||||
|
subpath = containerCfg.subpath;
|
||||||
imageStream = image;
|
imageStream = image;
|
||||||
port = 9091;
|
port = 9091;
|
||||||
|
|
||||||
extraEnv = {
|
extraEnv = {
|
||||||
PUID = "1000";
|
PUID = "1000";
|
||||||
PGID = "1000";
|
PGID = "1000";
|
||||||
TZ = "Europe/Zurich";
|
WHITELIST = "";# 127.0.0.1,::1,10.*";
|
||||||
|
# HOST_WHITELIST = "traefik-server,authentik-server,authentik-worker";
|
||||||
};
|
};
|
||||||
extraLabels = { } // (if serverCfg.containers ? authentik then {
|
extraLabels = {
|
||||||
"traefik.http.routers.${containerCfg.subdomain}.middlewares" = "authentik";
|
} // (if serverCfg.containers ? authentik then {
|
||||||
|
"traefik.http.routers.${routerName}.middlewares" = "authentik";
|
||||||
} else {});
|
} else {});
|
||||||
|
|
||||||
overrides = {
|
overrides = {
|
||||||
@@ -48,10 +54,20 @@ in {
|
|||||||
volumes = [
|
volumes = [
|
||||||
"${serverCfg.dataPath}/transmission/complete:/downloads/complete"
|
"${serverCfg.dataPath}/transmission/complete:/downloads/complete"
|
||||||
"${serverCfg.dataPath}/transmission/incomplete:/downloads/incomplete"
|
"${serverCfg.dataPath}/transmission/incomplete:/downloads/incomplete"
|
||||||
"${serverCfg.dataPath}/transmission/config:/config"
|
"${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"
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -31,7 +31,7 @@ in {
|
|||||||
secret = name;
|
secret = name;
|
||||||
extraEnv = {
|
extraEnv = {
|
||||||
PORT = "3000";
|
PORT = "3000";
|
||||||
# HOSTNAME = "${containerCfg.subdomain}.${serverCfg.hostDomain}";
|
# HOSTNAME = "${containerCfg.subdomain}.${serverCfg.domain}";
|
||||||
DATABASE_TYPE = "postgresql";
|
DATABASE_TYPE = "postgresql";
|
||||||
REDIS_URL = "redis://${builder.host}";
|
REDIS_URL = "redis://${builder.host}";
|
||||||
CLIENT_IP_HEADER = "X-Forwarded-For";
|
CLIENT_IP_HEADER = "X-Forwarded-For";
|
||||||
@@ -39,12 +39,7 @@ in {
|
|||||||
# DISABLE_LOGIN = "1";#(if serverCfg.containers?authentik then "1" else "0");
|
# DISABLE_LOGIN = "1";#(if serverCfg.containers?authentik then "1" else "0");
|
||||||
|
|
||||||
};
|
};
|
||||||
extraLabels = {
|
extraLabels = { } // ( if serverCfg.containers?authentik 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";
|
|
||||||
} // ( if serverCfg.containers?authentik then {
|
|
||||||
"traefik.http.routers.${containerCfg.subdomain}.middlewares" = if serverCfg.containers?authentik then "authentik" else "";
|
"traefik.http.routers.${containerCfg.subdomain}.middlewares" = if serverCfg.containers?authentik then "authentik" else "";
|
||||||
} else {});
|
} else {});
|
||||||
extraOptions = [
|
extraOptions = [
|
||||||
|
|||||||
@@ -1,45 +1,74 @@
|
|||||||
{ config, lib, pkgs, serverCfg }:
|
{ config, lib, pkgs, serverCfg }:
|
||||||
let
|
let
|
||||||
builder =
|
contBuilder =
|
||||||
{ image ? null, imageStream ? null, imageFile ? null
|
{ image ? null, imageStream ? null, imageFile ? null
|
||||||
, secret ? null
|
, secret ? null
|
||||||
, subdomain ? null, subpath?null, port ? 0
|
, subdomain ? null, subpath?null, port ? null
|
||||||
, extraEnv ? { }, extraLabels ? { }, extraOptions ? [ ]
|
, extraEnv ? { }, extraLabels ? { }, extraOptions ? [ ]
|
||||||
, overrides ? { }
|
, overrides ? { }
|
||||||
}:
|
}:
|
||||||
let
|
let
|
||||||
routerName = if subpath != null
|
routerName = if subpath != null
|
||||||
then "${subdomain}-${lib.strings.sanitizeDerivationName subpath}"
|
then "${subdomain}-${lib.strings.sanitizeDerivationName subpath}"
|
||||||
else subdomain;
|
else subdomain;
|
||||||
base = {
|
base = {
|
||||||
image = if imageStream != null then "${imageStream.imageName}:${imageStream.imageTag}"
|
image = if imageStream != null then "${imageStream.imageName}:${imageStream.imageTag}"
|
||||||
else image;
|
else if imageFile != null then "${imageFile.imageName}:${imageFile.imageTag}" else image;
|
||||||
imageStream = imageStream;
|
imageStream = imageStream;
|
||||||
imageFile = imageFile;
|
imageFile = imageFile;
|
||||||
|
|
||||||
environmentFiles = if secret!=null then [ config.sops.secrets."${lib.toUpper secret}".path ] else [];
|
environmentFiles = if secret!=null then [ config.sops.secrets."${lib.toUpper secret}".path ] else [];
|
||||||
environment = {} // extraEnv;
|
environment = {
|
||||||
|
TZ = config.time.timeZone;
|
||||||
|
} // extraEnv;
|
||||||
|
|
||||||
labels = (if subdomain!=null then ({
|
labels = (if subdomain!=null then ({
|
||||||
"traefik.enable" = "true";
|
"traefik.enable" = "true";
|
||||||
"traefik.http.routers.${routerName}.entrypoints" = "web-secure";
|
"traefik.http.routers.${routerName}.entrypoints" = "web-secure";
|
||||||
"traefik.http.routers.${routerName}.rule" = if subpath != null
|
"traefik.http.routers.${routerName}.rule" = if subpath != null
|
||||||
then "Host(`${subdomain}.${serverCfg.hostDomain}`) && PathPrefix(`/${subpath}`)"
|
then "Host(`${subdomain}.${serverCfg.domain}`) && PathPrefix(`/${subpath}`)"
|
||||||
else "Host(`${subdomain}.${serverCfg.hostDomain}`)";
|
else "Host(`${subdomain}.${serverCfg.domain}`)";
|
||||||
"traefik.http.routers.${routerName}.tls" = "true";
|
"traefik.http.routers.${routerName}.tls" = "true";
|
||||||
} // lib.optionalAttrs (port!=null) {
|
} // lib.optionalAttrs (port!=null) {
|
||||||
"traefik.http.services.${routerName}.loadbalancer.server.port" = toString port;
|
"traefik.http.services.${routerName}.loadbalancer.server.port" = toString port;
|
||||||
}) else {
|
}) else {
|
||||||
"traefik.enable" = "false";
|
"traefik.enable" = "false";
|
||||||
}) // extraLabels;
|
}) // extraLabels;
|
||||||
|
|
||||||
extraOptions = extraOptions ++ [
|
extraOptions = [
|
||||||
"--add-host=host.containers.internal:host-gateway"
|
"--add-host=host.containers.internal:host-gateway"
|
||||||
];
|
] ++ extraOptions;
|
||||||
};
|
};
|
||||||
in lib.recursiveUpdate base overrides;
|
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 {
|
in {
|
||||||
mkContainer = builder;
|
mkContainer = contBuilder;
|
||||||
|
mkVm = vmBuilder;
|
||||||
mkData = { name, dir, vars?{} }: pkgs.runCommand name vars ''
|
mkData = { name, dir, vars?{} }: pkgs.runCommand name vars ''
|
||||||
mkdir -p $out
|
mkdir -p $out
|
||||||
cp -r ${./data + "/${dir}"}/. $out/
|
cp -r ${./data + "/${dir}"}/. $out/
|
||||||
@@ -50,4 +79,7 @@ in {
|
|||||||
done
|
done
|
||||||
'';
|
'';
|
||||||
host = "host.containers.internal";
|
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:
|
metadata:
|
||||||
name: nextcloud-saml-setup
|
name: nextcloud-saml-setup
|
||||||
entries:
|
entries:
|
||||||
# 1. Create the SAML Provider
|
|
||||||
- model: authentik_providers_saml.samlprovider
|
- model: authentik_providers_saml.samlprovider
|
||||||
identifiers:
|
identifiers:
|
||||||
name: Nextcloud SAML
|
name: Nextcloud SAML
|
||||||
@@ -15,12 +14,10 @@ entries:
|
|||||||
invalidation_flow:
|
invalidation_flow:
|
||||||
!Find [authentik_flows.flow, [slug, default-provider-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
|
acs_url: https://@NEXTCLOUD_DOMAIN@/apps/user_saml/saml/acs
|
||||||
audience: https://@NEXTCLOUD_DOMAIN@/apps/user_saml/saml/metadata
|
audience: https://@NEXTCLOUD_DOMAIN@/apps/user_saml/saml/metadata
|
||||||
issuer: https://@AUTHENTIK_DOMAIN@
|
issuer: https://@AUTHENTIK_DOMAIN@
|
||||||
sp_binding: post
|
sp_binding: post
|
||||||
# Map the attributes for Name, Email, and Groups
|
|
||||||
property_mappings:
|
property_mappings:
|
||||||
- !Find [
|
- !Find [
|
||||||
authentik_core.propertymapping,
|
authentik_core.propertymapping,
|
||||||
@@ -43,32 +40,6 @@ entries:
|
|||||||
[name, "authentik default SAML Mapping: User ID"],
|
[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:
|
signing_kp:
|
||||||
!Find [
|
!Find [
|
||||||
authentik_crypto.certificatekeypair,
|
authentik_crypto.certificatekeypair,
|
||||||
@@ -77,7 +48,6 @@ entries:
|
|||||||
sign_assertion: true
|
sign_assertion: true
|
||||||
sign_response: false
|
sign_response: false
|
||||||
|
|
||||||
# 2. Create the Application
|
|
||||||
- model: authentik_core.application
|
- model: authentik_core.application
|
||||||
identifiers:
|
identifiers:
|
||||||
slug: nextcloud
|
slug: nextcloud
|
||||||
@@ -85,4 +55,4 @@ entries:
|
|||||||
name: Nextcloud
|
name: Nextcloud
|
||||||
provider:
|
provider:
|
||||||
!Find [authentik_providers_saml.samlprovider, [name, Nextcloud SAML]]
|
!Find [authentik_providers_saml.samlprovider, [name, Nextcloud SAML]]
|
||||||
group: "Cloud Services"
|
launch_url: "@NEXTCLOUD_DOMAIN@"
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ entries:
|
|||||||
slug: authentik-proxy
|
slug: authentik-proxy
|
||||||
attrs:
|
attrs:
|
||||||
name: "Domain Auth Provider"
|
name: "Domain Auth Provider"
|
||||||
|
group: _
|
||||||
provider:
|
provider:
|
||||||
!Find [
|
!Find [
|
||||||
authentik_providers_proxy.proxyprovider,
|
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
|
||||||
@@ -1,29 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<PluginConfiguration xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
|
|
||||||
<LdapUsers />
|
|
||||||
<LdapServer>@HOST@</LdapServer>
|
|
||||||
<LdapPort>389</LdapPort>
|
|
||||||
<UseSsl>false</UseSsl>
|
|
||||||
<UseStartTls>false</UseStartTls>
|
|
||||||
<SkipSslVerify>true</SkipSslVerify>
|
|
||||||
<LdapBindUser>cn=@LDAP_BIND_USER@,ou=users,@LDAP_DC_DOMAIN@</LdapBindUser>
|
|
||||||
<LdapBindPassword>@LDAP_BIND_PASSWORD@</LdapBindPassword>
|
|
||||||
<LdapBaseDn>@LDAP_DC_DOMAIN@</LdapBaseDn>
|
|
||||||
<LdapSearchFilter>(memberOf=cn=@LDAP_GROUP@,ou=groups,@LDAP_DC_DOMAIN@)</LdapSearchFilter>
|
|
||||||
<LdapAdminBaseDn />
|
|
||||||
<LdapAdminFilter>(memberOf=cn=@LDAP_ADMIN@,ou=groups,@LDAP_DC_DOMAIN@)</LdapAdminFilter>
|
|
||||||
<EnableLdapAdminFilterMemberUid>false</EnableLdapAdminFilterMemberUid>
|
|
||||||
<LdapSearchAttributes>uid, cn, mail, displayName</LdapSearchAttributes>
|
|
||||||
<LdapClientCertPath />
|
|
||||||
<LdapClientKeyPath />
|
|
||||||
<LdapRootCaPath />
|
|
||||||
<CreateUsersFromLdap>true</CreateUsersFromLdap>
|
|
||||||
<AllowPassChange>false</AllowPassChange>
|
|
||||||
<LdapUidAttribute>uid</LdapUidAttribute>
|
|
||||||
<LdapUsernameAttribute>cn</LdapUsernameAttribute>
|
|
||||||
<LdapPasswordAttribute>userPassword</LdapPasswordAttribute>
|
|
||||||
<EnableLdapProfileImageSync>false</EnableLdapProfileImageSync>
|
|
||||||
<LdapProfileImageAttribute>jpegphoto</LdapProfileImageAttribute>
|
|
||||||
<EnableAllFolders>true</EnableAllFolders>
|
|
||||||
<EnabledFolders />
|
|
||||||
<PasswordResetUrl />
|
|
||||||
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
|
||||||
|
}
|
||||||
@@ -15,14 +15,14 @@ in{
|
|||||||
lib.mapAttrs' (cName: cCfg: lib.nameValuePair "${appName}-${cName}" cCfg) app.containers
|
lib.mapAttrs' (cName: cCfg: lib.nameValuePair "${appName}-${cName}" cCfg) app.containers
|
||||||
) config.syscfg.server.loadedContainers;
|
) config.syscfg.server.loadedContainers;
|
||||||
allPathConfigs = lib.concatMap (app: app.paths) appsList;
|
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;
|
allCronsConfigs = lib.concatMap (app: app.cron) appsList;
|
||||||
|
allVMConfigs = builtins.filter (app: app.vm != null) appsList;
|
||||||
in{
|
in{
|
||||||
|
|
||||||
virtualisation.oci-containers = {
|
virtualisation.oci-containers = {
|
||||||
backend = "podman";
|
backend = "podman";
|
||||||
containers = mergedContainers;
|
containers = mergedContainers;
|
||||||
};
|
};
|
||||||
|
|
||||||
system.activationScripts.container-setup-dirs = {
|
system.activationScripts.container-setup-dirs = {
|
||||||
deps = [ "users" "groups" ];
|
deps = [ "users" "groups" ];
|
||||||
text = lib.concatStringsSep "\n" (map (cfg:
|
text = lib.concatStringsSep "\n" (map (cfg:
|
||||||
@@ -48,25 +48,46 @@ in{
|
|||||||
'';
|
'';
|
||||||
startAt = "weekly";
|
startAt = "weekly";
|
||||||
};
|
};
|
||||||
} // lib.listToAttrs (lib.concatMap (containerSet:
|
}
|
||||||
if containerSet.setup.script != null then [{
|
// lib.listToAttrs (lib.concatMap (e: [{
|
||||||
name = "${containerSet.name}-setup";
|
name = "${e.name}-vm";
|
||||||
value = {
|
value = {
|
||||||
description = "Run ${containerSet.name} setup";
|
description = "Isolated NixOS Guest VM for ${e.name}";
|
||||||
after = [ "podman-${containerSet.name}-${containerSet.setup.trigger}.service" ];
|
after = [ "network-online.target" ];
|
||||||
wants = [ "podman-${containerSet.name}-${containerSet.setup.trigger}.service" ];
|
wants = [ "network-online.target" ];
|
||||||
wantedBy = [ "multi-user.target" ];
|
wantedBy = [ "multi-user.target" ];
|
||||||
serviceConfig = {
|
environment = {
|
||||||
Type = "oneshot";
|
QEMU_VM_REG_SND = "0";
|
||||||
TimeoutStartSec = "360s";
|
NIX_DISK_IMAGE = "/media/data/kvm/${e.name}-guest.qcw2";
|
||||||
EnvironmentFile = if (containerSet.setup ? envFile) then containerSet.setup.envFile else [ ];
|
|
||||||
ExecStart = "${containerSet.setup.script}";
|
|
||||||
RemainAfterExit = true;
|
|
||||||
User = "root";
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
}] else []
|
serviceConfig = {
|
||||||
) appsList);
|
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 = e.envFile;
|
||||||
|
ExecStart = e.script;
|
||||||
|
RemainAfterExit = true;
|
||||||
|
User = "root";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}]) allSetupConfigs );
|
||||||
|
|
||||||
services.cron = {
|
services.cron = {
|
||||||
enable = true;
|
enable = true;
|
||||||
|
|||||||
@@ -9,9 +9,11 @@ in {
|
|||||||
config = lib.mkIf ( builtins.length allApps > 0) {
|
config = lib.mkIf ( builtins.length allApps > 0) {
|
||||||
services.postgresql = {
|
services.postgresql = {
|
||||||
enable = true;
|
enable = true;
|
||||||
enableTCPIP = true; # Required to listen on network interfaces
|
enableTCPIP = true;
|
||||||
|
extensions = ps: with ps; [ vectorchord pgvector ];
|
||||||
settings = {
|
settings = {
|
||||||
listen_addresses = lib.mkForce "*";
|
listen_addresses = lib.mkForce "*";
|
||||||
|
shared_preload_libraries = "vchord";
|
||||||
};
|
};
|
||||||
authentication = pkgs.lib.mkOverride 10 ''
|
authentication = pkgs.lib.mkOverride 10 ''
|
||||||
# TYPE DATABASE USER ADDRESS METHOD
|
# TYPE DATABASE USER ADDRESS METHOD
|
||||||
@@ -37,6 +39,28 @@ in {
|
|||||||
bind = "*";
|
bind = "*";
|
||||||
settings.protected-mode = "no";
|
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 = {
|
systemd.services.postgresql-init = {
|
||||||
@@ -51,7 +75,7 @@ in {
|
|||||||
};
|
};
|
||||||
|
|
||||||
script = ''
|
script = ''
|
||||||
${pkgs.coreutils}/bin/sleep 2
|
${pkgs.coreutils}/bin/sleep 20
|
||||||
PSQL="${pkgs.postgresql}/bin/psql"
|
PSQL="${pkgs.postgresql}/bin/psql"
|
||||||
${lib.concatMapStringsSep "\n" (name: ''
|
${lib.concatMapStringsSep "\n" (name: ''
|
||||||
$PSQL -tAc "ALTER DATABASE ${name}_db OWNER TO ${name}_user;"
|
$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;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
}
|
|
||||||
@@ -2,6 +2,7 @@ CUSTOM: |
|
|||||||
DEFAULT_ADMIN_USERNAME=...
|
DEFAULT_ADMIN_USERNAME=...
|
||||||
DEFAULT_ADMIN_PASSWORD=...
|
DEFAULT_ADMIN_PASSWORD=...
|
||||||
DEFAULT_ADMIN_EMAIL=...
|
DEFAULT_ADMIN_EMAIL=...
|
||||||
|
DEFAULT_LDAP_PASSWORD=...
|
||||||
TRAEFIK: |
|
TRAEFIK: |
|
||||||
INFOMANIAK_ACCESS_TOKEN=...
|
INFOMANIAK_ACCESS_TOKEN=...
|
||||||
AUTHENTIK: |
|
AUTHENTIK: |
|
||||||
@@ -9,6 +10,7 @@ AUTHENTIK: |
|
|||||||
POSTGRES_PASSWORD=...
|
POSTGRES_PASSWORD=...
|
||||||
AUTHENTIK_SECRET_KEY=...
|
AUTHENTIK_SECRET_KEY=...
|
||||||
AUTHENTIK_EMAIL__PASSWORD=...
|
AUTHENTIK_EMAIL__PASSWORD=...
|
||||||
|
AUTHENTIK_TOKEN=...
|
||||||
NEXTCLOUD: |
|
NEXTCLOUD: |
|
||||||
DB_PASSWORD=...
|
DB_PASSWORD=...
|
||||||
POSTGRES_PASSWORD=...
|
POSTGRES_PASSWORD=...
|
||||||
@@ -32,3 +34,8 @@ UMAMI: |
|
|||||||
DB_PASSWORD=...
|
DB_PASSWORD=...
|
||||||
DATABASE_URL=postgresql://username:mypassword@localhost:5432/mydb
|
DATABASE_URL=postgresql://username:mypassword@localhost:5432/mydb
|
||||||
APP_SECRET=...
|
APP_SECRET=...
|
||||||
|
IMMICH: |
|
||||||
|
DB_URL = "postgresql://immich_user:...@localhost:5432/immich_db";
|
||||||
|
SERVARR: |
|
||||||
|
SONARR__AUTH__APIKEY=...
|
||||||
|
RADARR__AUTH__APIKEY=...
|
||||||
|
|||||||
@@ -1,14 +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]
|
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]
|
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]
|
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]
|
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]
|
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]
|
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]
|
SEARXNG: ENC[AES256_GCM,data:gtKhEmMemzLRl4c3cYhMAQ+5vUth1IhWQeLvW1YtaG5TbhQHBR4PDREQOlGt+tlfGQrft+FeNhMSN/SKOp8gmScVWa+9qmltzxRGRpLm3m/VuBZvOlGdeUcKAX8zEH6A,iv:B2UEtjTRIjT6W+tH2gtcl6XMvZNgbvZUXTiBePGOu24=,tag:SHIF6eaWBLwy9RrEy1N9kg==,type:str]
|
||||||
UMAMI: ENC[AES256_GCM,data:l1eMel/8PlzDjnEbpgjXceu7l8zFnl6NPYihrzAJPoSl7fE794FfkmrEhm2tf+kI1HgpDnOVpAP847k3NFGWRtiIYwjHl2NFlw0UorMutzja7uBP5oktmjVZZs3SaZFSQaWMHPCP9b2+LiMVAZqhERHF5y4A4mwP+q5CUVTgLGnDaBFBX7hwV2KRCARNYtxHkA80cxxUx1yJD50WCqjhCl1to2jx6bE+eupZvKk6U0GmC4MiMHkCzpWdzdtLsLHP5yMxbXUCdDUKmMDWSrOTJULYSsE8R/dZH2DOwwkhS23tXY4ckSbOmruGTEbhbBk+LADBdk9ijD9oaa4gVnF6Vqoib0esUCt/9EJAizgTro7eFGgCcYH2dl2doPJq372ZioSoYD6jiSdrUyCUmKs4xGEocnvy8W/CVG4=,iv:4pM3CsuO+1jfFQ7b1S9PHjdlIpVXvDVurMswmwz9ZrU=,tag:a8hyyFJHyqmAjsURJcNt3Q==,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]
|
||||||
SERVARR: ENC[AES256_GCM,data:fukF7bejebMU7yp48fix,iv:CZkLyO8N8BqSk+0KDcMDrz1pbwaNH7Pg+NvNebdIdYM=,tag:AOMvnZOE0H6QDCmkPg3Kyw==,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:
|
sops:
|
||||||
age:
|
age:
|
||||||
- recipient: age1sxzuhh2fcd4pmaz4mdqq95t683d32ft22w9t2r7pk258u0s8wymsqdj7lg
|
- recipient: age1sxzuhh2fcd4pmaz4mdqq95t683d32ft22w9t2r7pk258u0s8wymsqdj7lg
|
||||||
@@ -29,8 +32,8 @@ sops:
|
|||||||
S1NaTVFTL0FCdm1EQmRsUnlhclZNZlEKEgIe60qkvY8+UocjQU+WM2dTL/1y3Kqk
|
S1NaTVFTL0FCdm1EQmRsUnlhclZNZlEKEgIe60qkvY8+UocjQU+WM2dTL/1y3Kqk
|
||||||
d4RrlLP9NSozwVsPYI4ntygvMSApbT4v0YvoO7gV90lkGWEvW1YDfA==
|
d4RrlLP9NSozwVsPYI4ntygvMSApbT4v0YvoO7gV90lkGWEvW1YDfA==
|
||||||
-----END AGE ENCRYPTED FILE-----
|
-----END AGE ENCRYPTED FILE-----
|
||||||
lastmodified: "2026-05-12T23:00:07Z"
|
lastmodified: "2026-05-21T21:42:55Z"
|
||||||
mac: ENC[AES256_GCM,data:g2Hbt81av0W6osMC3RcVPPkEPlrIeM4chlbQ1P+FrvxIQGWXvQlypnoYPLLBtfuXgUkASFJGQRM9dyUSvSwJczk3/HBoReZigyJRLNb5sfpF+YFHqplkX5hPDQ8iJDCWjpuIWiU0gH+hphm+V0nwB5o6iqeEkeZv8iIurEL/Des=,iv:hF4zb0fjonge/QmLpiOyghAMBAersVsWrOtk9oKPqbo=,tag:fusPQtNmQXS8u4/VB/L9SQ==,type:str]
|
mac: ENC[AES256_GCM,data:6FCnBXQ1ueiNECTZDo2AGw8CXCimGWKXQYbI+4in0tCfI477Ip9OISwodK4cFhZPKEYFOB4z5KU5vXJ014jErYDylaPlHYj7Bj/4ugkYt/ywDg3VEYZVVZfBI7bhe1x8/kbkg+Y6ZwURf74xPyg7X+7IH5VGQA4eKck5rTPwGZQ=,iv:04MI9S51azQLEvDtbxL0cf6NUNtV4hRALBcsMmrAFKg=,tag:5oxAZwCfWmatWnghZ97W6g==,type:str]
|
||||||
pgp:
|
pgp:
|
||||||
- created_at: "2026-05-05T23:46:27Z"
|
- created_at: "2026-05-05T23:46:27Z"
|
||||||
enc: |-
|
enc: |-
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
let
|
let
|
||||||
|
|
||||||
in with lib; {
|
in with lib; {
|
||||||
hostDomain = mkOption { type = types.str; };
|
domain = mkOption { type = types.str; };
|
||||||
mailDomain = mkOption { type = types.str; };
|
mailDomain = mkOption { type = types.str; };
|
||||||
mailServer = mkOption { type = types.str; };
|
mailServer = mkOption { type = types.str; };
|
||||||
|
|
||||||
@@ -29,12 +29,13 @@ in with lib; {
|
|||||||
|
|
||||||
paths = lib.mkOption {type = lib.types.listOf lib.types.attrs; default = [ ];};
|
paths = lib.mkOption {type = lib.types.listOf lib.types.attrs; default = [ ];};
|
||||||
containers = lib.mkOption {type = lib.types.attrsOf 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 = [ ];};
|
cron = lib.mkOption {type = lib.types.listOf lib.types.str; default = [ ];};
|
||||||
|
|
||||||
setup = {
|
setup = {
|
||||||
trigger = lib.mkOption {type = lib.types.str; default = "";};
|
trigger = lib.mkOption {type = lib.types.str; default = "";};
|
||||||
script = lib.mkOption {type = lib.types.nullOr lib.types.package; default = null;};
|
script = lib.mkOption {type = lib.types.nullOr lib.types.package; default = null;};
|
||||||
envFile = lib.mkOption {type = lib.types.nullOr lib.types.str; default = null;};
|
envFile = lib.mkOption {type = with lib.types; coercedTo str (x: [x]) (listOf str); default = [];};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
}));
|
}));
|
||||||
|
|||||||
@@ -19,54 +19,43 @@
|
|||||||
virt = true;
|
virt = true;
|
||||||
};
|
};
|
||||||
server = {
|
server = {
|
||||||
|
domain = "test.helcel.net";
|
||||||
openssh = true;
|
openssh = true;
|
||||||
web = true;
|
web = true;
|
||||||
|
|
||||||
hostDomain = "test.helcel.net";
|
# mail = {
|
||||||
|
# server = ...
|
||||||
|
# user = ...
|
||||||
|
# ...
|
||||||
|
# };
|
||||||
mailDomain = "test@helcel";
|
mailDomain = "test@helcel";
|
||||||
mailServer = "infomaniak.ch";
|
mailServer = "infomaniak.ch";
|
||||||
|
|
||||||
containers = {
|
containers = {
|
||||||
|
# ===== BASE =====
|
||||||
traefik = {
|
traefik.subdomain = "traefik";
|
||||||
subdomain = "traefik";
|
traefik.extra={provider="infomaniak";};
|
||||||
extra={provider="infomaniak";};
|
umami.subdomain = "umami";
|
||||||
};
|
authentik.subdomain = "sso";
|
||||||
authentik = {
|
searxng.subdomain = "searx";
|
||||||
subdomain = "sso";
|
# ===== CLOUD =====
|
||||||
port = 9000;
|
# nextcloud.subdomain = "cloud";
|
||||||
};
|
# collabora.subdomain = "office";
|
||||||
nextcloud = {
|
# etherpad.subdomain = "pad";
|
||||||
subdomain = "cloud";
|
# ethercalc.subdomain = "calc";
|
||||||
};
|
# immich.subdomain = "pic";
|
||||||
collabora = {
|
# ===== FLIX =====
|
||||||
subdomain = "office";
|
# invidious.subdomain = "yt";
|
||||||
};
|
jellyfin.subdomain = "flix";
|
||||||
etherpad = {
|
servarr.subdomain = "arr";
|
||||||
subdomain = "pad";
|
transmission = { subdomain = "arr"; subpath = "transmission"; };
|
||||||
};
|
handbrake = { subdomain = "arr"; subpath = "hb"; };
|
||||||
ethercalc = {
|
# ===== DEV =====
|
||||||
subdomain = "pad";
|
gitea.subdomain = "git";
|
||||||
};
|
# ===== HOME =====
|
||||||
gitea = {
|
openhab.subdomain = "hab";
|
||||||
subdomain = "git";
|
# trmnl = { subdomain = "hass"; subpath = "trmnl"; };
|
||||||
};
|
influx.subdomain = "metrum";
|
||||||
searxng = {
|
|
||||||
subdomain = "searx";
|
|
||||||
};
|
|
||||||
jellyfin = {
|
|
||||||
subdomain = "flix";
|
|
||||||
};
|
|
||||||
transmission = {
|
|
||||||
subdomain = "rflix";
|
|
||||||
subpath = "p2p";
|
|
||||||
};
|
|
||||||
# servarr = {
|
|
||||||
# subdomain = "arr";
|
|
||||||
# };
|
|
||||||
umami = {
|
|
||||||
subdomain = "umami";
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user