treewide: avoid repetitive keys in attrSets

This commit is contained in:
Felix Buehler 2023-11-12 23:36:30 +01:00
parent 2a3f606557
commit 4b21221569
19 changed files with 994 additions and 947 deletions

View file

@ -65,13 +65,15 @@ let
# Uncomment this to disable compression and speed up image creation time # Uncomment this to disable compression and speed up image creation time
#isoImage.squashfsCompression = "gzip -Xcompression-level 1"; #isoImage.squashfsCompression = "gzip -Xcompression-level 1";
boot.kernelPackages = linuxPackages_latest; boot = {
# Always copytoram so that, if the image is booted from, e.g., a kernelPackages = linuxPackages_latest;
# USB stick, nothing is mistakenly written to persistent storage. # Always copytoram so that, if the image is booted from, e.g., a
boot.kernelParams = [ "copytoram" ]; # USB stick, nothing is mistakenly written to persistent storage.
# Secure defaults kernelParams = [ "copytoram" ];
boot.tmp.cleanOnBoot = true; # Secure defaults
boot.kernel.sysctl = { "kernel.unprivileged_bpf_disabled" = 1; }; tmp.cleanOnBoot = true;
kernel.sysctl = { "kernel.unprivileged_bpf_disabled" = 1; };
};
services.pcscd.enable = true; services.pcscd.enable = true;
services.udev.packages = [ yubikey-personalization ]; services.udev.packages = [ yubikey-personalization ];
@ -121,14 +123,16 @@ let
# Disable networking so the system is air-gapped # Disable networking so the system is air-gapped
# Comment all of these lines out if you'll need internet access # Comment all of these lines out if you'll need internet access
boot.initrd.network.enable = false; boot.initrd.network.enable = false;
networking.dhcpcd.enable = false; networking = {
networking.dhcpcd.allowInterfaces = [ ]; dhcpcd.enable = false;
networking.interfaces = { }; dhcpcd.allowInterfaces = [ ];
networking.firewall.enable = true; interfaces = { };
networking.useDHCP = false; firewall.enable = true;
networking.useNetworkd = false; useDHCP = false;
networking.wireless.enable = false; useNetworkd = false;
networking.networkmanager.enable = lib.mkForce false; wireless.enable = false;
networkmanager.enable = lib.mkForce false;
};
# Unset history so it's never stored # Unset history so it's never stored
# Set GNUPGHOME to an ephemeral location and configure GPG with the # Set GNUPGHOME to an ephemeral location and configure GPG with the

View file

