feat: postgres, calibre, radicale, z2m (#133)

* hax

* hax

* shell monitoring

* hax radicale!

* hacking

* haxor

* hax

* hack

* feat: refactor paths etc for impermance

* fix: restic

* hax

* more hax

* feat: migrate z2m

* fix: websockets i guess

* cleanup

* hacks

* hax

* feat: miniflux + postgres

* feat: add calibre

* feat: calibre-web

* Auto lint/format

---------

Co-authored-by: Truxnell <9149206+truxnell@users.noreply.github.com>
Co-authored-by: truxnell <truxnell@users.noreply.github.com>
This commit is contained in:
Truxnell 2024-05-05 16:05:59 +10:00 committed by GitHub
parent 5a39b54ae8
commit 49621dec0e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
39 changed files with 1293 additions and 243 deletions

View file

@ -10,7 +10,7 @@
]; ];
config = {
mySystem.purpose = "Network Attached Storage"; mySystem.purpose = "Network Attached Storage";
mySystem.system.impermanence.enable = true; mySystem.system.impermanence.enable = true;
mySystem.services = { mySystem.services = {
@ -43,7 +43,6 @@
mySystem.system.motd.networkInterfaces = [ "eno1" ]; mySystem.system.motd.networkInterfaces = [ "eno1" ];
boot = { boot = {
initrd.availableKernelModules = [ "xhci_pci" "ahci" "mpt3sas" "nvme" "usbhid" "usb_storage" "sd_mod" ]; initrd.availableKernelModules = [ "xhci_pci" "ahci" "mpt3sas" "nvme" "usbhid" "usb_storage" "sd_mod" ];
@ -95,6 +94,44 @@
swapDevices = swapDevices =
[{ device = "/dev/disk/by-uuid/c2f716ef-9e8c-466b-bcb0-699397cb2dc0"; }]; [{ device = "/dev/disk/by-uuid/c2f716ef-9e8c-466b-bcb0-699397cb2dc0"; }];
# TODO does this live somewhere else?
# it is very machine-specific...
# add user with `sudo smbpasswd -a my_user`
services.samba = {
enable = true;
openFirewall = true;
extraConfig = ''
workgroup = WORKGROUP
server string = daedalus
netbios name = daedalus
security = user
#use sendfile = yes
#max protocol = smb2
# note: localhost is the ipv6 localhost ::1
hosts allow = 10.8.10. 127.0.0.1 localhost
hosts deny = 0.0.0.0/0
guest account = nobody
map to guest = bad user
'';
shares = {
backup = {
path = "/tank/backup";
"read only" = "no";
};
documents = {
path = "/tank/documents";
"read only" = "no";
};
natflix = {
path = "/tank/natflix";
"read only" = "no";
};
# paperless = {
# path = "/tank/Apps/paperless/incoming";
# "read only" = "no";
# };
};
};
};
} }

View file

@ -11,10 +11,13 @@
openssh.enable = true; openssh.enable = true;
podman.enable = true; podman.enable = true;
nginx.enable = true; nginx.enable = true;
openvscode-server.enable = true; openvscode-server.enable = true;
postgresql =
{ enable = true; backup = false; };
calibre-web = { enable = true; backup = false; dev = true; };
}; };
# mySystem.containers.calibre = { enable = true; backup = false; dev = true; };
mySystem.system.systemd.pushover-alerts.enable = false; mySystem.system.systemd.pushover-alerts.enable = false;
mySystem.nfs.nas.enable = true; mySystem.nfs.nas.enable = true;

View file

@ -11,6 +11,7 @@
mySystem.services = { mySystem.services = {
openssh.enable = true; openssh.enable = true;
podman.enable = true; podman.enable = true;
postgresql.enable = true;
nginx.enable = true; nginx.enable = true;
@ -33,7 +34,14 @@
openvscode-server.enable = true; openvscode-server.enable = true;
radicale.enable = true; radicale.enable = true;
miniflux.enable = true;
}; };
mySystem.containers = {
calibre.enable = true;
ecowitt2mqtt.enable = true;
};
mySystem.security.acme.enable = true; mySystem.security.acme.enable = true;

View file