@ -4,28 +4,30 @@ let
inherit (config.sops) secrets; inherit (config.sops) secrets;
in in
{ {
sops.secrets."acme/inwx" = { }; sops.secrets = {
sops.secrets."borgbackup/password" = { }; "acme/inwx" = { };
sops.secrets."borgbackup/ssh_key" = { }; "borgbackup/password" = { };
sops.secrets."sso/auth-key" = { }; "borgbackup/ssh_key" = { };
sops.secrets."sso/felix/password-hash" = { }; "sso/auth-key" = { };
sops.secrets."sso/felix/totp-secret" = { }; "sso/felix/password-hash" = { };
sops.secrets."paperless/password" = { }; "sso/felix/totp-secret" = { };
sops.secrets."nextcloud/password" = { "paperless/password" = { };
owner = config.users.users.nextcloud.name; "nextcloud/password" = {
}; owner = config.users.users.nextcloud.name;
sops.secrets."nextcloud-exporter/password" = { };
owner = config.users.users.nextcloud-exporter.name; "nextcloud-exporter/password" = {
}; owner = config.users.users.nextcloud-exporter.name;
sops.secrets."freshrss/password" = { };
owner = config.users.users.freshrss.name; "freshrss/password" = {
}; owner = config.users.users.freshrss.name;
sops.secrets."photoprism/password" = { }; };
sops.secrets."grafana/password" = { "photoprism/password" = { };
owner = config.users.users.grafana.name; "grafana/password" = {
}; owner = config.users.users.grafana.name;
sops.secrets."matrix-bot/password" = { };
owner = config.systemd.services.go-neb.serviceConfig.User; "matrix-bot/password" = {
owner = config.systemd.services.go-neb.serviceConfig.User;
};
}; };
# List services that you want to enable: # List services that you want to enable:

View file

@ -4,16 +4,18 @@ let
inherit (config.sops) secrets; inherit (config.sops) secrets;
in in
{ {
sops.secrets."acme/inwx" = { }; sops.secrets = {
sops.secrets."borgbackup/password" = { }; "acme/inwx" = { };
sops.secrets."borgbackup/ssh_key" = { }; "borgbackup/password" = { };
sops.secrets."dyndns/password" = { }; "borgbackup/ssh_key" = { };
sops.secrets."sso/auth-key" = { }; "dyndns/password" = { };
sops.secrets."sso/felix/password-hash" = { }; "sso/auth-key" = { };
sops.secrets."sso/felix/totp-secret" = { }; "sso/felix/password-hash" = { };
sops.secrets."prowlarr/apikey" = { }; "sso/felix/totp-secret" = { };
sops.secrets."radarr/apikey" = { }; "prowlarr/apikey" = { };
sops.secrets."sonarr/apikey" = { }; "radarr/apikey" = { };
"sonarr/apikey" = { };
};
# List services that you want to enable: # List services that you want to enable:
my.services = { my.services = {

View file

@ -25,108 +25,110 @@ in
} }
]; ];
services.prometheus = { services = {
alertmanager = { prometheus = {
enable = true; alertmanager = {
listenAddress = "127.0.0.1"; enable = true;
inherit (cfg) port; listenAddress = "127.0.0.1";
configuration = import ./config.nix; inherit (cfg) port;
webExternalUrl = "https://alerts.${domain}"; configuration = import ./config.nix;
# fix issue: https://gitlab.com/gitlab-org/omnibus-gitlab/-/issues/4556 webExternalUrl = "https://alerts.${domain}";
extraFlags = [ "--cluster.advertise-address 127.0.0.1:${toString cfg.port}" ]; # fix issue: https://gitlab.com/gitlab-org/omnibus-gitlab/-/issues/4556
extraFlags = [ "--cluster.advertise-address 127.0.0.1:${toString cfg.port}" ];
};
alertmanagers = [
{
static_configs = [
{
targets = [ "localhost:${toString cfg.port}" ];
}
];
}
];
scrapeConfigs = [
{
job_name = "alertmanager";
static_configs = [{
targets = [ "127.0.0.1:${toString cfg.port}" ];
labels = {
instance = config.networking.hostName;
};
}];
}
];
}; };
alertmanagers = [ grafana.provision = {
{ datasources.settings.datasources = [
static_configs = [ {
{ name = "Alertmanager";
targets = [ "localhost:${toString cfg.port}" ]; type = "alertmanager";
} url = "http://127.0.0.1:${toString cfg.port}";
]; jsonData = {
} implementation = "prometheus";
]; handleGrafanaManagedAlerts = config.services.prometheus.enable;
scrapeConfigs = [
{
job_name = "alertmanager";
static_configs = [{
targets = [ "127.0.0.1:${toString cfg.port}" ];
labels = {
instance = config.networking.hostName;
}; };
}]; }
} ];
]; };
};
services.grafana.provision = { grafana.provision = {
datasources.settings.datasources = [ dashboards.settings.providers = [
{
name = "Alertmanager";
options.path = pkgs.grafana-dashboards.alertmanager;
disableDeletion = true;
}
];
};
# for mail delivery
postfix.enable = true;
go-neb.config.services = [
{ {
name = "Alertmanager"; ID = "alertmanager_service";
type = "alertmanager"; Type = "alertmanager";
url = "http://127.0.0.1:${toString cfg.port}"; UserId = config.my.services.matrix-bot.Username;
jsonData = { Config = {
implementation = "prometheus"; # url contains "alertmanager_service" encoded as base64
handleGrafanaManagedAlerts = config.services.prometheus.enable; webhook_url = "http://localhost:4050/services/hooks/YWxlcnRtYW5hZ2VyX3NlcnZpY2U";
}; rooms = {
} "${config.my.services.matrix-bot.RoomID}" = {
]; #bots:nixos.org
}; text_template = ''
{{range .Alerts -}} [{{ .Status }}] {{index .Labels "alertname" }}: {{index .Annotations "description"}} {{ end -}}
services.grafana.provision = { '';
dashboards.settings.providers = [ # $$severity otherwise envsubst replaces $severity with an empty string
{ html_template = ''
name = "Alertmanager"; {{range .Alerts -}}
options.path = pkgs.grafana-dashboards.alertmanager; {{ $$severity := index .Labels "severity" }}
disableDeletion = true; {{ if eq .Status "firing" }}
} {{ if eq $$severity "critical"}}
]; <font color='red'><b>[FIRING - CRITICAL]</b></font>
}; {{ else if eq $$severity "warning"}}
<font color='orange'><b>[FIRING - WARNING]</b></font>
# for mail delivery {{ else }}
services.postfix.enable = true; <b>[FIRING - {{ $$severity }}]</b>
{{ end }}
services.go-neb.config.services = [
{
ID = "alertmanager_service";
Type = "alertmanager";
UserId = config.my.services.matrix-bot.Username;
Config = {
# url contains "alertmanager_service" encoded as base64
webhook_url = "http://localhost:4050/services/hooks/YWxlcnRtYW5hZ2VyX3NlcnZpY2U";
rooms = {
"${config.my.services.matrix-bot.RoomID}" = {
#bots:nixos.org
text_template = ''
{{range .Alerts -}} [{{ .Status }}] {{index .Labels "alertname" }}: {{index .Annotations "description"}} {{ end -}}
'';
# $$severity otherwise envsubst replaces $severity with an empty string
html_template = ''
{{range .Alerts -}}
{{ $$severity := index .Labels "severity" }}
{{ if eq .Status "firing" }}
{{ if eq $$severity "critical"}}
<font color='red'><b>[FIRING - CRITICAL]</b></font>
{{ else if eq $$severity "warning"}}
<font color='orange'><b>[FIRING - WARNING]</b></font>
{{ else }} {{ else }}
<b>[FIRING - {{ $$severity }}]</b> <font color='green'><b>[RESOLVED]</b></font>
{{ end }} {{ end }}
{{ else }} {{ index .Labels "alertname"}}: {{ index .Annotations "summary"}}
<font color='green'><b>[RESOLVED]</b></font> (
{{ end }} <a href="{{ index .Annotations "grafana" }}">📈 Grafana</a>,
{{ index .Labels "alertname"}}: {{ index .Annotations "summary"}} <a href="{{ .GeneratorURL }}">🔥 Prometheus</a>,
( <a href="{{ .SilenceURL }}">🔕 Silence</a>
<a href="{{ index .Annotations "grafana" }}">📈 Grafana</a>, )<br/>
<a href="{{ .GeneratorURL }}">🔥 Prometheus</a>, {{end -}}'';
<a href="{{ .SilenceURL }}">🔕 Silence</a> msg_type = "m.text"; # Must be either `m.text` or `m.notice`
)<br/> };
{{end -}}'';
msg_type = "m.text"; # Must be either `m.text` or `m.notice`
}; };
}; };
}; }
} ];
]; };
my.services.prometheus.rules = { my.services.prometheus.rules = {
alerts_silences_changed = { alerts_silences_changed = {

View file

@ -42,40 +42,49 @@ in
}; };
config = lib.mkIf cfg.enable { config = lib.mkIf cfg.enable {
services.prometheus.exporters.blackbox = { services = {
enable = true; prometheus.exporters.blackbox = {
configFile = pkgs.writeText "blackbox-config.yml" (builtins.toJSON blackBoxConfig); enable = true;
}; configFile = pkgs.writeText "blackbox-config.yml" (builtins.toJSON blackBoxConfig);
};
# relabels as in https://github.com/prometheus/blackbox_exporter#prometheus-configuration # relabels as in https://github.com/prometheus/blackbox_exporter#prometheus-configuration
services.prometheus = { prometheus = {
scrapeConfigs = [ scrapeConfigs = [
{
job_name = "blackbox";
metrics_path = "/probe";
params.module = [ "http_2xx" ];
static_configs = [
{
targets = cfg.http_endpoints;
labels = {
instance = config.networking.hostName;
};
}
];
relabel_configs = [
{
source_labels = [ "__address__" ];
target_label = "__param_target";
}
{
source_labels = [ "__param_target" ];
target_label = "instance";
}
{
target_label = "__address__";
replacement = "127.0.0.1:${toString config.services.prometheus.exporters.blackbox.port}";
}
];
}
];
};
grafana.provision.dashboards.settings.providers = [
{ {
job_name = "blackbox"; name = "Blackbox";
metrics_path = "/probe"; options.path = pkgs.grafana-dashboards.blackbox;
params.module = [ "http_2xx" ]; disableDeletion = true;
static_configs = [
{
targets = cfg.http_endpoints;
labels = {
instance = config.networking.hostName;
};
}
];
relabel_configs = [
{
source_labels = [ "__address__" ];
target_label = "__param_target";
}
{
source_labels = [ "__param_target" ];
target_label = "instance";
}
{
target_label = "__address__";
replacement = "127.0.0.1:${toString config.services.prometheus.exporters.blackbox.port}";
}
];
} }
]; ];
}; };
@ -143,12 +152,5 @@ in
}; };
}; };
}; };
services.grafana.provision.dashboards.settings.providers = [
{
name = "Blackbox";
options.path = pkgs.grafana-dashboards.blackbox;
disableDeletion = true;
}
];
}; };
} }

View file

@ -27,46 +27,48 @@ in
}; };
config = lib.mkIf cfg.enable { config = lib.mkIf cfg.enable {
services.blocky = { services = {
enable = true; blocky = {
enable = true;
settings = { settings = {
ports = { ports = {
tls = "853"; tls = "853";
http = cfg.httpPort; http = cfg.httpPort;
}; };
upstream = { upstream = {
default = [ default = [
"dns2.digitalcourage.de2" # classic "dns2.digitalcourage.de2" # classic
"tcp-tls:dns3.digitalcourage.de" # DoT "tcp-tls:dns3.digitalcourage.de" # DoT
"https://dns.digitale-gesellschaft.ch/dns-query" # DoH "https://dns.digitale-gesellschaft.ch/dns-query" # DoH
];
};
prometheus.enable = config.services.prometheus.enable;
} // cfg.settings;
};
prometheus.scrapeConfigs = [
{
job_name = "blocky";
static_configs = [
{
targets = [ "127.0.0.1:${toString cfg.httpPort}" ];
labels = {
instance = config.networking.hostName;
};
}
]; ];
}; }
prometheus.enable = config.services.prometheus.enable; ];
} // cfg.settings;
# untested
grafana.provision.dashboards.settings.providers = [
{
name = "Blocky";
options.path = pkgs.grafana-dashboards.blocky;
disableDeletion = true;
}
];
}; };
services.prometheus.scrapeConfigs = [
{
job_name = "blocky";
static_configs = [
{
targets = [ "127.0.0.1:${toString cfg.httpPort}" ];
labels = {
instance = config.networking.hostName;
};
}
];
}
];
# untested
services.grafana.provision.dashboards.settings.providers = [
{
name = "Blocky";
options.path = pkgs.grafana-dashboards.blocky;
disableDeletion = true;
}
];
}; };
} }

View file

@ -16,66 +16,70 @@ in
}; };
config = lib.mkIf cfg.enable { config = lib.mkIf cfg.enable {
services.gitea = { services = {
enable = true; gitea = {
settings = { enable = true;
server = { settings = {
HTTP_PORT = cfg.port; server = {
ROOT_URL = "https://code.${domain}"; HTTP_PORT = cfg.port;
ROOT_URL = "https://code.${domain}";
};
session.COOKIE_SECURE = true;
service.DISABLE_REGISTRATION = true;
ui.DEFAULT_THEME = "arc-green";
log.LEVEL = "Warn";
metrics.ENABLED = config.services.prometheus.enable;
}; };
session.COOKIE_SECURE = true; lfs.enable = true;
service.DISABLE_REGISTRATION = true; };
ui.DEFAULT_THEME = "arc-green";
log.LEVEL = "Warn"; prometheus = {
metrics.ENABLED = config.services.prometheus.enable; scrapeConfigs = [
{
job_name = "gitea";
static_configs = [
{
targets = [ "127.0.0.1:${toString cfg.port}" ];
labels = {
instance = config.networking.hostName;
};
}
];
}
];
};
grafana.provision = {
dashboards.settings.providers = [
{
name = "Gitea";
options.path = pkgs.grafana-dashboards.gitea;
disableDeletion = true;
}
];
}; };
lfs.enable = true;
}; };
# Proxy to Gitea # Proxy to Gitea
my.services.nginx.virtualHosts = [ my.services = {
{ nginx.virtualHosts = [
subdomain = "code";
inherit (cfg) port;
}
];
my.services.backup = {
paths = [
config.services.gitea.lfs.contentDir
config.services.gitea.repositoryRoot
];
};
services.prometheus = {
scrapeConfigs = [
{ {
job_name = "gitea"; subdomain = "code";
static_configs = [ inherit (cfg) port;
{
targets = [ "127.0.0.1:${toString cfg.port}" ];
labels = {
instance = config.networking.hostName;
};
}
];
} }
]; ];
};
services.grafana.provision = {
dashboards.settings.providers = [
{
name = "Gitea";
options.path = pkgs.grafana-dashboards.gitea;
disableDeletion = true;
}
];
};
my.services.prometheus.rules = { backup = {
gitea = { paths = [
condition = ''rate(promhttp_metric_handler_requests_total{job="gitea", code="500"}[5m]) > 3''; config.services.gitea.lfs.contentDir
description = "{{$labels.instance}}: gitea instances error rate went up: {{$value}} errors in 5 minutes"; config.services.gitea.repositoryRoot
];
};
prometheus.rules = {
gitea = {
condition = ''rate(promhttp_metric_handler_requests_total{job="gitea", code="500"}[5m]) > 3'';
description = "{{$labels.instance}}: gitea instances error rate went up: {{$value}} errors in 5 minutes";
};
}; };
}; };

View file

@ -31,48 +31,51 @@ in
}; };
config = lib.mkIf cfg.enable { config = lib.mkIf cfg.enable {
services.hedgedoc = { services = {
enable = true; hedgedoc = {
enable = true;
settings = { settings = {
domain = "notes.${domain}"; domain = "notes.${domain}";
inherit (cfg) port; inherit (cfg) port;
host = "127.0.0.1"; host = "127.0.0.1";
protocolUseSSL = true; protocolUseSSL = true;
db = { db = {
dialect = "sqlite"; dialect = "sqlite";
storage = "/var/lib/hedgedoc/hedgedoc.sqlite"; storage = "/var/lib/hedgedoc/hedgedoc.sqlite";
}; };
} // cfg.settings; } // cfg.settings;
}; };
# temporary fix for: https://github.com/NixOS/nixpkgs/issues/198250 prometheus = {
#systemd.services.hedgedoc.serviceConfig.StateDirectory = lib.mkForce "/var/lib/hedgedoc"; scrapeConfigs = [
systemd.services.hedgedoc.serviceConfig.StateDirectory = lib.mkForce "hedgedoc"; {
job_name = "hedgedoc";
static_configs = [
{
targets = [ "127.0.0.1:${toString cfg.port}" ];
labels = {
instance = config.networking.hostName;
};
}
];
}
];
};
services.prometheus = { grafana.provision.dashboards.settings.providers = [
scrapeConfigs = [
{ {
job_name = "hedgedoc"; name = "Hedgedoc";
static_configs = [ options.path = pkgs.grafana-dashboards.hedgedoc;
{ disableDeletion = true;
targets = [ "127.0.0.1:${toString cfg.port}" ];
labels = {
instance = config.networking.hostName;
};
}
];
} }
]; ];
}; };
services.grafana.provision.dashboards.settings.providers = [ # TODO remove for 23.11
{ # temporary fix for: https://github.com/NixOS/nixpkgs/issues/198250
name = "Hedgedoc"; #systemd.services.hedgedoc.serviceConfig.StateDirectory = lib.mkForce "/var/lib/hedgedoc";
options.path = pkgs.grafana-dashboards.hedgedoc; systemd.services.hedgedoc.serviceConfig.StateDirectory = lib.mkForce "hedgedoc";
disableDeletion = true;
}
];
my.services.nginx.virtualHosts = [ my.services.nginx.virtualHosts = [
{ {

View file

@ -37,53 +37,55 @@
type = lib.types.attrsOf type = lib.types.attrsOf
(lib.types.submodule { (lib.types.submodule {
options = { options = {
dashboard.url = lib.mkOption { dashboard = {
type = lib.types.nullOr lib.types.str; url = lib.mkOption {
description = '' type = lib.types.nullOr lib.types.str;
Url to webapp description = ''
''; Url to webapp
example = "http://192.168.1.10:1234"; '';
default = null; example = "http://192.168.1.10:1234";
}; default = null;
dashboard.name = lib.mkOption { };
type = lib.types.nullOr lib.types.str; name = lib.mkOption {
description = '' type = lib.types.nullOr lib.types.str;
Application name. description = ''
''; Application name.
example = "App"; '';
default = null; example = "App";
}; default = null;
dashboard.category = lib.mkOption { };
type = lib.types.nullOr lib.types.str; category = lib.mkOption {
description = '' type = lib.types.nullOr lib.types.str;
App category tag. description = ''
''; App category tag.
example = "app"; '';
default = null; example = "app";
}; default = null;
dashboard.icon = lib.mkOption { };
type = lib.types.nullOr lib.types.str; icon = lib.mkOption {
description = '' type = lib.types.nullOr lib.types.str;
Font Awesome application icon. description = ''
''; Font Awesome application icon.
example = "rss"; '';
default = null; example = "rss";
}; default = null;
dashboard.type = lib.mkOption { };
type = lib.types.nullOr lib.types.str; type = lib.mkOption {
description = '' type = lib.types.nullOr lib.types.str;
application type. description = ''
''; application type.
example = "Ping"; '';
default = "Ping"; example = "Ping";
}; default = "Ping";
dashboard.method = lib.mkOption { };
type = lib.types.enum [ "get" "head" ]; method = lib.mkOption {
description = '' type = lib.types.enum [ "get" "head" ];
method of request used description = ''
''; method of request used
example = "get"; '';
default = "head"; example = "get";
default = "head";
};
}; };
}; };
}); });

View file

@ -62,80 +62,98 @@ in
}; };
config = lib.mkIf cfg.enable { config = lib.mkIf cfg.enable {
services.loki = { services = {
enable = true; loki = {
configuration = { enable = true;
server = { configuration = {
http_listen_address = "127.0.0.1"; server = {
http_listen_port = cfg.port; http_listen_address = "127.0.0.1";
}; http_listen_port = cfg.port;
auth_enabled = false; };
auth_enabled = false;
common = { common = {
instance_addr = "127.0.0.1"; instance_addr = "127.0.0.1";
ring.kvstore.store = "inmemory"; ring.kvstore.store = "inmemory";
replication_factor = 1; replication_factor = 1;
path_prefix = "/tmp/loki"; path_prefix = "/tmp/loki";
};
ruler = lib.mkIf config.my.services.alertmanager.enable {
storage = {
type = "local";
local = {
# having the "fake" directory is important, because loki is running in single-tenant mode
directory = pkgs.writeTextDir "fake/loki-rules.yml" (builtins.toJSON {
groups = [
{
name = "alerting-rules";
rules = lib.mapAttrsToList
(name: opts: {
alert = name;
inherit (opts) condition labels;
for = opts.time;
annotations.description = opts.description;
})
cfg.rules;
}
];
});
};
}; };
alertmanager_url = "http://127.0.0.1:${toString config.my.services.alertmanager.port}"; ruler = lib.mkIf config.my.services.alertmanager.enable {
enable_alertmanager_v2 = true; storage = {
}; type = "local";
local = {
schema_config = { # having the "fake" directory is important, because loki is running in single-tenant mode
configs = [{ directory = pkgs.writeTextDir "fake/loki-rules.yml" (builtins.toJSON {
from = "2020-05-15"; groups = [
store = "boltdb-shipper"; {
object_store = "filesystem"; name = "alerting-rules";
schema = "v11"; rules = lib.mapAttrsToList
index = { (name: opts: {
prefix = "index_"; alert = name;
period = "24h"; inherit (opts) condition labels;
for = opts.time;
annotations.description = opts.description;
})
cfg.rules;
}
];
});
};
}; };
}];
alertmanager_url = "http://127.0.0.1:${toString config.my.services.alertmanager.port}";
enable_alertmanager_v2 = true;
};
schema_config = {
configs = [{
from = "2020-05-15";
store = "boltdb-shipper";
object_store = "filesystem";
schema = "v11";
index = {
prefix = "index_";
period = "24h";
};
}];
};
}; };
}; };
};
services.grafana.provision = { grafana.provision = {
datasources.settings.datasources = [ datasources.settings.datasources = [
{ {
name = "Loki"; name = "Loki";
type = "loki"; type = "loki";
access = "proxy"; access = "proxy";
url = "http://127.0.0.1:${toString cfg.port}"; url = "http://127.0.0.1:${toString cfg.port}";
} }
]; ];
dashboards.settings.providers = [ dashboards.settings.providers = [
{ {
name = "Loki"; name = "Loki";
options.path = pkgs.grafana-dashboards.loki; options.path = pkgs.grafana-dashboards.loki;
disableDeletion = true; disableDeletion = true;
} }
]; ];
};
prometheus = {
scrapeConfigs = [
{
job_name = "loki";
static_configs = [
{
targets = [ "127.0.0.1:${toString cfg.port}" ];
labels = {
instance = config.networking.hostName;
};
}
];
}
];
};
}; };
my.services.loki.rules = { my.services.loki.rules = {
@ -144,21 +162,5 @@ in
description = "Loki has a high logging rate"; description = "Loki has a high logging rate";
}; };
}; };
services.prometheus = {
scrapeConfigs = [
{
job_name = "loki";
static_configs = [
{
targets = [ "127.0.0.1:${toString cfg.port}" ];
labels = {
instance = config.networking.hostName;
};
}
];
}
];
};
}; };
} }