@ -59,7 +59,7 @@ in
services.nginx.virtualHosts."${app}.${config.networking.domain}" = { services.nginx.virtualHosts."${app}.${config.networking.domain}" = {
useACMEHost = config.networking.domain; useACMEHost = config.networking.domain;
forceSSL = true; forceSSL = true;
locations."/" = { locations."^~ /" = {
proxyPass = "http://${app}:${builtins.toString port}"; proxyPass = "http://${app}:${builtins.toString port}";
extraConfig = "resolver 10.88.0.1;"; extraConfig = "resolver 10.88.0.1;";

View file

@ -56,7 +56,7 @@ in
services.nginx.virtualHosts."${app}.${config.networking.domain}" = { services.nginx.virtualHosts."${app}.${config.networking.domain}" = {
useACMEHost = config.networking.domain; useACMEHost = config.networking.domain;
forceSSL = true; forceSSL = true;
locations."/" = { locations."^~ /" = {
proxyPass = "http://${app}:${builtins.toString port}"; proxyPass = "http://${app}:${builtins.toString port}";
extraConfig = "resolver 10.88.0.1;"; extraConfig = "resolver 10.88.0.1;";

View file

@ -59,7 +59,7 @@ in
services.nginx.virtualHosts."${app}.${config.networking.domain}" = { services.nginx.virtualHosts."${app}.${config.networking.domain}" = {
useACMEHost = config.networking.domain; useACMEHost = config.networking.domain;
forceSSL = true; forceSSL = true;
locations."/" = { locations."^~ /" = {
proxyPass = "http://${app}:${builtins.toString port}"; proxyPass = "http://${app}:${builtins.toString port}";
extraConfig = "resolver 10.88.0.1;"; extraConfig = "resolver 10.88.0.1;";

View file

@ -57,7 +57,7 @@ in
services.nginx.virtualHosts."${app}.${config.networking.domain}" = { services.nginx.virtualHosts."${app}.${config.networking.domain}" = {
useACMEHost = config.networking.domain; useACMEHost = config.networking.domain;
forceSSL = true; forceSSL = true;
locations."/" = { locations."^~ /" = {
proxyPass = "http://${app}:${builtins.toString port}"; proxyPass = "http://${app}:${builtins.toString port}";
extraConfig = "resolver 10.88.0.1;"; extraConfig = "resolver 10.88.0.1;";

View file

@ -60,7 +60,7 @@ in
services.nginx.virtualHosts."${app}.${config.networking.domain}" = { services.nginx.virtualHosts."${app}.${config.networking.domain}" = {
useACMEHost = config.networking.domain; useACMEHost = config.networking.domain;
forceSSL = true; forceSSL = true;
locations."/" = { locations."^~ /" = {
proxyPass = "http://${app}:${builtins.toString port}"; proxyPass = "http://${app}:${builtins.toString port}";
extraConfig = "resolver 10.88.0.1;"; extraConfig = "resolver 10.88.0.1;";

View file

@ -50,7 +50,7 @@ in
services.nginx.virtualHosts."${app}.${config.networking.domain}" = { services.nginx.virtualHosts."${app}.${config.networking.domain}" = {
useACMEHost = config.networking.domain; useACMEHost = config.networking.domain;
forceSSL = true; forceSSL = true;
locations."/" = { locations."^~ /" = {
proxyPass = "http://${app}:${builtins.toString port}"; proxyPass = "http://${app}:${builtins.toString port}";
extraConfig = "resolver 10.88.0.1;"; extraConfig = "resolver 10.88.0.1;";
}; };

View file

@ -0,0 +1,157 @@
{ lib
, config
, pkgs
, ...
}:
with lib;
let
cfg = config.mySystem.${category}.${app};
app = "calibre";
category = "containers";
description = "eBook managment";
image = "ghcr.io/linuxserver/calibre:version-v7.10.0";
user = "0"; #string
group = "0"; #string
port = 8091; #int
appFolder = "/var/lib/${app}";
# persistentFolder = "${config.mySystem.persistentFolder}/var/lib/${appFolder}";
host = "${app}" + (if cfg.dev then "-dev" else "");
url = "${host}.${config.networking.domain}";
in
{
options.mySystem.${category}.${app} =
{
enable = mkEnableOption "${app}";
addToHomepage = mkEnableOption "Add ${app} to homepage" // { default = true; };
monitor = mkOption
{
type = lib.types.bool;
description = "Enable gatus monitoring";
default = true;
};
prometheus = mkOption
{
type = lib.types.bool;
description = "Enable prometheus scraping";
default = true;
};
addToDNS = mkOption
{
type = lib.types.bool;
description = "Add to DNS list";
default = true;
};
dev = mkOption
{
type = lib.types.bool;
description = "Development instance";
default = false;
};
backup = mkOption
{
type = lib.types.bool;
description = "Enable backups";
default = true;
};
};
config = mkIf cfg.enable {
## Secrets
# sops.secrets."${category}/${app}/env" = {
# sopsFile = ./secrets.sops.yaml;
# owner = user;
# group = group;
# restartUnits = [ "${app}.service" ];
# };
users.users.truxnell.extraGroups = [ group ];
# Folder perms - only for containers
systemd.tmpfiles.rules = [
"d ${appFolder}/ 0750 ${user} ${group} -"
];
## service
virtualisation.oci-containers.containers = config.lib.mySystem.mkContainer {
inherit app image user group;
env = {
PUID = "568";
PGID = "568";
};
volumes = [
"${appFolder}:/config:rw"
"${config.mySystem.nasFolder}/natflix/:/media:rw"
];
ports = [ "${builtins.toString port}:8080" ];
caps = {
noNewPrivileges = true;
};
};
# homepage integration
mySystem.services.homepage.infrastructure = mkIf cfg.addToHomepage [
{
${app} = {
icon = "${app}.svg";
href = "https://${url}";
inherit description;
};
}
];
### gatus integration
mySystem.services.gatus.monitors = mkIf cfg.monitor [
{
name = app;
group = "${category}";
url = "https://${url}";
interval = "1m";
conditions = [ "[CONNECTED] == true" "[STATUS] == 200" "[RESPONSE_TIME] < 50" ];
}
];
### Ingress
services.nginx.virtualHosts.${url} = {
forceSSL = true;
useACMEHost = config.networking.domain;
locations."^~ /" = {
proxyPass = "http://127.0.0.1:${builtins.toString port}";
proxyWebsockets = true;
};
};
### firewall config
# networking.firewall = mkIf cfg.openFirewall {
# allowedTCPPorts = [ port ];
# allowedUDPPorts = [ port ];
# };
### backups
warnings = [
(mkIf (!cfg.backup && config.mySystem.purpose != "Development")
"WARNING: Backups for ${app} are disabled!")
];
services.restic.backups = mkIf cfg.backup (config.lib.mySystem.mkRestic
{
inherit app user;
paths = [ appFolder ];
inherit appFolder;
});
# services.postgresqlBackup = {
# databases = [ app ];
# };
};
}

View file

@ -13,6 +13,7 @@
./whoogle ./whoogle
./redlib ./redlib
./home-assistant ./home-assistant
# ./calibre ./calibre
./ecowitt2mqtt
]; ];
} }

View file

@ -0,0 +1,158 @@
{ lib
, config
, pkgs
, ...
}:
with lib;
let
cfg = config.mySystem.${category}.${app};
app = "ecowitt2mqtt";
category = "containers";
description = "Weather station to MQTT";
image = "ghcr.io/bachya/ecowitt2mqtt:latest@sha256:3c6e9be9332e1b04db836ec721208c753a01faa236c7cf76f966f3ec4d64621d";
user = "nobody"; #string
group = "nobody"; #string
port = 8080; #int
# appFolder = "/var/lib/${app}";
# persistentFolder = "${config.mySystem.persistentFolder}/var/lib/${appFolder}";
host = "${app}" + (if cfg.dev then "-dev" else "");
url = "${host}.${config.networking.domain}";
in
{
options.mySystem.${category}.${app} =
{
enable = mkEnableOption "${app}";
addToHomepage = mkEnableOption "Add ${app} to homepage" // { default = true; };
monitor = mkOption
{
type = lib.types.bool;
description = "Enable gatus monitoring";
default = true;
};
prometheus = mkOption
{
type = lib.types.bool;
description = "Enable prometheus scraping";
default = true;
};
addToDNS = mkOption
{
type = lib.types.bool;
description = "Add to DNS list";
default = true;
};
dev = mkOption
{
type = lib.types.bool;
description = "Development instance";
default = false;
};
backupLocal = mkOption
{
type = lib.types.bool;
description = "Enable local backups";
default = true;
};
backupRemote = mkOption
{
type = lib.types.bool;
description = "Enable remote backups";
default = true;
};
};
config = mkIf cfg.enable {
## Secrets
sops.secrets."${category}/${app}/env" = {
sopsFile = ./secrets.sops.yaml;
owner = user;
inherit group;
restartUnits = [ "podman-${app}.service" ];
};
users.users.truxnell.extraGroups = [ group ];
# Folder perms - only for containers
# systemd.tmpfiles.rules = [
# "d ${persistentFolder}/ 0750 ${user} ${group} -"
# ];
## service
virtualisation.oci-containers.containers = config.lib.mySystem.mkContainer {
inherit app image user group;
env = {
ECOWITT2MQTT_MQTT_BROKER = "mqtt.trux.dev";
ECOWITT2MQTT_MQTT_PORT = "1883";
ECOWITT2MQTT_MQTT_TOPIC = "ecowitt2mqtt/pws";
ECOWITT2MQTT_PORT = "8080";
ECOWITT2MQTT_HASS_DISCOVERY = "true";
ECOWITT2MQTT_OUTPUT_UNIT_SYSTEM = "metric"; # Come on guys nobody want to use freedum units"
};
envFiles = [ config.sops.secrets."${category}/${app}/env".path ];
};
# homepage integration
# mySystem.services.homepage.infrastructure = mkIf cfg.addToHomepage [
# {
# ${app} = {
# icon = "${app}.svg";
# href = "https://${url}";
# description = description;
# };
# }
# ];
### gatus integration
mySystem.services.gatus.monitors = mkIf cfg.monitor [
{
name = app;
group = "${category}";
url = "https://${url}/data/report"; # check the reporting URL for 405 'method not allowed's
interval = "1m";
conditions = [ "[CONNECTED] == true" "[STATUS] == 405" "[RESPONSE_TIME] < 50" ];
}
];
### Ingress
services.nginx.virtualHosts."${app}.${config.networking.domain}" = {
# I dont need/want ssl for this one, weather station expets http
# useACMEHost = config.networking.domain;
# forceSSL = true;
locations."^~ /" = {
proxyPass = "http://${app}:${builtins.toString port}";
proxyWebsockets = true;
extraConfig = "resolver 10.88.0.1;";
};
};
### firewall config
# networking.firewall = mkIf cfg.openFirewall {
# allowedTCPPorts = [ port ];
# allowedUDPPorts = [ port ];
# };
### backups
# warnings = [
# (mkIf (!cfg.backupLocal && config.mySystem.purpose != "Development")
# "WARNING: Local backups for ${app} are disabled!")
# (mkIf (!cfg.backupRemote && config.mySystem.purpose != "Development")
# "WARNING: Remote backups for ${app} are disabled!")
# ];
# services.restic.backups = mkIf cfg.backups config.lib.mySystem.mkRestic
# {
# inherit app user;
# paths = [ appFolder ];
# inherit appFolder;
# local = cfg.backupLocal;
# remote = cfg.backupRemote;
# };
};
}

View file

@ -0,0 +1,77 @@
containers:
ecowitt2mqtt:
env: ENC[AES256_GCM,data:VP1/jz+SAK+ilGAlTt51bDS9QjzV+KXxN+c++/ZdYl4pFZrideghqCkGdknn83Jv4DcnIb30AtUdGR0QfTXY4AfFUUwJfgL8EcOe2+0=,iv:GSQ3/4q/cdOWZxYcGuFBzzyhDxf9wyRustiZgqB8PbI=,tag:/kLE6z5s5DDKGRxix7EnLw==,type:str]
sops:
kms: []
gcp_kms: []
azure_kv: []
hc_vault: []
age:
- recipient: age1lj5vmr02qkudvv2xedfj5tq8x93gllgpr6tzylwdlt7lud4tfv5qfqsd5u
enc: |
-----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBjRENkdkgycktRYnRLbHh3
MFNXSXZRU1Bnc0hpK1ZsOERQcG5Qd1BpRUJRCk00WkFKQWhZcmx4SFozd0JRN09r
bHpaODh5UGNOUFErbVdsK1VFYmdqT3cKLS0tIHdzQ2hxaVl1M1U2VDgrcHBOdlFz
eUhsRzJtNjQ1Mm9kdlI2OTRZVUpjY2sKDt5anrouamcYEaQDjiZEuZpdqe3uO1Ee
0BidTQuN9jfWOlIljftZ2CiedlCoYzXlnGkjIOMoD3uvRTpmRPKBdw==
-----END AGE ENCRYPTED FILE-----
- recipient: age17edew3aahg3t5nte5g0a505sn96vnj8g8gqse8q06ccrrn2n3uysyshu2c
enc: |
-----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBxczQyV3JRNDVpZkZzSjVk
UWhzL28rZmtmSEI0N3pvWDlGUHhIQllySlM4CktXZlJCam1CUU9BNUpKTUgrRGJ4
NnE0c0x6WTRZMWsyVVBIVWw3Rmh6REUKLS0tIGJ4SllRa0p2c2p4UHkvMHBaUnhD
SU40RnZ4dXo1MXU2SjN2Q3ZHZXVjVUUKjeJxmnY397EK9MBe4uFpBQg0XE1p4y/0
cfck88D+LGvMrfBIsMcmMd2EmHsIzZt4SKMIr1ZQ+WdE+Mns6drHcQ==
-----END AGE ENCRYPTED FILE-----
- recipient: age1u4tht685sqg6dkmjyer96r93pl425u6353md6fphpd84jh3jwcusvm7mgk
enc: |
-----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSAwWWxFcWhWOERzdVVmdE83
bmRyRXU1Z3RUU3MydWVJNlBUdlQ2NFVWSGxVCjRFY1BKOER6ejRIVWhLTVp4eSt4
c0Q5Rld1RFZKWEtGcThtUlJVMnpEcVEKLS0tIDhWMW45d1ZqZ25RY0NvVzQ0NTBK
RGttN1JNbXhmS2NnTFpOQlNKUC9vbm8KILk4bxt2b58+igJ4c4PrjgJY9AyGrvsO
zBfxhFvlGuYjR8W1r6VcH7+JsQhKZrw07gIU5ornHvE34oxgxdYXrA==
-----END AGE ENCRYPTED FILE-----
- recipient: age1cp6vegrmqfkuj8nmt2u3z0sur7n0f7e9x9zmdv4zygp8j2pnucpsdkgagc
enc: |
-----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSB1b0x5QmJQbHA0STliVEM3
N0RZaFFud1VTbm4xUm5EMVFDMVRncGpDNWc0CndJYzVNMG8xZlo5dUlRb3BKVStF
OFdKYXdRL2ZwWlk1Uzd2WUthRTRzOEkKLS0tIFRVRnFpYWdTeGhNc1VpdCsrTFcv
bW5uVGNsQjFmUjNvQnYzc2pSMUliUjQKBzI9IGdKfEXNRVh3s7fon/6VS9ZUSpG/
hOHQ93wX0VAy6wO+iugDfM/1Zuc2V66+T4wYPfUtWapj009L31eufw==
-----END AGE ENCRYPTED FILE-----
- recipient: age1ekt5xz7u2xgdzgsrffhd9x22n80cn4thxd8zxjy2ey5vq3ca7gnqz25g5r
enc: |
-----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBabEN5eXd2Q1A2R0ZsalYy
MW43TUdqaG80MmtzVit1QVdvcExQRjZYT1NjCnA5L2NxRks5UDZVYVVsRGJJZ3cz
bGhtVDFTVXFCTGtuUFRaNmtzVUdRZUEKLS0tIC9qYnRxZ2FqWllybnpMMU9NNDFx
VGNTdnhiMW9KcE9ST3RPUitaMVdsRVkKLaABu7ZJ1Nc4ROkrzoRiMe4vdLKMmzMW
O0ylhZRqqHx4nWGSBgx680zKm3WAx3o97nH3IFCaLlHuNGm46iOYkg==
-----END AGE ENCRYPTED FILE-----
- recipient: age1jpeh4s553taxkyxhzlshzqjfrtvmmp5lw0hmpgn3mdnmgzku332qe082dl
enc: |
-----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBDM2IvQU1IRnVVRVo0bmUx
QjMyZVRiN3pSUkNYUVYzSXVUSVBPTXdIWUZzCjZaOHo4bllaTklTb29XdVAySHJY
bmMwT3RxS0p5MjFHdXhIL0tIRERNTTAKLS0tIHBlQ2RJYTNDQm1oRGN3b2JIOXFi
YUdHa1RVVTRBbmJkdkdDa2JCbG40NlEKta9VW9hc2saA/nzWoER35S7Yl7M9CHQ7
YmZ4LIz8IqB6Sihk1pcmX2RAeMA8uMPfEMEBejwiuM57NjEFMgllbw==
-----END AGE ENCRYPTED FILE-----
- recipient: age1j2r8mypw44uvqhfs53424h6fu2rkr5m7asl7rl3zn3xzva9m3dcqpa97gw
enc: |
-----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBZSHYrejZ3RExjek1FR21Y
VlNvbmdnSjNrcHBkNFByNXpGTGkwMnVQMWlRCjg5U2l6NHVCakprVS9kUTFRa1lk
SXpEYU9WZHVlcGRSS1UyaDhkak5jMFEKLS0tIC8reGg4RWNDb1AzUkx5TGg1WVN1
ZzBpamhpVFRBZHZ0MTdJRTl5d2RqK3MKQuNJCLdNBa9DG8V4sRVQ6GKhWjcKK9eC
FcTxyUBPNDMVYlHR5DxONIfzT9ymlSReFUkwjXAgVTwhqLOgpTUGAw==
-----END AGE ENCRYPTED FILE-----
lastmodified: "2024-05-03T09:52:12Z"
mac: ENC[AES256_GCM,data:Z4u5T4kJ+dRCE9qZENBstOMMrSSw503PRS5qwNmrx1kTbaNN5VPd/mrkxWM8pZudLxo3pBnCZSPYju1tpzQisRBcu/BwMAiBLx4xSJsEJhG//fWbv93XUGJ7i7nlW2Kl1V0QUrG1WSz5RXx1PE2oTEQCbRSGZyYo5FtHZsOhHEA=,iv:HidiueXjLNwBV1vY/7vNqWCDr+rfwN00NpfC9RuPZms=,tag:qkfNjPX6Ti2EnnWnqjtvNw==,type:str]
pgp: []
unencrypted_suffix: _unencrypted
version: 3.8.1

View file

@ -69,7 +69,7 @@ in
RCON_PORT = "27019"; RCON_PORT = "27019";
}; };
environmentFiles = [ config.sops.secrets."services/${app}/env".path ]; environmentFiles = [ config.sops.secrets."services/${app}/env".path ];
ports = [ (builtins.toString port) ]; # expose port ports = [ "${builtins.toString port}:${builtins.toString port}/UDP" ]; # expose port
}; };
networking.firewall = mkIf cfg.openFirewall { networking.firewall = mkIf cfg.openFirewall {

View file

@ -110,7 +110,7 @@ in
services.nginx.virtualHosts."${app}.${config.networking.domain}" = { services.nginx.virtualHosts."${app}.${config.networking.domain}" = {
useACMEHost = config.networking.domain; useACMEHost = config.networking.domain;
forceSSL = true; forceSSL = true;
locations."/" = { locations."^~ /" = {
proxyPass = "http://${app}:${builtins.toString port}"; proxyPass = "http://${app}:${builtins.toString port}";
extraConfig = "resolver 10.88.0.1;"; extraConfig = "resolver 10.88.0.1;";
}; };

View file

@ -52,7 +52,7 @@ in
services.nginx.virtualHosts."${app}.${config.networking.domain}" = { services.nginx.virtualHosts."${app}.${config.networking.domain}" = {
useACMEHost = config.networking.domain; useACMEHost = config.networking.domain;
forceSSL = true; forceSSL = true;
locations."/" = { locations."^~ /" = {
proxyPass = "http://${app}:${builtins.toString port}"; proxyPass = "http://${app}:${builtins.toString port}";
proxyWebsockets = true; proxyWebsockets = true;
extraConfig = "resolver 10.88.0.1;"; extraConfig = "resolver 10.88.0.1;";
@ -61,7 +61,7 @@ in
mySystem.services.homepage.media = mkIf cfg.addToHomepage [ mySystem.services.homepage.home = mkIf cfg.addToHomepage [
{ {
${app} = { ${app} = {
icon = "${app}.svg"; icon = "${app}.svg";

View file

@ -300,7 +300,7 @@ in
services.nginx.virtualHosts."${app}.${config.networking.domain}" = { services.nginx.virtualHosts."${app}.${config.networking.domain}" = {
useACMEHost = config.networking.domain; useACMEHost = config.networking.domain;
forceSSL = true; forceSSL = true;
locations."/" = { locations."^~ /" = {
proxyPass = "http://${app}:${builtins.toString port}"; proxyPass = "http://${app}:${builtins.toString port}";
extraConfig = "resolver 10.88.0.1;"; extraConfig = "resolver 10.88.0.1;";

View file

@ -55,7 +55,7 @@ in
services.nginx.virtualHosts."${app}.${config.networking.domain}" = { services.nginx.virtualHosts."${app}.${config.networking.domain}" = {
useACMEHost = config.networking.domain; useACMEHost = config.networking.domain;
forceSSL = true; forceSSL = true;
locations."/" = { locations."^~ /" = {
proxyPass = "http://${app}:${builtins.toString port}"; proxyPass = "http://${app}:${builtins.toString port}";
extraConfig = "resolver 10.88.0.1;"; extraConfig = "resolver 10.88.0.1;";

View file

@ -53,7 +53,7 @@ in
services.nginx.virtualHosts."${app}.${config.networking.domain}" = { services.nginx.virtualHosts."${app}.${config.networking.domain}" = {
useACMEHost = config.networking.domain; useACMEHost = config.networking.domain;
forceSSL = true; forceSSL = true;
locations."/" = { locations."^~ /" = {
proxyPass = "http://${app}:${builtins.toString port}"; proxyPass = "http://${app}:${builtins.toString port}";
extraConfig = "resolver 10.88.0.1;"; extraConfig = "resolver 10.88.0.1;";

View file

@ -15,7 +15,7 @@ let
port = 8080; #int port = 8080; #int
appFolder = "/var/lib/${app}"; appFolder = "/var/lib/${app}";
# persistentFolder = "${config.mySystem.persistentFolder}/var/lib/${appFolder}"; # persistentFolder = "${config.mySystem.persistentFolder}/var/lib/${appFolder}";
host = "${app}" + (if cfg.development then "-dev" else ""); host = "${app}" + (if cfg.dev then "-dev" else "");
url = "${host}.${config.networking.domain}"; url = "${host}.${config.networking.domain}";
in in
{ {
@ -41,7 +41,7 @@ in
description = "Add to DNS list"; description = "Add to DNS list";
default = true; default = true;
}; };
development = mkOption dev = mkOption
{ {
type = lib.types.bool; type = lib.types.bool;
description = "Development instance"; description = "Development instance";
@ -83,11 +83,6 @@ in
## container ## container
virtualisation.oci-containers.containers = config.lib.mySystem.mkContainer { virtualisation.oci-containers.containers = config.lib.mySystem.mkContainer {
inherit app image user group; inherit app image user group;
env = {
test = "derp";
};
envFiles = [ ];
volumes = [ ];
}; };
# homepage integration # homepage integration

View file

@ -43,7 +43,7 @@ in
services.nginx.virtualHosts."${app}.${config.networking.domain}" = { services.nginx.virtualHosts."${app}.${config.networking.domain}" = {
useACMEHost = config.networking.domain; useACMEHost = config.networking.domain;
forceSSL = true; forceSSL = true;
locations."/" = { locations."^~ /" = {
proxyPass = "http://${app}:${builtins.toString port}"; proxyPass = "http://${app}:${builtins.toString port}";
extraConfig = "resolver 10.88.0.1;"; extraConfig = "resolver 10.88.0.1;";

View file

@ -51,7 +51,7 @@ in
services.nginx.virtualHosts."${app}.${config.networking.domain}" = { services.nginx.virtualHosts."${app}.${config.networking.domain}" = {
useACMEHost = config.networking.domain; useACMEHost = config.networking.domain;
forceSSL = true; forceSSL = true;
locations."/" = { locations."^~ /" = {
proxyPass = "http://${app}:${builtins.toString port}"; proxyPass = "http://${app}:${builtins.toString port}";
extraConfig = "resolver 10.88.0.1;"; extraConfig = "resolver 10.88.0.1;";

View file

@ -41,7 +41,7 @@ in
services.nginx.virtualHosts."${app}.${config.networking.domain}" = { services.nginx.virtualHosts."${app}.${config.networking.domain}" = {
useACMEHost = config.networking.domain; useACMEHost = config.networking.domain;
forceSSL = true; forceSSL = true;
locations."/" = { locations."^~ /" = {
proxyPass = "http://${app}:${builtins.toString port}"; proxyPass = "http://${app}:${builtins.toString port}";
extraConfig = "resolver 10.88.0.1;"; extraConfig = "resolver 10.88.0.1;";

View file

@ -52,7 +52,7 @@ in
services.nginx.virtualHosts."${app}.${config.networking.domain}" = { services.nginx.virtualHosts."${app}.${config.networking.domain}" = {
useACMEHost = config.networking.domain; useACMEHost = config.networking.domain;
forceSSL = true; forceSSL = true;
locations."/" = { locations."^~ /" = {
proxyPass = "http://${app}:${builtins.toString port}"; proxyPass = "http://${app}:${builtins.toString port}";
extraConfig = "resolver 10.88.0.1;"; extraConfig = "resolver 10.88.0.1;";

View file

@ -22,11 +22,11 @@ with lib;
user = "${options.user}:${options.group}"; user = "${options.user}:${options.group}";
environment = { environment = {
TZ = config.time.timeZone; TZ = config.time.timeZone;
} // options.env; } // lib.attrsets.attrByPath [ "env" ] { } options;
environmentFiles = lib.attrsets.attrByPath [ "envFiles" ] [ ] options; environmentFiles = lib.attrsets.attrByPath [ "envFiles" ] [ ] options;
volumes = [ "/etc/localtime:/etc/localtime:ro" ] volumes = [ "/etc/localtime:/etc/localtime:ro" ]
++ lib.attrsets.attrByPath [ "volumes" ] [ ] options; ++ lib.attrsets.attrByPath [ "volumes" ] [ ] options;
ports = lib.attrsets.attrByPath [ "ports" ] [ ] options;
extraOptions = containerExtraOptions; extraOptions = containerExtraOptions;
}; };
} }

View file

@ -0,0 +1,138 @@
{ lib
, config
, pkgs
, ...
}:
with lib;
let
cfg = config.mySystem.${category}.${app};
app = "calibre-web";
category = "services";
description = "Calibre web-server";
# image = "%{image}";
inherit (config.services.calibre-web) user;#string
inherit (config.services.calibre-web) group;#string
port = 8083; #int
appFolder = "/var/lib/${app}";
# persistentFolder = "${config.mySystem.persistentFolder}/var/lib/${appFolder}";
host = "${app}" + (if cfg.dev then "-dev" else "");
url = "${host}.${config.networking.domain}";
in
{
options.mySystem.${category}.${app} =
{
enable = mkEnableOption "${app}";
addToHomepage = mkEnableOption "Add ${app} to homepage" // { default = true; };
monitor = mkOption
{
type = lib.types.bool;
description = "Enable gatus monitoring";
default = true;
};
prometheus = mkOption
{
type = lib.types.bool;
description = "Enable prometheus scraping";
default = true;
};
addToDNS = mkOption
{
type = lib.types.bool;
description = "Add to DNS list";
default = true;
};
dev = mkOption
{
type = lib.types.bool;
description = "Development instance";
default = false;
};
backup = mkOption
{
type = lib.types.bool;
description = "Enable backups";
default = true;
};
};
config = mkIf cfg.enable {
## Secrets
# sops.secrets."${category}/${app}/env" = {
# sopsFile = ./secrets.sops.yaml;
# owner = user;
# group = group;
# restartUnits = [ "${app}.service" ];
# };
users.users.truxnell.extraGroups = [ group ];
# Folder perms - only for containers
# systemd.tmpfiles.rules = [
# "d ${persistentFolder}/ 0750 ${user} ${group} -"
# ];
## service
services.calibre-web = {
enable = true;
listen.port = port;
options = {
calibreLibrary = "${config.mySystem.nasFolder}/natflix/books/";
};
};
# homepage integration
mySystem.services.homepage.infrastructure = mkIf cfg.addToHomepage [
{
${app} = {
icon = "${app}.svg";
href = "https://${url}";
inherit description;
};
}
];
### gatus integration
mySystem.services.gatus.monitors = mkIf cfg.monitor [
{
name = app;
group = "${category}";
url = "https://${url}";
interval = "1m";
conditions = [ "[CONNECTED] == true" "[STATUS] == 200" "[RESPONSE_TIME] < 50" ];
}
];
### Ingress
services.nginx.virtualHosts.${url} = {
forceSSL = true;
useACMEHost = config.networking.domain;
locations."^~ /" = {
proxyPass = "http://127.0.0.1:${builtins.toString port}";
};
};
### firewall config
# networking.firewall = mkIf cfg.openFirewall {
# allowedTCPPorts = [ port ];
# allowedUDPPorts = [ port ];
# };
### backups
warnings = [
(mkIf (!cfg.backup && config.mySystem.purpose != "Development")
"WARNING: Backups for ${app} are disabled!")
];
services.restic.backups = mkIf cfg.backup (config.lib.mySystem.mkRestic
{
inherit app user;
paths = [ appFolder ];
inherit appFolder;
});
};
}

View file

@ -25,5 +25,7 @@
./radicale ./radicale
./node-red ./node-red
./nginx ./nginx
./miniflux
./calibre-web
]; ];
} }

View file

@ -14,7 +14,7 @@ let
port = 2342; #int port = 2342; #int
appFolder = "/var/lib/${app}"; appFolder = "/var/lib/${app}";
# persistentFolder = "${config.mySystem.persistentFolder}/var/lib/${appFolder}"; # persistentFolder = "${config.mySystem.persistentFolder}/var/lib/${appFolder}";
host = "${app}" + (if cfg.development then "-dev" else ""); host = "${app}" + (if cfg.dev then "-dev" else "");
url = "${host}.${config.networking.domain}"; url = "${host}.${config.networking.domain}";
in in
{ {
@ -40,7 +40,7 @@ in
description = "Add to DNS list"; description = "Add to DNS list";
default = true; default = true;
}; };
development = mkOption dev = mkOption
{ {
type = lib.types.bool; type = lib.types.bool;
description = "Development instance"; description = "Development instance";

View file

@ -12,13 +12,13 @@ let
image = "%{image}"; image = "%{image}";
user = "%{user kah}"; #string user = "%{user kah}"; #string
group = "%{group kah}"; #string group = "%{group kah}"; #string
port = %{ port }; #int port = 1234; #int
appFolder = "/var/lib/${app}"; appFolder = "/var/lib/${app}";
# persistentFolder = "${config.mySystem.persistentFolder}/var/lib/${appFolder}"; # persistentFolder = "${config.mySystem.persistentFolder}/var/lib/${appFolder}";
host="${app}" ++ mkIf cfg.development "-dev"; host = "${app}" + (if cfg.dev then "-dev" else "");
url = "${host}.${config.networking.domain}"; url = "${host}.${config.networking.domain}";
in in
{ {
options.mySystem.${category}.${app} = options.mySystem.${category}.${app} =
{ {
enable = mkEnableOption "${app}"; enable = mkEnableOption "${app}";
@ -41,26 +41,21 @@ let
description = "Add to DNS list"; description = "Add to DNS list";
default = true; default = true;
}; };
development = mkOption dev = mkOption
{ {
type = lib.types.bool; type = lib.types.bool;
description = "Development instance"; description = "Development instance";
default = false; default = false;
}; };
backupLocal = mkOption backup = mkOption
{ {
type = lib.types.bool; type = lib.types.bool;
description = "Enable local backups"; description = "Enable backups";
default = true;
};
backupRemote = mkOption
{
type = lib.types.bool;
description = "Enable remote backups";
default = true; default = true;
}; };
}; };
config = mkIf cfg.enable { config = mkIf cfg.enable {
@ -92,7 +87,7 @@ let
${app} = { ${app} = {
icon = "${app}.svg"; icon = "${app}.svg";
href = "https://${url}"; href = "https://${url}";
description = description; inherit description;
}; };
} }
]; ];
@ -110,8 +105,8 @@ let
### Ingress ### Ingress
services.nginx.virtualHosts.${url} = { services.nginx.virtualHosts.${url} = {
useACMEHost = host;
forceSSL = true; forceSSL = true;
useACMEHost = config.networking.domain;
locations."^~ /" = { locations."^~ /" = {
proxyPass = "http://127.0.0.1:${builtins.toString port}"; proxyPass = "http://127.0.0.1:${builtins.toString port}";
}; };
@ -126,21 +121,23 @@ let
### backups ### backups
warnings = [ warnings = [
(mkIf (!cfg.backupLocal && config.mySystem.purpose != "Development") (mkIf (!cfg.backup && config.mySystem.purpose != "Development")
"WARNING: Local backups for ${app} are disabled!") "WARNING: Backups for ${app} are disabled!")
(mkIf (!cfg.backupRemote && config.mySystem.purpose != "Development")
"WARNING: Remote backups for ${app} are disabled!")
]; ];
services.restic.backups = mkIf cfg.backups config.lib.mySystem.mkRestic services.restic.backups = mkIf cfg.backup (config.lib.mySystem.mkRestic
{ {
inherit app user; inherit app user;
paths = [ appFolder ]; paths = [ appFolder ];
inherit appFolder; inherit appFolder;
local=cfg.backupLocal; });
remote=cfg.backupRemote;
};
# services.postgresqlBackup = {
# databases = [ app ];
# };
}; };
} }

View file

@ -0,0 +1,180 @@
{ lib
, config
, pkgs
, ...
}:
with lib;
let
cfg = config.mySystem.${category}.${app};
app = "miniflux";
category = "services";
description = "Minimalist feed reader";
# image = "%{image}";
user = app; #string
group = app; #string
port = 8072; #int
appFolder = "/var/lib/${app}";
# persistentFolder = "${config.mySystem.persistentFolder}/var/lib/${appFolder}";
host = "${app}" + (if cfg.dev then "-dev" else "");
url = "${host}.${config.networking.domain}";
databaseUrl = "user=miniflux host=/run/postgresql dbname=miniflux";
in
{
options.mySystem.${category}.${app} =
{
enable = mkEnableOption "${app}";
addToHomepage = mkEnableOption "Add ${app} to homepage" // { default = true; };
monitor = mkOption
{
type = lib.types.bool;
description = "Enable gatus monitoring";
default = true;
};
prometheus = mkOption
{
type = lib.types.bool;
description = "Enable prometheus scraping";
default = true;
};
addToDNS = mkOption
{
type = lib.types.bool;
description = "Add to DNS list";
default = true;
};
dev = mkOption
{
type = lib.types.bool;
description = "Development instance";
default = false;
};
backup = mkOption
{
type = lib.types.bool;
description = "Enable backups";
default = true;
};
};
config = mkIf cfg.enable {
## Secrets
sops.secrets."${category}/${app}/env" = {
sopsFile = ./secrets.sops.yaml;
owner = user;
inherit group;
restartUnits = [ "${app}.service" ];
};
users.users.truxnell.extraGroups = [ group ];
users.users.miniflux = {
isSystemUser = true;
group = "miniflux";
};
users.groups.miniflux = { };
environment.persistence."${config.mySystem.system.impermanence.persistPath}" = lib.mkIf config.mySystem.system.impermanence.enable {
directories = [{ directory = appFolder; inherit user; inherit group; mode = "750"; }];
};
## service
services.miniflux = {
enable = true;
adminCredentialsFile = config.sops.secrets."${category}/${app}/env".path;
config = {
LISTEN_ADDR = "localhost:${builtins.toString port}";
DATABASE_URL = databaseUrl;
RUN_MIGRATIONS = "1";
CREATE_ADMIN = "1";
};
};
# automatically reset feed errors regular
# systemd.services.miniflux-reset-feed-errors = {
# description = "Miniflux reset feed errors";
# wantedBy = [ "multi-user.target" ];
# after = [ "network.target" "${app}.service" ];
# environment.DATABASE_URL = databaseUrl;
# startAt = "00/4:00"; # Every four hours.
# serviceConfig = {
# Type = "oneshot";
# DynamicUser = true;
# RuntimeDirectory = "miniflux"; # Creates /run/miniflux.
## EnvironmentFile = cfg.envFilePath;
# ExecStart = pkgs.writeShellScriptBin "miniflux-reset-feed-errors" ''
# ${cfg.package}/bin/miniflux -reset-feed-errors
# '';
# };
# };
# homepage integration
mySystem.services.homepage.infrastructure = mkIf cfg.addToHomepage [
{
${app} = {
icon = "${app}.svg";
href = "https://${url}";
inherit description;
};
}
];
# ensure postgresql setup
services.postgresql = {
ensureDatabases = [ app ];
ensureUsers = [{
name = app;
ensureDBOwnership = true;
}];
};
### gatus integration
mySystem.services.gatus.monitors = mkIf cfg.monitor [
{
name = app;
group = "${category}";
url = "https://${url}";
interval = "1m";
conditions = [ "[CONNECTED] == true" "[STATUS] == 200" "[RESPONSE_TIME] < 50" ];
}
];
### Ingress
services.nginx.virtualHosts.${url} = {
forceSSL = true;
useACMEHost = config.networking.domain;
locations."^~ /" = {
proxyPass = "http://127.0.0.1:${builtins.toString port}";
};
};
### firewall config
# networking.firewall = mkIf cfg.openFirewall {
# allowedTCPPorts = [ port ];
# allowedUDPPorts = [ port ];
# };
### backups
warnings = [
(mkIf (!cfg.backup && config.mySystem.purpose != "Development")
"WARNING: Backups for ${app} are disabled!")
];
# services.restic.backups = mkIf cfg.backup (config.lib.mySystem.mkRestic
# {
# inherit app user;
# paths = [ appFolder ];
# inherit appFolder;
# });
services.postgresqlBackup = mkIf cfg.backup {
databases = [ app ];
};
};
}

View file

@ -0,0 +1,77 @@
services:
miniflux:
env: ENC[AES256_GCM,data:ZJeYF8xK2WhiIINfpnSQvqhKMywyI7sln/SMe7TN9RhPtO2bninS3+Fa50qWvEDlg3c=,iv:2B1LG2t3VbjSNLIIQrcvF9FtT+9ca+2+5Ox7hUsehFA=,tag:ydHlhoMqJkCVy+MYqblpUQ==,type:str]
sops:
kms: []
gcp_kms: []
azure_kv: []
hc_vault: []
age:
- recipient: age1lj5vmr02qkudvv2xedfj5tq8x93gllgpr6tzylwdlt7lud4tfv5qfqsd5u
enc: |
-----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBSNGIxTVpnYVJhaUNsOWtW
RUMrbzJkUWZvYkZPcUU5c2ZLNkFHeU9PSm44CnFHTFB4dlBzOVFUQldxNzlxRkNG
MkFncUQ3QlVpYmgrUHBkYk1nZm5hTEEKLS0tICtMeWx6bnAxMDBmWVhsakdNcUNP
MXdTTTJia2Jyb2hTMExtOEdJMkZJN2cKO8qhHDYVpSg3KhrGizcErDIg6ejYyBNE
V6w7jKHl5aaNLTaP4nBt7jMM3DQzc1pnFJBCPwj8uYQcG9Bd5+j7yw==
-----END AGE ENCRYPTED FILE-----
- recipient: age17edew3aahg3t5nte5g0a505sn96vnj8g8gqse8q06ccrrn2n3uysyshu2c
enc: |
-----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBWR2JzQWFFQ29uL0FvZGNT
dU9pMklGaVRnWlRpZ3pKM3ZZTTVQQkxZUHhrCkxBTzMrR09zcW04WDlPRjZVbjNI
TVVxWVRaS2pKaGtmdVc4WUE5SEI2MXcKLS0tIE9ZcjQzM1prSm1yRXFVNWxZeWVv
a0x2cDhiUDlBZ2ZZVGZRbytBUEJqN2sK/+tVAbPLLBil/sHSBj0tKacvsEHGbXto
V1InpgnmZG3vmrFJM/i2ApjwpBC1diNzrd25A7jQZyriUw6CZdKZlw==
-----END AGE ENCRYPTED FILE-----
- recipient: age1u4tht685sqg6dkmjyer96r93pl425u6353md6fphpd84jh3jwcusvm7mgk
enc: |
-----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBJZEhscHdsWk5nek1BUEhJ
OWI1ekNZNEYvVXhrN0Y2U1FYRThZUlhsWVdNCnFERkNqTUkwY1FzRDRZS3ljWXpm
VFM1ejVjZVRkVnZyTlM5eldKMm15bkEKLS0tIHlvbXA0ZFU4R2FUSXlKU3A0S1lH
N0ExUFFxd3lFK3pvQzRJS1FzUy80ZE0KlCXPhxh5DHsL+QueUYpyfsr9/bjYoO+J
bNo45Dm0WruG+KUGYpk3+lK5ASAnSsEfL+q7NkyBVLT/ag+DSvL9IA==
-----END AGE ENCRYPTED FILE-----
- recipient: age1cp6vegrmqfkuj8nmt2u3z0sur7n0f7e9x9zmdv4zygp8j2pnucpsdkgagc
enc: |
-----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSA1T2duOFNQQlRKMGhLZDA2
dG0zek9DelIveDZ3ZUNIWWx3M2hmZEdva2dFClRndEkzVXpRU1REbXY4ZEJhSElO
Z1RXdkZ3UGxrcjNXbExCYml4cEpCSjgKLS0tIGlObjkyT1JZV3VYYitqbk5NNkdC
dEI0azRtU0lCT25GSTRtaldNdzJzQVUKlkmuvYVDI9qkQpVhHMtJTOGSYXDQQoeo
/zUI1mrmplCoWhipSMmxg7pxEorK8VUxhX7iBg5360xRBbXWqhGh3w==
-----END AGE ENCRYPTED FILE-----
- recipient: age1ekt5xz7u2xgdzgsrffhd9x22n80cn4thxd8zxjy2ey5vq3ca7gnqz25g5r
enc: |
-----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBjQzc2OEQxOGp6Yk01eWE4
ZjVlYjhMMzQrdG5UdmdENEMrVVMvTnhURTJvCkZ5T2JmeXc3eTBKYy9EbGYzM0s5
cmlRc3dvYWMzeUtxMEt1QjBtVXVtSTQKLS0tIFJtQjIwbUxyWjl6MTlRUEluNlVu
RGlHc1Jobnl5bkxaSnhHWWlScjIxbWsKxSnmobPuqg4aQcEGw2LcaBtpAMb1mkUX
rj/UniXbYnZkIibIj/qaWpFuUbo1iCavl8cWzTrm/cjLSVjoIVYGAQ==
-----END AGE ENCRYPTED FILE-----
- recipient: age1jpeh4s553taxkyxhzlshzqjfrtvmmp5lw0hmpgn3mdnmgzku332qe082dl
enc: |
-----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBnYVVrL0RIdkV4bmpjN2ZN
SlZaRllGcU1KanY5ZnhZcFNMelRmSEQvSUI0CmFqRUNQMXRlcjNPOUg0UmUzdlVp
ZThjMFlnZ3pXdkJNZEZrbk1zUGJHOUUKLS0tIHFOdzhUT1VRdlFzZEplcU4zaHdR
R01xaEt1bkgvNElXUlY3WWx5RUF4Sm8KX9E/YojwOmaulW2j80jR3zGH0WmWzXZq
5TBWE0M4+cQPoEpA48deAV1y+oKQovFjSR08ytvZxFCK7RbKkz6h4g==
-----END AGE ENCRYPTED FILE-----
- recipient: age1j2r8mypw44uvqhfs53424h6fu2rkr5m7asl7rl3zn3xzva9m3dcqpa97gw
enc: |
-----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSAyVk9ZMGFmK1FUeG53N2lZ
V25BdDMvT01TSjZ1MEx6NEVaU2dESnRRb0RFCklHZ05KYUNtVFV0QmFGTDJDejEv
VjNlU293MjQ5ZDhLYUVmRTg3c3FwWTgKLS0tIEx0dDdZY3czekl1b1FaQWp0d092
V1JHaFgxUHNCWjNteWNpckVSNGdLQW8KAYTC47LvPauQiDjylAegfl5zAdJMtzZV
BP1aNMPDIidKKiK3wonpslnNkT83sHDpdDf+IpvnBuWL9oj36fY+yA==
-----END AGE ENCRYPTED FILE-----
lastmodified: "2024-05-05T01:01:40Z"
mac: ENC[AES256_GCM,data:bazV7Inpog/BX/rPAQEbDqpZuOtCVQUzTXjPpnwHrAbWobYYV9QeBCOhRxXQj8jU37lsnN9vNxNeAtJMQbLQu1KVYt6qs9tFVm/kGNeaAQooXQqaUXCHnQoVNuJbZmKjkiWgpepWfkoKyP4NAuBUG8jT1DMYIp6QvuzjxRwyiI8=,iv:Nu38xj+oYkEruqZ/PvKQ6/7aHYzOxQ6gv1jPU39fEOg=,tag:+doKpf6O7iUyBnIZaV2PMQ==,type:str]
pgp: []
unencrypted_suffix: _unencrypted
version: 3.8.1

View file

@ -8,7 +8,17 @@ let
cfg = config.mySystem.nfs.nas; cfg = config.mySystem.nfs.nas;
in in
{ {
options.mySystem.nfs.nas.enable = mkEnableOption "Mount NAS"; options.mySystem.nfs.nas = {
enable = mkEnableOption "Mount NAS";
lazy = mkOption
{
type = lib.types.bool;
description = "Enable lazymount";
default = false;
};
};
config = mkIf cfg.enable config = mkIf cfg.enable
{ {
@ -17,7 +27,7 @@ in
environment.systemPackages = with pkgs; [ nfs-utils ]; environment.systemPackages = with pkgs; [ nfs-utils ];
systemd.mounts = [{ systemd.mounts = lib.mkIf cfg.lazy [{
type = "nfs"; type = "nfs";
mountConfig = { mountConfig = {
Options = "noatime"; Options = "noatime";
@ -26,7 +36,7 @@ in
where = "/mnt/nas"; where = "/mnt/nas";
}]; }];
systemd.automounts = [{ systemd.automounts = lib.mkIf cfg.lazy [{
wantedBy = [ "multi-user.target" ]; wantedBy = [ "multi-user.target" ];
automountConfig = { automountConfig = {
TimeoutIdleSec = "600"; TimeoutIdleSec = "600";
@ -34,5 +44,10 @@ in
where = "/mnt/nas"; where = "/mnt/nas";
}]; }];
fileSystems."${config.mySystem.nasFolder}" = lib.mkIf (!cfg.lazy) {
device = "daedalus.${config.mySystem.internalDomain}:/tank";
fsType = "nfs";
};
}; };
} }

View file

@ -32,6 +32,7 @@ in
forceSSL = true; forceSSL = true;
locations."/" = { locations."/" = {
proxyPass = "http://127.0.0.1:${builtins.toString config.services.node-red.port}"; proxyPass = "http://127.0.0.1:${builtins.toString config.services.node-red.port}";
proxyWebsockets = true;
}; };
}; };
@ -39,7 +40,7 @@ in
directories = [{ directory = appFolder; inherit user; inherit group; mode = "750"; }]; directories = [{ directory = appFolder; inherit user; inherit group; mode = "750"; }];
}; };
mySystem.services.homepage.media = mkIf cfg.addToHomepage [ mySystem.services.homepage.home = mkIf cfg.addToHomepage [
{ {
${app} = { ${app} = {
icon = "${app}.svg"; icon = "${app}.svg";

View file

@ -38,9 +38,9 @@ in
}; };
}; };
mySystem.services.homepage.media = mkIf cfg.addToHomepage [ mySystem.services.homepage.infrastructure = mkIf cfg.addToHomepage [
{ {
code-shodan = { "code-${config.networking.hostName}" = {
icon = "vscode.svg"; icon = "vscode.svg";
href = "https://${url}"; href = "https://${url}";

View file

@ -5,26 +5,84 @@
}: }:
with lib; with lib;
let let
cfg = config.mySystem.services.postgresql; cfg = config.mySystem.${category}.${app};
app = "postgresql";
category = "services";
description = "Postgres RDMS";
# user = "%{user kah}"; #string
# group = "%{group kah}"; #string
# port = 1234; #int
appFolder = "/var/lib/${app}";
# persistentFolder = "${config.mySystem.persistentFolder}/var/lib/${appFolder}";
# host = "${app}" + (if cfg.dev then "-dev" else "");
# url = "${host}.${config.networking.domain}";
in in
{ {
options.mySystem.services.postgresql.enable = mkEnableOption "postgresql"; options.mySystem.${category}.${app} =
{
enable = mkEnableOption "${app}";
addToHomepage = mkEnableOption "Add ${app} to homepage" // { default = true; };
prometheus = mkOption
{
type = lib.types.bool;
description = "Enable prometheus scraping";
default = true;
};
backup = mkOption
{
type = lib.types.bool;
description = "Enable backups";
default = true;
};
};
config = mkIf cfg.enable { config = mkIf cfg.enable {
## Secrets
# sops.secrets."${category}/${app}/env" = {
# sopsFile = ./secrets.sops.yaml;
# owner = user;
# group = group;
# restartUnits = [ "${app}.service" ];
# };
services.postgresql = { services.postgresql = {
enable = true; enable = true;
authentication = ''
local homeassistant homeassistant ident map=ha
'';
identMap = '' identMap = ''
ha root homeassistant # ArbitraryMapName systemUser DBUser
superuser_map root postgres
superuser_map postgres postgres
# Let other names login as themselves
superuser_map /^(.*)$ \1
''; '';
ensureDatabases = [ "homeassistant" ]; authentication = ''
ensureUsers = [ #type database DBuser auth-method optional_ident_map
{ name = "homeassistant"; ensureDBOwnership = true; } local sameuser all peer map=superuser_map
]; '';
settings = {
max_connections = 200;
random_page_cost = 1.1;
}; };
};
# enable backups
services.postgresqlBackup = mkIf cfg.backup {
enable = lib.mkForce true;
location = "${config.mySystem.nasFolder}/backup/nixos/postgresql";
};
### firewall config
# networking.firewall = mkIf cfg.openFirewall {
# allowedTCPPorts = [ port ];
# allowedUDPPorts = [ port ];
# };
}; };
} }

View file

@ -14,7 +14,7 @@ let
port = 9001; #int port = 9001; #int
appFolder = "/var/lib/${app}"; appFolder = "/var/lib/${app}";
# persistentFolder = "${config.mySystem.persistentFolder}/var/lib/${appFolder}"; # persistentFolder = "${config.mySystem.persistentFolder}/var/lib/${appFolder}";
host = "${app}" + (if cfg.development then "-dev" else ""); host = "${app}" + (if cfg.dev then "-dev" else "");
url = "${host}.${config.networking.domain}"; url = "${host}.${config.networking.domain}";
in in
{ {
@ -34,7 +34,7 @@ in
description = "Add to DNS list"; description = "Add to DNS list";
default = true; default = true;
}; };
development = mkOption dev = mkOption
{ {
type = lib.types.bool; type = lib.types.bool;
description = "Development instance"; description = "Development instance";

View file

@ -15,7 +15,7 @@ let
port = 5232; #int port = 5232; #int
appFolder = "/var/lib/${app}"; appFolder = "/var/lib/${app}";
# persistentFolder = "${config.mySystem.persistentFolder}/var/lib/${appFolder}"; # persistentFolder = "${config.mySystem.persistentFolder}/var/lib/${appFolder}";
host = "${app}" + (if cfg.development then "-dev" else ""); host = "${app}" + (if cfg.dev then "-dev" else "");
url = "${host}.${config.networking.domain}"; url = "${host}.${config.networking.domain}";
in in
{ {
@ -41,7 +41,7 @@ in
description = "Add to DNS list"; description = "Add to DNS list";
default = true; default = true;
}; };
development = mkOption dev = mkOption
{ {
type = lib.types.bool; type = lib.types.bool;
description = "Development instance"; description = "Development instance";

View file

@ -0,0 +1,143 @@
{ lib
, config
, pkgs
, ...
}:
with lib;
let
cfg = config.mySystem.${category}.${app};
app = "rss-bridge";
category = "services";
description = "rss feed for sites without";
# image = "%{image}";
inherit (services.rss-bridge) user;#string
inherit (services.rss-bridge) group;#string
port = 1234; #int
appFolder = "/var/lib/${app}";
# persistentFolder = "${config.mySystem.persistentFolder}/var/lib/${appFolder}";
host = "${app}" + (if cfg.dev then "-dev" else "");
url = "${host}.${config.networking.domain}";
in
{
options.mySystem.${category}.${app} =
{
enable = mkEnableOption "${app}";
addToHomepage = mkEnableOption "Add ${app} to homepage" // { default = true; };
monitor = mkOption
{
type = lib.types.bool;
description = "Enable gatus monitoring";
default = true;
};
prometheus = mkOption
{
type = lib.types.bool;
description = "Enable prometheus scraping";
default = true;
};
addToDNS = mkOption
{
type = lib.types.bool;
description = "Add to DNS list";
default = true;
};
dev = mkOption
{
type = lib.types.bool;
description = "Development instance";
default = false;
};
backup = mkOption
{
type = lib.types.bool;
description = "Enable backups";
default = true;
};
};
config = mkIf cfg.enable {
## Secrets
# sops.secrets."${category}/${app}/env" = {
# sopsFile = ./secrets.sops.yaml;
# owner = user;
# group = group;
# restartUnits = [ "${app}.service" ];
# };
users.users.truxnell.extraGroups = [ group ];
# Folder perms - only for containers
# systemd.tmpfiles.rules = [
# "d ${persistentFolder}/ 0750 ${user} ${group} -"
# ];
## service
# services.test= {
# enable = true;
# };
# homepage integration
mySystem.services.homepage.infrastructure = mkIf cfg.addToHomepage [
{
${app} = {
icon = "${app}.svg";
href = "https://${url}";
inherit description;
};
}
];
### gatus integration
mySystem.services.gatus.monitors = mkIf cfg.monitor [
{
name = app;
group = "${category}";
url = "https://${url}";
interval = "1m";
conditions = [ "[CONNECTED] == true" "[STATUS] == 200" "[RESPONSE_TIME] < 50" ];
}
];
### Ingress
services.nginx.virtualHosts.${url} = {
forceSSL = true;
useACMEHost = config.networking.domain;
locations."^~ /" = {
proxyPass = "http://127.0.0.1:${builtins.toString port}";
};
};
### firewall config
# networking.firewall = mkIf cfg.openFirewall {
# allowedTCPPorts = [ port ];
# allowedUDPPorts = [ port ];
# };
### backups
warnings = [
(mkIf (!cfg.backup && config.mySystem.purpose != "Development")
"WARNING: Backups for ${app} are disabled!")
];
services.restic.backups = config.lib.mySystem.mkRestic
{
inherit app user;
paths = [ appFolder ];
inherit appFolder;
};
# services.postgresqlBackup = {
# databases = [ app ];
# };
};
}

View file

@ -16,7 +16,10 @@ with config;
# TODO decide if i drop to bash on pis? # TODO decide if i drop to bash on pis?
shell.fish.enable = true; shell.fish.enable = true;
nfs.nas.enable = true; nfs.nas = {
enable = true;
lazy = true;
};
system.resticBackup.local.enable = false; system.resticBackup.local.enable = false;
system.resticBackup.remote.enable = false; system.resticBackup.remote.enable = false;