View file

@ -39,15 +39,49 @@ in
}; };
config = lib.mkIf cfg.enable { config = lib.mkIf cfg.enable {
services.navidrome = { services = {
enable = true; navidrome = {
enable = true;
settings = cfg.settings // { settings = cfg.settings // {
Port = cfg.port; Port = cfg.port;
Address = "127.0.0.1"; Address = "127.0.0.1";
MusicFolder = cfg.musicFolder; MusicFolder = cfg.musicFolder;
LogLevel = "info"; LogLevel = "info";
Prometheus.Enabled = config.services.prometheus.enable; Prometheus.Enabled = config.services.prometheus.enable;
};
};
prometheus = {
scrapeConfigs = [
{
job_name = "navidrome";
static_configs = [
{
targets = [ "127.0.0.1:${toString cfg.port}" ];
labels = {
instance = config.networking.hostName;
};
}
];
}
];
};
grafana.provision = {
dashboards.settings.providers = [
{
name = "Navidrome";
options.path = pkgs.grafana-dashboards.navidrome;
disableDeletion = true;
}
];
};
};
my.services.prometheus.rules = {
navidrome_not_enough_albums = {
condition = ''http_navidrome_album_count != 1'';
description = "navidrome: not enough albums as expected: {{$value}}";
}; };
}; };
@ -58,38 +92,6 @@ in
} }
]; ];
services.prometheus = {
scrapeConfigs = [
{
job_name = "navidrome";
static_configs = [
{
targets = [ "127.0.0.1:${toString cfg.port}" ];
labels = {
instance = config.networking.hostName;
};
}
];
}
];
};
services.grafana.provision = {
dashboards.settings.providers = [
{
name = "Navidrome";
options.path = pkgs.grafana-dashboards.navidrome;
disableDeletion = true;
}
];
};
my.services.prometheus.rules = {
navidrome_not_enough_albums = {
condition = ''http_navidrome_album_count != 1'';
description = "navidrome: not enough albums as expected: {{$value}}";
};
};
webapps.apps.navidrome = { webapps.apps.navidrome = {
dashboard = { dashboard = {
name = "Music"; name = "Music";

View file

@ -51,70 +51,103 @@ in
}; };
config = lib.mkIf cfg.enable { config = lib.mkIf cfg.enable {
services.nextcloud = { services = {
enable = true; nextcloud = {
package = pkgs.nextcloud27; enable = true;
hostName = "cloud.${domain}"; package = pkgs.nextcloud27;
maxUploadSize = cfg.maxSize; hostName = "cloud.${domain}";
autoUpdateApps.enable = true; maxUploadSize = cfg.maxSize;
config = { autoUpdateApps.enable = true;
adminuser = cfg.admin; config = {
adminpassFile = cfg.passwordFile; adminuser = cfg.admin;
inherit (cfg) defaultPhoneRegion; adminpassFile = cfg.passwordFile;
inherit (cfg) defaultPhoneRegion;
overwriteProtocol = "https"; # Nginx only allows SSL overwriteProtocol = "https"; # Nginx only allows SSL
#dbtype = "pgsql"; #dbtype = "pgsql";
#dbhost = "/run/postgresql"; #dbhost = "/run/postgresql";
};
extraApps = {
calendar = let version = "4.5.2"; in pkgs.fetchNextcloudApp {
url = "https://github.com/nextcloud-releases/calendar/releases/download/v${version}/calendar-v${version}.tar.gz";
sha256 = "sha256-n7GjgAyw2SLoZTEfakmI3IllWUk6o1MF89Zt3WGhR6A=";
};
contacts = let version = "5.4.2"; in pkgs.fetchNextcloudApp {
url = "https://github.com/nextcloud-releases/contacts/releases/download/v${version}/contacts-v${version}.tar.gz";
sha256 = "sha256-IkKHJ3MY/UPZqa4H86WGOEOypffMIHyJ9WvMqkq/4t8=";
};
tasks = let version = "0.15.0"; in pkgs.fetchNextcloudApp {
url = "https://github.com/nextcloud/tasks/releases/download/v${version}/tasks.tar.gz";
sha256 = "sha256-zMMqtEWiXmhB1C2IeWk8hgP7eacaXLkT7Tgi4NK6PCg=";
};
deck = let version = "1.11.0"; in pkgs.fetchNextcloudApp {
url = "https://github.com/nextcloud/deck/releases/download/v${version}/deck.tar.gz";
sha256 = "sha256-stb9057pP8WXIhztNl7H8ymLqSZzSulgKgB2cbib2pQ=";
};
};
}; };
extraApps = { #postgresql = {
calendar = let version = "4.5.2"; in pkgs.fetchNextcloudApp { # enable = true;
url = "https://github.com/nextcloud-releases/calendar/releases/download/v${version}/calendar-v${version}.tar.gz"; # ensureDatabases = [ "nextcloud" ];
sha256 = "sha256-n7GjgAyw2SLoZTEfakmI3IllWUk6o1MF89Zt3WGhR6A="; # ensureUsers = [
}; # {
contacts = let version = "5.4.2"; in pkgs.fetchNextcloudApp { # name = "nextcloud";
url = "https://github.com/nextcloud-releases/contacts/releases/download/v${version}/contacts-v${version}.tar.gz"; # ensurePermissions."DATABASE nextcloud" = "ALL PRIVILEGES";
sha256 = "sha256-IkKHJ3MY/UPZqa4H86WGOEOypffMIHyJ9WvMqkq/4t8="; # }
}; # ];
tasks = let version = "0.15.0"; in pkgs.fetchNextcloudApp { #};
url = "https://github.com/nextcloud/tasks/releases/download/v${version}/tasks.tar.gz";
sha256 = "sha256-zMMqtEWiXmhB1C2IeWk8hgP7eacaXLkT7Tgi4NK6PCg="; # The service above configures the domain, no need for my wrapper
}; nginx.virtualHosts."cloud.${domain}" = {
deck = let version = "1.11.0"; in pkgs.fetchNextcloudApp { forceSSL = true;
url = "https://github.com/nextcloud/deck/releases/download/v${version}/deck.tar.gz"; useACMEHost = domain;
sha256 = "sha256-stb9057pP8WXIhztNl7H8ymLqSZzSulgKgB2cbib2pQ=";
}; # so homer can get the online status
extraConfig = lib.optionalString config.my.services.homer.enable ''
add_header Access-Control-Allow-Origin https://${domain};
'';
};
prometheus.exporters.nextcloud = {
enable = true;
url = "https://cloud.${domain}";
username = cfg.admin;
passwordFile = cfg.exporterPasswordFile;
port = cfg.exporterPort;
};
prometheus.scrapeConfigs = [
{
job_name = "nextcloud";
static_configs = [
{
targets = [ "127.0.0.1:${toString cfg.exporterPort}" ];
labels = {
instance = config.networking.hostName;
};
}
];
}
];
grafana.provision = {
dashboards.settings.providers = [
{
name = "Nextcloud";
options.path = pkgs.grafana-dashboards.nextcloud;
disableDeletion = true;
}
];
}; };
}; };
#services.postgresql = {
# enable = true;
# ensureDatabases = [ "nextcloud" ];
# ensureUsers = [
# {
# name = "nextcloud";
# ensurePermissions."DATABASE nextcloud" = "ALL PRIVILEGES";
# }
# ];
#};
#systemd.services."nextcloud-setup" = { #systemd.services."nextcloud-setup" = {
# requires = [ "postgresql.service" ]; # requires = [ "postgresql.service" ];
# after = [ "postgresql.service" ]; # after = [ "postgresql.service" ];
#}; #};
# The service above configures the domain, no need for my wrapper
services.nginx.virtualHosts."cloud.${domain}" = {
forceSSL = true;
useACMEHost = domain;
# so homer can get the online status
extraConfig = lib.optionalString config.my.services.homer.enable ''
add_header Access-Control-Allow-Origin https://${domain};
'';
};
my.services.backup = { my.services.backup = {
exclude = [ exclude = [
# image previews can take up a lot of space # image previews can take up a lot of space
@ -122,37 +155,6 @@ in
]; ];
}; };
services.prometheus.exporters.nextcloud = {
enable = true;
url = "https://cloud.${domain}";
username = cfg.admin;
passwordFile = cfg.exporterPasswordFile;
port = cfg.exporterPort;
};
services.prometheus.scrapeConfigs = [
{
job_name = "nextcloud";
static_configs = [
{
targets = [ "127.0.0.1:${toString cfg.exporterPort}" ];
labels = {
instance = config.networking.hostName;
};
}
];
}
];
services.grafana.provision = {
dashboards.settings.providers = [
{
name = "Nextcloud";
options.path = pkgs.grafana-dashboards.nextcloud;
disableDeletion = true;
}
];
};
webapps.apps.nextcloud = { webapps.apps.nextcloud = {
dashboard = { dashboard = {
name = "Cloud"; name = "Cloud";

View file

@ -153,8 +153,7 @@ in
}; };
}; };
config = lib.mkIf cfg.enable { config = lib.mkIf cfg.enable {
assertions = [ ] assertions = lib.flip builtins.map cfg.virtualHosts ({ subdomain, ... } @ args:
++ (lib.flip builtins.map cfg.virtualHosts ({ subdomain, ... } @ args:
let let
conflicts = [ "port" "root" ]; conflicts = [ "port" "root" ];
optionsNotNull = builtins.map (v: args.${v} != null) conflicts; optionsNotNull = builtins.map (v: args.${v} != null) conflicts;
@ -167,7 +166,7 @@ in
lib.concatStringsSep ", " (builtins.map (v: "'${v}'") conflicts) lib.concatStringsSep ", " (builtins.map (v: "'${v}'") conflicts)
} configured. } configured.
''; '';
})) })
# ++ ( # ++ (
# let # let
# ports = lib.my.mapFilter # ports = lib.my.mapFilter
@ -201,184 +200,219 @@ in
# map mkAssertion nonUniques # map mkAssertion nonUniques
# ) # )
; ;
services.nginx = { services = {
enable = true; nginx = {
statusPage = true; # For monitoring scraping.
recommendedGzipSettings = true;
recommendedOptimisation = true;
recommendedTlsSettings = true;
recommendedProxySettings = true;
recommendedBrotliSettings = true;
# Only allow PFS-enabled ciphers with AES256
sslCiphers = "AES256+EECDH:AES256+EDH:!aNULL";
commonHttpConfig = ''
# Add HSTS header with preloading to HTTPS requests.
# Adding this header to HTTP requests is discouraged
map $scheme $hsts_header {
https "max-age=31536000; includeSubdomains; preload";
}
add_header Strict-Transport-Security $hsts_header;
# CORS header
# some applications set it to wildcard, therefore this overrides it
proxy_hide_header Access-Control-Allow-Origin;
add_header Access-Control-Allow-Origin https://${config.networking.domain};
# Minimize information leaked to other domains
add_header 'Referrer-Policy' 'strict-origin-when-cross-origin';
# Disable embedding as a frame
add_header X-Frame-Options DENY;
# Prevent injection of code in other mime types (XSS Attacks)
add_header X-Content-Type-Options nosniff;
# Enable XSS protection of the browser.
# May be unnecessary when CSP is configured properly (see above)
add_header X-XSS-Protection "1; mode=block";
# This might create errors
proxy_cookie_path / "/; secure; HttpOnly; SameSite=strict";
# Enable CSP for your services.
#add_header Content-Security-Policy "script-src 'self'; object-src 'none'; base-uri 'none';" always;
'';
virtualHosts =
let
genAttrs' = values: f: lib.listToAttrs (map f values);
inherit (config.networking) domain;
mkVHost = { subdomain, ... } @ args: lib.nameValuePair
"${subdomain}.${domain}"
(lib.foldl lib.recursiveUpdate { } [
# Base configuration
{
forceSSL = true;
useACMEHost = domain;
}
# Proxy to port
(lib.optionalAttrs (args.port != null) {
locations."/".proxyPass =
"http://127.0.0.1:${toString args.port}";
# TODO make ipv6 possible
# http://[::1]:${toString args.port};
})
# Serve filesystem content
(lib.optionalAttrs (args.root != null) {
inherit (args) root;
})
# VHost specific configuration
args.extraConfig
# SSO configuration
(lib.optionalAttrs args.sso.enable {
extraConfig = (args.extraConfig.extraConfig or "") + ''
error_page 401 = @error401;
'';
locations."@error401".return = ''
302 https://${cfg.sso.subdomain}.${config.networking.domain}/login?go=$scheme://$http_host$request_uri
'';
locations."/" = {
extraConfig =
(args.extraConfig.locations."/".extraConfig or "") + ''
# Use SSO
auth_request /sso-auth;
# Set username through header
auth_request_set $username $upstream_http_x_username;
proxy_set_header X-User $username;
# Renew SSO cookie on request
auth_request_set $cookie $upstream_http_set_cookie;
add_header Set-Cookie $cookie;
'';
};
locations."/sso-auth" = {
proxyPass = "http://localhost:${toString cfg.sso.port}/auth";
extraConfig = ''
# Do not allow requests from outside
internal;
# Do not forward the request body
proxy_pass_request_body off;
proxy_set_header Content-Length "";
# Set X-Application according to subdomain for matching
proxy_set_header X-Application "${subdomain}";
# Set origin URI for matching
proxy_set_header X-Origin-URI $request_uri;
'';
};
})
])
;
in
genAttrs' cfg.virtualHosts mkVHost;
sso = {
enable = true; enable = true;
configuration = { statusPage = true; # For monitoring scraping.
listen = {
addr = "127.0.0.1"; recommendedGzipSettings = true;
inherit (cfg.sso) port; recommendedOptimisation = true;
}; recommendedTlsSettings = true;
audit_log = { recommendedProxySettings = true;
target = [ recommendedBrotliSettings = true;
"fd://stdout"
]; # Only allow PFS-enabled ciphers with AES256
events = [ sslCiphers = "AES256+EECDH:AES256+EDH:!aNULL";
"access_denied"
"login_success" commonHttpConfig = ''
"login_failure" # Add HSTS header with preloading to HTTPS requests.
"logout" # Adding this header to HTTP requests is discouraged
"validate" map $scheme $hsts_header {
]; https "max-age=31536000; includeSubdomains; preload";
headers = [ }
"x-origin-uri" add_header Strict-Transport-Security $hsts_header;
"x-application"
]; # CORS header
}; # some applications set it to wildcard, therefore this overrides it
cookie = { proxy_hide_header Access-Control-Allow-Origin;
domain = ".${config.networking.domain}"; add_header Access-Control-Allow-Origin https://${config.networking.domain};
secure = true;
authentication_key = { # Minimize information leaked to other domains
_secret = cfg.sso.authKeyFile; add_header 'Referrer-Policy' 'strict-origin-when-cross-origin';
};
}; # Disable embedding as a frame
login = { add_header X-Frame-Options DENY;
title = "Bühlers's SSO";
default_method = "simple"; # Prevent injection of code in other mime types (XSS Attacks)
hide_mfa_field = false; add_header X-Content-Type-Options nosniff;
names = {
simple = "Username / Password"; # Enable XSS protection of the browser.
}; # May be unnecessary when CSP is configured properly (see above)
}; add_header X-XSS-Protection "1; mode=block";
providers = {
simple = # This might create errors
let proxy_cookie_path / "/; secure; HttpOnly; SameSite=strict";
applyUsers = lib.flip lib.mapAttrs cfg.sso.users;
in # Enable CSP for your services.
{ #add_header Content-Security-Policy "script-src 'self'; object-src 'none'; base-uri 'none';" always;
users = applyUsers (_: v: { _secret = v.passwordHashFile; }); '';
mfa = applyUsers (_: v: [{
provider = "totp"; virtualHosts =
attributes = { let
secret = { genAttrs' = values: f: lib.listToAttrs (map f values);
_secret = v.totpSecretFile; inherit (config.networking) domain;
mkVHost = { subdomain, ... } @ args: lib.nameValuePair
"${subdomain}.${domain}"
(lib.foldl lib.recursiveUpdate { } [
# Base configuration
{
forceSSL = true;
useACMEHost = domain;
}
# Proxy to port
(lib.optionalAttrs (args.port != null) {
locations."/".proxyPass =
"http://127.0.0.1:${toString args.port}";
# TODO make ipv6 possible
# http://[::1]:${toString args.port};
})
# Serve filesystem content
(lib.optionalAttrs (args.root != null) {
inherit (args) root;
})
# VHost specific configuration
args.extraConfig
# SSO configuration
(lib.optionalAttrs args.sso.enable {
extraConfig = (args.extraConfig.extraConfig or "") + ''
error_page 401 = @error401;
'';
locations = {
"@error401".return = ''
302 https://${cfg.sso.subdomain}.${config.networking.domain}/login?go=$scheme://$http_host$request_uri
'';
"/" = {
extraConfig =
(args.extraConfig.locations."/".extraConfig or "") + ''
# Use SSO
auth_request /sso-auth;
# Set username through header
auth_request_set $username $upstream_http_x_username;
proxy_set_header X-User $username;
# Renew SSO cookie on request
auth_request_set $cookie $upstream_http_set_cookie;
add_header Set-Cookie $cookie;
'';
};
"/sso-auth" = {
proxyPass = "http://localhost:${toString cfg.sso.port}/auth";
extraConfig = ''
# Do not allow requests from outside
internal;
# Do not forward the request body
proxy_pass_request_body off;
proxy_set_header Content-Length "";
# Set X-Application according to subdomain for matching
proxy_set_header X-Application "${subdomain}";
# Set origin URI for matching
proxy_set_header X-Origin-URI $request_uri;
'';
}; };
}; };
}]); })
inherit (cfg.sso) groups; ])
;
in
genAttrs' cfg.virtualHosts mkVHost;
sso = {
enable = true;
configuration = {
listen = {
addr = "127.0.0.1";
inherit (cfg.sso) port;
};
audit_log = {
target = [
"fd://stdout"
];
events = [
"access_denied"
"login_success"
"login_failure"
"logout"
"validate"
];
headers = [
"x-origin-uri"
"x-application"
];
};
cookie = {
domain = ".${config.networking.domain}";
secure = true;
authentication_key = {
_secret = cfg.sso.authKeyFile;
}; };
}; };
acl = { login = {
rule_sets = [ title = "Bühlers's SSO";
{ default_method = "simple";
rules = [{ field = "x-application"; present = true; }]; hide_mfa_field = false;
allow = [ "@root" ]; names = {
} simple = "Username / Password";
]; };
};
providers = {
simple =
let
applyUsers = lib.flip lib.mapAttrs cfg.sso.users;
in
{
users = applyUsers (_: v: { _secret = v.passwordHashFile; });
mfa = applyUsers (_: v: [{
provider = "totp";
attributes = {
secret = {
_secret = v.totpSecretFile;
};
};
}]);
inherit (cfg.sso) groups;
};
};
acl = {
rule_sets = [
{
rules = [{ field = "x-application"; present = true; }];
allow = [ "@root" ];
}
];
};
}; };
}; };
}; };
# services.prometheus = lib.mkIf cfg.monitoring.enable {
prometheus = {
exporters.nginx = {
enable = true;
listenAddress = "127.0.0.1";
};
scrapeConfigs = [
{
job_name = "nginx";
static_configs = [
{
targets = [ "127.0.0.1:${toString config.services.prometheus.exporters.nginx.port}" ];
labels = {
instance = config.networking.hostName;
};
}
];
}
];
};
grafana.provision = {
dashboards.settings.providers = [
{
name = "Nginx";
options.path = pkgs.grafana-dashboards.nginx;
disableDeletion = true;
}
];
};
}; };
my.services.nginx.virtualHosts = [ my.services.nginx.virtualHosts = [
{ {
subdomain = "login"; subdomain = "login";
@ -407,35 +441,5 @@ in
}; };
}; };
}; };
# services.prometheus = lib.mkIf cfg.monitoring.enable {
services.prometheus = {
exporters.nginx = {
enable = true;
listenAddress = "127.0.0.1";
};
scrapeConfigs = [
{
job_name = "nginx";
static_configs = [
{
targets = [ "127.0.0.1:${toString config.services.prometheus.exporters.nginx.port}" ];
labels = {
instance = config.networking.hostName;
};
}
];
}
];
};
services.grafana.provision = {
dashboards.settings.providers = [
{
name = "Nginx";
options.path = pkgs.grafana-dashboards.nginx;
disableDeletion = true;
}
];
};
}; };
} }

View file

@ -78,112 +78,116 @@ in
}; };
config = lib.mkIf cfg.enable { config = lib.mkIf cfg.enable {
services.prometheus = { services = {
enable = true; prometheus = {
webExternalUrl = "https://monitor.${domain}"; enable = true;
inherit (cfg) port; webExternalUrl = "https://monitor.${domain}";
listenAddress = "127.0.0.1";
inherit (cfg) retentionTime;
globalConfig = {
scrape_interval = cfg.scrapeInterval;
};
ruleFiles = [
(pkgs.writeText "prometheus-rules.yml" (builtins.toJSON {
groups = [
{
name = "alerting-rules";
rules = lib.mapAttrsToList
(name: opts: {
alert = name;
expr = opts.condition;
for = opts.time;
inherit (opts) labels;
annotations = {
inherit (opts) description;
grafana = lib.optionalString config.services.grafana.enable "https://visualization.${domain}";
};
})
cfg.rules;
}
];
}))
];
scrapeConfigs = [
{
job_name = "prometheus";
static_configs = [{
targets = [ "127.0.0.1:${toString cfg.port}" ];
labels = {
instance = config.networking.hostName;
};
}];
}
];
};
my.services.node-exporter.enable = true;
services.grafana.provision = {
datasources.settings.datasources = [
{
name = "Prometheus";
type = "prometheus";
isDefault = true;
url = "http://127.0.0.1:${toString config.services.prometheus.port}";
jsonData = {
prometheusType = "Prometheus";
prometheusVersion = toString pkgs.prometheus.version;
timeInterval = config.services.prometheus.globalConfig.scrape_interval;
};
}
];
dashboards.settings.providers = [
{
name = "Prometheus";
options.path = pkgs.grafana-dashboards.prometheus;
disableDeletion = true;
}
];
};
my.services.prometheus.rules = {
prometheus_too_many_restarts = {
condition = ''changes(process_start_time_seconds{job=~"prometheus|alertmanager"}[15m]) > 2'';
description = "Prometheus has restarted more than twice in the last 15 minutes. It might be crashlooping";
};
alert_manager_config_not_synced = {
condition = ''count(count_values("config_hash", alertmanager_config_hash)) > 1'';
description = "Configurations of AlertManager cluster instances are out of sync";
};
prometheus_not_connected_to_alertmanager = {
condition = "prometheus_notifications_alertmanagers_discovered < 1";
description = "Prometheus cannot connect the alertmanager\n VALUE = {{ $value }}\n LABELS = {{ $labels }}";
};
prometheus_rule_evaluation_failures = {
condition = "increase(prometheus_rule_evaluation_failures_total[3m]) > 0";
description = "Prometheus encountered {{ $value }} rule evaluation failures, leading to potentially ignored alerts.\n VALUE = {{ $value }}\n LABELS = {{ $labels }}";
};
prometheus_template_expansion_failures = {
condition = "increase(prometheus_template_text_expansion_failures_total[3m]) > 0";
time = "0m";
description = "Prometheus encountered {{ $value }} template text expansion failures\n VALUE = {{ $value }}\n LABELS = {{ $labels }}";
};
};
my.services.nginx.virtualHosts = [
{
subdomain = "monitor";
inherit (cfg) port; inherit (cfg) port;
} listenAddress = "127.0.0.1";
];
inherit (cfg) retentionTime;
globalConfig = {
scrape_interval = cfg.scrapeInterval;
};
ruleFiles = [
(pkgs.writeText "prometheus-rules.yml" (builtins.toJSON {
groups = [
{
name = "alerting-rules";
rules = lib.mapAttrsToList
(name: opts: {
alert = name;
expr = opts.condition;
for = opts.time;
inherit (opts) labels;
annotations = {
inherit (opts) description;
grafana = lib.optionalString config.services.grafana.enable "https://visualization.${domain}";
};
})
cfg.rules;
}
];
}))
];
scrapeConfigs = [
{
job_name = "prometheus";
static_configs = [{
targets = [ "127.0.0.1:${toString cfg.port}" ];
labels = {
instance = config.networking.hostName;
};
}];
}
];
};
grafana.provision = {
datasources.settings.datasources = [
{
name = "Prometheus";
type = "prometheus";
isDefault = true;
url = "http://127.0.0.1:${toString config.services.prometheus.port}";
jsonData = {
prometheusType = "Prometheus";
prometheusVersion = toString pkgs.prometheus.version;
timeInterval = config.services.prometheus.globalConfig.scrape_interval;
};
}
];
dashboards.settings.providers = [
{
name = "Prometheus";
options.path = pkgs.grafana-dashboards.prometheus;
disableDeletion = true;
}
];
};
};
my.services = {
node-exporter.enable = true;
prometheus.rules = {
prometheus_too_many_restarts = {
condition = ''changes(process_start_time_seconds{job=~"prometheus|alertmanager"}[15m]) > 2'';
description = "Prometheus has restarted more than twice in the last 15 minutes. It might be crashlooping";
};
alert_manager_config_not_synced = {
condition = ''count(count_values("config_hash", alertmanager_config_hash)) > 1'';
description = "Configurations of AlertManager cluster instances are out of sync";
};
prometheus_not_connected_to_alertmanager = {
condition = "prometheus_notifications_alertmanagers_discovered < 1";
description = "Prometheus cannot connect the alertmanager\n VALUE = {{ $value }}\n LABELS = {{ $labels }}";
};
prometheus_rule_evaluation_failures = {
condition = "increase(prometheus_rule_evaluation_failures_total[3m]) > 0";
description = "Prometheus encountered {{ $value }} rule evaluation failures, leading to potentially ignored alerts.\n VALUE = {{ $value }}\n LABELS = {{ $labels }}";
};
prometheus_template_expansion_failures = {
condition = "increase(prometheus_template_text_expansion_failures_total[3m]) > 0";
time = "0m";
description = "Prometheus encountered {{ $value }} template text expansion failures\n VALUE = {{ $value }}\n LABELS = {{ $labels }}";
};
};
nginx.virtualHosts = [
{
subdomain = "monitor";
inherit (cfg) port;
}
];
};
webapps.apps = { webapps.apps = {
prometheus.dashboard = { prometheus.dashboard = {

View file

@ -19,32 +19,34 @@ in
autoPrune.enable = true; autoPrune.enable = true;
}; };
services.cadvisor.enable = config.services.prometheus.enable; services = {
cadvisor.enable = config.services.prometheus.enable;
services.prometheus = { prometheus = {
scrapeConfigs = [ scrapeConfigs = [
{ {
job_name = "docker"; job_name = "docker";
static_configs = [ static_configs = [
{ {
targets = [ "127.0.0.1:${toString config.services.cadvisor.port}" ]; targets = [ "127.0.0.1:${toString config.services.cadvisor.port}" ];
labels = { labels = {
instance = config.networking.hostName; instance = config.networking.hostName;
}; };
} }
]; ];
} }
]; ];
}; };
# dashboard untested # dashboard untested
services.grafana.provision = { grafana.provision = {
dashboards.settings.providers = [ dashboards.settings.providers = [
{ {
name = "Docker"; name = "Docker";
options.path = pkgs.grafana-dashboards.cadvisor; options.path = pkgs.grafana-dashboards.cadvisor;
disableDeletion = true; disableDeletion = true;
} }
]; ];
};
}; };
}; };
} }

View file

@ -24,8 +24,10 @@ in
programs.steam.enable = true; programs.steam.enable = true;
hardware.opengl.driSupport32Bit = true; hardware = {
hardware.opengl.extraPackages32 = with pkgs.pkgsi686Linux; [ libva ]; opengl.driSupport32Bit = true;
hardware.pulseaudio.support32Bit = true; opengl.extraPackages32 = with pkgs.pkgsi686Linux; [ libva ];
pulseaudio.support32Bit = true;
};
}; };
} }

View file

@ -9,7 +9,6 @@ in
config = lib.mkIf cfg.enable { config = lib.mkIf cfg.enable {
programs.gnome-disks.enable = true; programs.gnome-disks.enable = true;
services.udisks2.enable = true;
xdg.mime.enable = true; xdg.mime.enable = true;
@ -17,9 +16,12 @@ in
programs.dconf.enable = true; programs.dconf.enable = true;
# gnome services # gnome services
services.dbus.packages = [ pkgs.dconf ]; services = {
services.udev.packages = [ pkgs.gnome.gnome-settings-daemon ]; udisks2.enable = true;
services.gnome.gnome-keyring.enable = true; dbus.packages = [ pkgs.dconf ];
udev.packages = [ pkgs.gnome.gnome-settings-daemon ];
gnome.gnome-keyring.enable = true;
};
environment.systemPackages = with pkgs; [ environment.systemPackages = with pkgs; [
glib glib

View file

@ -14,24 +14,26 @@ in
# enable trash & network-mount # enable trash & network-mount
services.gvfs.enable = true; services.gvfs.enable = true;
environment.sessionVariables.NAUTILUS_4_EXTENSION_DIR = "${config.system.path}/lib/nautilus/extensions-4";
environment.pathsToLink = [
"/share/nautilus-python/extensions"
];
services.gnome.glib-networking.enable = true; # network-mount services.gnome.glib-networking.enable = true; # network-mount
# default-programs # default-programs
xdg.mime.enable = true; xdg.mime.enable = true;
xdg.icons.enable = true; xdg.icons.enable = true;
environment.systemPackages = with pkgs; [ environment = {
gnome.nautilus systemPackages = with pkgs; [
gnome.nautilus
ffmpegthumbnailer # thumbnails ffmpegthumbnailer # thumbnails
gnome.nautilus-python # enable plugins gnome.nautilus-python # enable plugins
gst_all_1.gst-libav # thumbnails gst_all_1.gst-libav # thumbnails
nautilus-open-any-terminal # terminal-context-entry nautilus-open-any-terminal # terminal-context-entry
]; ];
sessionVariables.NAUTILUS_4_EXTENSION_DIR = "${config.system.path}/lib/nautilus/extensions-4";
pathsToLink = [
"/share/nautilus-python/extensions"
];
};
}; };
} }

View file

@ -28,48 +28,50 @@ in
]; ];
environment.pathsToLink = [ "/libexec" ]; environment.pathsToLink = [ "/libexec" ];
programs.wshowkeys.enable = true; programs = {
programs.light.enable = true; wshowkeys.enable = true;
light.enable = true;
programs.sway = { sway = {
enable = true; enable = true;
wrapperFeatures = { wrapperFeatures = {
gtk = true; gtk = true;
base = true; base = true;
};
extraPackages = with pkgs; [
brightnessctl
dmenu
foot
gammastep
grim
i3status-rust
mako
slurp
swayidle
swaylock
wdisplays
wf-recorder
wl-clipboard
wofi
xwayland
# wshowkeys
];
extraSessionCommands = ''
export XDG_SESSION_TYPE=wayland
export XDG_CURRENT_DESKTOP=sway
export SDL_VIDEODRIVER=wayland
export QT_QPA_PLATFORM=wayland
export QT_WAYLAND_DISABLE_WINDOWDECORATION="1"
export _JAVA_AWT_WM_NONREPARENTING=1
export CLUTTER_BACKEND=wayland
export SAL_USE_VCLPLUGIN=gtk3
export MOZ_ENABLE_WAYLAND=1
export MOZ_USE_XINPUT2=1
export NIXOS_OZONE_WL=1
'';
}; };
extraPackages = with pkgs; [
brightnessctl
dmenu
foot
gammastep
grim
i3status-rust
mako
slurp
swayidle
swaylock
wdisplays
wf-recorder
wl-clipboard
wofi
xwayland
# wshowkeys
];
extraSessionCommands = ''
export XDG_SESSION_TYPE=wayland
export XDG_CURRENT_DESKTOP=sway
export SDL_VIDEODRIVER=wayland
export QT_QPA_PLATFORM=wayland
export QT_WAYLAND_DISABLE_WINDOWDECORATION="1"
export _JAVA_AWT_WM_NONREPARENTING=1
export CLUTTER_BACKEND=wayland
export SAL_USE_VCLPLUGIN=gtk3
export MOZ_ENABLE_WAYLAND=1
export MOZ_USE_XINPUT2=1
export NIXOS_OZONE_WL=1
'';
}; };
}; };
} }