diff options
Diffstat (limited to 'modules/nixos')
89 files changed, 13467 insertions, 0 deletions
diff --git a/modules/nixos/acme.nix b/modules/nixos/acme.nix new file mode 100644 index 0000000..d3ad661 --- /dev/null +++ b/modules/nixos/acme.nix @@ -0,0 +1,32 @@ +{ + config, + lib, + ... +}: +with lib; let + cfg = config.nixfiles.modules.acme; +in { + imports = [ + (mkAliasOptionModule ["certs"] ["security" "acme" "certs"]) + ]; + + options.nixfiles.modules.acme = { + enable = mkEnableOption "ACME"; + + email = mkOption { + description = "Email for notifications."; + type = with types; str; + default = "admin+acme@${my.domain.shire}"; + }; + }; + + config = mkIf cfg.enable { + security.acme = { + acceptTerms = true; + defaults = { + inherit (cfg) email; + validMinDays = 60; + }; + }; + }; +} diff --git a/modules/nixos/alertmanager.nix b/modules/nixos/alertmanager.nix new file mode 100644 index 0000000..871b0c4 --- /dev/null +++ b/modules/nixos/alertmanager.nix @@ -0,0 +1,63 @@ +{ + config, + lib, + ... +}: +with lib; let + cfg = config.nixfiles.modules.alertmanager; +in { + options.nixfiles.modules.alertmanager = { + enable = mkEnableOption "Alertmanager"; + + port = mkOption { + description = "Port."; + type = with types; port; + default = 30112; + }; + + domain = mkOption { + description = "Domain name sans protocol scheme."; + type = with types; nullOr str; + default = "alertmanager.${config.networking.domain}"; + }; + }; + + config = mkIf cfg.enable { + nixfiles.modules.nginx = with cfg; { + enable = true; + upstreams.alertmanager.servers."127.0.0.1:${toString cfg.port}" = {}; + virtualHosts.${cfg.domain} = { + locations."/".proxyPass = "http://alertmanager"; + extraConfig = nginxInternalOnly; + }; + }; + + services.prometheus.alertmanager = { + enable = true; + + listenAddress = "127.0.0.1"; + inherit (cfg) port; + + extraFlags = ["--web.external-url=https://${cfg.domain}"]; + + configuration = { + global = { + smtp_from = "alertmanager@${my.domain.shire}"; + smtp_smarthost = "${my.domain.shire}:584"; + }; + + route = { + receiver = my.username; + group_by = ["alertname"]; + }; + + receivers = [ + { + name = my.username; + email_configs = [{to = "${my.username}+alert@${my.domain.shire}";}]; + } + ]; + }; + }; + }; +} diff --git a/modules/nixos/android.nix b/modules/nixos/android.nix new file mode 100644 index 0000000..307490a --- /dev/null +++ b/modules/nixos/android.nix @@ -0,0 +1,18 @@ +{ + config, + lib, + inputs, + pkgs, + ... +}: +with lib; let + cfg = config.nixfiles.modules.android; +in { + options.nixfiles.modules.android.enable = mkEnableOption "support for Android devices"; + + config = mkIf cfg.enable { + programs.adb.enable = true; + + my.extraGroups = ["adbusers"]; + }; +} diff --git a/modules/nixos/bluetooth.nix b/modules/nixos/bluetooth.nix new file mode 100644 index 0000000..8347361 --- /dev/null +++ b/modules/nixos/bluetooth.nix @@ -0,0 +1,28 @@ +{ + config, + lib, + pkgs, + ... +}: +with lib; let + cfg = config.nixfiles.modules.bluetooth; +in { + options.nixfiles.modules.bluetooth.enable = + mkEnableOption "Bluetooth support"; + + config = mkIf cfg.enable { + hardware.bluetooth = { + enable = true; + settings.General.FastConnectable = true; + }; + + environment = { + etc."bluetooth/input.conf".text = generators.toINI {} { + General = { + IdleTimeout = 15; + UserspaceHID = true; + }; + }; + }; + }; +} diff --git a/modules/nixos/common/console.nix b/modules/nixos/common/console.nix new file mode 100644 index 0000000..3c73695 --- /dev/null +++ b/modules/nixos/common/console.nix @@ -0,0 +1,6 @@ +{config, ...}: { + console = { + earlySetup = true; + useXkbConfig = config.services.xserver.enable; + }; +} diff --git a/modules/nixos/common/default.nix b/modules/nixos/common/default.nix new file mode 100644 index 0000000..8724c8b --- /dev/null +++ b/modules/nixos/common/default.nix @@ -0,0 +1,19 @@ +_: { + imports = [ + ./console.nix + ./documentation.nix + ./home-manager.nix + ./kernel.nix + ./locale.nix + ./networking.nix + ./nix.nix + ./secrets.nix + ./security.nix + ./services.nix + ./shell.nix + ./systemd.nix + ./tmp.nix + ./users.nix + ./xdg.nix + ]; +} diff --git a/modules/nixos/common/documentation.nix b/modules/nixos/common/documentation.nix new file mode 100644 index 0000000..f909108 --- /dev/null +++ b/modules/nixos/common/documentation.nix @@ -0,0 +1,31 @@ +{ + config, + lib, + pkgs, + this, + ... +}: +with lib; { + config = mkIf this.isHeadful { + documentation = { + dev.enable = true; + nixos.enable = true; + + man.man-db.manualPages = + (pkgs.buildEnv { + name = "man-paths"; + paths = with config; + environment.systemPackages ++ hm.home.packages; + pathsToLink = ["/share/man"]; + extraOutputsToInstall = ["man"]; + ignoreCollisions = true; + }) + .overrideAttrs (_: _: {__contentAddressed = true;}); + }; + + environment.sessionVariables = { + MANOPT = "--no-hyphenation"; + MANPAGER = "${pkgs.less}/bin/less -+F"; + }; + }; +} diff --git a/modules/nixos/common/home-manager.nix b/modules/nixos/common/home-manager.nix new file mode 100644 index 0000000..52f2fd3 --- /dev/null +++ b/modules/nixos/common/home-manager.nix @@ -0,0 +1,3 @@ +{inputs, ...}: { + imports = [inputs.home-manager.nixosModule]; +} diff --git a/modules/nixos/common/kernel.nix b/modules/nixos/common/kernel.nix new file mode 100644 index 0000000..2fc40f9 --- /dev/null +++ b/modules/nixos/common/kernel.nix @@ -0,0 +1,39 @@ +{lib, ...}: +with lib; { + boot = { + # I don't use it even on laptops. It's also /required/ to disable it for + # ZFS[1]. + # [1]: https://github.com/openzfs/zfs/issues/260 + # [1]: https://github.com/openzfs/zfs/issues/12842 + kernelParams = ["hibernate=no"]; + + kernel.sysctl = { + "fs.file-max" = pow 2 17; + "fs.inotify.max_user_watches" = pow 2 19; + "fs.suid_dumpable" = 0; + "kernel.core_uses_pid" = 1; + "kernel.exec-shield" = 1; + "kernel.kptr_restrict" = 1; + "kernel.maps_protect" = 1; + "kernel.msgmax" = pow 2 16; + "kernel.msgmnb" = pow 2 16; + "kernel.pid_max" = pow 2 16; + "kernel.randomize_va_space" = 2; + "kernel.shmall" = pow 2 28; + "kernel.shmmax" = pow 2 28; + "kernel.sysrq" = 0; + "vm.dirty_background_bytes" = pow 2 22; + "vm.dirty_background_ratio" = 5; + "vm.dirty_bytes" = pow 2 22; + "vm.dirty_ratio" = 30; + "vm.min_free_kbytes" = pow 2 16; + "vm.mmap_min_addr" = pow 2 12; + "vm.overcommit_memory" = mkDefault 0; + "vm.overcommit_ratio" = mkDefault 50; + "vm.vfs_cache_pressure" = 50; + }; + }; + + # https://docs.kernel.org/admin-guide/mm/ksm.html + hardware.ksm.enable = true; +} diff --git a/modules/nixos/common/locale.nix b/modules/nixos/common/locale.nix new file mode 100644 index 0000000..62d19f4 --- /dev/null +++ b/modules/nixos/common/locale.nix @@ -0,0 +1,24 @@ +{lib, ...}: +with lib; { + i18n = { + defaultLocale = mkDefault "en_GB.UTF-8"; + supportedLocales = [ + "C.UTF-8/UTF-8" + "en_GB.UTF-8/UTF-8" + "en_US.UTF-8/UTF-8" + "ja_JP.UTF-8/UTF-8" + "ru_RU.UTF-8/UTF-8" + ]; + }; + + services.xserver = { + layout = comcat ["us" "ru"]; + xkbVariant = comcat ["" "phonetic"]; + xkbOptions = comcat [ + "terminate:ctrl_alt_bksp" + "caps:escape" + "compose:menu" + "grp:win_space_toggle" + ]; + }; +} diff --git a/modules/nixos/common/networking.nix b/modules/nixos/common/networking.nix new file mode 100644 index 0000000..6109933 --- /dev/null +++ b/modules/nixos/common/networking.nix @@ -0,0 +1,108 @@ +{ + config, + lib, + pkgs, + this, + ... +}: +with lib; { + # TODO Support multiple interfaces and IP addresses. + networking = mkMerge [ + { + domain = my.domain.shire; + + hostName = this.hostname; + hostId = substring 0 8 (builtins.hashString "md5" this.hostname); + + # Remove default hostname mappings. This is required at least by the current + # implementation of the montoring module. + hosts = { + "127.0.0.2" = mkForce []; + "::1" = mkForce []; + }; + + nameservers = mkDefault dns.const.quad9.default; + + useDHCP = false; + + firewall = { + enable = true; + + rejectPackets = false; + + allowPing = true; + pingLimit = "--limit 1/minute --limit-burst 5"; + + logRefusedConnections = false; + logRefusedPackets = false; + logRefusedUnicastsOnly = false; + logReversePathDrops = false; + }; + } + (let + interface = "eth0"; # This assumes `usePredictableInterfaceNames` is false. + in + mkIf (hasAttr "ipv4" this && hasAttr "ipv6" this) { + usePredictableInterfaceNames = false; # NOTE This can break something! + interfaces.${interface} = { + ipv4.addresses = with this.ipv4; + optional (isString address && isInt prefixLength) { + inherit address prefixLength; + }; + + ipv6.addresses = with this.ipv6; + optional (isString address && isInt prefixLength) { + inherit address prefixLength; + }; + }; + defaultGateway = with this.ipv4; + mkIf (isString gatewayAddress) { + inherit interface; + address = gatewayAddress; + }; + defaultGateway6 = with this.ipv6; + mkIf (isString gatewayAddress) { + inherit interface; + address = gatewayAddress; + }; + }) + (mkIf this.isHeadful { + interfaces = { + eth0.useDHCP = mkDefault true; + wlan0.useDHCP = mkDefault true; + }; + + networkmanager = { + enable = mkDefault true; + wifi.backend = "iwd"; + }; + + wireless = { + enable = false; + iwd.enable = mkDefault true; + userControlled.enable = true; + allowAuxiliaryImperativeNetworks = true; + }; + }) + ]; + + environment.shellAliases = listToAttrs (map + ({ + name, + value, + }: + nameValuePair name "${pkgs.iproute2}/bin/${value}") [ + { + name = "bridge"; + value = "bridge -color=always"; + } + { + name = "ip"; + value = "ip -color=always"; + } + { + name = "tc"; + value = "tc -color=always"; + } + ]); +} diff --git a/modules/nixos/common/nix.nix b/modules/nixos/common/nix.nix new file mode 100644 index 0000000..07136a0 --- /dev/null +++ b/modules/nixos/common/nix.nix @@ -0,0 +1,39 @@ +{ + config, + inputs, + lib, + this, + ... +}: +with lib; let + cfg = config.nixfiles.modules.common.nix; +in { + options.nixfiles.modules.common.nix.allowedUnfreePackages = mkOption { + description = "A list of allowed unfree packages."; + type = with types; listOf str; + default = []; + }; + + config = { + nix.settings.trusted-users = ["@wheel"]; + + nixpkgs = { + config.allowUnfreePredicate = p: elem (getName p) cfg.allowedUnfreePackages; + + overlays = with inputs; [ + agenix.overlay + # nix-minecraft-servers.overlays.default + xmonad-ng.overlays.default + ]; + }; + + system.stateVersion = with builtins; + head (split "\n" (readFile "${inputs.nixpkgs}/.version")); + + environment = { + sessionVariables.NIX_SHELL_PRESERVE_PROMPT = "1"; + localBinInPath = true; + defaultPackages = []; + }; + }; +} diff --git a/modules/nixos/common/secrets.nix b/modules/nixos/common/secrets.nix new file mode 100644 index 0000000..4fcdc61 --- /dev/null +++ b/modules/nixos/common/secrets.nix @@ -0,0 +1,45 @@ +{ + config, + inputs, + lib, + pkgs, + this, + ... +}: +with lib; { + imports = [ + inputs.agenix.nixosModule + (mkAliasOptionModule ["secrets"] ["age" "secrets"]) + ]; + + config = { + age = { + identityPaths = + if this.isHeadful + then ["${config.my.home}/.ssh/id_${my.ssh.type}"] + else + map (attr: attr.path) (filter (attr: attr.type == my.ssh.type) + config.services.openssh.hostKeys); + + # This can be used to auto-add all secrets, thus eleminating the need to + # specify path to each envrypted file. The drawback is that this will + # expose *all* secrets to all machines and try to decrypt them all even on + # machines where the secret will not be used. + # + # secrets = + # let + # secretsSourceDir = "${inputs.self}/age"; + # in + # mapAttrs' + # (name: _: + # nameValuePair name { + # file = "${secretsSourceDir}/${name}"; + # owner = mkDefault my.username; + # group = mkDefault config.my.group; + # }) + # (builtins.readDir secretsSourceDir); + }; + + environment.systemPackages = with pkgs; [agenix]; + }; +} diff --git a/modules/nixos/common/security.nix b/modules/nixos/common/security.nix new file mode 100644 index 0000000..09c5da1 --- /dev/null +++ b/modules/nixos/common/security.nix @@ -0,0 +1,29 @@ +{ + inputs, + lib, + ... +}: +with lib; { + security = { + sudo = { + enable = true; + execWheelOnly = true; + wheelNeedsPassword = false; + # https://mwl.io/archives/1000 + extraConfig = '' + Defaults env_keep += "SSH_CLIENT SSH_CONNECTION SSH_TTY SSH_AUTH_SOCK" + ''; + }; + + polkit = { + enable = true; + # https://wiki.archlinux.org/title/Polkit#Bypass_password_prompt + extraConfig = '' + polkit.addRule(function (action, subject) { + if (subject.isInGroup('wheel')) + return polkit.Result.YES; + }); + ''; + }; + }; +} diff --git a/modules/nixos/common/services.nix b/modules/nixos/common/services.nix new file mode 100644 index 0000000..725502a --- /dev/null +++ b/modules/nixos/common/services.nix @@ -0,0 +1,10 @@ +_: { + services = { + # https://github.com/Irqbalance/irqbalance/issues/54#issuecomment-319245584 + # https://unix.stackexchange.com/questions/710603/should-the-irqbalance-daemon-be-used-on-a-modern-desktop-x86-system + irqbalance.enable = true; + + # https://github.com/NixOS/nixpkgs/issues/135888 + nscd.enableNsncd = true; + }; +} diff --git a/modules/nixos/common/shell.nix b/modules/nixos/common/shell.nix new file mode 100644 index 0000000..5fbc441 --- /dev/null +++ b/modules/nixos/common/shell.nix @@ -0,0 +1,3 @@ +_: { + programs.command-not-found.enable = false; +} diff --git a/modules/nixos/common/systemd.nix b/modules/nixos/common/systemd.nix new file mode 100644 index 0000000..5c7282d --- /dev/null +++ b/modules/nixos/common/systemd.nix @@ -0,0 +1,22 @@ +{pkgs, ...}: { + hm.systemd.user.startServices = "sd-switch"; + + services.journald.extraConfig = '' + SystemMaxUse=5G + ''; + + systemd = let + extraConfig = '' + DefaultTimeoutStartSec=30s + DefaultTimeoutStopSec=15s + ''; + in { + inherit extraConfig; + user = {inherit extraConfig;}; + }; + + environment.sessionVariables = { + SYSTEMD_PAGER = "${pkgs.less}/bin/less"; + SYSTEMD_LESS = "FRSXMK"; + }; +} diff --git a/modules/nixos/common/tmp.nix b/modules/nixos/common/tmp.nix new file mode 100644 index 0000000..d56e2b6 --- /dev/null +++ b/modules/nixos/common/tmp.nix @@ -0,0 +1,18 @@ +_: { + systemd.mounts = [ + { + type = "tmpfs"; + what = "tmpfs"; + where = "/tmp"; + mountConfig.Options = [ + "huge=within_size" + "mode=1777" + "noatime" + "nodev" + "nosuid" + "rw" + "size=25%" + ]; + } + ]; +} diff --git a/modules/nixos/common/users.nix b/modules/nixos/common/users.nix new file mode 100644 index 0000000..22e8023 --- /dev/null +++ b/modules/nixos/common/users.nix @@ -0,0 +1,19 @@ +{lib, ...}: +with lib; { + users = { + mutableUsers = false; + + users = { + root.hashedPassword = "@HASHED_PASSWORD@"; + + ${my.username} = { + isNormalUser = true; + uid = 1000; + description = my.fullname; + inherit (my) hashedPassword; + openssh.authorizedKeys.keys = [my.ssh.key]; + extraGroups = ["wheel"]; + }; + }; + }; +} diff --git a/modules/nixos/common/xdg.nix b/modules/nixos/common/xdg.nix new file mode 100644 index 0000000..8ddf1ac --- /dev/null +++ b/modules/nixos/common/xdg.nix @@ -0,0 +1,87 @@ +{ + config, + lib, + this, + ... +}: +with lib; { + imports = let + withBase = s: ["home-manager" "users" my.username "xdg" s]; + in [ + (mkAliasOptionModule ["dirs" "cache"] (withBase "cacheHome")) + (mkAliasOptionModule ["dirs" "config"] (withBase "configHome")) + (mkAliasOptionModule ["dirs" "data"] (withBase "dataHome")) + (mkAliasOptionModule ["dirs" "state"] (withBase "stateHome")) + (mkAliasOptionModule ["userDirs"] (withBase "userDirs")) + ]; + + hm.xdg = mkMerge [ + { + enable = true; + + userDirs = let + inherit (config.my) home; + tmp = home + "/tmp"; + in { + enable = true; + + desktop = tmp; + documents = "${home}/doc"; + download = tmp; + music = tmp; + pictures = tmp; + publicShare = "${home}/share"; + templates = tmp; + videos = tmp; + }; + } + (mkIf this.isHeadful { + mimeApps = { + enable = true; + defaultApplications = mkMerge (mapAttrsToList + (n: v: genAttrs v (_: ["${n}.desktop"])) + { + emacsclient = [ + "application/json" + "application/vnd.ms-publisher" + "application/x-desktop" + "application/x-shellscript" + "application/x-trash" + "application/x-wine-extension-ini" + "application/xml" + "text/markdown" + "text/plain" + ]; + firefox = [ + "text/html" + "x-scheme-handler/http" + "x-scheme-handler/https" + ]; + imv = [ + "image/bmp" + "image/gif" + "image/jpeg" + "image/jpg" + "image/png" + "image/svg+xml" + "image/tiff" + "image/webp" + ]; + mpv = [ + "audio/aac" + "audio/flac" + "audio/mp3" + "audio/ogg" + "audio/wav" + "audio/webm" + "video/mkv" + "video/mp4" + "video/ogg" + "video/webm" + "video/x-matroska" + ]; + }); + }; + }) + ]; +} diff --git a/modules/nixos/default.nix b/modules/nixos/default.nix new file mode 100644 index 0000000..b35e461 --- /dev/null +++ b/modules/nixos/default.nix @@ -0,0 +1,59 @@ +_: { + imports = [ + ./acme.nix + ./alertmanager.nix + ./android.nix + ./bluetooth.nix + ./common + ./discord.nix + ./docker.nix + ./dwm.nix + ./emacs.nix + ./endlessh-go.nix + ./endlessh.nix + ./fail2ban.nix + ./fonts.nix + ./games + ./git.nix + ./gnupg.nix + ./gotify.nix + ./grafana.nix + ./hydra.nix + ./ipfs.nix + ./kde.nix + ./libvirtd.nix + ./lidarr.nix + ./loki.nix + ./lxc.nix + ./matrix + ./monitoring + ./nextcloud.nix + ./nginx.nix + ./node-exporter.nix + ./nsd.nix + ./openssh.nix + ./podman.nix + ./postgresql.nix + ./profiles + ./prometheus.nix + ./promtail.nix + ./psd.nix + ./radarr.nix + ./radicale.nix + ./rss-bridge.nix + ./rtorrent.nix + ./searx.nix + ./shadowsocks.nix + ./soju.nix + ./solaar.nix + ./sonarr.nix + ./sound.nix + ./syncthing.nix + ./throttled.nix + ./unbound.nix + ./vaultwarden.nix + ./wireguard.nix + ./x11.nix + ./xmonad.nix + ]; +} diff --git a/modules/nixos/discord.nix b/modules/nixos/discord.nix new file mode 100644 index 0000000..190b5fc --- /dev/null +++ b/modules/nixos/discord.nix @@ -0,0 +1,22 @@ +{ + config, + lib, + pkgs, + ... +}: +with lib; let + cfg = config.nixfiles.modules.discord; +in { + options.nixfiles.modules.discord.enable = + mkEnableOption "Steam runtime"; + + config = mkIf cfg.enable { + nixfiles.modules.common.nix.allowedUnfreePackages = ["discord"]; + + hm.home.packages = with pkgs; [ + (discord.override { + withOpenASAR = true; + }) + ]; + }; +} diff --git a/modules/nixos/docker.nix b/modules/nixos/docker.nix new file mode 100644 index 0000000..e642030 --- /dev/null +++ b/modules/nixos/docker.nix @@ -0,0 +1,41 @@ +{ + config, + inputs, + lib, + pkgs, + ... +}: +with lib; let + cfg = config.nixfiles.modules.docker; +in { + options.nixfiles.modules.docker.enable = mkEnableOption "Docker"; + + config = mkIf cfg.enable { + assertions = [ + { + assertion = !config.nixfiles.modules.podman.enable; + message = "Pick only one!"; + } + ]; + + secrets.containers-auth = { + file = "${inputs.self}/secrets/containers-auth"; + path = "${config.my.home}/.docker/config.json"; + owner = my.username; + inherit (config.my) group; + }; + + virtualisation.docker.enable = true; + + environment.systemPackages = with pkgs; [docker-compose]; + + my.extraGroups = ["docker"]; + + hm.programs.bash = { + shellAliases.d = "${pkgs.docker}/bin/docker"; + initExtra = mkAfter '' + _complete_alias d _docker docker + ''; + }; + }; +} diff --git a/modules/nixos/dwm.nix b/modules/nixos/dwm.nix new file mode 100644 index 0000000..618d8ed --- /dev/null +++ b/modules/nixos/dwm.nix @@ -0,0 +1,159 @@ +{ + config, + lib, + pkgs, + ... +}: +with lib; let + cfg = config.nixfiles.modules.dwm; +in { + options.nixfiles.modules.dwm.enable = mkEnableOption "dwm"; + + config = mkIf cfg.enable { + nixfiles.modules.x11.enable = true; + + hm.xsession = { + enable = true; + + windowManager.command = let + pkg = pkgs.dwm.override { + conf = let + font = with config.fontScheme.monospaceFont; "${family}:size=${toString size}"; + colour = config.colourScheme; + in '' + static const unsigned int borderpx = 1; + static const unsigned int snap = 32; + static const int showbar = 1; + static const int topbar = 1; + + static const char *fonts[] = { + "${font}" + }; + + static const char *colors[][3] = { + [SchemeNorm] = { + "${colour.white}", + "${colour.black}", + "${colour.black}", + }, + [SchemeSel] = { + "${colour.black}", + "${colour.white}", + "${colour.white}", + }, + }; + + static const char *tags[] = { + "1", + "2", + "3", + "4", + "5", + "6", + "7", + "8", + "9" + }; + + static const Rule rules[] = { + { "Emacs", NULL, NULL, 1 << 0, 0, -1 }, + }; + + static const float mfact = 0.666; + static const int nmaster = 1; + static const int resizehints = 0; + static const int lockfullscreen = 1; + + static const Layout layouts[] = { + { "[]=", tile }, + { "><>", NULL }, + { "[M]", monocle }, + }; + + #define MODKEY Mod4Mask + #define TAGKEYS(KEY,TAG) \ + { MODKEY, KEY, view, { .ui = 1 << TAG } }, \ + { MODKEY|ControlMask, KEY, toggleview, { .ui = 1 << TAG } }, \ + { MODKEY|ShiftMask, KEY, tag, { .ui = 1 << TAG } }, \ + { MODKEY|ControlMask|ShiftMask, KEY, toggletag, { .ui = 1 << TAG } }, + + static char dmenumon[2] = "0"; + static const char *dmenucmd[] = { + "${pkgs.dmenu}/bin/dmenu_run", + "-m", dmenumon, + "-fn", "${font}", + "-nb", "${colour.black}", + "-nf", "${colour.white}", + "-sb", "${colour.white}", + "-sf", "${colour.black}", + NULL, + }; + static const char *termcmd[] = { + "${pkgs.alacritty}/bin/alacritty", + NULL, + }; + + static const Key keys[] = { + { MODKEY, XK_x, spawn, {.v = dmenucmd } }, + { MODKEY, XK_Return, spawn, {.v = termcmd } }, + { MODKEY, XK_b, togglebar, {0} }, + { MODKEY, XK_j, focusstack, {.i = +1 } }, + { MODKEY, XK_k, focusstack, {.i = -1 } }, + { MODKEY|ShiftMask, XK_k, incnmaster, {.i = +1 } }, + { MODKEY|ShiftMask, XK_j, incnmaster, {.i = -1 } }, + { MODKEY, XK_comma, setmfact, {.f = -0.05} }, + { MODKEY, XK_period, setmfact, {.f = +0.05} }, + { MODKEY, XK_p, zoom, {0} }, + { MODKEY, XK_Tab, view, {0} }, + { MODKEY, XK_d, killclient, {0} }, + { MODKEY, XK_t, setlayout, {.v = &layouts[0]} }, + { MODKEY, XK_m, setlayout, {.v = &layouts[1]} }, + { MODKEY, XK_f, setlayout, {.v = &layouts[2]} }, + { MODKEY, XK_o, togglefloating, {0} }, + { MODKEY, XK_0, view, {.ui = ~0 } }, + { MODKEY|ShiftMask, XK_0, tag, {.ui = ~0 } }, + { MODKEY, XK_h, focusmon, {.i = -1 } }, + { MODKEY, XK_l, focusmon, {.i = +1 } }, + { MODKEY|ShiftMask, XK_h, tagmon, {.i = -1 } }, + { MODKEY|ShiftMask, XK_l, tagmon, {.i = +1 } }, + TAGKEYS( XK_1, 0) + TAGKEYS( XK_2, 1) + TAGKEYS( XK_3, 2) + TAGKEYS( XK_4, 3) + TAGKEYS( XK_5, 4) + TAGKEYS( XK_6, 5) + TAGKEYS( XK_7, 6) + TAGKEYS( XK_8, 7) + TAGKEYS( XK_9, 8) + { MODKEY|ShiftMask, XK_q, quit, {0} }, + }; + + static const Button buttons[] = { + { ClkLtSymbol, 0, Button1, setlayout, {0} }, + { ClkLtSymbol, 0, Button3, setlayout, {.v = &layouts[2]} }, + { ClkWinTitle, 0, Button2, zoom, {0} }, + { ClkStatusText, 0, Button2, spawn, {.v = termcmd } }, + { ClkClientWin, MODKEY, Button1, movemouse, {0} }, + { ClkClientWin, MODKEY, Button2, togglefloating, {0} }, + { ClkClientWin, MODKEY, Button3, resizemouse, {0} }, + { ClkTagBar, 0, Button1, view, {0} }, + { ClkTagBar, 0, Button3, toggleview, {0} }, + { ClkTagBar, MODKEY, Button1, tag, {0} }, + { ClkTagBar, MODKEY, Button3, toggletag, {0} }, + }; + ''; + }; + in "${pkg}/bin/dwm"; + }; + + hm.services.dwm-status = { + enable = true; + # package = pkgs.dwm-status.override { + # enableAlsaUtils = false; + # }; + order = ["audio" "backlight" "battery" "cpu_load" "network" "time"]; + }; + + services.xserver.displayManager.startx.enable = true; + }; +} diff --git a/modules/nixos/emacs.nix b/modules/nixos/emacs.nix new file mode 100644 index 0000000..800d411 --- /dev/null +++ b/modules/nixos/emacs.nix @@ -0,0 +1,30 @@ +{ + config, + inputs, + lib, + ... +}: +with lib; let + cfg = config.nixfiles.modules.emacs; +in { + config = mkIf cfg.enable { + secrets.authinfo = { + file = "${inputs.self}/secrets/authinfo"; + owner = my.username; + inherit (config.my) group; + }; + + nixfiles.modules.x11.enable = true; + + hm = { + programs.doom-emacs.extraConfig = '' + (appendq! auth-sources '("${config.secrets.authinfo.path}")) + ''; + + services.emacs = { + enable = true; + client.enable = true; + }; + }; + }; +} diff --git a/modules/nixos/endlessh-go.nix b/modules/nixos/endlessh-go.nix new file mode 100644 index 0000000..435305d --- /dev/null +++ b/modules/nixos/endlessh-go.nix @@ -0,0 +1,30 @@ +{ + config, + lib, + this, + ... +}: +with lib; let + cfg = config.nixfiles.modules.endlessh-go; +in { + options.nixfiles.modules.endlessh-go.enable = mkEnableOption "endlessh-go"; + + config = let + port = 22; + in + mkIf cfg.enable { + services.endlessh-go = { + enable = true; + listenAddress = "0.0.0.0"; + inherit port; + prometheus = { + enable = true; + listenAddress = this.wireguard.ipv4.address; + port = 9229; + }; + extraOptions = ["-geoip_supplier=ip-api" "-v=1"]; + }; + + networking.firewall.allowedTCPPorts = [port]; + }; +} diff --git a/modules/nixos/endlessh.nix b/modules/nixos/endlessh.nix new file mode 100644 index 0000000..67789fd --- /dev/null +++ b/modules/nixos/endlessh.nix @@ -0,0 +1,24 @@ +{ + config, + lib, + pkgs, + ... +}: +with lib; let + cfg = config.nixfiles.modules.endlessh; +in { + options.nixfiles.modules.endlessh.enable = mkEnableOption "endlessh"; + + config = let + port = 22; + in + mkIf cfg.enable { + services.endlessh = { + enable = true; + inherit port; + extraOptions = ["-v" "-4"]; + }; + + networking.firewall.allowedTCPPorts = [port]; + }; +} diff --git a/modules/nixos/fail2ban.nix b/modules/nixos/fail2ban.nix new file mode 100644 index 0000000..5ac3c9c --- /dev/null +++ b/modules/nixos/fail2ban.nix @@ -0,0 +1,32 @@ +{ + config, + lib, + this, + ... +}: +with lib; let + cfg = config.nixfiles.modules.fail2ban; +in { + options.nixfiles.modules.fail2ban.enable = + mkEnableOption "fail2ban"; + + config = mkIf cfg.enable { + services.fail2ban = { + enable = true; + + bantime-increment = { + enable = true; + maxtime = "24h"; + rndtime = "8m"; + }; + + ignoreIP = + optionals (hasAttr "wireguard" this) + (with config.nixfiles.modules.wireguard; [ipv4.subnet ipv6.subnet]); + + jails.DEFAULT = '' + blocktype = DROP + ''; + }; + }; +} diff --git a/modules/nixos/fonts.nix b/modules/nixos/fonts.nix new file mode 100644 index 0000000..d4a7330 --- /dev/null +++ b/modules/nixos/fonts.nix @@ -0,0 +1,45 @@ +{ + config, + lib, + ... +}: +with lib; let + cfg = config.nixfiles.modules.fonts; +in { + config = mkMerge [ + (mkIf cfg.enable { + hm.fonts.fontconfig.enable = true; + fonts.fontconfig = { + enable = true; + + defaultFonts = { + monospace = [ + "Iosevka" + "Sarasa Mono K" + "Sarasa Mono J" + "Sarasa Mono SC" + "Sarasa Mono CL" + ]; + sansSerif = [ + "Iosevka Aile" + "Sarasa Gothic K" + "Sarasa Gothic J" + "Sarasa Gothic SC" + "Sarasa Gothic CL" + ]; + serif = [ + "Iosevka Etoile" + "Sarasa Gothic K" + "Sarasa Gothic J" + "Sarasa Gothic SC" + "Sarasa Gothic CL" + ]; + }; + }; + }) + (mkIf (!cfg.enable) { + hm.fonts.fontconfig.enable = false; + fonts.fontconfig.enable = false; + }) + ]; +} diff --git a/modules/nixos/games/default.nix b/modules/nixos/games/default.nix new file mode 100644 index 0000000..1c5766b --- /dev/null +++ b/modules/nixos/games/default.nix @@ -0,0 +1,38 @@ +{ + config, + lib, + ... +}: +with lib; let + cfg = config.nixfiles.modules.games; +in { + imports = [ + ./gamemode.nix + ./gog.nix + ./lutris.nix + ./mangohud.nix + ./minecraft.nix + ./steam-run.nix + ./steam.nix + ]; + + options.nixfiles.modules.games.enable32BitSupport = + mkEnableOption "support for games"; + + config = mkIf cfg.enable32BitSupport { + services = { + jack.alsa.support32Bit = config.services.jack.alsa.enable; + + pipewire.alsa.support32Bit = config.services.pipewire.alsa.enable; + }; + + hardware = { + opengl = mkIf config.hardware.opengl.enable { + extraPackages32 = config.hardware.opengl.extraPackages; + driSupport32Bit = config.hardware.opengl.driSupport; + }; + + pulseaudio.support32Bit = config.hardware.pulseaudio.enable; + }; + }; +} diff --git a/modules/nixos/games/gamemode.nix b/modules/nixos/games/gamemode.nix new file mode 100644 index 0000000..051d12e --- /dev/null +++ b/modules/nixos/games/gamemode.nix @@ -0,0 +1,13 @@ +{ + config, + lib, + ... +}: +with lib; let + cfg = config.nixfiles.modules.games.gamemode; +in { + options.nixfiles.modules.games.gamemode.enable = + mkEnableOption "Feral GameMode"; + + config = mkIf cfg.enable {programs.gamemode.enable = true;}; +} diff --git a/modules/nixos/games/gog.nix b/modules/nixos/games/gog.nix new file mode 100644 index 0000000..86039f1 --- /dev/null +++ b/modules/nixos/games/gog.nix @@ -0,0 +1,18 @@ +{ + config, + lib, + pkgs, + ... +}: +with lib; let + cfg = config.nixfiles.modules.games.gog; +in { + options.nixfiles.modules.games.gog.enable = + mkEnableOption "stand-alone GOG clients and the ability to run GOG games"; + + config = mkIf cfg.enable { + nixfiles.modules.games.steam-run.enable = true; + + hm.home.packages = with pkgs; [lgogdownloader]; + }; +} diff --git a/modules/nixos/games/lutris.nix b/modules/nixos/games/lutris.nix new file mode 100644 index 0000000..72179fc --- /dev/null +++ b/modules/nixos/games/lutris.nix @@ -0,0 +1,32 @@ +{ + config, + lib, + pkgs, + ... +}: +with lib; let + cfg = config.nixfiles.modules.games.lutris; +in { + options.nixfiles.modules.games.lutris.enable = mkEnableOption "Lutris"; + + config = mkIf cfg.enable { + nixfiles.modules.games = { + gamemode.enable = true; + mangohud.enable = true; + steam-run.enable = true; + }; + + hm.home.packages = with pkgs; [ + (lutris.override { + lutris-unwrapped = lutris-unwrapped.override { + wine = buildFHSUserEnv { + # We don't really need Wine because Lutris downloads a required + # runtime for us. + name = "empty"; + }; + }; + }) + vkBasalt + ]; + }; +} diff --git a/modules/nixos/games/mangohud.nix b/modules/nixos/games/mangohud.nix new file mode 100644 index 0000000..d693c82 --- /dev/null +++ b/modules/nixos/games/mangohud.nix @@ -0,0 +1,26 @@ +{ + config, + lib, + ... +}: +with lib; let + cfg = config.nixfiles.modules.games.mangohud; +in { + options.nixfiles.modules.games.mangohud.enable = mkEnableOption "MangoHud"; + + config = mkIf cfg.enable { + hm.programs.mangohud = { + enable = true; + settings = { + fps = true; + frame_timing = true; + gpu_stats = true; + gpu_temp = true; + cpu_stats = true; + cpu_temp = true; + ram = true; + vram = true; + }; + }; + }; +} diff --git a/modules/nixos/games/minecraft.nix b/modules/nixos/games/minecraft.nix new file mode 100644 index 0000000..e53f9eb --- /dev/null +++ b/modules/nixos/games/minecraft.nix @@ -0,0 +1,50 @@ +{ + config, + lib, + pkgs, + ... +}: +with lib; let + cfg = config.nixfiles.modules.games.minecraft; +in { + options.nixfiles.modules.games.minecraft = { + client.enable = mkEnableOption "Minecraft client"; + server = { + enable = mkEnableOption "Minecraft server"; + + memory = mkOption { + description = "Amount of RAM to allocate."; + type = types.str; + default = "2G"; + }; + }; + }; + + config = mkMerge [ + (mkIf cfg.client.enable { + hm.home.packages = with pkgs; [pollymc]; + }) + (mkIf cfg.server.enable { + # Configurations, opslist, whitelist and plugins are managed imperatively. + # TODO Make it declarative. + services.minecraft-server = { + enable = true; + eula = true; + + package = pkgs.minecraftServers.purpur_1_19_2; + + # TODO Make a PR fixing trailing whitespace on this. + jvmOpts = + (concatStringsSep " " [ + "-Xmx${cfg.server.memory}" + "-Xms${cfg.server.memory}" + "--add-modules=jdk.incubator.vector" + ]) + + " "; + }; + + # Defined in /var/lib/minecraft/server.properties. + networking.firewall.allowedTCPPorts = [55565]; + }) + ]; +} diff --git a/modules/nixos/games/steam-run.nix b/modules/nixos/games/steam-run.nix new file mode 100644 index 0000000..1a1e61f --- /dev/null +++ b/modules/nixos/games/steam-run.nix @@ -0,0 +1,73 @@ +{ + config, + lib, + pkgs, + ... +}: +with lib; let + cfg = config.nixfiles.modules.games.steam-run; +in { + options.nixfiles.modules.games.steam-run = { + enable = mkEnableOption "native Steam runtime"; + + quirks = { + mountAndBladeWarband = mkEnableOption ''fixes for "Mount & Blade: Warband" issues''; + cryptOfTheNecrodancer = mkEnableOption ''fixes for "Crypt of the NecroDancer" issues''; + }; + }; + + config = mkIf cfg.enable { + nixfiles.modules = { + common.nix.allowedUnfreePackages = ["steam" "steam-run"]; + + games = { + enable32BitSupport = true; + gamemode.enable = true; + }; + }; + + hm.home.packages = with pkgs; [ + (steam.override { + extraLibraries = _: + with cfg.quirks; + [] + ++ optionals mountAndBladeWarband [ + (glew.overrideAttrs (_: super: let + opname = super.pname; + in rec { + pname = "${opname}-runfix"; + inherit (super) version; + src = fetchurl { + url = "mirror://sourceforge/${opname}/${opname}-${version}.tgz"; + hash = "sha256-BN6R5+Z2MDm8EZQAlc2cf4gLq6ghlqd2X3J6wFqZPJU="; + }; + })) + (fmodex.overrideAttrs (_: super: let + opname = super.pname; + in rec { + pname = "${opname}-runfix"; + inherit (super) version; + installPhase = let + libPath = makeLibraryPath [ + alsa-lib + libpulseaudio + stdenv.cc.cc + ]; + in '' + install -Dm755 api/lib/libfmodex64-${version}.so $out/lib/libfmodex64.so + patchelf --set-rpath ${libPath} $out/lib/libfmodex64.so + ''; + })) + ] + ++ optionals cryptOfTheNecrodancer [ + (import (builtins.fetchTarball { + url = "https://github.com/NixOS/nixpkgs/archive/d1c3fea7ecbed758168787fe4e4a3157e52bc808.tar.gz"; + sha256 = "0ykm15a690v8lcqf2j899za3j6hak1rm3xixdxsx33nz7n3swsyy"; + }) {inherit (config.nixpkgs) config localSystem;}) + .flac + ]; + }) + .run + ]; + }; +} diff --git a/modules/nixos/games/steam.nix b/modules/nixos/games/steam.nix new file mode 100644 index 0000000..8dfa72c --- /dev/null +++ b/modules/nixos/games/steam.nix @@ -0,0 +1,25 @@ +{ + config, + lib, + pkgs, + ... +}: +with lib; let + cfg = config.nixfiles.modules.games.steam; +in { + options.nixfiles.modules.games.steam.enable = + mkEnableOption "Steam runtime"; + + config = mkIf cfg.enable { + nixfiles.modules = { + common.nix.allowedUnfreePackages = ["steam" "steam-original"]; + + games = { + enable32BitSupport = true; + gamemode.enable = true; + }; + }; + + hm.home.packages = with pkgs; [steam]; + }; +} diff --git a/modules/nixos/git.nix b/modules/nixos/git.nix new file mode 100644 index 0000000..f754588 --- /dev/null +++ b/modules/nixos/git.nix @@ -0,0 +1,117 @@ +{ + config, + lib, + inputs, + pkgs, + ... +}: +with lib; let + cfg = config.nixfiles.modules.git; +in { + options.nixfiles.modules.git.server = { + enable = mkEnableOption "Git server"; + + domain = mkOption { + description = "Domain name sans protocol scheme."; + type = with types; nullOr str; + default = "git.${config.networking.domain}"; + }; + + package = mkOption { + description = "Package."; + type = types.package; + default = pkgs.cgit-pink; + }; + }; + + config = mkMerge [ + (mkIf cfg.client.enable { + secrets = { + glab-cli-config = { + file = "${inputs.self}/secrets/glab-cli-config"; + path = "${config.dirs.config}/glab-cli/config.yml"; + owner = my.username; + inherit (config.my) group; + }; + gh-hosts = { + file = "${inputs.self}/secrets/gh-hosts"; + path = "${config.dirs.config}/gh/hosts.yml"; + owner = my.username; + inherit (config.my) group; + }; + hut = { + file = "${inputs.self}/secrets/hut"; + path = "${config.dirs.config}/hut/config"; + owner = my.username; + inherit (config.my) group; + }; + }; + }) + (mkIf cfg.server.enable { + nixfiles.modules.nginx = { + enable = true; + virtualHosts.${cfg.server.domain} = { + locations = { + "/".extraConfig = let + cgitrc = pkgs.writeText "cgitrc" '' + root-title=azahi’s git stuff + root-desc=鯛も一人はうまからず + + about-filter=${cfg.server.package}/lib/cgit/filters/about-formatting.sh + source-filter=${cfg.server.package}/lib/cgit/filters/syntax-highlighting.py + commit-filter=${cfg.server.package}/lib/cgit/filters/commit-links.sh + + enable-git-config=1 + enable-gitweb-owner=1 + remove-suffix=1 + + snapshots=tar.gz tar.bz2 zip + + readme=:README + readme=:README.md + readme=:README.org + readme=:README.txt + readme=:readme + readme=:readme.md + readme=:readme.org + readme=:readme.txt + + scan-path=${config.services.gitolite.dataDir}/repositories + ''; + in '' + include ${config.services.nginx.package}/conf/fastcgi_params; + fastcgi_split_path_info ^(/?)(.+)$; + fastcgi_pass unix:${config.services.fcgiwrap.socketAddress}; + fastcgi_param SCRIPT_FILENAME ${cfg.server.package}/cgit/cgit.cgi; + fastcgi_param CGIT_CONFIG ${cgitrc}; + fastcgi_param PATH_INFO $uri; + fastcgi_param QUERY_STRING $args; + fastcgi_param HTTP_HOST $server_name; + ''; + # FIXME This breaks sources previewing for these files. + "~* ^/(.+.(ico|css|png))$".extraConfig = '' + alias ${cfg.server.package}/cgit/$1; + ''; + }; + }; + }; + + services = let + user = "git"; + group = "git"; + in { + gitolite = { + # TODO Make the configuration purely declarative. + enable = true; + inherit user group; + adminPubkey = my.ssh.key; + }; + + fcgiwrap = { + enable = true; + inherit user group; + }; + }; + }) + ]; +} diff --git a/modules/nixos/gnupg.nix b/modules/nixos/gnupg.nix new file mode 100644 index 0000000..b86be9b --- /dev/null +++ b/modules/nixos/gnupg.nix @@ -0,0 +1,38 @@ +{ + config, + lib, + ... +}: +with lib; let + cfg = config.nixfiles.modules.gnupg; +in { + options.nixfiles.modules.gnupg.pinentry = mkOption { + description = "Name of a pinentry implementation."; + type = types.str; + default = "curses"; + }; + + config = mkIf cfg.enable { + hm = { + programs.gpg.homedir = "${config.dirs.data}/gnupg"; + + services.gpg-agent = { + enable = true; + + enableSshSupport = true; + enableScDaemon = false; + + defaultCacheTtl = 999999; + defaultCacheTtlSsh = 999999; + maxCacheTtl = 999999; + maxCacheTtlSsh = 999999; + + grabKeyboardAndMouse = true; + + sshKeys = [my.pgp.grip]; + + pinentryFlavor = cfg.pinentry; + }; + }; + }; +} diff --git a/modules/nixos/gotify.nix b/modules/nixos/gotify.nix new file mode 100644 index 0000000..db47bb4 --- /dev/null +++ b/modules/nixos/gotify.nix @@ -0,0 +1,75 @@ +{ + config, + lib, + ... +}: +with lib; let + cfg = config.nixfiles.modules.gotify; +in { + options.nixfiles.modules.gotify = { + enable = mkEnableOption "Gotify"; + + domain = mkOption { + description = "Domain name sans protocol scheme."; + type = with types; str; + default = "gotify.${config.networking.domain}"; + }; + }; + + config = let + db = "gotify"; + in + mkIf cfg.enable { + nixfiles.modules = { + nginx = { + enable = true; + upstreams.gotify.servers."127.0.0.1:${toString config.services.gotify.port}" = {}; + virtualHosts.${cfg.domain} = { + locations."/" = { + proxyPass = "http://gotify"; + proxyWebsockets = true; + }; + extraConfig = nginxInternalOnly; + }; + }; + postgresql = { + enable = true; + extraPostStart = [ + '' + $PSQL "${db}" -tAc 'GRANT ALL ON SCHEMA "public" TO "${db}"' + '' + ]; + }; + }; + + services = { + gotify = { + enable = true; + port = 7665; + }; + + postgresql = { + ensureDatabases = [db]; + ensureUsers = [ + { + name = db; + ensurePermissions."DATABASE \"${db}\"" = "ALL"; + } + ]; + }; + }; + + systemd.services.gotify-server = { + after = ["network-online.target" "postgresql.service"]; + environment = { + GOTIFY_DATABASE_DIALECT = "postgres"; + GOTIFY_DATABASE_CONNECTION = concatStringsSep " " [ + "host=/run/postgresql" + "user=${db}" + "dbname=${db}" + "sslmode=disable" + ]; + }; + }; + }; +} diff --git a/modules/nixos/grafana.nix b/modules/nixos/grafana.nix new file mode 100644 index 0000000..a614502 --- /dev/null +++ b/modules/nixos/grafana.nix @@ -0,0 +1,119 @@ +{ + config, + inputs, + lib, + ... +}: +with lib; let + cfg = config.nixfiles.modules.grafana; +in { + options.nixfiles.modules.grafana = { + enable = mkEnableOption "Grafana"; + + port = mkOption { + description = "Port."; + type = with types; port; + default = 30101; + }; + + domain = mkOption { + description = "Domain name sans protocol scheme."; + type = with types; nullOr str; + default = "grafana.${config.networking.domain}"; + }; + }; + + config = let + db = "grafana"; + in + mkIf cfg.enable { + secrets = { + grafana-key = { + file = "${inputs.self}/secrets/grafana-key"; + owner = "grafana"; + group = "grafana"; + }; + grafana-admin-password = { + file = "${inputs.self}/secrets/grafana-admin-password"; + owner = "grafana"; + group = "grafana"; + }; + grafana-smtp-password = { + file = "${inputs.self}/secrets/grafana-smtp-password"; + owner = "grafana"; + group = "grafana"; + }; + }; + + nixfiles.modules = { + nginx = { + enable = true; + upstreams.grafana.servers."127.0.0.1:${toString cfg.port}" = {}; + virtualHosts.${cfg.domain} = { + locations."/" = { + proxyPass = "http://grafana"; + proxyWebsockets = true; + }; + extraConfig = nginxInternalOnly; + }; + }; + postgresql = { + enable = true; + extraPostStart = [ + '' + $PSQL "${db}" -tAc 'GRANT ALL ON SCHEMA "public" TO "${db}"' + '' + ]; + }; + }; + + services = { + grafana = { + enable = true; + + settings = { + server = with cfg; { + protocol = "http"; + http_addr = "127.0.0.1"; + http_port = port; + inherit domain; + enable_gzip = true; + }; + database = { + type = "postgres"; + host = "/run/postgresql"; + name = db; + user = db; + }; + smtp = { + enable = true; + user = "azahi@shire.me"; + host = my.domain.shire; + password = "$__file{${config.secrets.grafana-smtp-password.path}}"; + }; + user = { + allow_org_create = false; + allow_sign_up = false; + auto_assign_org = false; + auto_assign_org_role = "Viewer"; + }; + security = with config.secrets; { + secret_key = grafana-key.path; + admin_password = grafana-admin-password.path; + }; + analytics.reporting_enable = false; + }; + }; + + postgresql = { + ensureDatabases = [db]; + ensureUsers = [ + { + name = db; + ensurePermissions."DATABASE \"${db}\"" = "ALL"; + } + ]; + }; + }; + }; +} diff --git a/modules/nixos/hydra.nix b/modules/nixos/hydra.nix new file mode 100644 index 0000000..590fecb --- /dev/null +++ b/modules/nixos/hydra.nix @@ -0,0 +1,57 @@ +{ + config, + lib, + ... +}: +with lib; let + cfg = config.nixfiles.modules.hydra; +in { + options.nixfiles.modules.hydra = { + enable = mkEnableOption "Hydra"; + + domain = mkOption { + description = "Domain name sans protocol scheme."; + type = with types; str; + default = "hydra.${config.networking.domain}"; + }; + + port = mkOption { + description = "Port."; + type = with types; port; + default = 7754; + }; + }; + + config = mkIf cfg.enable { + nixfiles.modules = { + nginx = { + enable = true; + upstreams.hydra.servers."127.0.0.1:${toString cfg.port}" = {}; + virtualHosts.${cfg.domain}.locations."/".proxyPass = "http://hydra"; + }; + postgresql.enable = true; + }; + + services = let + db = "hydra"; + in { + hydra = { + enable = true; + listenHost = "127.0.0.1"; + inherit (cfg) port; + dbi = "dbi:Pg:dbname=${db};user=${db}"; + hydraURL = cfg.domain; + }; + + postgresql = { + ensureDatabases = [db]; + ensureUsers = [ + { + name = db; + ensurePermissions."DATABASE \"${db}\"" = "ALL"; + } + ]; + }; + }; + }; +} diff --git a/modules/nixos/ipfs.nix b/modules/nixos/ipfs.nix new file mode 100644 index 0000000..0ec64e5 --- /dev/null +++ b/modules/nixos/ipfs.nix @@ -0,0 +1,167 @@ +{ + config, + lib, + this, + ... +}: +with lib; let + cfg = config.nixfiles.modules.ipfs; + + swarmDefaultPort = 4001; + apiDefaultPort = 5001; + gatewayDefaultPort = 6001; +in { + options.nixfiles.modules.ipfs = { + enable = mkEnableOption "IPFS daemon"; + + domain = mkOption { + description = "Domain name sans protocol scheme."; + type = with types; str; + default = "ipfs.${config.networking.fqdn}"; + }; + + swarmPort = mkOption { + description = "Swarm port."; + type = with types; port; + default = + if this.isHeadless + then swarmDefaultPort + 990 + else swarmDefaultPort; + }; + + apiPort = mkOption { + description = "API port."; + type = with types; port; + default = + if this.isHeadless + then apiDefaultPort + 990 + else apiDefaultPort; + }; + + gatewayPort = mkOption { + description = "Gateway port."; + type = with types; port; + default = + if this.isHeadless + then gatewayDefaultPort + 990 + else gatewayDefaultPort; + }; + }; + + config = mkIf cfg.enable (mkMerge [ + { + services.ipfs = { + enable = true; + + user = my.username; + inherit (config.my) group; + + dataDir = "${config.dirs.data}/ipfs"; + + swarmAddress = let + port = toString cfg.swarmPort; + in + if this.isHeadless + then [ + "/ip4/127.0.0.1/tcp/${port}" + "/ip4/127.0.0.1/udp/${port}/quic" + ] + else [ + "/ip4/0.0.0.0/tcp/${port}" + "/ip6/::/tcp/${port}" + "/ip4/0.0.0.0/udp/${port}/quic" + "/ip6/::/udp/${port}/quic" + ]; + apiAddress = "/ip4/127.0.0.1/tcp/${toString cfg.apiPort}"; + gatewayAddress = "/ip4/127.0.0.1/tcp/${toString cfg.gatewayPort}"; + + autoMigrate = true; + autoMount = true; + emptyRepo = true; + enableGC = true; + + extraConfig = mkMerge [ + (let + filterAddresses = + [ + "/ip4/100.64.0.0/ipcidr/10" + "/ip4/169.254.0.0/ipcidr/16" + "/ip4/172.16.0.0/ipcidr/12" + "/ip4/192.0.0.0/ipcidr/24" + "/ip4/192.0.2.0/ipcidr/24" + "/ip4/192.168.0.0/ipcidr/16" + "/ip4/198.18.0.0/ipcidr/15" + "/ip4/198.51.100.0/ipcidr/24" + "/ip4/203.0.113.0/ipcidr/24" + "/ip4/240.0.0.0/ipcidr/4" + "/ip6/100::/ipcidr/64" + "/ip6/2001:2::/ipcidr/48" + "/ip6/2001:db8::/ipcidr/32" + "/ip6/fe80::/ipcidr/10" + ] + ++ optionals (!hasAttr "wireguard" this) [ + "/ip4/10.0.0.0/ipcidr/8" + "/ip6/fc00::/ipcidr/7" + ]; + in { + Addresses = with config.services.ipfs; { + # https://github.com/NixOS/nixpkgs/pull/165259 + # I think this shit broke inheritance... Gotta test more and make + # a PR I guess. + API = apiAddress; + Gateway = gatewayAddress; + Swarm = swarmAddress; + + NoAnnounce = filterAddresses; + }; + Swarm.AddrFilters = filterAddresses; + API.HTTPHeaders.Access-Control-Allow-Methods = ["GET" "POST" "PUT"]; + }) + (mkIf this.isHeadful { + API.HTTPHeaders.Access-Control-Allow-Origin = ["*"]; + }) + (mkIf this.isHeadless { + API.HTTPHeaders.Access-Control-Allow-Origin = ["https://${cfg.domain}" "https://api.${cfg.domain}"]; + }) + ]; + }; + + networking.firewall = rec { + allowedTCPPorts = [swarmDefaultPort]; + allowedUDPPorts = allowedTCPPorts; + }; + } + (mkIf this.isHeadless { + nixfiles.modules.nginx = { + enable = true; + upstreams = { + ipfs_gateway.servers."127.0.0.1:${toString cfg.gatewayPort}" = {}; + ipfs_swarm.servers."127.0.0.1:${toString cfg.swarmPort}" = {}; + ipfs_api.servers."127.0.0.1:${toString cfg.apiPort}" = {}; + }; + virtualHosts = { + ${cfg.domain}.locations."/".proxyPass = "http://ipfs_gateway"; + "swarm.${cfg.domain}" = { + serverName = cfg.domain; + listen = [ + { + addr = "0.0.0.0"; + port = swarmDefaultPort; + } + { + addr = "[::0]"; + port = swarmDefaultPort; + } + ]; + locations."/".proxyPass = "http://ipfs_swarm"; + }; + "api.${cfg.domain}" = { + # TODO Redirect "/" to "/webui" but keep other endpoints. + locations."/".proxyPass = "http://ipfs_api"; + extraConfig = nginxInternalOnly; + }; + }; + }; + }) + ]); +} diff --git a/modules/nixos/kde.nix b/modules/nixos/kde.nix new file mode 100644 index 0000000..a430294 --- /dev/null +++ b/modules/nixos/kde.nix @@ -0,0 +1,49 @@ +{ + config, + lib, + pkgs, + ... +}: +with lib; let + cfg = config.nixfiles.modules.kde; +in { + options.nixfiles.modules.kde.enable = mkEnableOption "KDE Plasma"; + + config = mkIf cfg.enable { + nixfiles.modules = { + gnupg.pinentry = "qt"; + sound.enable = true; + x11.enable = true; + }; + + hm = { + home.sessionVariables.GTK_USE_PORTAL = 1; + + programs.firefox.profiles.default.settings = { + "widget.use-xdg-desktop-portal.file-picker" = 1; + "widget.use-xdg-desktop-portal.mime-handler" = 1; + }; + }; + + services.xserver = { + desktopManager.plasma5 = { + enable = true; + excludePackages = with pkgs.plasma5Packages; [ + elisa + gwenview + khelpcenter + okular + print-manager + ]; + }; + displayManager = { + sddm.enable = true; + + # NOTE https://github.com/NixOS/nixpkgs/pull/199881 + setupCommands = lib.mkForce ""; + }; + }; + + environment.systemPackages = with pkgs; [pinentry-qt]; + }; +} diff --git a/modules/nixos/libvirtd.nix b/modules/nixos/libvirtd.nix new file mode 100644 index 0000000..ae8b336 --- /dev/null +++ b/modules/nixos/libvirtd.nix @@ -0,0 +1,44 @@ +{ + config, + lib, + pkgs, + ... +}: +with lib; let + cfg = config.nixfiles.modules.libvirtd; +in { + options.nixfiles.modules.libvirtd.enable = mkEnableOption "libvirtd"; + + config = mkIf cfg.enable { + virtualisation.libvirtd = { + enable = true; + + onBoot = "ignore"; + onShutdown = "shutdown"; + + extraConfig = '' + log_level = 4 + log_outputs = "4:stderr" + ''; + + qemu = { + package = pkgs.qemu_kvm; + runAsRoot = false; + + ovmf = { + enable = true; + packages = [pkgs.OVMFFull.fd]; + }; + + swtpm = { + enable = false; # Is this required for Windows 11? + package = pkgs.swtpm-tpm2; + }; + }; + }; + + environment.systemPackages = with pkgs; [virt-manager qemu-utils]; + + my.extraGroups = ["libvirtd"]; + }; +} diff --git a/modules/nixos/lidarr.nix b/modules/nixos/lidarr.nix new file mode 100644 index 0000000..f73f917 --- /dev/null +++ b/modules/nixos/lidarr.nix @@ -0,0 +1,28 @@ +{ + config, + lib, + ... +}: +with lib; let + cfg = config.nixfiles.modules.lidarr; +in { + options.nixfiles.modules.lidarr = { + enable = mkEnableOption "Lidarr"; + + domain = mkOption { + description = "Domain name sans protocol scheme."; + type = with types; str; + default = "lidarr.${config.networking.fqdn}"; + }; + }; + + config = mkIf cfg.enable { + nixfiles.modules.nginx = { + enable = true; + upstreams.lidarr.servers."127.0.0.1:8686" = {}; + virtualHosts.${cfg.domain}.locations."/".proxyPass = "http://lidarr"; + }; + + services.lidarr.enable = true; + }; +} diff --git a/modules/nixos/loki.nix b/modules/nixos/loki.nix new file mode 100644 index 0000000..1582164 --- /dev/null +++ b/modules/nixos/loki.nix @@ -0,0 +1,102 @@ +{ + config, + lib, + pkgs, + ... +}: +with lib; let + cfg = config.nixfiles.modules.loki; +in { + options.nixfiles.modules.loki = { + enable = mkEnableOption "Loki"; + + port = mkOption { + description = "Port."; + type = with types; port; + default = 30171; + }; + + domain = mkOption { + description = "Domain name sans protocol scheme."; + type = with types; str; + default = "loki.${config.networking.domain}"; + }; + }; + + config = mkIf cfg.enable { + nixfiles.modules.nginx = with cfg; { + enable = true; + upstreams.loki.servers."127.0.0.1:${toString cfg.port}" = {}; + virtualHosts.${domain} = { + locations."/".proxyPass = "http://loki"; + extraConfig = nginxInternalOnly; + }; + }; + + services.loki = { + enable = true; + + configuration = rec { + auth_enabled = false; + + server = rec { + http_listen_address = "127.0.0.1"; + http_listen_port = cfg.port; + + grpc_listen_address = "127.0.0.1"; + grpc_listen_port = http_listen_port + 1; + + log_level = "warn"; + }; + + common = rec { + path_prefix = "/var/lib/loki"; + storage.filesystem = { + chunks_directory = "${path_prefix}/chunker"; + rules_directory = "${path_prefix}/ruler"; + }; + replication_factor = 1; + instance_interface_names = ["lo"]; + ring = { + instance_addr = "127.0.0.1"; + kvstore.store = "inmemory"; + }; + }; + + ruler = { + rule_path = "${common.path_prefix}/ruler"; + storage = { + type = "local"; + local.directory = + pkgs.writeTextDir "ruler/ruler.yml" + (generators.toJSON {} {groups = [{name = "default";}];}); + }; + }; + + schema_config.configs = [ + { + from = "2020-01-01"; + store = "boltdb-shipper"; + object_store = "filesystem"; + schema = "v11"; + index = { + prefix = "index_"; + period = "24h"; + }; + chunks = { + prefix = "chunks_"; + period = "24h"; + }; + } + ]; + + analytics.reporting_enabled = false; + }; + }; + + systemd.tmpfiles.rules = [ + "d /var/lib/loki 0700 loki loki - -" + "d /var/lib/loki/ruler 0700 loki loki - -" + ]; + }; +} diff --git a/modules/nixos/lxc.nix b/modules/nixos/lxc.nix new file mode 100644 index 0000000..4f7805f --- /dev/null +++ b/modules/nixos/lxc.nix @@ -0,0 +1,16 @@ +{ + config, + lib, + ... +}: +with lib; let + cfg = config.nixfiles.modules.lxc; +in { + options.nixfiles.modules.lxc.enable = + mkEnableOption "LXC/LXD"; + + config = mkIf cfg.enable { + virtualisation.lxd.enable = true; + my.extraGroups = "lxd"; + }; +} diff --git a/modules/nixos/matrix/default.nix b/modules/nixos/matrix/default.nix new file mode 100644 index 0000000..bd221c4 --- /dev/null +++ b/modules/nixos/matrix/default.nix @@ -0,0 +1 @@ +_: {imports = [./dendrite.nix ./element.nix ./synapse.nix];} diff --git a/modules/nixos/matrix/dendrite.nix b/modules/nixos/matrix/dendrite.nix new file mode 100644 index 0000000..0fad5f2 --- /dev/null +++ b/modules/nixos/matrix/dendrite.nix @@ -0,0 +1,157 @@ +{ + config, + lib, + inputs, + pkgs, + ... +}: +with lib; let + cfg = config.nixfiles.modules.matrix.dendrite; +in { + options.nixfiles.modules.matrix.dendrite = { + enable = mkEnableOption "Dendrite Matrix server"; + + domain = mkOption { + type = types.str; + default = config.networking.domain; + description = "Domain name sans protocol scheme."; + }; + }; + + config = let + db = "dendrite"; + in + mkIf cfg.enable { + secrets.dendrite-private-key = { + file = "${inputs.self}/secrets/dendrite-private-key"; + mode = "0444"; # The user is dynamic so the file must be world-readable. + }; + secrets.dendrite-environment-file = { + file = "${inputs.self}/secrets/dendrite-environment-file"; + mode = "0444"; # The user is dynamic so the file must be world-readable. + }; + + nixfiles.modules = { + nginx = { + enable = true; + upstreams.dendrite.servers."127.0.0.1:${toString config.services.dendrite.httpPort}" = {}; + virtualHosts.${cfg.domain}.locations = { + "/_matrix".proxyPass = "http://dendrite"; + "= /.well-known/matrix/server" = { + extraConfig = '' + add_header Content-Type application/json; + ''; + return = "200 '${ + generators.toJSON {} {"m.server" = "${cfg.domain}:443";} + }'"; + }; + "= /.well-known/matrix/client" = { + extraConfig = '' + add_header Content-Type application/json; + add_header Access-Control-Allow-Origin *; + ''; + return = "200 '${ + generators.toJSON {} { + "m.homeserver".base_url = "https://${cfg.domain}"; + } + }'"; + }; + }; + }; + postgresql = { + enable = true; + extraPostStart = [ + '' + $PSQL "${db}" -tAc 'GRANT ALL ON SCHEMA "public" TO "${db}"' + '' + ]; + }; + }; + + services = { + dendrite = { + enable = true; + httpPort = 8008; + environmentFile = config.secrets.dendrite-environment-file.path; + settings = { + version = 2; + global = { + server_name = cfg.domain; + private_key = config.secrets.dendrite-private-key.path; + database = { + connection_string = "postgresql://${db}@/${db}?host=/run/postgresql"; + max_open_conns = 64; + max_idle_connections = 8; + }; + cache = { + max_size_estimated = "1gb"; + max_age = "1h"; + }; + trusted_third_party_id_servers = [ + "matrix.org" + "nixos.org" + "vector.im" + ]; + presence = { + enable_inbound = false; + enable_outbound = false; + }; + }; + client_api = { + registration_disabled = true; + guests_disabled = true; + registration_shared_secret = "$REGISTRATION_SHARED_SECRET"; + }; + media_api = { + max_file_size_bytes = 0; + dynamic_thumbnails = true; + max_thumbnail_generators = 8; + thumbnail_sizes = [ + { + width = 32; + height = 32; + method = "crop"; + } + { + width = 96; + height = 96; + method = "crop"; + } + { + width = 640; + height = 480; + method = "scale"; + } + ]; + }; + logging = [ + { + type = "std"; + level = "warn"; + } + ]; + }; + }; + + postgresql = { + ensureDatabases = [db]; + ensureUsers = [ + { + name = db; + ensurePermissions."DATABASE \"${db}\"" = "ALL"; + } + ]; + }; + }; + + systemd.services.dendrite.serviceConfig.ExecStart = + mkForce + (concatStringsSep " " [ + "${pkgs.dendrite}/bin/dendrite-monolith-server" + "--config /run/dendrite/dendrite.yaml" + "--http-bind-address 127.0.0.1:${ + toString config.services.dendrite.httpPort + }" + ]); + }; +} diff --git a/modules/nixos/matrix/element.nix b/modules/nixos/matrix/element.nix new file mode 100644 index 0000000..3d47800 --- /dev/null +++ b/modules/nixos/matrix/element.nix @@ -0,0 +1,59 @@ +{ + config, + lib, + pkgs, + ... +}: +with lib; let + cfg = config.nixfiles.modules.matrix.element; +in { + options.nixfiles.modules.matrix.element = { + enable = mkEnableOption "Element, a Matrix web interface"; + + domain = mkOption { + description = "Domain name sans protocol scheme."; + type = with types; nullOr str; + default = "element.${config.networking.domain}"; + }; + + homeserver = mkOption { + description = "Default Matrix homeserver."; + type = with types; str; + default = my.domain.azahi; + }; + }; + + config = mkIf cfg.enable { + assertions = [ + { + assertion = with config.nixfiles.modules.matrix; + (synapse.enable || dendrite.enable) && !(!synapse.enable && !dendrite.enable); + message = "Synapse or Dendrite must be enabled"; + } + ]; + + nixfiles.modules.nginx = with cfg; { + enable = true; + virtualHosts.${domain}.locations."/".root = pkgs.element-web.override { + conf = { + default_server_config."m.homeserver" = { + base_url = "https://${homeserver}"; + server_name = homeserver; + }; + disable_custom_urls = true; + disable_guests = true; + disable_login_language_selector = true; + disable_3pid_login = true; + brand = homeserver; + branding.authFooterLinks = [ + { + text = "Hosted on NixOS"; + url = "https://nixos.org"; + } + ]; + default_theme = "dark"; + }; + }; + }; + }; +} diff --git a/modules/nixos/matrix/synapse.nix b/modules/nixos/matrix/synapse.nix new file mode 100644 index 0000000..6ff5e0d --- /dev/null +++ b/modules/nixos/matrix/synapse.nix @@ -0,0 +1,93 @@ +{ + config, + lib, + pkgs, + ... +}: +with lib; let + cfg = config.nixfiles.modules.matrix.synapse; +in { + options.nixfiles.modules.matrix.synapse = { + enable = mkEnableOption "Synapse Matrix server"; + + domain = mkOption { + description = "Domain name sans protocol scheme."; + type = with types; str; + default = config.networking.domain; + }; + }; + + config = let + bind_address = "127.0.0.1"; + port = 8448; + in + mkIf cfg.enable { + nixfiles.modules = { + nginx = { + enable = true; + upstreams.synapse.servers."${bind_address}:${toString port}" = {}; + virtualHosts.${cfg.domain}.locations = { + "~ ^(/_matrix|/_synapse/client)".proxyPass = "http://synapse"; + "= /.well-known/matrix/server" = { + extraConfig = '' + add_header Content-Type application/json; + ''; + return = "200 '${ + generators.toJSON {} {"m.server" = "${cfg.domain}:443";} + }'"; + }; + "= /.well-known/matrix/client" = { + extraConfig = '' + add_header Content-Type application/json; + add_header Access-Control-Allow-Origin *; + ''; + return = "200 '${ + generators.toJSON {} { + "m.homeserver".base_url = "https://${cfg.domain}"; + } + }'"; + }; + }; + }; + postgresql.enable = true; + }; + + services = let + db = "synapse"; + in { + matrix-synapse = { + enable = true; + server_name = config.networking.domain; + + database_type = "psycopg2"; + database_name = db; + database_user = db; + + listeners = [ + { + inherit bind_address port; + type = "http"; + tls = false; + x_forwarded = true; + resources = [ + { + names = ["client" "federation"]; + compress = false; + } + ]; + } + ]; + }; + + postgresql = { + ensureDatabases = [db]; + ensureUsers = [ + { + name = db; + ensurePermissions."DATABASE \"${db}\"" = "ALL"; + } + ]; + }; + }; + }; +} diff --git a/modules/nixos/monitoring/dashboards/endlessh.json b/modules/nixos/monitoring/dashboards/endlessh.json new file mode 100644 index 0000000..0b47ee2 --- /dev/null +++ b/modules/nixos/monitoring/dashboards/endlessh.json @@ -0,0 +1,1457 @@ +{ + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": { + "type": "datasource", + "uid": "grafana" + }, + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "target": { + "limit": 100, + "matchAny": false, + "tags": [], + "type": "dashboard" + }, + "type": "dashboard" + } + ] + }, + "description": "", + "editable": true, + "fiscalYearStartMonth": 0, + "gnetId": 15156, + "graphTooltip": 0, + "links": [], + "liveNow": false, + "panels": [ + { + "datasource": { + "type": "datasource", + "uid": "-- Dashboard --" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 3, + "w": 4, + "x": 0, + "y": 0 + }, + "id": 36, + "options": { + "colorMode": "value", + "graphMode": "none", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "/^Total number connections that endlessh trapped$/", + "values": false + }, + "text": {}, + "textMode": "auto" + }, + "pluginVersion": "9.1.2", + "targets": [ + { + "datasource": { + "type": "datasource", + "uid": "-- Dashboard --" + }, + "panelId": 49, + "refId": "A" + } + ], + "title": "Connections", + "transformations": [ + { + "id": "filterByRefId", + "options": { + "include": "Seen" + } + }, + { + "id": "filterByValue", + "options": { + "filters": [ + { + "config": { + "id": "greaterOrEqual", + "options": { + "value": 0 + } + }, + "fieldName": "Value #Seen" + } + ], + "match": "any", + "type": "include" + } + }, + { + "id": "groupBy", + "options": { + "fields": { + "Value #Seen": { + "aggregations": [ + "sum" + ], + "operation": "aggregate" + }, + "ip": { + "aggregations": [], + "operation": "groupby" + } + } + } + }, + { + "id": "reduce", + "options": { + "includeTimeField": false, + "labelsToFields": false, + "mode": "reduceFields", + "reducers": [ + "sum" + ] + } + }, + { + "id": "organize", + "options": { + "excludeByName": {}, + "indexByName": {}, + "renameByName": { + "Value #Seen (sum)": "Total number connections that endlessh trapped" + } + } + } + ], + "type": "stat" + }, + { + "datasource": { + "type": "datasource", + "uid": "-- Dashboard --" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + }, + "unit": "s" + }, + "overrides": [] + }, + "gridPos": { + "h": 3, + "w": 4, + "x": 4, + "y": 0 + }, + "id": 42, + "options": { + "colorMode": "value", + "graphMode": "none", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "/^Time spent on endlessh$/", + "values": false + }, + "text": {}, + "textMode": "auto" + }, + "pluginVersion": "9.1.2", + "targets": [ + { + "datasource": { + "type": "datasource", + "uid": "-- Dashboard --" + }, + "panelId": 49, + "refId": "A" + } + ], + "title": "Trapped Time", + "transformations": [ + { + "id": "filterByRefId", + "options": { + "include": "Trapped" + } + }, + { + "id": "filterByValue", + "options": { + "filters": [ + { + "config": { + "id": "greaterOrEqual", + "options": { + "value": 0 + } + }, + "fieldName": "Value #Trapped" + } + ], + "match": "all", + "type": "include" + } + }, + { + "id": "groupBy", + "options": { + "fields": { + "Value #Trapped": { + "aggregations": [ + "sum" + ], + "operation": "aggregate" + }, + "ip": { + "aggregations": [], + "operation": "groupby" + } + } + } + }, + { + "id": "reduce", + "options": { + "includeTimeField": false, + "labelsToFields": false, + "mode": "reduceFields", + "reducers": [ + "sum" + ] + } + }, + { + "id": "organize", + "options": { + "excludeByName": {}, + "indexByName": {}, + "renameByName": { + "Total": "Seconds spent on endlessh", + "Value #Trapped (sum)": "Time spent on endlessh" + } + } + } + ], + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + }, + "unit": "bytes" + }, + "overrides": [] + }, + "gridPos": { + "h": 3, + "w": 4, + "x": 8, + "y": 0 + }, + "id": 18, + "options": { + "colorMode": "value", + "graphMode": "none", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "text": {}, + "textMode": "auto" + }, + "pluginVersion": "9.1.2", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "exemplar": true, + "expr": "sum(increase(endlessh_sent_bytes_total{instance=~\"$host\",job=~\"$job\"}[$__range]))", + "hide": false, + "interval": "", + "legendFormat": "Bytes sent by endlessh", + "refId": "sent_bytes" + } + ], + "title": "Sent Bytes", + "type": "stat" + }, + { + "datasource": { + "type": "datasource", + "uid": "-- Dashboard --" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 3, + "w": 4, + "x": 12, + "y": 0 + }, + "id": 38, + "options": { + "colorMode": "value", + "graphMode": "none", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "/^Unique IPs connected$/", + "values": false + }, + "text": {}, + "textMode": "auto" + }, + "pluginVersion": "9.1.2", + "targets": [ + { + "datasource": { + "type": "datasource", + "uid": "-- Dashboard --" + }, + "panelId": 49, + "refId": "A" + } + ], + "title": "Unique IPs", + "transformations": [ + { + "id": "filterByRefId", + "options": { + "include": "Seen" + } + }, + { + "id": "groupBy", + "options": { + "fields": { + "Value #Seen": { + "aggregations": [ + "sum" + ], + "operation": "aggregate" + }, + "ip": { + "aggregations": [], + "operation": "groupby" + } + } + } + }, + { + "id": "reduce", + "options": { + "includeTimeField": false, + "labelsToFields": false, + "mode": "reduceFields", + "reducers": [ + "count" + ] + } + }, + { + "id": "organize", + "options": { + "excludeByName": {}, + "indexByName": {}, + "renameByName": { + "Value #Seen (sum)": "Unique IPs connected", + "ip": "" + } + } + } + ], + "type": "stat" + }, + { + "datasource": { + "type": "datasource", + "uid": "-- Dashboard --" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "links": [ + { + "targetBlank": true, + "title": "whois", + "url": "https://search.arin.net/rdap/?query=${__value.text}" + } + ], + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 3, + "w": 4, + "x": 16, + "y": 0 + }, + "id": 45, + "options": { + "colorMode": "value", + "graphMode": "none", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "/^Client IP of the latest connection$/", + "values": false + }, + "text": {}, + "textMode": "value" + }, + "pluginVersion": "9.1.2", + "targets": [ + { + "datasource": { + "type": "datasource", + "uid": "-- Dashboard --" + }, + "panelId": 49, + "refId": "A" + } + ], + "title": "Latest Connection", + "transformations": [ + { + "id": "filterByRefId", + "options": { + "include": "Seen" + } + }, + { + "id": "filterByValue", + "options": { + "filters": [ + { + "config": { + "id": "greaterOrEqual", + "options": { + "value": 0 + } + }, + "fieldName": "Value #Seen" + } + ], + "match": "any", + "type": "include" + } + }, + { + "id": "groupBy", + "options": { + "fields": { + "Time": { + "aggregations": [ + "lastNotNull" + ], + "operation": "aggregate" + }, + "Value #Seen": { + "aggregations": [ + "sum" + ], + "operation": "aggregate" + }, + "ip": { + "aggregations": [], + "operation": "groupby" + } + } + } + }, + { + "id": "sortBy", + "options": { + "fields": {}, + "sort": [ + { + "field": "Time (lastNotNull)" + } + ] + } + }, + { + "id": "organize", + "options": { + "excludeByName": { + "instance": true, + "job": true + }, + "indexByName": {}, + "renameByName": { + "Time (lastNotNull)": "Time", + "Value #Seen (sum)": "Count", + "ip": "Client IP of the latest connection" + } + } + } + ], + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "#EAB839", + "value": 10 + }, + { + "color": "red", + "value": 20 + } + ] + }, + "unit": "short" + }, + "overrides": [] + }, + "gridPos": { + "h": 3, + "w": 4, + "x": 20, + "y": 0 + }, + "id": 20, + "options": { + "colorMode": "value", + "graphMode": "none", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "text": {}, + "textMode": "auto" + }, + "pluginVersion": "9.1.2", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "exemplar": true, + "expr": "sum((endlessh_client_open_count_total{instance=~\"$host\",job=~\"$job\"}) - (endlessh_client_closed_count_total{instance=~\"$host\",job=~\"$job\"} offset $__interval or endlessh_client_open_count_total{instance=~\"$host\",job=~\"$job\"} * 0))", + "instant": false, + "interval": "", + "legendFormat": "Open Connections", + "refId": "current_open" + } + ], + "title": "Current Connections", + "type": "stat" + }, + { + "datasource": { + "type": "datasource", + "uid": "-- Dashboard --" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "stepAfter", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "min": -0.01, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 4, + "w": 12, + "x": 0, + "y": 3 + }, + "id": 30, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": false + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "datasource", + "uid": "-- Dashboard --" + }, + "panelId": 20, + "refId": "A" + } + ], + "title": "Concurrent Connections", + "type": "timeseries" + }, + { + "datasource": { + "type": "datasource", + "uid": "-- Dashboard --" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + } + }, + "mappings": [] + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 3 + }, + "id": 32, + "options": { + "displayLabels": [], + "legend": { + "displayMode": "table", + "placement": "right", + "showLegend": true, + "values": [ + "value", + "percent" + ] + }, + "pieType": "pie", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "datasource", + "uid": "-- Dashboard --" + }, + "panelId": 49, + "refId": "A" + } + ], + "title": "Connections by country", + "transformations": [ + { + "id": "filterByRefId", + "options": { + "include": "Seen" + } + }, + { + "id": "filterByValue", + "options": { + "filters": [ + { + "config": { + "id": "greaterOrEqual", + "options": { + "value": 0 + } + }, + "fieldName": "Value #Seen" + } + ], + "match": "any", + "type": "include" + } + }, + { + "id": "groupBy", + "options": { + "fields": { + "Value #Seen": { + "aggregations": [ + "sum" + ], + "operation": "aggregate" + }, + "country": { + "aggregations": [ + "last" + ], + "operation": "aggregate" + }, + "ip": { + "aggregations": [], + "operation": "groupby" + } + } + } + }, + { + "id": "groupBy", + "options": { + "fields": { + "Value #Seen (sum)": { + "aggregations": [ + "sum" + ], + "operation": "aggregate" + }, + "country (last)": { + "aggregations": [], + "operation": "groupby" + } + } + } + }, + { + "id": "rowsToFields", + "options": {} + } + ], + "type": "piechart" + }, + { + "datasource": { + "type": "datasource", + "uid": "-- Dashboard --" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "continuous-GrYlRd" + }, + "custom": { + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "#96D98D", + "value": null + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 12, + "w": 12, + "x": 0, + "y": 7 + }, + "id": 48, + "links": [], + "options": { + "basemap": { + "config": {}, + "name": "Layer 0", + "type": "default" + }, + "controls": { + "mouseWheelZoom": false, + "showAttribution": false, + "showDebug": false, + "showScale": false, + "showZoom": true + }, + "layers": [ + { + "config": { + "color": { + "field": "Connections", + "fixed": "dark-green" + }, + "fillOpacity": 0.4, + "shape": "circle", + "showLegend": false, + "size": { + "field": "Connections", + "fixed": 5, + "max": 10, + "min": 2 + }, + "style": { + "color": { + "field": "Connections", + "fixed": "dark-green" + }, + "size": { + "field": "Connections", + "fixed": 5, + "max": 9, + "min": 2 + }, + "text": { + "field": "location (lastNotNull) (lastNotNull)", + "fixed": "", + "mode": "fixed" + } + } + }, + "location": { + "geohash": "Geohash", + "mode": "geohash" + }, + "name": "Layer 1", + "type": "markers" + } + ], + "tooltip": { + "mode": "details" + }, + "view": { + "id": "zero", + "lat": 0, + "lon": 0, + "zoom": 1 + } + }, + "pluginVersion": "9.1.2", + "targets": [ + { + "datasource": { + "type": "datasource", + "uid": "-- Dashboard --" + }, + "panelId": 49, + "refId": "A" + } + ], + "title": "Locations", + "transformations": [ + { + "id": "filterByRefId", + "options": { + "include": "Seen" + } + }, + { + "id": "filterByValue", + "options": { + "filters": [ + { + "config": { + "id": "greaterOrEqual", + "options": { + "value": 0 + } + }, + "fieldName": "Value #Seen" + } + ], + "match": "any", + "type": "include" + } + }, + { + "id": "groupBy", + "options": { + "fields": { + "Value #Seen": { + "aggregations": [ + "sum" + ], + "operation": "aggregate" + }, + "geohash": { + "aggregations": [ + "lastNotNull" + ], + "operation": "groupby" + }, + "location": { + "aggregations": [ + "lastNotNull" + ], + "operation": "aggregate" + } + } + } + }, + { + "id": "organize", + "options": { + "excludeByName": {}, + "indexByName": { + "Value #geo (lastNotNull) (sum)": 2, + "geohash (lastNotNull)": 0, + "location (lastNotNull) (lastNotNull)": 1 + }, + "renameByName": { + "Value #Seen (sum)": "Connections", + "geohash": "Geohash", + "location (lastNotNull)": "Location" + } + } + } + ], + "type": "geomap" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "custom": { + "align": "auto", + "displayMode": "auto", + "filterable": true, + "inspect": false, + "minWidth": 50 + }, + "links": [], + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + } + }, + "overrides": [ + { + "matcher": { + "id": "byName", + "options": "Last Connection" + }, + "properties": [ + { + "id": "custom.minWidth", + "value": 150 + }, + { + "id": "unit", + "value": "dateTimeAsIso" + }, + { + "id": "custom.align", + "value": "left" + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "IP" + }, + "properties": [ + { + "id": "links", + "value": [ + { + "targetBlank": true, + "title": "search ARIN", + "url": "https://search.arin.net/rdap/?query=${__data.fields.IP}" + } + ] + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "Trapped Time" + }, + "properties": [ + { + "id": "unit", + "value": "s" + } + ] + } + ] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 11 + }, + "id": 49, + "options": { + "footer": { + "fields": "", + "reducer": [ + "sum" + ], + "show": false + }, + "frameIndex": 0, + "showHeader": true, + "sortBy": [ + { + "desc": true, + "displayName": "Last Connection" + } + ] + }, + "pluginVersion": "9.1.2", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "exemplar": true, + "expr": "(endlessh_client_open_count{instance=~\"$host\",job=~\"$job\"} - endlessh_client_open_count{instance=~\"$host\",job=~\"$job\"} offset $__interval) > 0 or (endlessh_client_open_count{instance=~\"$host\",job=~\"$job\"}!=0 unless endlessh_client_open_count{instance=~\"$host\",job=~\"$job\"} offset $__interval)", + "format": "table", + "hide": false, + "interval": "", + "legendFormat": "Seen {{ip}}", + "refId": "Seen" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "exemplar": true, + "expr": "(endlessh_client_trapped_time_seconds{instance=~\"$host\",job=~\"$job\"} - endlessh_client_trapped_time_seconds{instance=~\"$host\",job=~\"$job\"} offset $__interval) > 0 or (endlessh_client_trapped_time_seconds{instance=~\"$host\",job=~\"$job\"}!=0 unless endlessh_client_trapped_time_seconds{instance=~\"$host\",job=~\"$job\"} offset $__interval)", + "format": "table", + "hide": false, + "interval": "", + "legendFormat": "Trapped {{ip}}", + "refId": "Trapped" + } + ], + "title": "Clients", + "transformations": [ + { + "id": "filterByValue", + "options": { + "filters": [ + { + "config": { + "id": "greaterOrEqual", + "options": { + "value": 0 + } + }, + "fieldName": "Value #Seen" + }, + { + "config": { + "id": "greaterOrEqual", + "options": { + "value": 0 + } + }, + "fieldName": "Value #Trapped" + } + ], + "match": "any", + "type": "include" + } + }, + { + "id": "merge", + "options": {} + }, + { + "id": "calculateField", + "options": { + "alias": "Seen", + "mode": "reduceRow", + "reduce": { + "include": [ + "Value #Seen" + ], + "reducer": "sum" + } + } + }, + { + "id": "calculateField", + "options": { + "alias": "Trapped", + "mode": "reduceRow", + "reduce": { + "include": [ + "Value #Trapped" + ], + "reducer": "sum" + } + } + }, + { + "id": "filterByValue", + "options": { + "filters": [ + { + "config": { + "id": "greaterOrEqual", + "options": { + "value": 0 + } + }, + "fieldName": "Seen" + }, + { + "config": { + "id": "greaterOrEqual", + "options": { + "value": 0 + } + }, + "fieldName": "Trapped" + } + ], + "match": "all", + "type": "include" + } + }, + { + "id": "groupBy", + "options": { + "fields": { + "Seen": { + "aggregations": [ + "sum" + ], + "operation": "aggregate" + }, + "Time": { + "aggregations": [ + "max" + ], + "operation": "aggregate" + }, + "Trapped": { + "aggregations": [ + "sum" + ], + "operation": "aggregate" + }, + "country": { + "aggregations": [ + "lastNotNull" + ], + "operation": "aggregate" + }, + "ip": { + "aggregations": [], + "operation": "groupby" + } + } + } + }, + { + "id": "filterByValue", + "options": { + "filters": [ + { + "config": { + "id": "isNull", + "options": {} + }, + "fieldName": "ip" + } + ], + "match": "any", + "type": "exclude" + } + }, + { + "id": "organize", + "options": { + "excludeByName": {}, + "indexByName": { + "Seen (sum)": 3, + "Time (max)": 0, + "Trapped (sum)": 4, + "country (lastNotNull)": 2, + "ip": 1 + }, + "renameByName": { + "Seen (sum)": "Connections", + "Time (max)": "Last Connection", + "Trapped (sum)": "Trapped Time", + "country (lastNotNull)": "Country", + "ip": "IP" + } + } + } + ], + "type": "table" + } + ], + "refresh": "", + "schemaVersion": 37, + "style": "dark", + "tags": [], + "templating": { + "list": [ + { + "current": { + "selected": false, + "text": "default", + "value": "default" + }, + "hide": 0, + "includeAll": false, + "label": "datasource", + "multi": false, + "name": "DS_PROMETHEUS", + "options": [], + "query": "prometheus", + "refresh": 1, + "regex": "", + "skipUrlSync": false, + "type": "datasource" + }, + { + "allValue": ".*", + "current": { + "selected": false, + "text": "All", + "value": "$__all" + }, + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "definition": "label_values(endlessh_client_open_count_total, job)", + "hide": 0, + "includeAll": true, + "label": "Job", + "multi": true, + "name": "job", + "options": [], + "query": { + "query": "label_values(endlessh_client_open_count_total, job)", + "refId": "StandardVariableQuery" + }, + "refresh": 1, + "regex": "", + "skipUrlSync": false, + "sort": 1, + "type": "query" + }, + { + "allValue": ".*", + "current": { + "selected": false, + "text": "All", + "value": "$__all" + }, + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "definition": "label_values(endlessh_client_open_count_total{job=~\"$job\"}, instance)", + "hide": 0, + "includeAll": true, + "label": "Host", + "multi": true, + "name": "host", + "options": [], + "query": { + "query": "label_values(endlessh_client_open_count_total{job=~\"$job\"}, instance)", + "refId": "StandardVariableQuery" + }, + "refresh": 1, + "regex": "", + "skipUrlSync": false, + "sort": 1, + "type": "query" + } + ] + }, + "time": { + "from": "now-24h", + "to": "now" + }, + "timepicker": {}, + "timezone": "", + "title": "Endlessh", + "uid": "ATIxYkO7k", + "version": 1, + "weekStart": "" +} \ No newline at end of file diff --git a/modules/nixos/monitoring/dashboards/nginx.json b/modules/nixos/monitoring/dashboards/nginx.json new file mode 100644 index 0000000..b2cc499 --- /dev/null +++ b/modules/nixos/monitoring/dashboards/nginx.json @@ -0,0 +1,567 @@ +{ + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": { + "type": "datasource", + "uid": "grafana" + }, + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "target": { + "limit": 100, + "matchAny": false, + "tags": [], + "type": "dashboard" + }, + "type": "dashboard" + } + ] + }, + "description": "", + "editable": true, + "fiscalYearStartMonth": 0, + "graphTooltip": 0, + "id": 31, + "links": [], + "liveNow": false, + "panels": [ + { + "collapsed": false, + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 0 + }, + "id": 4, + "panels": [], + "targets": [ + { + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "refId": "A" + } + ], + "title": "Status", + "type": "row" + }, + { + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [ + { + "options": { + "0": { + "text": "Down" + }, + "1": { + "text": "Up" + } + }, + "type": "value" + } + ], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "#E02F44", + "value": null + }, + { + "color": "#FF9830", + "value": 1 + }, + { + "color": "#299c46", + "value": 1 + } + ] + }, + "unit": "none" + }, + "overrides": [] + }, + "gridPos": { + "h": 3, + "w": 12, + "x": 0, + "y": 1 + }, + "id": 8, + "links": [], + "maxDataPoints": 100, + "options": { + "colorMode": "background", + "graphMode": "none", + "justifyMode": "auto", + "orientation": "horizontal", + "reduceOptions": { + "calcs": ["lastNotNull"], + "fields": "", + "values": false + }, + "textMode": "auto" + }, + "pluginVersion": "9.1.0", + "repeat": "instance", + "repeatDirection": "h", + "targets": [ + { + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "expr": "nginx_up{instance=~\"$instance\"}", + "format": "time_series", + "instant": false, + "intervalFactor": 1, + "refId": "A" + } + ], + "title": "NGINX Status for $instance", + "type": "stat" + }, + { + "collapsed": false, + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 4 + }, + "id": 6, + "panels": [], + "targets": [ + { + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "refId": "A" + } + ], + "title": "Metrics", + "type": "row" + }, + { + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "Connections (rate)", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 10, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "decimals": 1, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "short" + }, + "overrides": [] + }, + "gridPos": { + "h": 10, + "w": 12, + "x": 0, + "y": 5 + }, + "id": 10, + "links": [], + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "pluginVersion": "9.1.0", + "targets": [ + { + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "expr": "irate(nginx_connections_accepted{instance=~\"$instance\"}[$__range])", + "format": "time_series", + "instant": false, + "intervalFactor": 1, + "legendFormat": "{{instance}} accepted", + "refId": "A" + }, + { + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "expr": "irate(nginx_connections_handled{instance=~\"$instance\"}[$__range])", + "format": "time_series", + "instant": false, + "intervalFactor": 1, + "legendFormat": "{{instance}} handled", + "refId": "B" + } + ], + "title": "Processed connections", + "type": "timeseries" + }, + { + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "Connections", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 10, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "decimals": 0, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "short" + }, + "overrides": [] + }, + "gridPos": { + "h": 10, + "w": 12, + "x": 12, + "y": 5 + }, + "id": 12, + "links": [], + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "pluginVersion": "9.1.0", + "targets": [ + { + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "expr": "nginx_connections_active{instance=~\"$instance\"}", + "format": "time_series", + "intervalFactor": 1, + "legendFormat": "{{instance}} active", + "refId": "A" + }, + { + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "expr": "nginx_connections_reading{instance=~\"$instance\"}", + "format": "time_series", + "intervalFactor": 1, + "legendFormat": "{{instance}} reading", + "refId": "B" + }, + { + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "expr": "nginx_connections_waiting{instance=~\"$instance\"}", + "format": "time_series", + "intervalFactor": 1, + "legendFormat": "{{instance}} waiting", + "refId": "C" + }, + { + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "expr": "nginx_connections_writing{instance=~\"$instance\"}", + "format": "time_series", + "intervalFactor": 1, + "legendFormat": "{{instance}} writing", + "refId": "D" + } + ], + "title": "Active Connections", + "type": "timeseries" + }, + { + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 10, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "short" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 24, + "x": 0, + "y": 15 + }, + "id": 15, + "links": [], + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "pluginVersion": "9.1.0", + "targets": [ + { + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "expr": "irate(nginx_http_requests_total{instance=~\"$instance\"}[$__range])", + "format": "time_series", + "intervalFactor": 1, + "legendFormat": "{{instance}} total requests", + "refId": "A" + } + ], + "title": "Total requests", + "type": "timeseries" + } + ], + "refresh": "5s", + "schemaVersion": 37, + "style": "dark", + "tags": [], + "templating": { + "list": [ + { + "current": { + "selected": false, + "text": "default", + "value": "default" + }, + "hide": 0, + "includeAll": false, + "label": "datasource", + "multi": false, + "name": "DS_PROMETHEUS", + "options": [], + "query": "prometheus", + "refresh": 1, + "regex": "", + "skipUrlSync": false, + "type": "datasource" + }, + { + "current": { + "selected": false, + "text": "All", + "value": "$__all" + }, + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "definition": "label_values(nginx_up, instance)", + "hide": 0, + "includeAll": true, + "label": "", + "multi": true, + "name": "instance", + "options": [], + "query": { + "query": "label_values(nginx_up, instance)", + "refId": "StandardVariableQuery" + }, + "refresh": 1, + "regex": "", + "skipUrlSync": false, + "sort": 0, + "tagValuesQuery": "", + "tagsQuery": "", + "type": "query", + "useTags": false + } + ] + }, + "time": { + "from": "now-1h", + "to": "now" + }, + "timepicker": { + "refresh_intervals": [ + "5s", + "10s", + "30s", + "1m", + "5m", + "15m", + "30m", + "1h", + "2h", + "1d" + ], + "time_options": ["5m", "15m", "1h", "6h", "12h", "24h", "2d", "7d", "30d"] + }, + "timezone": "", + "title": "NGINX", + "uid": "Yoeroupho", + "version": 1, + "weekStart": "" +} diff --git a/modules/nixos/monitoring/dashboards/postgresql.json b/modules/nixos/monitoring/dashboards/postgresql.json new file mode 100644 index 0000000..4e533f7 --- /dev/null +++ b/modules/nixos/monitoring/dashboards/postgresql.json @@ -0,0 +1,3086 @@ +{ + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": { + "type": "datasource", + "uid": "grafana" + }, + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "target": { + "limit": 100, + "matchAny": false, + "tags": [], + "type": "dashboard" + }, + "type": "dashboard" + } + ] + }, + "description": "", + "editable": true, + "fiscalYearStartMonth": 0, + "graphTooltip": 0, + "id": 29, + "links": [], + "liveNow": false, + "panels": [ + { + "collapsed": false, + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 0 + }, + "id": 34, + "panels": [], + "targets": [ + { + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "refId": "A" + } + ], + "title": "General Counters, CPU, Memory and File Descriptor Stats", + "type": "row" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [ + { + "options": { + "match": "null", + "result": { + "text": "N/A" + } + }, + "type": "special" + } + ], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "none" + }, + "overrides": [] + }, + "gridPos": { + "h": 2, + "w": 4, + "x": 0, + "y": 1 + }, + "id": 36, + "links": [], + "maxDataPoints": 100, + "options": { + "colorMode": "value", + "graphMode": "none", + "justifyMode": "auto", + "orientation": "horizontal", + "reduceOptions": { + "calcs": ["mean"], + "fields": "", + "values": false + }, + "textMode": "name" + }, + "pluginVersion": "9.1.0", + "targets": [ + { + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "pg_static{instance=\"$instance\"}", + "format": "time_series", + "instant": true, + "intervalFactor": 1, + "legendFormat": "{{short_version}}", + "refId": "A" + } + ], + "title": "Version", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "description": "start time of the process", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [ + { + "options": { + "match": "null", + "result": { + "text": "N/A" + } + }, + "type": "special" + } + ], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "dateTimeFromNow" + }, + "overrides": [] + }, + "gridPos": { + "h": 2, + "w": 4, + "x": 4, + "y": 1 + }, + "id": 28, + "links": [], + "maxDataPoints": 100, + "options": { + "colorMode": "none", + "graphMode": "none", + "justifyMode": "auto", + "orientation": "horizontal", + "reduceOptions": { + "calcs": ["mean"], + "fields": "", + "values": false + }, + "textMode": "auto" + }, + "pluginVersion": "9.1.0", + "targets": [ + { + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "pg_postmaster_start_time_seconds{instance=\"$instance\"} * 1000", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "", + "range": true, + "refId": "A" + } + ], + "title": "Start Time", + "type": "stat" + }, + { + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [ + { + "options": { + "match": "null", + "result": { + "text": "N/A" + } + }, + "type": "special" + } + ], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "decbytes" + }, + "overrides": [] + }, + "gridPos": { + "h": 2, + "w": 4, + "x": 8, + "y": 1 + }, + "id": 10, + "links": [], + "maxDataPoints": 100, + "options": { + "colorMode": "none", + "graphMode": "none", + "justifyMode": "auto", + "orientation": "horizontal", + "reduceOptions": { + "calcs": ["lastNotNull"], + "fields": "", + "values": false + }, + "textMode": "auto" + }, + "pluginVersion": "9.1.0", + "targets": [ + { + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "expr": "SUM(pg_stat_database_tup_fetched{datname=~\"$datname\", instance=~\"$instance\"})", + "format": "time_series", + "intervalFactor": 2, + "refId": "A", + "step": 4 + } + ], + "title": "Current fetch data", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [ + { + "options": { + "match": "null", + "result": { + "text": "N/A" + } + }, + "type": "special" + } + ], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "decbytes" + }, + "overrides": [] + }, + "gridPos": { + "h": 2, + "w": 4, + "x": 12, + "y": 1 + }, + "id": 11, + "links": [], + "maxDataPoints": 100, + "options": { + "colorMode": "none", + "graphMode": "none", + "justifyMode": "auto", + "orientation": "horizontal", + "reduceOptions": { + "calcs": ["lastNotNull"], + "fields": "", + "values": false + }, + "textMode": "auto" + }, + "pluginVersion": "9.1.0", + "targets": [ + { + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "SUM(pg_stat_database_tup_inserted{datname=~\"$datname\", instance=~\"$instance\"})", + "format": "time_series", + "intervalFactor": 2, + "range": true, + "refId": "A", + "step": 4 + } + ], + "title": "Current insert data", + "type": "stat" + }, + { + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [ + { + "options": { + "match": "null", + "result": { + "text": "N/A" + } + }, + "type": "special" + } + ], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "decbytes" + }, + "overrides": [] + }, + "gridPos": { + "h": 2, + "w": 4, + "x": 16, + "y": 1 + }, + "id": 12, + "links": [], + "maxDataPoints": 100, + "options": { + "colorMode": "none", + "graphMode": "none", + "justifyMode": "auto", + "orientation": "horizontal", + "reduceOptions": { + "calcs": ["lastNotNull"], + "fields": "", + "values": false + }, + "textMode": "auto" + }, + "pluginVersion": "9.1.0", + "targets": [ + { + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "expr": "SUM(pg_stat_database_tup_updated{datname=~\"$datname\", instance=~\"$instance\"})", + "format": "time_series", + "intervalFactor": 2, + "refId": "A", + "step": 4 + } + ], + "title": "Current update data", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [ + { + "options": { + "match": "null", + "result": { + "text": "N/A" + } + }, + "type": "special" + } + ], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "none" + }, + "overrides": [] + }, + "gridPos": { + "h": 2, + "w": 4, + "x": 20, + "y": 1 + }, + "id": 38, + "links": [], + "maxDataPoints": 100, + "options": { + "colorMode": "none", + "graphMode": "none", + "justifyMode": "auto", + "orientation": "horizontal", + "reduceOptions": { + "calcs": ["mean"], + "fields": "", + "values": false + }, + "textMode": "auto" + }, + "pluginVersion": "9.1.0", + "targets": [ + { + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "pg_settings_max_connections{instance=\"$instance\"}", + "format": "time_series", + "intervalFactor": 1, + "range": true, + "refId": "A" + } + ], + "title": "Max Connections", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "description": "Average user and system CPU time spent in seconds.", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 10, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "links": [], + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "s" + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 8, + "x": 0, + "y": 3 + }, + "id": 22, + "links": [], + "options": { + "legend": { + "calcs": ["mean", "lastNotNull", "max", "min"], + "displayMode": "table", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "pluginVersion": "9.1.0", + "targets": [ + { + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "avg(rate(process_cpu_seconds_total{instance=\"$instance\"}[$__range]) * 1000)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "CPU Time", + "range": true, + "refId": "A" + } + ], + "title": "Average CPU Usage", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "description": "Virtual and Resident memory size in bytes, averages over 5 min interval", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 10, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "links": [], + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "decbytes" + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 8, + "x": 8, + "y": 3 + }, + "id": 24, + "links": [], + "options": { + "legend": { + "calcs": ["mean", "lastNotNull", "max", "min"], + "displayMode": "table", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "pluginVersion": "9.1.0", + "targets": [ + { + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "avg(rate(process_resident_memory_bytes{instance=\"$instance\"}[$__range]))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "Resident Mem", + "range": true, + "refId": "A" + }, + { + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "avg(rate(process_virtual_memory_bytes{instance=\"$instance\"}[$__range]))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "Virtual Mem", + "range": true, + "refId": "B" + } + ], + "title": "Average Memory Usage", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "description": "Number of open file descriptors", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 10, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "links": [], + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "short" + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 8, + "x": 16, + "y": 3 + }, + "id": 26, + "links": [], + "options": { + "legend": { + "calcs": ["mean", "lastNotNull", "max", "min"], + "displayMode": "table", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "pluginVersion": "9.1.0", + "targets": [ + { + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "process_open_fds{instance=\"$instance\"}", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "Open FD", + "range": true, + "refId": "A" + } + ], + "title": "Open File Descriptors", + "type": "timeseries" + }, + { + "collapsed": false, + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 10 + }, + "id": 32, + "panels": [], + "targets": [ + { + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "refId": "A" + } + ], + "title": "Settings", + "type": "row" + }, + { + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [ + { + "options": { + "match": "null", + "result": { + "text": "N/A" + } + }, + "type": "special" + } + ], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "bytes" + }, + "overrides": [] + }, + "gridPos": { + "h": 3, + "w": 3, + "x": 0, + "y": 11 + }, + "id": 40, + "links": [], + "maxDataPoints": 100, + "options": { + "colorMode": "none", + "graphMode": "none", + "justifyMode": "auto", + "orientation": "horizontal", + "reduceOptions": { + "calcs": ["lastNotNull"], + "fields": "", + "values": false + }, + "textMode": "auto" + }, + "pluginVersion": "9.1.0", + "targets": [ + { + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "expr": "pg_settings_shared_buffers_bytes{instance=\"$instance\"}", + "format": "time_series", + "intervalFactor": 1, + "refId": "A" + } + ], + "title": "Shared Buffers", + "type": "stat" + }, + { + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [ + { + "options": { + "match": "null", + "result": { + "text": "N/A" + } + }, + "type": "special" + } + ], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "bytes" + }, + "overrides": [] + }, + "gridPos": { + "h": 3, + "w": 3, + "x": 3, + "y": 11 + }, + "id": 42, + "links": [], + "maxDataPoints": 100, + "options": { + "colorMode": "none", + "graphMode": "none", + "justifyMode": "auto", + "orientation": "horizontal", + "reduceOptions": { + "calcs": ["lastNotNull"], + "fields": "", + "values": false + }, + "textMode": "auto" + }, + "pluginVersion": "9.1.0", + "targets": [ + { + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "expr": "pg_settings_effective_cache_size_bytes{instance=\"$instance\"}", + "format": "time_series", + "intervalFactor": 1, + "refId": "A" + } + ], + "title": "Effective Cache", + "type": "stat" + }, + { + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [ + { + "options": { + "match": "null", + "result": { + "text": "N/A" + } + }, + "type": "special" + } + ], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "bytes" + }, + "overrides": [] + }, + "gridPos": { + "h": 3, + "w": 3, + "x": 6, + "y": 11 + }, + "id": 44, + "links": [], + "maxDataPoints": 100, + "options": { + "colorMode": "none", + "graphMode": "none", + "justifyMode": "auto", + "orientation": "horizontal", + "reduceOptions": { + "calcs": ["lastNotNull"], + "fields": "", + "values": false + }, + "textMode": "auto" + }, + "pluginVersion": "9.1.0", + "targets": [ + { + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "expr": "pg_settings_maintenance_work_mem_bytes{instance=\"$instance\"}", + "format": "time_series", + "intervalFactor": 1, + "refId": "A" + } + ], + "title": "Maintenance Work Mem", + "type": "stat" + }, + { + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [ + { + "options": { + "match": "null", + "result": { + "text": "N/A" + } + }, + "type": "special" + } + ], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "bytes" + }, + "overrides": [] + }, + "gridPos": { + "h": 3, + "w": 3, + "x": 9, + "y": 11 + }, + "id": 46, + "links": [], + "maxDataPoints": 100, + "options": { + "colorMode": "none", + "graphMode": "none", + "justifyMode": "auto", + "orientation": "horizontal", + "reduceOptions": { + "calcs": ["lastNotNull"], + "fields": "", + "values": false + }, + "textMode": "auto" + }, + "pluginVersion": "9.1.0", + "targets": [ + { + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "expr": "pg_settings_work_mem_bytes{instance=\"$instance\"}", + "format": "time_series", + "intervalFactor": 1, + "legendFormat": "", + "refId": "A" + } + ], + "title": "Work Mem", + "type": "stat" + }, + { + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "decimals": 1, + "mappings": [ + { + "options": { + "match": "null", + "result": { + "text": "N/A" + } + }, + "type": "special" + } + ], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "bytes" + }, + "overrides": [] + }, + "gridPos": { + "h": 3, + "w": 3, + "x": 12, + "y": 11 + }, + "id": 48, + "links": [], + "maxDataPoints": 100, + "options": { + "colorMode": "none", + "graphMode": "none", + "justifyMode": "auto", + "orientation": "horizontal", + "reduceOptions": { + "calcs": ["lastNotNull"], + "fields": "", + "values": false + }, + "textMode": "auto" + }, + "pluginVersion": "9.1.0", + "targets": [ + { + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "expr": "pg_settings_max_wal_size_bytes{instance=\"$instance\"}", + "format": "time_series", + "intervalFactor": 1, + "refId": "A" + } + ], + "title": "Max WAL Size", + "type": "stat" + }, + { + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [ + { + "options": { + "match": "null", + "result": { + "text": "N/A" + } + }, + "type": "special" + } + ], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "none" + }, + "overrides": [] + }, + "gridPos": { + "h": 3, + "w": 3, + "x": 15, + "y": 11 + }, + "id": 50, + "links": [], + "maxDataPoints": 100, + "options": { + "colorMode": "none", + "graphMode": "none", + "justifyMode": "auto", + "orientation": "horizontal", + "reduceOptions": { + "calcs": ["lastNotNull"], + "fields": "", + "values": false + }, + "textMode": "auto" + }, + "pluginVersion": "9.1.0", + "targets": [ + { + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "expr": "pg_settings_random_page_cost{instance=\"$instance\"}", + "format": "time_series", + "intervalFactor": 1, + "refId": "A" + } + ], + "title": "Random Page Cost", + "type": "stat" + }, + { + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [ + { + "options": { + "match": "null", + "result": { + "text": "N/A" + } + }, + "type": "special" + } + ], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "none" + }, + "overrides": [] + }, + "gridPos": { + "h": 3, + "w": 2, + "x": 18, + "y": 11 + }, + "id": 52, + "links": [], + "maxDataPoints": 100, + "options": { + "colorMode": "none", + "graphMode": "none", + "justifyMode": "auto", + "orientation": "horizontal", + "reduceOptions": { + "calcs": ["lastNotNull"], + "fields": "", + "values": false + }, + "textMode": "auto" + }, + "pluginVersion": "9.1.0", + "targets": [ + { + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "expr": "pg_settings_seq_page_cost", + "format": "time_series", + "intervalFactor": 1, + "refId": "A" + } + ], + "title": "Seq Page Cost", + "type": "stat" + }, + { + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [ + { + "options": { + "match": "null", + "result": { + "text": "N/A" + } + }, + "type": "special" + } + ], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "none" + }, + "overrides": [] + }, + "gridPos": { + "h": 3, + "w": 2, + "x": 20, + "y": 11 + }, + "id": 54, + "links": [], + "maxDataPoints": 100, + "options": { + "colorMode": "none", + "graphMode": "none", + "justifyMode": "auto", + "orientation": "horizontal", + "reduceOptions": { + "calcs": ["mean"], + "fields": "", + "values": false + }, + "textMode": "auto" + }, + "pluginVersion": "9.1.0", + "targets": [ + { + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "expr": "pg_settings_max_worker_processes{instance=\"$instance\"}", + "format": "time_series", + "intervalFactor": 1, + "refId": "A" + } + ], + "title": "Max Worker Processes", + "type": "stat" + }, + { + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [ + { + "options": { + "match": "null", + "result": { + "text": "N/A" + } + }, + "type": "special" + } + ], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "none" + }, + "overrides": [] + }, + "gridPos": { + "h": 3, + "w": 2, + "x": 22, + "y": 11 + }, + "id": 56, + "links": [], + "maxDataPoints": 100, + "options": { + "colorMode": "none", + "graphMode": "none", + "justifyMode": "auto", + "orientation": "horizontal", + "reduceOptions": { + "calcs": ["lastNotNull"], + "fields": "", + "values": false + }, + "textMode": "auto" + }, + "pluginVersion": "9.1.0", + "targets": [ + { + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "expr": "pg_settings_max_parallel_workers{instance=\"$instance\"}", + "format": "time_series", + "intervalFactor": 1, + "refId": "A" + } + ], + "title": "Max Parallel Workers", + "type": "stat" + }, + { + "collapsed": false, + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 14 + }, + "id": 30, + "panels": [], + "targets": [ + { + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "refId": "A" + } + ], + "title": "Database Stats", + "type": "row" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineStyle": { + "fill": "solid" + }, + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": true, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "decimals": 0, + "links": [], + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "none" + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 8, + "x": 0, + "y": 15 + }, + "id": 1, + "links": [], + "options": { + "legend": { + "calcs": ["mean", "lastNotNull", "max"], + "displayMode": "table", + "placement": "right", + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "pluginVersion": "9.1.0", + "targets": [ + { + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "pg_stat_activity_count{datname=~\"$datname\", instance=~\"$instance\", state=\"active\"} != 0", + "format": "time_series", + "interval": "", + "intervalFactor": 2, + "legendFormat": "{{datname}}, s: {{state}}", + "range": true, + "refId": "A", + "step": 2 + } + ], + "title": "Active sessions", + "type": "timeseries" + }, + { + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 10, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "links": [], + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "short" + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 8, + "x": 8, + "y": 15 + }, + "id": 60, + "links": [], + "options": { + "legend": { + "calcs": ["mean", "lastNotNull", "sum"], + "displayMode": "table", + "placement": "right", + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "pluginVersion": "9.1.0", + "targets": [ + { + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "expr": "irate(pg_stat_database_xact_commit{instance=\"$instance\", datname=~\"$datname\"}[$__range])", + "format": "time_series", + "intervalFactor": 1, + "legendFormat": "{{datname}} commits", + "refId": "A" + }, + { + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "expr": "irate(pg_stat_database_xact_rollback{instance=\"$instance\", datname=~\"$datname\"}[$__range])", + "format": "time_series", + "intervalFactor": 1, + "legendFormat": "{{datname}} rollbacks", + "refId": "B" + } + ], + "title": "Transactions", + "type": "timeseries" + }, + { + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 10, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "links": [], + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "bytes" + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 8, + "x": 16, + "y": 15 + }, + "id": 8, + "links": [], + "options": { + "legend": { + "calcs": ["mean", "lastNotNull", "sum"], + "displayMode": "table", + "placement": "right", + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "pluginVersion": "9.1.0", + "targets": [ + { + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "expr": "pg_stat_database_tup_updated{datname=~\"$datname\", instance=~\"$instance\"} != 0", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{datname}}", + "refId": "A", + "step": 2 + } + ], + "title": "Update data", + "type": "timeseries" + }, + { + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 10, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "links": [], + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "bytes" + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 8, + "x": 0, + "y": 22 + }, + "id": 5, + "links": [], + "options": { + "legend": { + "calcs": ["mean", "lastNotNull", "sum"], + "displayMode": "table", + "placement": "right", + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "pluginVersion": "9.1.0", + "targets": [ + { + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "expr": "pg_stat_database_tup_fetched{datname=~\"$datname\", instance=~\"$instance\"} != 0", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{datname}}", + "refId": "A", + "step": 2 + } + ], + "title": "Fetch data", + "type": "timeseries" + }, + { + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 10, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "links": [], + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "bytes" + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 8, + "x": 8, + "y": 22 + }, + "id": 6, + "links": [], + "options": { + "legend": { + "calcs": ["mean", "lastNotNull", "sum"], + "displayMode": "table", + "placement": "right", + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "pluginVersion": "9.1.0", + "targets": [ + { + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "expr": "pg_stat_database_tup_inserted{datname=~\"$datname\", instance=~\"$instance\"} != 0", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{datname}}", + "refId": "A", + "step": 2 + } + ], + "title": "Insert data", + "type": "timeseries" + }, + { + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 10, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "decimals": 0, + "links": [], + "mappings": [], + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "short" + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 8, + "x": 16, + "y": 22 + }, + "id": 3, + "links": [], + "options": { + "legend": { + "calcs": ["mean", "lastNotNull", "sum"], + "displayMode": "table", + "placement": "right", + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "pluginVersion": "9.1.0", + "targets": [ + { + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "expr": "pg_locks_count{datname=~\"$datname\", instance=~\"$instance\", mode=~\"$mode\"} != 0", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{datname}},{{mode}}", + "refId": "A", + "step": 2 + } + ], + "title": "Lock tables", + "type": "timeseries" + }, + { + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 10, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "links": [], + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "bytes" + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 8, + "x": 0, + "y": 29 + }, + "id": 14, + "links": [], + "options": { + "legend": { + "calcs": ["mean", "lastNotNull", "sum"], + "displayMode": "table", + "placement": "right", + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "pluginVersion": "9.1.0", + "targets": [ + { + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "expr": "pg_stat_database_tup_returned{datname=~\"$datname\", instance=~\"$instance\"} != 0", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{datname}}", + "refId": "A", + "step": 2 + } + ], + "title": "Return data", + "type": "timeseries" + }, + { + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 10, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "links": [], + "mappings": [], + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "short" + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 8, + "x": 8, + "y": 29 + }, + "id": 4, + "links": [], + "options": { + "legend": { + "calcs": ["lastNotNull", "max"], + "displayMode": "table", + "placement": "right", + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "pluginVersion": "9.1.0", + "targets": [ + { + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "expr": "pg_stat_activity_count{datname=~\"$datname\", instance=~\"$instance\", state=~\"idle|idle in transaction|idle in transaction (aborted)\"}", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{datname}}, s: {{state}}", + "refId": "A", + "step": 2 + } + ], + "title": "Idle sessions", + "type": "timeseries" + }, + { + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 10, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "links": [], + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "bytes" + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 8, + "x": 16, + "y": 29 + }, + "id": 7, + "links": [], + "options": { + "legend": { + "calcs": ["mean", "lastNotNull", "sum"], + "displayMode": "table", + "placement": "right", + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "pluginVersion": "9.1.0", + "targets": [ + { + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "expr": "pg_stat_database_tup_deleted{datname=~\"$datname\", instance=~\"$instance\"} != 0", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{datname}}", + "refId": "A", + "step": 2 + } + ], + "title": "Delete data", + "type": "timeseries" + }, + { + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 10, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "decimals": 4, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "percentunit" + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 8, + "x": 0, + "y": 36 + }, + "id": 62, + "links": [], + "options": { + "legend": { + "calcs": ["mean", "lastNotNull"], + "displayMode": "table", + "placement": "right", + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "pluginVersion": "9.1.0", + "targets": [ + { + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "expr": "pg_stat_database_blks_hit{instance=\"$instance\", datname=~\"$datname\"} / (pg_stat_database_blks_read{instance=\"$instance\", datname=~\"$datname\"} + pg_stat_database_blks_hit{instance=\"$instance\", datname=~\"$datname\"})", + "format": "time_series", + "intervalFactor": 1, + "legendFormat": "{{ datname }}", + "refId": "A" + } + ], + "title": "Cache Hit Rate", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 10, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "short" + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 8, + "x": 8, + "y": 36 + }, + "id": 64, + "links": [], + "options": { + "legend": { + "calcs": ["mean", "lastNotNull", "max", "min"], + "displayMode": "table", + "placement": "right", + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "pluginVersion": "9.1.0", + "targets": [ + { + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "irate(pg_stat_bgwriter_buffers_backend_total{instance=\"$instance\"}[$__range])", + "format": "time_series", + "intervalFactor": 1, + "legendFormat": "buffers_backend", + "range": true, + "refId": "A" + }, + { + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "irate(pg_stat_bgwriter_buffers_alloc_total{instance=\"$instance\"}[$__range])", + "format": "time_series", + "intervalFactor": 1, + "legendFormat": "buffers_alloc", + "range": true, + "refId": "B" + }, + { + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "irate(pg_stat_bgwriter_buffers_backend_fsync_total{instance=\"$instance\"}[$__range])", + "format": "time_series", + "intervalFactor": 1, + "legendFormat": "backend_fsync", + "range": true, + "refId": "C" + }, + { + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "irate(pg_stat_bgwriter_buffers_checkpoint_total{instance=\"$instance\"}[$__range])", + "format": "time_series", + "intervalFactor": 1, + "legendFormat": "buffers_checkpoint", + "range": true, + "refId": "D" + }, + { + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "irate(pg_stat_bgwriter_buffers_clean_total{instance=\"$instance\"}[$__range])", + "format": "time_series", + "intervalFactor": 1, + "legendFormat": "buffers_clean", + "range": true, + "refId": "E" + } + ], + "title": "Buffers (bgwriter)", + "type": "timeseries" + }, + { + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 10, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "short" + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 8, + "x": 16, + "y": 36 + }, + "id": 66, + "links": [], + "options": { + "legend": { + "calcs": ["mean", "lastNotNull", "sum"], + "displayMode": "table", + "placement": "right", + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "pluginVersion": "9.1.0", + "targets": [ + { + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "expr": "irate(pg_stat_database_conflicts{instance=\"$instance\", datname=~\"$datname\"}[$__range])", + "format": "time_series", + "intervalFactor": 1, + "legendFormat": "{{datname}} conflicts", + "refId": "B" + }, + { + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "expr": "irate(pg_stat_database_deadlocks{instance=\"$instance\", datname=~\"$datname\"}[$__range])", + "format": "time_series", + "intervalFactor": 1, + "legendFormat": "{{datname}} deadlocks", + "refId": "A" + } + ], + "title": "Conflicts/Deadlocks", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "description": "Total amount of data written to temporary files by queries in this database. All temporary files are counted, regardless of why the temporary file was created, and regardless of the log_temp_files setting.", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 10, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "bytes" + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 8, + "x": 0, + "y": 43 + }, + "id": 68, + "links": [], + "options": { + "legend": { + "calcs": ["mean", "lastNotNull", "sum"], + "displayMode": "table", + "placement": "right", + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "pluginVersion": "9.1.0", + "targets": [ + { + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "irate(pg_stat_database_temp_bytes{instance=\"$instance\", datname=~\"$datname\"}[$__range])", + "format": "time_series", + "intervalFactor": 1, + "legendFormat": "{{datname}}", + "range": true, + "refId": "A" + } + ], + "title": "Temp File (Bytes)", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 10, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "ms" + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 16, + "x": 8, + "y": 43 + }, + "id": 70, + "links": [], + "options": { + "legend": { + "calcs": ["mean", "lastNotNull", "max", "min"], + "displayMode": "table", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "pluginVersion": "9.1.0", + "targets": [ + { + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "irate(pg_stat_bgwriter_checkpoint_write_time_total{instance=\"$instance\"}[$__range])", + "format": "time_series", + "intervalFactor": 1, + "legendFormat": "write_time - Total amount of time that has been spent in the portion of checkpoint processing where files are written to disk.", + "range": true, + "refId": "B" + }, + { + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "irate(pg_stat_bgwriter_checkpoint_sync_time_total{instance=\"$instance\"}[$__range])", + "format": "time_series", + "intervalFactor": 1, + "legendFormat": "sync_time - Total amount of time that has been spent in the portion of checkpoint processing where files are synchronized to disk.", + "range": true, + "refId": "A" + } + ], + "title": "Checkpoint Stats", + "type": "timeseries" + } + ], + "refresh": "5s", + "schemaVersion": 37, + "style": "dark", + "tags": [], + "templating": { + "list": [ + { + "current": { + "selected": false, + "text": "Prometheus", + "value": "Prometheus" + }, + "hide": 0, + "includeAll": false, + "label": "datasource", + "multi": false, + "name": "DS_PROMETHEUS", + "options": [], + "query": "prometheus", + "refresh": 1, + "regex": "", + "skipUrlSync": false, + "type": "datasource" + }, + { + "current": { + "selected": false, + "text": "manwe:9187", + "value": "manwe:9187" + }, + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "definition": "query_result(pg_up)", + "hide": 0, + "includeAll": false, + "label": "Instance", + "multi": false, + "name": "instance", + "options": [], + "query": { + "query": "query_result(pg_up)", + "refId": "StandardVariableQuery" + }, + "refresh": 1, + "regex": "/.*instance=\"([^\"]+).*/", + "skipUrlSync": false, + "sort": 1, + "tagValuesQuery": "", + "tagsQuery": "", + "type": "query", + "useTags": false + }, + { + "current": { + "selected": false, + "text": "All", + "value": "$__all" + }, + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "definition": "label_values(datname)", + "hide": 0, + "includeAll": true, + "label": "Database", + "multi": true, + "name": "datname", + "options": [], + "query": { + "query": "label_values(datname)", + "refId": "StandardVariableQuery" + }, + "refresh": 1, + "regex": "", + "skipUrlSync": false, + "sort": 1, + "tagValuesQuery": "", + "tagsQuery": "", + "type": "query", + "useTags": false + }, + { + "current": { + "selected": false, + "text": "All", + "value": "$__all" + }, + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "definition": "label_values({mode=~\"accessexclusivelock|accesssharelock|exclusivelock|rowexclusivelock|rowsharelock|sharelock|sharerowexclusivelock|shareupdateexclusivelock\"}, mode)", + "hide": 0, + "includeAll": true, + "label": "Lock table", + "multi": true, + "name": "mode", + "options": [], + "query": { + "query": "label_values({mode=~\"accessexclusivelock|accesssharelock|exclusivelock|rowexclusivelock|rowsharelock|sharelock|sharerowexclusivelock|shareupdateexclusivelock\"}, mode)", + "refId": "StandardVariableQuery" + }, + "refresh": 1, + "regex": "", + "skipUrlSync": false, + "sort": 0, + "tagValuesQuery": "", + "tagsQuery": "", + "type": "query", + "useTags": false + } + ] + }, + "time": { + "from": "now-1h", + "to": "now" + }, + "timepicker": { + "refresh_intervals": [ + "5s", + "10s", + "30s", + "1m", + "5m", + "15m", + "30m", + "1h", + "2h", + "1d" + ], + "time_options": ["5m", "15m", "1h", "6h", "12h", "24h", "2d", "7d", "30d"] + }, + "timezone": "", + "title": "PostgreSQL", + "uid": "fiuGhahwi", + "version": 1, + "weekStart": "" +} diff --git a/modules/nixos/monitoring/dashboards/unbound.json b/modules/nixos/monitoring/dashboards/unbound.json new file mode 100644 index 0000000..8a0d503 --- /dev/null +++ b/modules/nixos/monitoring/dashboards/unbound.json @@ -0,0 +1,2991 @@ +{ + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": { + "type": "datasource", + "uid": "grafana" + }, + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "target": { + "limit": 100, + "matchAny": false, + "tags": [], + "type": "dashboard" + }, + "type": "dashboard" + } + ] + }, + "description": "", + "editable": true, + "fiscalYearStartMonth": 0, + "gnetId": 11705, + "graphTooltip": 0, + "id": 39, + "links": [], + "liveNow": false, + "panels": [ + { + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "description": "Total number of queries received at the selected time range.", + "fieldConfig": { + "defaults": { + "color": { + "fixedColor": "rgb(31, 120, 193)", + "mode": "fixed" + }, + "mappings": [ + { + "options": { + "match": "null", + "result": { + "text": "N/A" + } + }, + "type": "special" + } + ], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "none" + }, + "overrides": [] + }, + "gridPos": { + "h": 3, + "w": 4, + "x": 0, + "y": 0 + }, + "id": 24, + "links": [], + "maxDataPoints": 100, + "options": { + "colorMode": "none", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "horizontal", + "reduceOptions": { + "calcs": ["sum"], + "fields": "", + "values": false + }, + "textMode": "auto" + }, + "pluginVersion": "9.1.0", + "targets": [ + { + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "sum(increase(unbound_queries_total{instance=\"$instance\"}[$__range]))", + "range": true, + "refId": "A" + } + ], + "title": "Queries", + "type": "stat" + }, + { + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "fixedColor": "rgb(31, 120, 193)", + "mode": "fixed" + }, + "mappings": [ + { + "options": { + "match": "null", + "result": { + "text": "N/A" + } + }, + "type": "special" + } + ], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "none" + }, + "overrides": [] + }, + "gridPos": { + "h": 3, + "w": 4, + "x": 4, + "y": 0 + }, + "id": 26, + "links": [], + "maxDataPoints": 100, + "options": { + "colorMode": "none", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "horizontal", + "reduceOptions": { + "calcs": ["lastNotNull"], + "fields": "", + "values": false + }, + "textMode": "auto" + }, + "pluginVersion": "9.1.0", + "targets": [ + { + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "expr": "sum(rate(unbound_queries_total{instance=\"$instance\"}[$__range]))", + "refId": "A" + } + ], + "title": "Queries per second", + "type": "stat" + }, + { + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "description": "Total number of queries that were refused or dropped because they failed the access control settings at the selected time range.", + "fieldConfig": { + "defaults": { + "color": { + "fixedColor": "rgb(31, 120, 193)", + "mode": "fixed" + }, + "mappings": [ + { + "options": { + "match": "null", + "result": { + "text": "N/A" + } + }, + "type": "special" + } + ], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "short" + }, + "overrides": [] + }, + "gridPos": { + "h": 3, + "w": 4, + "x": 8, + "y": 0 + }, + "id": 8, + "links": [], + "maxDataPoints": 100, + "options": { + "colorMode": "none", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "horizontal", + "reduceOptions": { + "calcs": ["sum"], + "fields": "", + "values": false + }, + "textMode": "auto" + }, + "pluginVersion": "9.1.0", + "targets": [ + { + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "increase(unbound_unwanted_queries_total{instance=\"$instance\"}[$__range])", + "range": true, + "refId": "A" + } + ], + "title": "Unwanted queries", + "type": "stat" + }, + { + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "description": "Total number of replies that were unwanted or unsolicited at the selected time range.\n\nA sharp increase in unwanted traffic indicates a possible spoof run in progress.", + "fieldConfig": { + "defaults": { + "color": { + "fixedColor": "rgb(31, 120, 193)", + "mode": "fixed" + }, + "mappings": [ + { + "options": { + "match": "null", + "result": { + "text": "N/A" + } + }, + "type": "special" + } + ], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "short" + }, + "overrides": [] + }, + "gridPos": { + "h": 3, + "w": 4, + "x": 12, + "y": 0 + }, + "id": 49, + "links": [], + "maxDataPoints": 100, + "options": { + "colorMode": "none", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "horizontal", + "reduceOptions": { + "calcs": ["sum"], + "fields": "", + "values": false + }, + "textMode": "auto" + }, + "pluginVersion": "9.1.0", + "targets": [ + { + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "expr": "increase(unbound_unwanted_replies_total{instance=\"$instance\"}[$__range])", + "refId": "A" + } + ], + "title": "Unwanted replies", + "type": "stat" + }, + { + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "description": "Max memory in use by caches at the selected time range.", + "fieldConfig": { + "defaults": { + "color": { + "fixedColor": "rgb(31, 120, 193)", + "mode": "fixed" + }, + "mappings": [ + { + "options": { + "match": "null", + "result": { + "text": "N/A" + } + }, + "type": "special" + } + ], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "bytes" + }, + "overrides": [] + }, + "gridPos": { + "h": 3, + "w": 4, + "x": 16, + "y": 0 + }, + "id": 48, + "links": [], + "maxDataPoints": 100, + "options": { + "colorMode": "none", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "horizontal", + "reduceOptions": { + "calcs": ["lastNotNull"], + "fields": "", + "values": false + }, + "textMode": "auto" + }, + "pluginVersion": "9.1.0", + "targets": [ + { + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "sum(rate(unbound_memory_caches_bytes{instance=\"$instance\"}[$__range]))", + "range": true, + "refId": "A" + } + ], + "title": "Memory cache size", + "type": "stat" + }, + { + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "description": "The number of threads created to serve clients", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [ + { + "options": { + "match": "null", + "result": { + "text": "N/A" + } + }, + "type": "special" + } + ], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "short" + }, + "overrides": [] + }, + "gridPos": { + "h": 3, + "w": 4, + "x": 20, + "y": 0 + }, + "id": 2, + "links": [], + "maxDataPoints": 100, + "options": { + "colorMode": "none", + "graphMode": "none", + "justifyMode": "auto", + "orientation": "horizontal", + "reduceOptions": { + "calcs": ["lastNotNull"], + "fields": "", + "values": false + }, + "textMode": "auto" + }, + "pluginVersion": "9.1.0", + "targets": [ + { + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "expr": "unbound_num_threads{instance=\"$instance\"}", + "format": "time_series", + "instant": false, + "refId": "A" + } + ], + "title": "Threads", + "type": "stat" + }, + { + "cards": { + "cardPadding": 0 + }, + "color": { + "cardColor": "#3274D9", + "colorScale": "sqrt", + "colorScheme": "interpolateSpectral", + "exponent": 0.5, + "mode": "opacity" + }, + "dataFormat": "timeseries", + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "description": "Query response time in seconds", + "fieldConfig": { + "defaults": { + "custom": { + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "scaleDistribution": { + "type": "linear" + } + } + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 16, + "x": 0, + "y": 3 + }, + "heatmap": {}, + "hideZeroBuckets": false, + "highlightCards": true, + "id": 46, + "legend": { + "show": false + }, + "options": { + "calculate": true, + "calculation": {}, + "cellGap": 1, + "cellValues": {}, + "color": { + "exponent": 0.5, + "fill": "#3274D9", + "mode": "opacity", + "scale": "exponential", + "scheme": "Oranges", + "steps": 128 + }, + "exemplars": { + "color": "rgba(255,0,255,0.7)" + }, + "filterValues": { + "le": 1e-9 + }, + "legend": { + "show": false + }, + "rowsFrame": { + "layout": "ge" + }, + "showValue": "never", + "tooltip": { + "show": true, + "yHistogram": true + }, + "yAxis": { + "axisPlacement": "left", + "decimals": 0, + "min": "0", + "reverse": false, + "unit": "s" + } + }, + "pluginVersion": "9.1.0", + "reverseYBuckets": false, + "targets": [ + { + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "rate(unbound_response_time_seconds_bucket{instance=\"$instance\", le=~\"0.001024|0.002048|0.004096|0.008192|0.016384|0.032768|0.065536|0.131072|0.262144|0.524288|1|2|4|8|16|32|64\"}[$__range])", + "format": "heatmap", + "instant": false, + "intervalFactor": 1, + "legendFormat": "{{le}}", + "refId": "A" + } + ], + "title": "Response time", + "tooltip": { + "show": true, + "showHistogram": true + }, + "type": "heatmap", + "xAxis": { + "show": true + }, + "yAxis": { + "decimals": 0, + "format": "s", + "logBase": 1, + "min": "0", + "show": true + }, + "yBucketBound": "upper" + }, + { + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + } + }, + "mappings": [] + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 8, + "x": 16, + "y": 3 + }, + "id": 37, + "links": [], + "maxDataPoints": 3, + "options": { + "displayLabels": ["value"], + "legend": { + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "pieType": "pie", + "reduceOptions": { + "calcs": ["lastNotNull"], + "fields": "", + "values": false + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "8.3.6", + "targets": [ + { + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "sum(increase(unbound_cache_hits_total{instance=\"$instance\"}[$__range]))", + "legendFormat": "Hits", + "range": true, + "refId": "A" + }, + { + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "exemplar": true, + "expr": "sum(increase(unbound_cache_misses_total{instance=\"$instance\"}[$__range]))", + "format": "time_series", + "interval": "", + "legendFormat": "Misses", + "range": true, + "refId": "B" + } + ], + "title": "Cache hit/miss ratio", + "type": "piechart" + }, + { + "collapsed": false, + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 10 + }, + "id": 12, + "panels": [], + "targets": [ + { + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "refId": "A" + } + ], + "title": "Queries", + "type": "row" + }, + { + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "description": "Total number of queries that were made using TCP towards the Unbound server at the selected time range.", + "fieldConfig": { + "defaults": { + "color": { + "fixedColor": "rgb(31, 120, 193)", + "mode": "fixed" + }, + "mappings": [ + { + "options": { + "match": "null", + "result": { + "text": "N/A" + } + }, + "type": "special" + } + ], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "none" + }, + "overrides": [] + }, + "gridPos": { + "h": 3, + "w": 5, + "x": 0, + "y": 11 + }, + "id": 16, + "links": [], + "maxDataPoints": 100, + "options": { + "colorMode": "none", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "horizontal", + "reduceOptions": { + "calcs": ["sum"], + "fields": "", + "values": false + }, + "textMode": "auto" + }, + "pluginVersion": "9.1.0", + "targets": [ + { + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "expr": "increase(unbound_query_tcp_total{instance=\"$instance\"}[$__range])", + "refId": "A" + } + ], + "title": "Incoming TCP queries", + "type": "stat" + }, + { + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "description": "Total number of queries that were made using TCP towards the Unbound server at the selected time range.", + "fieldConfig": { + "defaults": { + "color": { + "fixedColor": "rgb(31, 120, 193)", + "mode": "fixed" + }, + "mappings": [ + { + "options": { + "match": "null", + "result": { + "text": "N/A" + } + }, + "type": "special" + } + ], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "none" + }, + "overrides": [] + }, + "gridPos": { + "h": 3, + "w": 5, + "x": 5, + "y": 11 + }, + "id": 22, + "links": [], + "maxDataPoints": 100, + "options": { + "colorMode": "none", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "horizontal", + "reduceOptions": { + "calcs": ["sum"], + "fields": "", + "values": false + }, + "textMode": "auto" + }, + "pluginVersion": "9.1.0", + "targets": [ + { + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "expr": "increase(unbound_query_tcp_out_total{instance=\"$instance\"}[$__range])", + "refId": "A" + } + ], + "title": "Outgoing TCP queries", + "type": "stat" + }, + { + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "description": "Total number of queries that had been rate limited at the selected time range.", + "fieldConfig": { + "defaults": { + "color": { + "fixedColor": "rgb(31, 120, 193)", + "mode": "fixed" + }, + "mappings": [ + { + "options": { + "match": "null", + "result": { + "text": "N/A" + } + }, + "type": "special" + } + ], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "none" + }, + "overrides": [] + }, + "gridPos": { + "h": 3, + "w": 4, + "x": 10, + "y": 11 + }, + "id": 50, + "links": [], + "maxDataPoints": 100, + "options": { + "colorMode": "none", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "horizontal", + "reduceOptions": { + "calcs": ["sum"], + "fields": "", + "values": false + }, + "textMode": "auto" + }, + "pluginVersion": "9.1.0", + "targets": [ + { + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "expr": "increase(unbound_query_ratelimited_total{instance=\"$instance\"}[$__range])", + "refId": "A" + } + ], + "title": "Rate limited queries", + "type": "stat" + }, + { + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "description": "Total number of queries that were made using TLS towards the Unbound server at the selected time range.", + "fieldConfig": { + "defaults": { + "color": { + "fixedColor": "rgb(31, 120, 193)", + "mode": "fixed" + }, + "mappings": [ + { + "options": { + "match": "null", + "result": { + "text": "N/A" + } + }, + "type": "special" + } + ], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "none" + }, + "overrides": [] + }, + "gridPos": { + "h": 3, + "w": 5, + "x": 14, + "y": 11 + }, + "id": 18, + "links": [], + "maxDataPoints": 100, + "options": { + "colorMode": "none", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "horizontal", + "reduceOptions": { + "calcs": ["sum"], + "fields": "", + "values": false + }, + "textMode": "auto" + }, + "pluginVersion": "9.1.0", + "targets": [ + { + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "expr": "increase(unbound_query_tls_total{instance=\"$instance\"}[$__range])", + "refId": "A" + } + ], + "title": "Incoming TLS queries", + "type": "stat" + }, + { + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "description": "Total number of queries that were made using TLS resumption at the selected time range.", + "fieldConfig": { + "defaults": { + "color": { + "fixedColor": "rgb(31, 120, 193)", + "mode": "fixed" + }, + "mappings": [ + { + "options": { + "match": "null", + "result": { + "text": "N/A" + } + }, + "type": "special" + } + ], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "none" + }, + "overrides": [] + }, + "gridPos": { + "h": 3, + "w": 5, + "x": 19, + "y": 11 + }, + "id": 19, + "links": [], + "maxDataPoints": 100, + "options": { + "colorMode": "none", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "horizontal", + "reduceOptions": { + "calcs": ["sum"], + "fields": "", + "values": false + }, + "textMode": "auto" + }, + "pluginVersion": "9.1.0", + "targets": [ + { + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "expr": "increase(unbound_query_tls_resume_total{instance=\"$instance\"}[$__range])", + "refId": "A" + } + ], + "title": "TLS resumption queries", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "description": "Total number of queries that were made using IPv6 towards the Unbound server at the selected time range.", + "fieldConfig": { + "defaults": { + "color": { + "fixedColor": "rgb(31, 120, 193)", + "mode": "fixed" + }, + "mappings": [ + { + "options": { + "match": "null", + "result": { + "text": "N/A" + } + }, + "type": "special" + } + ], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "none" + }, + "overrides": [] + }, + "gridPos": { + "h": 3, + "w": 5, + "x": 0, + "y": 14 + }, + "id": 20, + "links": [], + "maxDataPoints": 100, + "options": { + "colorMode": "none", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "horizontal", + "reduceOptions": { + "calcs": ["sum"], + "fields": "", + "values": false + }, + "textMode": "auto" + }, + "pluginVersion": "9.1.0", + "targets": [ + { + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "increase(unbound_query_ipv6_total{instance=\"$instance\"}[$__range])", + "range": true, + "refId": "A" + } + ], + "title": "IPv6 queries", + "type": "stat" + }, + { + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "description": "Total amount of answers that were secure (AD) at the selected time range.", + "fieldConfig": { + "defaults": { + "color": { + "fixedColor": "rgb(31, 120, 193)", + "mode": "fixed" + }, + "mappings": [ + { + "options": { + "match": "null", + "result": { + "text": "N/A" + } + }, + "type": "special" + } + ], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "none" + }, + "overrides": [] + }, + "gridPos": { + "h": 3, + "w": 5, + "x": 5, + "y": 14 + }, + "id": 51, + "links": [], + "maxDataPoints": 100, + "options": { + "colorMode": "none", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "horizontal", + "reduceOptions": { + "calcs": ["sum"], + "fields": "", + "values": false + }, + "textMode": "auto" + }, + "pluginVersion": "9.1.0", + "targets": [ + { + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "expr": "increase(unbound_answers_secure_total{instance=\"$instance\"}[$__range])", + "refId": "A" + } + ], + "title": "Secure answers", + "type": "stat" + }, + { + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "description": "Total amount of answers that were bogus (withheld as SERVFAIL) at the selected time range.", + "fieldConfig": { + "defaults": { + "color": { + "fixedColor": "rgb(31, 120, 193)", + "mode": "fixed" + }, + "mappings": [ + { + "options": { + "match": "null", + "result": { + "text": "N/A" + } + }, + "type": "special" + } + ], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "none" + }, + "overrides": [] + }, + "gridPos": { + "h": 3, + "w": 4, + "x": 10, + "y": 14 + }, + "id": 52, + "links": [], + "maxDataPoints": 100, + "options": { + "colorMode": "none", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "horizontal", + "reduceOptions": { + "calcs": ["sum"], + "fields": "", + "values": false + }, + "textMode": "auto" + }, + "pluginVersion": "9.1.0", + "targets": [ + { + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "increase(unbound_answers_bogus_total{instance=\"$instance\"}[$__range])", + "range": true, + "refId": "A" + } + ], + "title": "Bogus answers", + "type": "stat" + }, + { + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "description": "Total number of queries that had an EDNS OPT record present at the selected time range.", + "fieldConfig": { + "defaults": { + "color": { + "fixedColor": "rgb(31, 120, 193)", + "mode": "fixed" + }, + "mappings": [ + { + "options": { + "match": "null", + "result": { + "text": "N/A" + } + }, + "type": "special" + } + ], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "none" + }, + "overrides": [] + }, + "gridPos": { + "h": 3, + "w": 5, + "x": 14, + "y": 14 + }, + "id": 17, + "links": [], + "maxDataPoints": 100, + "options": { + "colorMode": "none", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "horizontal", + "reduceOptions": { + "calcs": ["sum"], + "fields": "", + "values": false + }, + "textMode": "auto" + }, + "pluginVersion": "9.1.0", + "targets": [ + { + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "expr": "increase(unbound_query_edns_present_total{instance=\"$instance\"}[$__range])", + "refId": "A" + } + ], + "title": "EDNS OPT queries", + "type": "stat" + }, + { + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "description": "Total number of queries that had an EDNS OPT record with the DO (DNSSEC OK) bit set present at the selected time range.", + "fieldConfig": { + "defaults": { + "color": { + "fixedColor": "rgb(31, 120, 193)", + "mode": "fixed" + }, + "mappings": [ + { + "options": { + "match": "null", + "result": { + "text": "N/A" + } + }, + "type": "special" + } + ], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "none" + }, + "overrides": [] + }, + "gridPos": { + "h": 3, + "w": 5, + "x": 19, + "y": 14 + }, + "id": 21, + "links": [], + "maxDataPoints": 100, + "options": { + "colorMode": "none", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "horizontal", + "reduceOptions": { + "calcs": ["sum"], + "fields": "", + "values": false + }, + "textMode": "auto" + }, + "pluginVersion": "9.1.0", + "targets": [ + { + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "expr": "increase(unbound_query_edns_DO_total{instance=\"$instance\"}[$__range])", + "refId": "A" + } + ], + "title": "EDNS OPT + DNSSEC OK queries", + "type": "stat" + }, + { + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "description": "Total number of queries with a given query type", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 10, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "links": [], + "mappings": [], + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "reqps" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 17 + }, + "id": 35, + "options": { + "legend": { + "calcs": [], + "displayMode": "table", + "placement": "right", + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "pluginVersion": "9.1.0", + "targets": [ + { + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "expr": "rate(unbound_query_types_total{instance=\"$instance\"}[$__range])", + "legendFormat": "{{type}}", + "refId": "A" + } + ], + "title": "Total queries by type", + "type": "timeseries" + }, + { + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "description": "Total number of answers by rcode", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 10, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "links": [], + "mappings": [], + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "reqps" + }, + "overrides": [ + { + "matcher": { + "id": "byName", + "options": "NXDOMAIN" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "dark-orange", + "mode": "fixed" + } + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "SERVFAIL" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "dark-red", + "mode": "fixed" + } + } + ] + } + ] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 17 + }, + "id": 40, + "options": { + "legend": { + "calcs": [], + "displayMode": "table", + "placement": "right", + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "pluginVersion": "9.1.0", + "targets": [ + { + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "expr": "rate(unbound_answer_rcodes_total{instance=\"$instance\"}[$__range])", + "legendFormat": "{{rcode}}", + "refId": "A" + }, + { + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "expr": "rate(unbound_answers_secure_total{instance=\"$instance\"}[$__range])", + "legendFormat": "Secure (AD)", + "refId": "B" + }, + { + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "expr": "rate(unbound_answers_bogus_total{instance=\"$instance\"}[$__range])", + "legendFormat": "Bogus (SERVFAIL)", + "refId": "C" + }, + { + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "expr": "rate(unbound_rrset_bogus_total{instance=\"$instance\"}[$__range])", + "legendFormat": "RRset bogus", + "refId": "D" + } + ], + "title": "Total answers by rcode", + "type": "timeseries" + }, + { + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "description": "Total number of queries with a given query class", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 10, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "links": [], + "mappings": [], + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "reqps" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 25 + }, + "id": 38, + "options": { + "legend": { + "calcs": [], + "displayMode": "table", + "placement": "right", + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "pluginVersion": "9.1.0", + "targets": [ + { + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "expr": "rate(unbound_query_classes_total{instance=\"$instance\"}[$__range])", + "legendFormat": "{{class}}", + "refId": "A" + } + ], + "title": "Total queries by class", + "type": "timeseries" + }, + { + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "description": "Total number of queries with a given query opcode", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 10, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "links": [], + "mappings": [], + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "reqps" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 25 + }, + "id": 39, + "options": { + "legend": { + "calcs": [], + "displayMode": "table", + "placement": "right", + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "pluginVersion": "9.1.0", + "targets": [ + { + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "expr": "rate(unbound_query_opcodes_total{instance=\"$instance\"}[$__range])", + "legendFormat": "{{opcode}}", + "refId": "A" + } + ], + "title": "Total queries by opcode", + "type": "timeseries" + }, + { + "collapsed": false, + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 33 + }, + "id": 54, + "panels": [], + "targets": [ + { + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "refId": "A" + } + ], + "title": "Request list", + "type": "row" + }, + { + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "description": "Current size of the request list, including internally generated queries", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 10, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "links": [], + "mappings": [], + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "short" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 24, + "x": 0, + "y": 34 + }, + "id": 55, + "options": { + "legend": { + "calcs": ["lastNotNull"], + "displayMode": "table", + "placement": "right", + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "pluginVersion": "9.1.0", + "targets": [ + { + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "expr": "sum(rate(unbound_request_list_current_all{instance=\"$instance\"}[$__range]))", + "legendFormat": "all", + "refId": "A" + }, + { + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "expr": "sum(rate(unbound_request_list_current_user{instance=\"$instance\"}[$__range]))", + "legendFormat": "user", + "refId": "B" + } + ], + "title": "Request list size", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "description": "Number of queries that were dropped because the request list was full", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 10, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "links": [], + "mappings": [], + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "short" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 42 + }, + "id": 56, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "pluginVersion": "9.1.0", + "targets": [ + { + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "sum(rate(unbound_request_list_exceeded_total{instance=\"$instance\"}[$__range]))", + "legendFormat": "", + "range": true, + "refId": "A" + } + ], + "title": "Total exceeded queries", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "description": "Total number of requests in the request list that were overwritten by newer entries", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 10, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "links": [], + "mappings": [], + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "short" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 42 + }, + "id": 57, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "pluginVersion": "9.1.0", + "targets": [ + { + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "sum(rate(unbound_request_list_overwritten_total{instance=\"$instance\"}[$__range]))", + "legendFormat": "", + "range": true, + "refId": "A" + } + ], + "title": "Total overwritten queries", + "type": "timeseries" + }, + { + "collapsed": false, + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 50 + }, + "id": 30, + "panels": [], + "targets": [ + { + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "refId": "A" + } + ], + "title": "DNSCrypt", + "type": "row" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "description": "Total number of queries that were encrypted and successfully decapsulated by dnscrypt", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 10, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "links": [], + "mappings": [], + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "reqps" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 51 + }, + "id": 28, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": false + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "pluginVersion": "9.1.0", + "targets": [ + { + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "sum(rate(unbound_dnscrypt_valid_queries_total{instance=\"$instance\"}[$__range]))", + "range": true, + "refId": "A" + } + ], + "title": "Total successful queries", + "type": "timeseries" + }, + { + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "description": "Total number of queries that were requesting dnscrypt certificates", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 10, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "links": [], + "mappings": [], + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "reqps" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 51 + }, + "id": 31, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": false + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "pluginVersion": "9.1.0", + "targets": [ + { + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "expr": "sum(irate(unbound_dnscrypt_cert_queries_total{instance=\"$instance\"}[$__range]))", + "refId": "A" + } + ], + "title": "Total certificate requesting queries", + "type": "timeseries" + }, + { + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "description": "Total number of queries received on dnscrypt port that were cleartext and not a request for certificates", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 10, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "links": [], + "mappings": [], + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "reqps" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 59 + }, + "id": 32, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": false + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "pluginVersion": "9.1.0", + "targets": [ + { + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "expr": "sum(irate(unbound_dnscrypt_cleartext_queries_total{instance=\"$instance\"}[$__range]))", + "refId": "A" + } + ], + "title": "Total received cleartext queries", + "type": "timeseries" + }, + { + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "description": "Total number of requests that were neither cleartext, not valid dnscrypt messages", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 10, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "links": [], + "mappings": [], + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "reqps" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 59 + }, + "id": 33, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": false + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "pluginVersion": "9.1.0", + "targets": [ + { + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "expr": "sum(irate(unbound_dnscrypt_malformed_queries_total{instance=\"$instance\"}[$__range]))", + "refId": "A" + } + ], + "title": "Total malformed queries", + "type": "timeseries" + }, + { + "collapsed": false, + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 67 + }, + "id": 43, + "panels": [], + "targets": [ + { + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "refId": "A" + } + ], + "title": "Cache", + "type": "row" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "description": "Hits: total number of queries that were successfully answered using a cache lookup.\n\nMisses: total number of cache queries that needed recursive processing", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 10, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "links": [], + "mappings": [], + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "ops" + }, + "overrides": [ + { + "matcher": { + "id": "byName", + "options": "hits" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "#73BF69", + "mode": "fixed" + } + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "misses" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "#F2495C", + "mode": "fixed" + } + } + ] + } + ] + }, + "gridPos": { + "h": 7, + "w": 24, + "x": 0, + "y": 68 + }, + "id": 60, + "options": { + "legend": { + "calcs": ["mean", "lastNotNull"], + "displayMode": "table", + "placement": "right", + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "pluginVersion": "9.1.0", + "targets": [ + { + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "sum(irate(unbound_cache_hits_total{instance=\"$instance\"}[$__range]))", + "legendFormat": "hits", + "range": true, + "refId": "A" + }, + { + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "expr": "sum(irate(unbound_cache_misses_total{instance=\"$instance\"}[$__range]))", + "legendFormat": "misses", + "refId": "B" + } + ], + "title": "Cache hits / misses", + "type": "timeseries" + }, + { + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "description": "Memory in bytes by caches", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 10, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "links": [], + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "bytes" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 75 + }, + "id": 6, + "options": { + "legend": { + "calcs": ["lastNotNull"], + "displayMode": "table", + "placement": "right", + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "pluginVersion": "9.1.0", + "targets": [ + { + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "expr": "rate(unbound_memory_caches_bytes{instance=\"$instance\"}[$__range])", + "legendFormat": "{{cache}}", + "refId": "A" + } + ], + "title": "Cache size", + "type": "timeseries" + }, + { + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "description": "The number of cached entries", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 10, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "links": [], + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "short" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 75 + }, + "id": 41, + "options": { + "legend": { + "calcs": ["lastNotNull"], + "displayMode": "table", + "placement": "right", + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "pluginVersion": "9.1.0", + "targets": [ + { + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "expr": "unbound_cache_count_total{instance=\"$instance\"}", + "legendFormat": "{{type}}", + "refId": "A" + } + ], + "title": "Cached entries", + "type": "timeseries" + } + ], + "refresh": "5s", + "schemaVersion": 37, + "style": "dark", + "tags": [], + "templating": { + "list": [ + { + "current": { + "selected": false, + "text": "manwe:9167", + "value": "manwe:9167" + }, + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "definition": "label_values(unbound_up, instance)", + "hide": 0, + "includeAll": false, + "multi": false, + "name": "instance", + "options": [], + "query": { + "query": "label_values(unbound_up, instance)", + "refId": "Prometheus-instance-Variable-Query" + }, + "refresh": 2, + "regex": "", + "skipUrlSync": false, + "sort": 0, + "tagValuesQuery": "", + "tagsQuery": "", + "type": "query", + "useTags": false + }, + { + "current": { + "selected": false, + "text": "Prometheus", + "value": "Prometheus" + }, + "hide": 0, + "includeAll": false, + "label": "datasource", + "multi": false, + "name": "DS_PROMETHEUS", + "options": [], + "query": "prometheus", + "refresh": 1, + "regex": "", + "skipUrlSync": false, + "type": "datasource" + } + ] + }, + "time": { + "from": "now-1h", + "to": "now" + }, + "timepicker": { + "refresh_intervals": [ + "5s", + "10s", + "30s", + "1m", + "5m", + "15m", + "30m", + "1h", + "2h", + "1d" + ], + "time_options": ["5m", "15m", "1h", "6h", "12h", "24h", "2d", "7d", "30d"] + }, + "timezone": "", + "title": "Unbound", + "uid": "Eighooghi", + "version": 1, + "weekStart": "" +} diff --git a/modules/nixos/monitoring/default.nix b/modules/nixos/monitoring/default.nix new file mode 100644 index 0000000..4ff4c50 --- /dev/null +++ b/modules/nixos/monitoring/default.nix @@ -0,0 +1,176 @@ +{ + config, + lib, + pkgs, + ... +}: +with lib; let + cfg = config.nixfiles.modules.monitoring; +in { + options.nixfiles.modules.monitoring.enable = mkEnableOption '' + a custom monitoring stack bas on the Grafana Labs toolkit + ''; + + config = mkIf cfg.enable { + nixfiles.modules = { + grafana.enable = true; + loki.enable = true; + prometheus.enable = true; + alertmanager.enable = true; + }; + + services = { + grafana.provision = { + enable = true; + + # https://grafana.com/docs/grafana/latest/administration/provisioning/#data-sources + datasources.settings.datasources = with config.nixfiles.modules; [ + { + name = "Prometheus"; + type = "prometheus"; + access = "proxy"; + url = "https://${prometheus.domain}"; + isDefault = true; + } + { + name = "Loki"; + type = "loki"; + access = "proxy"; + url = "https://${loki.domain}"; + } + ]; + + # https://grafana.com/docs/grafana/latest/administration/provisioning/#dashboards + dashboards.settings.providers = [ + # System dashboard is imported manually from here[1]. Too bad + # provisioned dashboards cannot properly integrate dynamic datasources + # yet. + # + # [1]: https://grafana.com/grafana/dashboards/1860-node-exporter-full + { + name = "endlessh"; + options.path = ./dashboards/endlessh.json; + } + { + name = "unbound"; + options.path = ./dashboards/unbound.json; + } + { + name = "nginx"; + options.path = ./dashboards/nginx.json; + } + { + name = "postgersql"; + options.path = ./dashboards/postgresql.json; + } + ]; + }; + + loki.configuration.ruler.alertmanager_url = "https://${config.nixfiles.modules.alertmanager.domain}"; + + prometheus = { + # It would be nice if these could be generated dynamically. That would + # require a complete rework of how configurations are defined, though. + scrapeConfigs = let + mkTargets = hosts: port: map (host: "${host.hostname}:${toString port}") hosts; + in + with my.configurations; + with config.services.prometheus.exporters; [ + { + job_name = "endlessh-go"; + static_configs = [ + { + targets = + mkTargets + [ + manwe + varda + yavanna + ] + config.services.endlessh-go.prometheus.port; + } + ]; + } + { + job_name = "nginx"; + static_configs = [ + { + targets = + mkTargets + [ + manwe + varda + yavanna + ] + nginx.port; + } + ]; + } + { + job_name = "node"; + static_configs = [ + { + targets = + mkTargets + [ + manwe + varda + yavanna + ] + node.port; + } + ]; + } + { + job_name = "postgres"; + static_configs = [ + { + targets = + mkTargets + [ + manwe + ] + postgres.port; + } + ]; + } + { + job_name = "unbound"; + static_configs = [ + { + targets = + mkTargets + [ + manwe + ] + unbound.port; + } + ]; + } + { + job_name = "wireguard"; + static_configs = [ + { + targets = + mkTargets + [ + manwe + ] + wireguard.port; + } + ]; + } + ]; + + alertmanagers = [ + { + scheme = "https"; + static_configs = [ + {targets = [config.nixfiles.modules.alertmanager.domain];} + ]; + } + ]; + }; + }; + }; +} diff --git a/modules/nixos/nextcloud.nix b/modules/nixos/nextcloud.nix new file mode 100644 index 0000000..69bea8a --- /dev/null +++ b/modules/nixos/nextcloud.nix @@ -0,0 +1,133 @@ +{ + config, + lib, + pkgs, + ... +}: +with lib; let + cfg = config.nixfiles.modules.nextcloud; +in { + options.nixfiles.modules.nextcloud = { + enable = mkEnableOption "Nextcloud"; + + domain = mkOption { + description = "Domain name sans protocol scheme."; + type = with types; str; + default = "nextcloud.${config.networking.domain}"; + }; + }; + + config = mkIf cfg.enable { + nixfiles.modules = { + nginx = { + enable = true; + virtualHosts.${cfg.domain} = {}; + }; + postgresql.enable = true; + }; + + services = let + db = "nextcloud"; + in { + nextcloud = mkMerge [ + { + enable = true; + package = pkgs.nextcloud23; + + hostName = cfg.domain; + + appstoreEnable = false; + + config = { + adminpassFile = null; # This needs to be set as secret. + + dbtype = "pgsql"; + dbhost = "/run/postgresql"; + dbuser = db; + dbname = db; + + defaultPhoneRegion = "RU"; + }; + + extraApps = let + mkNextcloudApp = { + name, + version, + hash, + }: + pkgs.fetchNextcloudApp { + inherit name version hash; + url = "https://github.com/nextcloud/${name}/archive/refs/tags/v${version}.tar.gz"; + }; + in { + contacts = mkNextcloudApp { + name = "contacts"; + version = "4.0.1"; + sha256 = "sha256-dXKsG8KmlUojeY5dUn/XsMD3KaSh4QcZFOGDdcqlSvE="; + }; + calendar = mkNextcloudApp { + name = "calendar"; + version = "3.0.5"; + sha256 = "sha256-aKUKm7fWJQxOWwma56Tv+GGIo+p0n30Nhoyt4XoxsjI="; + }; + files_rightclick = mkNextcloudApp { + name = "files_rightclick"; + version = "23.0.1"; + sha256 = "sha256-VYODzkvvGrtpyRoug/8UPKhAgfCx1ltP1JdGPiB/lts="; + }; + unsplash = mkNextcloudApp { + name = "unsplash"; + version = "1.2.4"; + sha256 = "sha256-KGSkBOrNu0nK0YvAPYaxEL/kZNoJQD1oBV2aUBxh6cI="; + }; + previewgenerator = mkNextcloudApp { + name = "previewgenerator"; + version = "3.4.1"; + sha256 = "sha256-IUdj0xWt5zHxQoiMv1bYyYTzekuOFrsRIe530QOwC/w="; + }; + bruteforcesettings = mkNextcloudApp { + name = "bruteforcesettings"; + version = "2.3.0"; + sha256 = "sha256-J7ujmiPaw8GI7vDfVPXEum2XAMWvahciP8C6iXgckdE="; + }; + }; + } + (mkIf config.nixfiles.modules.acme.enable { + https = true; + config.overwriteProtocol = "https"; + }) + ]; + + postgresql = { + ensureDatabases = [db]; + ensureUsers = [ + { + name = db; + ensurePermissions."DATABASE \"${db}\"" = "ALL"; + } + ]; + }; + }; + + systemd = { + services = { + nextcloud-setup.after = ["network-online.target" "postgresql.service"]; + + nextcloud-preview-generate-cron.serviceConfig = { + Type = "oneshot"; + User = "nextcloud"; + ExecStart = "${config.services.nextcloud.occ}/bin/nextcloud-occ preview:pre-generate"; + }; + }; + + timers.nextcloud-preview-generate = { + wantedBy = ["timers.target"]; + timerConfig = { + OnBootSec = "15m"; + OnUnitActiveSec = "15m"; + Unit = "nextcloud-preview-generate-cron.service"; + }; + }; + }; + }; +} diff --git a/modules/nixos/nginx.nix b/modules/nixos/nginx.nix new file mode 100644 index 0000000..b8ab24d --- /dev/null +++ b/modules/nixos/nginx.nix @@ -0,0 +1,99 @@ +{ + config, + lib, + pkgs, + this, + ... +}: +with lib; let + cfg = config.nixfiles.modules.nginx; +in { + options.nixfiles.modules.nginx = { + enable = mkEnableOption "Nginx"; + + upstreams = mkOption { + description = "Defines a group of servers to use as proxy target."; + type = with types; anything; + default = null; + }; + + virtualHosts = mkOption { + description = "Attrset of virtual hosts."; + type = with types; anything; + default = null; + }; + }; + + config = mkIf cfg.enable { + services = { + nginx = { + enable = true; + enableReload = true; + + package = pkgs.nginxMainline; + + statusPage = true; + + serverTokens = false; + + recommendedGzipSettings = true; + recommendedOptimisation = true; + recommendedProxySettings = true; + recommendedTlsSettings = true; + + commonHttpConfig = concatStrings [ + '' + add_header X-Robots-Tag "noindex, nofollow, noarchive, nosnippet"; + '' + (optionalString (hasAttr "wireguard" this) + (with config.nixfiles.modules.wireguard; '' + geo $internal { + default 0; + 127.0.0.1/32 1; + ::1/128 1; + ${ipv4.subnet} 1; + ${ipv6.subnet} 1; + } + '')) + ]; + + inherit (cfg) upstreams; + + virtualHosts = + { + default = { + default = true; + rejectSSL = true; + locations."/".return = "444"; + }; + } + // (mkIf (cfg.virtualHosts != null) (mapAttrs (_: attr: + mkMerge [ + attr + (mkIf config.nixfiles.modules.acme.enable { + enableACME = true; + forceSSL = true; + }) + ]) + cfg.virtualHosts)); + }; + + fail2ban.jails = { + nginx-http-auth = '' + enabled = true + ''; + nginx-botsearch = '' + enabled = true + ''; + }; + + prometheus.exporters.nginx = { + enable = true; + listenAddress = mkDefault this.wireguard.ipv4.address; + port = mkDefault 9113; + }; + }; + + networking.firewall.allowedTCPPorts = [80 443]; + }; +} diff --git a/modules/nixos/node-exporter.nix b/modules/nixos/node-exporter.nix new file mode 100644 index 0000000..43f48f6 --- /dev/null +++ b/modules/nixos/node-exporter.nix @@ -0,0 +1,34 @@ +{ + config, + lib, + this, + ... +}: +with lib; let + cfg = config.nixfiles.modules.node-exporter; +in { + options.nixfiles.modules.node-exporter.enable = mkEnableOption "Prometheus Node Exporter"; + + config = mkIf cfg.enable { + services.prometheus.exporters.node = { + enable = true; + listenAddress = mkDefault this.wireguard.ipv4.address; + port = 9100; + enabledCollectors = [ + "buddyinfo" + "cgroups" + "ethtool" + "interrupts" + "ksmd" + "lnstat" + "logind" + "mountstats" + "network_route" + "processes" + "qdisc" + "systemd" + "zoneinfo" + ]; + }; + }; +} diff --git a/modules/nixos/nsd.nix b/modules/nixos/nsd.nix new file mode 100644 index 0000000..0dade8f --- /dev/null +++ b/modules/nixos/nsd.nix @@ -0,0 +1,174 @@ +{ + config, + inputs, + lib, + this, + ... +}: +with lib; let + cfg = config.nixfiles.modules.nsd; +in { + options.nixfiles.modules.nsd = { + enable = mkEnableOption "NSD"; + + fqdn = mkOption { + description = "FQDN of this nameserver."; + type = with types; str; + default = "ns.${config.networking.domain}"; + }; + }; + + config = mkIf cfg.enable { + services = { + nsd = { + enable = true; + interfaces = with this; [ipv4.address ipv6.address]; + ipTransparent = true; + ratelimit.enable = true; + + zones = let + dns = inputs.dns-nix.lib; + in + with dns.combinators; let + ips = hostname: + with my.configurations.${hostname}; { + A = [(a ipv4.address)]; + AAAA = [(aaaa ipv6.address)]; + }; + + mkEmailEntries = { + domain ? my.domain.shire, + dkimKey ? null, + }: { + MX = [(mx.mx 10 "${domain}.")]; + TXT = [(spf.strict ["a" "mx"])]; + DMARC = [ + { + p = "quarantine"; + sp = "quarantine"; + rua = ["mailto:admin+rua@${domain}"]; + ruf = ["mailto:admin+ruf@${domain}"]; + } + ]; + DKIM = optional (dkimKey != null) { + selector = "mail"; + p = dkimKey; + }; + }; + + mkZone = { + domain, + sldIps ? (ips "manwe"), + extra ? {}, + }: { + ${domain}.data = dns.toString domain (mkMerge [ + { + TTL = 60 * 60; + + SOA = { + nameServer = "${cfg.fqdn}."; + adminEmail = "admin+dns@${my.domain.shire}"; + serial = 2022091601; # Don't forget to bump the revision! + }; + + NS = with my.domain; [ + "ns1.${shire}" + # "ns2.${shire}" + ]; + + CAA = letsEncrypt "admin+caa@${my.domain.shire}"; + } + sldIps + extra + ]); + }; + + # https://ariadne.id/ + # https://docs.keyoxide.org/service-providers/dns/ + ariadneIdProof.TXT = ["openpgp4fpr:${my.pgp.fingerprint}"]; + in + mkMerge [ + (mkZone { + domain = my.domain.shire; + extra = mkMerge [ + (mkEmailEntries { + dkimKey = "@DKIM_KEY@"; + }) + { + subdomains = rec { + manwe = ips "manwe"; + "*.manwe" = manwe; + varda = ips "varda"; + "*.varda" = varda; + yavanna = ips "yavanna"; + "*.yavanna" = yavanna; + + ns1 = manwe; + # ns2 = varda; + + alertmanager = manwe; + bitwarden = manwe; + git = manwe; + gotify = manwe; + grafana = manwe; + loki = manwe; + prometheus = manwe; + radicale = manwe; + rss-bridge = manwe; + vaultwarden = manwe; + + minecraft = varda; + + flood = yavanna; + }; + } + ]; + }) + (mkZone { + domain = my.domain.azahi; + extra = mkMerge [ + (mkEmailEntries { + dkimKey = "@DKIM_KEY@"; + }) + ariadneIdProof + { + subdomains.git = ips "manwe"; + } + ]; + }) + (mkZone { + domain = my.domain.gondor; + extra = mkMerge [ + (mkEmailEntries { + dkimKey = "@DKIM_KEY@"; + }) + { + subdomains.frodo = ips "manwe" // ariadneIdProof; + } + ]; + }) + (mkZone { + domain = my.domain.rohan; + extra = mkMerge [ + (mkEmailEntries { + dkimKey = "@DKIM_KEY@"; + }) + { + subdomains.frodo = ips "manwe" // ariadneIdProof; + } + ]; + }) + ]; + }; + + fail2ban.jails.nsd = '' + enabled = true + ''; + }; + + networking.firewall = rec { + allowedTCPPorts = [53]; + allowedUDPPorts = allowedTCPPorts; + }; + }; +} diff --git a/modules/nixos/openssh.nix b/modules/nixos/openssh.nix new file mode 100644 index 0000000..00d2852 --- /dev/null +++ b/modules/nixos/openssh.nix @@ -0,0 +1,34 @@ +{ + config, + lib, + pkgs, + ... +}: +with lib; let + cfg = config.nixfiles.modules.openssh; +in { + options.nixfiles.modules.openssh.server.enable = + mkEnableOption "OpenSSH server"; + + config = mkIf cfg.server.enable { + programs.mosh.enable = true; + + services = let + port = 22022; # Port 22 should be occupied by a tarpit. + in { + openssh = { + enable = true; + ports = [port]; + logLevel = "VERBOSE"; # Required by fail2ban. + permitRootLogin = "no"; + passwordAuthentication = false; + }; + + fail2ban.jails.sshd = '' + enabled = true + mode = aggressive + port = ${toString port} + ''; + }; + }; +} diff --git a/modules/nixos/podman.nix b/modules/nixos/podman.nix new file mode 100644 index 0000000..1c5378b --- /dev/null +++ b/modules/nixos/podman.nix @@ -0,0 +1,41 @@ +{ + config, + lib, + inputs, + pkgs, + ... +}: +with lib; let + cfg = config.nixfiles.modules.podman; +in { + options.nixfiles.modules.podman.enable = mkEnableOption "Podman"; + + config = mkIf cfg.enable { + assertions = [ + { + assertion = !config.nixfiles.modules.docker.enable; + message = "Pick only one!"; + } + ]; + + secrets.containers-auth = { + file = "${inputs.self}/secrets/containers-auth"; + path = "${config.dirs.config}/containers/auth.json"; + owner = my.username; + inherit (config.my) group; + }; + + virtualisation.podman.enable = true; + + environment.systemPackages = with pkgs; [podman-compose]; + + my.extraGroups = ["podman"]; + + hm.programs.bash = { + shellAliases.p = "${pkgs.podman}/bin/podman"; + initExtra = mkAfter '' + _complete_alias p __start_podman podman + ''; + }; + }; +} diff --git a/modules/nixos/postgresql.nix b/modules/nixos/postgresql.nix new file mode 100644 index 0000000..df05e7e --- /dev/null +++ b/modules/nixos/postgresql.nix @@ -0,0 +1,87 @@ +{ + config, + lib, + pkgs, + this, + ... +}: +with lib; let + cfg = config.nixfiles.modules.postgresql; +in { + options.nixfiles.modules.postgresql = { + enable = mkEnableOption "PostgreSQL"; + + package = mkOption { + type = types.package; + default = pkgs.postgresql_15; + description = "PostgreSQL package to use."; + }; + + extraPostStart = mkOption { + type = with types; listOf str; + default = []; + description = '' + Additional post-startup commands. + + This could be used to provide a crude interface to grant permissions and + such. + ''; + }; + }; + + config = mkIf cfg.enable { + assertions = [ + { + assertion = any (x: x == "en_GB.UTF-8/UTF-8") config.i18n.supportedLocales; + message = "The locale must be available"; + } + ]; + + services = { + postgresql = { + enable = true; + + inherit (cfg) package; + + initdbArgs = [ + "--encoding=UTF8" + "--locale-provider=icu" + "--icu-locale=en_GB@collation=posix" + "--locale=en_GB.UTF-8" + "--lc-collate=C" + "--lc-ctype=C" + ]; + + authentication = '' + local all all trust + ''; + }; + + prometheus.exporters.postgres = { + enable = true; + listenAddress = mkDefault this.wireguard.ipv4.address; + port = mkDefault 9187; + }; + }; + + systemd.services.postgresql.postStart = + optionalString (cfg.extraPostStart != []) + concatStringsSep "\n" + cfg.extraPostStart; + + environment.sessionVariables.PSQLRC = toString (pkgs.writeText "psqlrc" '' + \set QUIET 1 + + \timing + \x auto + \pset null '[NULL]' + \set PROMPT1 '%[%033[1m%]%M %n@%/%R%[%033[0m%]% λ ' + \set PROMPT2 ' … > ' + \set VERBOSITY verbose + \set HISTCONTROL ignoredups + \set HISTFILE /dev/null + + \unset QUIET + ''); + }; +} diff --git a/modules/nixos/profiles/default.nix b/modules/nixos/profiles/default.nix new file mode 100644 index 0000000..d5ab838 --- /dev/null +++ b/modules/nixos/profiles/default.nix @@ -0,0 +1,33 @@ +{ + config, + lib, + pkgs, + this, + ... +}: +with lib; let + cfg = config.nixfiles.modules.profiles.default; +in { + imports = [ + ./dev + ./headful.nix + ./headless.nix + ]; + + config = mkIf cfg.enable { + programs.less = { + enable = true; + envVariables.LESSHISTFILE = "-"; + }; + + environment.systemPackages = with pkgs; [ + cryptsetup + lshw + lsof + pciutils + psmisc + usbutils + util-linux + ]; + }; +} diff --git a/modules/nixos/profiles/dev/containers.nix b/modules/nixos/profiles/dev/containers.nix new file mode 100644 index 0000000..195b892 --- /dev/null +++ b/modules/nixos/profiles/dev/containers.nix @@ -0,0 +1,27 @@ +{ + config, + lib, + pkgs, + ... +}: +with lib; let + cfg = config.nixfiles.modules.profiles.dev.containers; +in { + config = mkIf cfg.enable { + nixfiles.modules.podman.enable = true; + + hm = { + home = { + sessionVariables.MINIKUBE_HOME = "${config.dirs.config}/minikube"; + + packages = with pkgs; [buildah]; + }; + + xdg.dataFile."minikube/config/config.json".text = generators.toJSON {} { + config.Rootless = true; + driver = "podman"; + container-runtime = "cri-o"; + }; + }; + }; +} diff --git a/modules/nixos/profiles/dev/default.nix b/modules/nixos/profiles/dev/default.nix new file mode 100644 index 0000000..83d41c0 --- /dev/null +++ b/modules/nixos/profiles/dev/default.nix @@ -0,0 +1,19 @@ +{ + config, + lib, + pkgs, + this, + ... +}: +with lib; let + cfg = config.nixfiles.modules.profiles.dev.default; +in { + config = mkIf cfg.enable { + hm.home.language = { + collate = "C"; + messages = "C"; + }; + + my.extraGroups = ["kvm"]; + }; +} diff --git a/modules/nixos/profiles/headful.nix b/modules/nixos/profiles/headful.nix new file mode 100644 index 0000000..01c442e --- /dev/null +++ b/modules/nixos/profiles/headful.nix @@ -0,0 +1,88 @@ +{ + config, + lib, + pkgs, + this, + ... +}: +with lib; let + cfg = config.nixfiles.modules.profiles.headful; +in { + config = mkIf cfg.enable { + nixfiles.modules = { + chromium.enable = true; + firefox.enable = true; + sound.enable = true; + x11.enable = true; + + dwm.enable = mkDefault false; + kde.enable = mkDefault true; + xmonad.enable = mkDefault false; + }; + + hm = { + home.packages = with pkgs; [ + calibre + imv + neochat + tdesktop + tor-browser + ]; + + programs.bash.shellAliases.open = "${pkgs.xdg-utils}/bin/xdg-open"; + }; + + boot = { + # Pretty much placebo but has some nice patches for `-march=native` + # optimisations, P-State Zen4 support and Fsync for Wine. + kernelPackages = mkDefault pkgs.linuxPackages_xanmod_latest; + + # There are (arguably) not a lot of reasons to keep mitigations enabled + # for on machine that is not web-facing. First of all, to completely + # mitigate any possible Spectre holes one would need to disable + # Hyperthreading altogether which will essentially put one's computer into + # the stone age by not being able to to effectively utilise multi-core its + # multicore capabilities. Secondly, by enabling mitigations, we introduce + # a plethora of performace overheads[1], which, albeit small, but still + # contribute to the overall speed of things. This is however still poses a + # security risk, which I am willing to take. + # + # [1]: https://www.phoronix.com/scan.php?page=article&item=spectre-meltdown-2&num=11 + kernelParams = ["mitigations=off"]; + + loader = { + efi.canTouchEfiVariables = true; + + systemd-boot = { + enable = true; + configurationLimit = 10; + }; + }; + }; + + hardware.opengl = { + enable = true; + driSupport = true; + }; + + programs = { + iftop.enable = true; + mtr.enable = true; + traceroute.enable = true; + }; + + services = { + # https://github.com/NixOS/nixpkgs/issues/135888 + upower.enable = true; + + psd.enable = true; + }; + + environment.systemPackages = with pkgs; [ + ethtool + nethogs + ]; + + my.extraGroups = ["audio" "video" "input"]; + }; +} diff --git a/modules/nixos/profiles/headless.nix b/modules/nixos/profiles/headless.nix new file mode 100644 index 0000000..9faf531 --- /dev/null +++ b/modules/nixos/profiles/headless.nix @@ -0,0 +1,42 @@ +{ + config, + lib, + pkgs, + this, + ... +}: +with lib; let + cfg = config.nixfiles.modules.profiles.headless; +in { + config = mkIf cfg.enable { + nixfiles.modules = { + openssh.server.enable = true; + endlessh-go.enable = true; + + fail2ban.enable = true; + + node-exporter.enable = true; + promtail.enable = true; + }; + + # Pin version to prevent any surprises. + boot.kernelPackages = pkgs.linuxPackages_5_15_hardened; + + nix = { + gc = { + automatic = true; + dates = "weekly"; + options = "--delete-older-than 30d"; + }; + + optimise = { + automatic = true; + dates = ["daily"]; + }; + }; + + services.udisks2.enable = false; + + xdg.sounds.enable = false; + }; +} diff --git a/modules/nixos/prometheus.nix b/modules/nixos/prometheus.nix new file mode 100644 index 0000000..a75c151 --- /dev/null +++ b/modules/nixos/prometheus.nix @@ -0,0 +1,49 @@ +{ + config, + lib, + ... +}: +with lib; let + cfg = config.nixfiles.modules.prometheus; +in { + options.nixfiles.modules.prometheus = { + enable = mkEnableOption "Prometheus"; + + port = mkOption { + description = "Port."; + type = with types; port; + default = 30111; + }; + + domain = mkOption { + description = "Domain name sans protocol scheme."; + type = with types; str; + default = "prometheus.${config.networking.domain}"; + }; + }; + + config = mkIf cfg.enable { + nixfiles.modules.nginx = with cfg; { + enable = true; + upstreams.prometheus.servers."127.0.0.1:${toString cfg.port}" = {}; + virtualHosts.${domain} = { + locations."/".proxyPass = "http://prometheus"; + extraConfig = nginxInternalOnly; + }; + }; + + services.prometheus = with cfg; { + enable = true; + + listenAddress = "127.0.0.1"; + inherit port; + + extraFlags = [ + "--web.external-url=https://${domain}" + "--storage.tsdb.retention.size=50GB" + "--storage.tsdb.retention.time=1y" + "--storage.tsdb.wal-compression" + ]; + }; + }; +} diff --git a/modules/nixos/promtail.nix b/modules/nixos/promtail.nix new file mode 100644 index 0000000..552df82 --- /dev/null +++ b/modules/nixos/promtail.nix @@ -0,0 +1,53 @@ +{ + config, + lib, + this, + ... +}: +with lib; let + cfg = config.nixfiles.modules.promtail; +in { + options.nixfiles.modules.promtail = { + enable = mkEnableOption "Promtail"; + + loki = { + url = mkOption { + description = "Address of a listening Loki service."; + type = with types; str; + default = "https://${config.nixfiles.modules.loki.domain}"; + }; + }; + }; + + config = mkIf cfg.enable { + services.promtail = { + enable = true; + + configuration = { + server = rec { + http_listen_address = this.wireguard.ipv4.address; + http_listen_port = 30181; + + grpc_listen_address = this.wireguard.ipv4.address; + grpc_listen_port = http_listen_port + 1; + + log_level = "warn"; + }; + + clients = [{url = "${cfg.loki.url}/loki/api/v1/push";}]; + + positions.filename = "/tmp/positions.yaml"; + + scrape_configs = [ + { + job_name = "journal"; + journal = { + max_age = "24h"; + labels.job = "systemd-journal"; + }; + } + ]; + }; + }; + }; +} diff --git a/modules/nixos/psd.nix b/modules/nixos/psd.nix new file mode 100644 index 0000000..77d3c66 --- /dev/null +++ b/modules/nixos/psd.nix @@ -0,0 +1,60 @@ +{ + config, + lib, + pkgs, + ... +}: +with lib; let + cfg = config.nixfiles.modules.psd; +in { + options.nixfiles.modules.psd.enable = + mkEnableOption "Profile Sync Daemon"; + + config = mkIf cfg.enable { + hm = { + home.packages = with pkgs; [profile-sync-daemon]; + + xdg.configFile."psd/psd.conf".text = '' + USE_OVERLAYFS="yes" + ''; + }; + + systemd.user = { + services = { + psd = { + unitConfig = { + Description = "Profile-sync-daemon"; + Wants = ["psd-resync.service"]; + RequiresMountsFor = "/home/"; + After = ["local-fs.target"]; + }; + serviceConfig = { + RemainAfterExit = true; + ExecStart = "${pkgs.profile-sync-daemon}/bin/profile-sync-daemon startup"; + ExecStop = "${pkgs.profile-sync-daemon}/bin/profile-sync-daemon unsync"; + }; + wantedBy = ["graphical.target"]; + }; + + psd-resync = { + unitConfig = { + Description = "Profile-sync-daemon resync"; + After = ["psd.service"]; + Wants = ["psd-resync.timer"]; + BindsTo = ["psd.service"]; + }; + serviceConfig.ExecStart = "${pkgs.profile-sync-daemon}/bin/profile-sync-daemon resync"; + wantedBy = ["graphical.target"]; + }; + }; + + timers.psd-resync = { + unitConfig = { + Description = "Profile-sync-daemon resync timer"; + BindsTo = ["psd.service"]; + }; + timerConfig.OnUnitActiveSec = "1h"; + }; + }; + }; +} diff --git a/modules/nixos/radarr.nix b/modules/nixos/radarr.nix new file mode 100644 index 0000000..0abfdf2 --- /dev/null +++ b/modules/nixos/radarr.nix @@ -0,0 +1,28 @@ +{ + config, + lib, + ... +}: +with lib; let + cfg = config.nixfiles.modules.radarr; +in { + options.nixfiles.modules.radarr = { + enable = mkEnableOption "Radarr"; + + domain = mkOption { + description = "Domain name sans protocol scheme."; + type = with types; str; + default = "radarr.${config.networking.fqdn}"; + }; + }; + + config = mkIf cfg.enable { + nixfiles.modules.nginx = { + enable = true; + upstreams.radarr.servers."127.0.0.1:7878" = {}; + virtualHosts.${cfg.domain}.locations."/".proxyPass = "http://radarr"; + }; + + services.radarr.enable = true; + }; +} diff --git a/modules/nixos/radicale.nix b/modules/nixos/radicale.nix new file mode 100644 index 0000000..c903d39 --- /dev/null +++ b/modules/nixos/radicale.nix @@ -0,0 +1,52 @@ +{ + config, + inputs, + lib, + ... +}: +with lib; let + cfg = config.nixfiles.modules.radicale; +in { + options.nixfiles.modules.radicale = { + enable = mkEnableOption "Radicale"; + + domain = mkOption { + description = "Domain name sans protocol scheme."; + type = with types; str; + default = "radicale.${config.networking.domain}"; + }; + }; + + config = let + port = 5232; + in + mkIf cfg.enable { + secrets.radicale-htpasswd = { + file = "${inputs.self}/secrets/radicale-htpasswd"; + owner = "radicale"; + group = "radicale"; + }; + + nixfiles.modules.nginx = { + enable = true; + upstreams.radicale.servers."127.0.0.1:${toString port}" = {}; + virtualHosts.${cfg.domain} = { + locations."/".proxyPass = "http://radicale"; + extraConfig = nginxInternalOnly; + }; + }; + + services.radicale = { + enable = true; + settings = { + server.hosts = ["127.0.0.1:${toString port}"]; + web.type = "none"; + auth = { + type = "htpasswd"; + htpasswd_filename = config.secrets.radicale-htpasswd.path; + htpasswd_encryption = "bcrypt"; + }; + }; + }; + }; +} diff --git a/modules/nixos/rss-bridge.nix b/modules/nixos/rss-bridge.nix new file mode 100644 index 0000000..fef1070 --- /dev/null +++ b/modules/nixos/rss-bridge.nix @@ -0,0 +1,31 @@ +{ + config, + lib, + ... +}: +with lib; let + cfg = config.nixfiles.modules.rss-bridge; +in { + options.nixfiles.modules.rss-bridge = { + enable = mkEnableOption "RSS-Bridge"; + + domain = mkOption { + description = "Domain name sans protocol scheme."; + type = with types; str; + default = "rss-bridge.${config.networking.domain}"; + }; + }; + + config = mkIf cfg.enable { + nixfiles.modules.nginx = { + enable = true; + virtualHosts.${cfg.domain}.extraConfig = nginxInternalOnly; + }; + + services.rss-bridge = { + enable = true; + virtualHost = cfg.domain; + whitelist = ["*"]; + }; + }; +} diff --git a/modules/nixos/rtorrent.nix b/modules/nixos/rtorrent.nix new file mode 100644 index 0000000..4014a3b --- /dev/null +++ b/modules/nixos/rtorrent.nix @@ -0,0 +1,297 @@ +{ + config, + lib, + pkgs, + ... +}: +with lib; let + cfg = config.nixfiles.modules.rtorrent; +in { + options.nixfiles.modules.rtorrent = { + enable = mkEnableOption "rTorrent"; + + flood = { + enable = mkEnableOption "Flood" // {default = cfg.enable;}; + + domain = mkOption { + description = "Domain name sans protocol scheme."; + type = with types; str; + default = "flood.${config.networking.domain}"; + }; + }; + }; + + config = let + user = "rtorrent"; + group = "rtorrent"; + baseDir = "/var/lib/rtorrent"; + rpcSocket = "${baseDir}/rpc.socket"; + in + mkIf cfg.enable (mkMerge [ + (let + port = 50000; + in { + systemd = { + services.rtorrent = { + description = "rTorrent"; + after = ["network.target" "local-fs.target"]; + serviceConfig = let + leechDir = "${baseDir}/leech"; + seedDir = "${baseDir}/seed"; + sessionDir = "${baseDir}/session"; + logDir = "${baseDir}/log"; + configFile = let + moveCompleted = let + pkg = pkgs.writeShellApplication { + name = "move-completed"; + runtimeInputs = with pkgs; [ + coreutils-full + gnused + findutils + ]; + text = '' + set -x + + leech_path="$1" + seed_path="$2" + # seed_path="$(echo "$2" | sed 's@+@ @g;s@%@\\x@g' | xargs -0 printf '%b')" + + mkdir -pv "$seed_path" + mv -fv "$leech_path" "$seed_path" + ''; + }; + in "${pkg}/bin/move-completed"; + in + pkgs.writeText "rtorrent.rc" '' + method.insert = cfg.leech, private|const|string, (cat, "${leechDir}") + method.insert = cfg.seed, private|const|string, (cat, "${seedDir}") + method.insert = cfg.session, private|const|string, (cat, "${sessionDir}") + method.insert = cfg.log, private|const|string, (cat, "${logDir}") + method.insert = cfg.rpcsocket, private|const|string, (cat, "${rpcSocket}") + + directory.default.set = (cat, (cfg.leech)) + session.path.set = (cat, (cfg.session)) + + network.port_range.set = ${toString port}-${toString port} + network.port_random.set = no + + dht.mode.set = disable + protocol.pex.set = no + + trackers.use_udp.set = no + + protocol.encryption.set = allow_incoming,try_outgoing,enable_retry + + pieces.memory.max.set = ${toString (pow 2 11)}M + pieces.preload.type.set = 2 + + network.xmlrpc.size_limit.set = ${toString (pow 2 17)} + + network.max_open_files.set = ${toString (pow 2 10)} + network.max_open_sockets.set = ${toString (pow 2 10)} + + network.http.max_open.set = ${toString (pow 2 8)} + + throttle.global_down.max_rate.set_kb = 0 + throttle.global_up.max_rate.set_kb = 0 + + encoding.add = UTF-8 + system.umask.set = 0027 + system.cwd.set = (directory.default) + + network.scgi.open_local = (cat, (cfg.rpcsocket)) + + method.insert = d.move_completed, simple, "\ + d.directory.set=$argument.1=;\ + execute=${moveCompleted}, $argument.0=, $argument.1=;\ + d.save_full_session=\ + " + method.insert = d.leech_path, simple, "\ + if=(d.is_multi_file),\ + (cat, (d.directory), /),\ + (cat, (d.directory), /, (d.name))\ + " + method.insert = d.seed_path, simple, "\ + cat=$cfg.seed=, /, $d.custom1=\ + " + method.set_key = event.download.finished, move_complete, "\ + d.move_completed=$d.leech_path=, $d.seed_path=\ + " + + log.open_file = "log", (cat, (cfg.log), "/", "default.log") + log.add_output = "info", "log" + log.execute = (cat, (cfg.log), "/", "execute.log") + ''; + in { + Restart = "on-failure"; + RestartSec = 3; + + KillMode = "process"; + KillSignal = "SIGHUP"; + + User = user; + Group = group; + + ExecStartPre = concatStringsSep " " [ + "${pkgs.coreutils-full}/bin/mkdir -p" + leechDir + seedDir + sessionDir + logDir + ]; + ExecStart = concatStringsSep " " [ + "${pkgs.rtorrent}/bin/rtorrent" + "-n" + "-o system.daemon.set=true" + "-o network.bind_address.set=0.0.0.0" + "-o import=${configFile}" + ]; + ExecStop = concatStringsSep " " [ + "${pkgs.coreutils-full}/bin/rm -rf" + rpcSocket + ]; + + RuntimeDirectory = "rtorrent"; + RuntimeDirectoryMode = 0750; + UMask = 0027; + AmbientCapabilities = [""]; + CapabilityBoundingSet = [""]; + LockPersonality = true; + MemoryDenyWriteExecute = true; + NoNewPrivileges = true; + PrivateDevices = true; + PrivateTmp = true; + PrivateUsers = true; + ProtectClock = true; + ProtectControlGroups = true; + ProtectHome = true; + ProtectHostname = true; + ProtectKernelLogs = true; + ProtectKernelModules = true; + ProtectKernelTunables = true; + ProcSubset = "pid"; + RemoveIPC = true; + RestrictAddressFamilies = ["AF_UNIX" "AF_INET" "AF_INET6"]; + RestrictNamespaces = true; + RestrictRealtime = true; + RestrictSUIDSGID = true; + SystemCallArchitectures = "native"; + SystemCallFilter = ["@system-service" "~@resources" "~@privileged"]; + }; + wantedBy = ["multi-user.target"]; + }; + + tmpfiles.rules = ["d '${baseDir}' 0750 ${user} ${group} -"]; + }; + + users = { + users.${user} = { + inherit group; + shell = pkgs.bashInteractive; + home = baseDir; + description = "rTorrent"; + isSystemUser = true; + }; + groups.${group} = {}; + }; + my.extraGroups = [group]; + + networking.firewall.allowedTCPPorts = [port]; + + boot.kernel.sysctl = { + "net.core.rmem_max" = mkOverride 500 (pow 2 24); + "net.core.wmem_max" = mkOverride 500 (pow 2 24); + "net.ipv4.tcp_fin_timeout" = mkOverride 500 30; + "net.ipv4.tcp_rmem" = mkOverride 500 (mkTcpMem 12 23 24); + "net.ipv4.tcp_slow_start_after_idle" = 0; + "net.ipv4.tcp_tw_recycle" = mkOverride 500 1; + "net.ipv4.tcp_tw_reuse" = mkOverride 500 1; + "net.ipv4.tcp_wmem" = mkOverride 500 (mkTcpMem 12 23 24); + }; + }) + (let + port = 50001; + pkg = pkgs.nodePackages.flood; + in + mkIf cfg.flood.enable { + nixfiles.modules.nginx = { + enable = true; + upstreams.flood.servers."127.0.0.1:${toString port}" = {}; + virtualHosts.${cfg.flood.domain} = { + root = "${pkg}/lib/node_modules/flood/dist/assets"; + locations = { + "/".tryFiles = "$uri /index.html"; + "/api" = { + proxyPass = "http://flood"; + extraConfig = '' + proxy_buffering off; + proxy_cache off; + ''; + }; + }; + extraConfig = nginxInternalOnly; + }; + }; + + systemd.services.flood = { + description = "Flood"; + after = ["network.target" "rtorrent.service"]; + path = with pkgs; [mediainfo]; + serviceConfig = { + Restart = "on-failure"; + RestartSec = 3; + + User = user; + Group = group; + + ExecStart = concatStringsSep " " [ + "${pkg}/bin/flood" + "--allowedpath=${baseDir}" + "--baseuri=/" + "--rundir=${baseDir}/flood" + "--host=127.0.0.1" + "--port=${toString port}" + "--rtsocket=${rpcSocket}" + "--ssl=false" + "--auth=none" + ]; + + RuntimeDirectory = "rtorrent"; + RuntimeDirectoryMode = 0750; + UMask = 0027; + AmbientCapabilities = [""]; + CapabilityBoundingSet = [""]; + LockPersonality = true; + NoNewPrivileges = true; + PrivateDevices = true; + PrivateTmp = true; + PrivateUsers = true; + ProtectClock = true; + ProtectControlGroups = true; + ProtectHome = true; + ProtectHostname = true; + ProtectKernelLogs = true; + ProtectKernelModules = true; + ProtectKernelTunables = true; + ProcSubset = "pid"; + ProtectProc = "invisible"; + RemoveIPC = true; + RestrictAddressFamilies = ["AF_UNIX" "AF_INET" "AF_INET6"]; + RestrictNamespaces = true; + RestrictRealtime = true; + RestrictSUIDSGID = true; + SystemCallArchitectures = "native"; + SystemCallFilter = [ + "~@cpu-emulation" + "~@debug" + "~@mount" + "~@obsolete" + "~@privileged" + "~@resources" + ]; + }; + wantedBy = ["multi-user.target"]; + }; + }) + ]); +} diff --git a/modules/nixos/searx.nix b/modules/nixos/searx.nix new file mode 100644 index 0000000..9462d5d --- /dev/null +++ b/modules/nixos/searx.nix @@ -0,0 +1,78 @@ +{ + config, + inputs, + lib, + ... +}: +with lib; let + cfg = config.nixfiles.modules.searx; +in { + options.nixfiles.modules.searx = { + enable = mkEnableOption "SearX"; + + port = mkOption { + description = "Port."; + type = with types; port; + default = 61001; + }; + + domain = mkOption { + description = "Domain name sans protocol scheme."; + type = with types; nullOr str; + default = "searx.${config.networking.domain}"; + }; + }; + + config = mkIf cfg.enable { + secrets.searx-environment = { + file = "${inputs.self}/secrets/searx-environment"; + owner = "searx"; + group = "searx"; + }; + + nixfiles.modules.nginx = { + enable = true; + upstreams.searx.servers."127.0.0.1:${toString cfg.port}" = {}; + virtualHosts.${cfg.domain} = { + locations."/".proxyPass = "http://searx"; + extraConfig = nginxInternalOnly; + }; + }; + + services = { + searx = { + enable = true; + + settings = { + general = { + instance_name = cfg.domain; + contact_url = "mailto:admin+searx@${config.networking.domain}"; + git_url = false; + git_branch = false; + docs_url = false; + wiki_url = false; + twitter_url = false; + }; + server = { + bind_address = "127.0.0.1"; + inherit (cfg) port; + secret_key = "@SEARX_SECRET_KEY@"; + base_url = false; + image_proxy = false; + default_http_headers = { + Referrer-Policy = "no-referrer"; + X-Content-Type-Options = "nosniff"; + X-Download-Options = "noopen"; + X-Robots-Tag = "noindex, nofollow, nosnippet, noarchive"; + }; + }; + search = { + safe_search = 0; + autocomplete = ""; + }; + }; + environmentFile = config.secrets.searx-environment.path; + }; + }; + }; +} diff --git a/modules/nixos/shadowsocks.nix b/modules/nixos/shadowsocks.nix new file mode 100644 index 0000000..b59359c --- /dev/null +++ b/modules/nixos/shadowsocks.nix @@ -0,0 +1,116 @@ +{ + config, + inputs, + lib, + pkgs, + ... +}: +with lib; let + cfg = config.nixfiles.modules.shadowsocks; +in { + options.nixfiles.modules.shadowsocks = { + enable = mkEnableOption "Shadowsocks"; + + port = mkOption { + type = with types; port; + default = 8388; + description = "Port."; + }; + }; + + config = mkIf cfg.enable { + secrets.shadowsocks-password.file = "${inputs.self}/secrets/shadowsocks-password"; + + services = { + shadowsocks = { + enable = true; + passwordFile = config.secrets.shadowsocks-password.path; + localAddress = ["0.0.0.0"]; + mode = "tcp_only"; + }; + + fail2ban.jails.shadowsocks-libev = '' + enabled = true + filter = shadowsocks-libev + port = ${toString cfg.port} + ''; + }; + + systemd.services.shadowsocks-libev.path = with pkgs; + mkForce [ + (writeShellApplication { + name = "ss-server"; + runtimeInputs = [shadowsocks-libev]; + text = let + # https://github.com/shadowsocks/shadowsocks-libev/blob/master/acl/server_block_local.acl + aclFile = writeText "outbound_block_list.acl" '' + [outbound_block_list] + 0.0.0.0/8 + 10.0.0.0/8 + 100.64.0.0/10 + 127.0.0.0/8 + 169.254.0.0/16 + 172.16.0.0/12 + 192.0.0.0/24 + 192.0.2.0/24 + 192.88.99.0/24 + 192.168.0.0/16 + 198.18.0.0/15 + 198.51.100.0/24 + 203.0.113.0/24 + 224.0.0.0/4 + 240.0.0.0/4 + 255.255.255.255/32 + ::1/128 + ::ffff:127.0.0.1/104 + fc00::/7 + fe80::/10 + ''; + in '' + ss-server --acl ${aclFile} "$@" + ''; + }) + coreutils-full + jq + ]; + + environment.etc = mkIf config.nixfiles.modules.fail2ban.enable { + "fail2ban/filter.d/shadowsocks-libev.conf".text = '' + [Definition] + failregex = ^.*failed to handshake with <ADDR>: authentication error$ + ignoreregex = + journalmatch = _SYSTEMD_UNIT=shadowsocks-libev.service + ''; + }; + + networking.firewall = { + allowedTCPPorts = [cfg.port]; + extraCommands = '' + iptables -A nixos-fw -p tcp --syn --dport ${ + toString cfg.port + } -m connlimit --connlimit-above 32 -j nixos-fw-refuse + ''; + }; + + boot.kernel.sysctl = { + "net.core.rmem_max" = mkOverride 100 (pow 2 26); + "net.core.wmem_max" = mkOverride 100 (pow 2 26); + "net.core.netdev_max_backlog" = pow 2 18; + "net.core.somaxconn" = pow 2 12; + "net.ipv4.tcp_syncookies" = 1; + "net.ipv4.tcp_tw_reuse" = mkOverride 100 1; + "net.ipv4.tcp_tw_recycle" = mkOverride 100 0; + "net.ipv4.tcp_fin_timeout" = mkOverride 100 30; + "net.ipv4.tcp_keepalive_time" = 60 * 20; + "net.ipv4.ip_local_port_range" = "10000 65000"; + "net.ipv4.tcp_max_syn_backlog" = pow 2 13; + "net.ipv4.tcp_max_tw_buckets" = pow 2 12; + "net.ipv4.tcp_fastopen" = 3; + "net.ipv4.tcp_mem" = mkOverride 100 (mkTcpMem 15 16 17); + "net.ipv4.tcp_rmem" = mkOverride 100 (mkTcpMem 12 16 26); + "net.ipv4.tcp_wmem" = mkOverride 100 (mkTcpMem 12 16 26); + "net.ipv4.tcp_mtu_probing" = 1; + "net.ipv4.tcp_congestion_control" = "hybla"; + }; + }; +} diff --git a/modules/nixos/soju.nix b/modules/nixos/soju.nix new file mode 100644 index 0000000..14faf00 --- /dev/null +++ b/modules/nixos/soju.nix @@ -0,0 +1,117 @@ +{ + config, + lib, + pkgs, + this, + ... +}: +with lib; let + cfg = config.nixfiles.modules.soju; +in { + options.nixfiles.modules.soju = { + enable = mkEnableOption "soju"; + + protocol = mkOption { + description = "Port."; + type = with types; enum ["ircs" "irc+insecure"]; + default = "irc+insecure"; + }; + + address = mkOption { + description = "Address."; + type = with types; str; + default = this.wireguard.ipv4.address; + }; + + port = mkOption { + description = "Port."; + type = with types; port; + default = 6667; + }; + + domain = mkOption { + description = "Domain."; + type = with types; str; + default = config.networking.fqdn; + }; + }; + + config = let + db = "soju"; + in + mkIf cfg.enable { + nixfiles.modules.postgresql = { + enable = true; + extraPostStart = [ + '' + $PSQL "${db}" -tAc 'GRANT ALL ON SCHEMA "public" TO "${db}"' + '' + ]; + }; + + services.postgresql = { + ensureDatabases = [db]; + ensureUsers = [ + { + name = db; + ensurePermissions."DATABASE \"${db}\"" = "ALL"; + } + ]; + }; + + systemd.services.soju = { + description = "soju IRC bouncer"; + wantedBy = ["multi-user.target"]; + after = ["network-online.target" "postgresql.service"]; + serviceConfig = { + ExecStart = let + # https://soju.im/doc/soju.1.html + configFile = pkgs.writeText "soju.conf" '' + listen ${cfg.protocol}://${cfg.address}:${toString cfg.port} + db postgres ${ + concatStringsSep " " [ + "host=/run/postgresql" + "user=${db}" + "dbname=${db}" + "sslmode=disable" + ] + } + hostname ${cfg.domain} + title ${cfg.domain} + ''; + in + concatStringsSep " " [ + "${pkgs.soju}/bin/soju" + "-config ${configFile}" + ]; + DynamicUser = true; + AmbientCapabilities = [""]; + CapabilityBoundingSet = [""]; + UMask = "0077"; + LockPersonality = true; + MemoryDenyWriteExecute = true; + NoNewPrivileges = true; + PrivateDevices = true; + PrivateTmp = true; + PrivateUsers = true; + ProtectClock = true; + ProtectControlGroups = true; + ProtectHome = true; + ProtectHostname = true; + ProtectKernelLogs = true; + ProtectKernelModules = true; + ProtectKernelTunables = true; + ProtectSystem = "strict"; + ProtectProc = "invisible"; + ProcSubset = "pid"; + RemoveIPC = true; + RestrictAddressFamilies = ["AF_UNIX" "AF_INET" "AF_INET6"]; + RestrictNamespaces = true; + RestrictRealtime = true; + RestrictSUIDSGID = true; + SystemCallArchitectures = "native"; + SystemCallFilter = ["@system-service" "~@privileged"]; + }; + }; + }; +} diff --git a/modules/nixos/solaar.nix b/modules/nixos/solaar.nix new file mode 100644 index 0000000..ceff23d --- /dev/null +++ b/modules/nixos/solaar.nix @@ -0,0 +1,54 @@ +{ + config, + lib, + pkgs, + ... +}: +with lib; let + cfg = config.nixfiles.modules.solaar; +in { + options.nixfiles.modules.solaar = { + enable = mkEnableOption "Solaar"; + }; + + config = mkIf cfg.enable { + hm = { + home.packages = with pkgs; [solaar]; + + systemd.user.services.solaar = { + Unit = { + Description = "Device manager for Logitech devices"; + After = ["graphical-session-pre.target"]; + PartOf = ["graphical-session.target"]; + }; + Service = { + # The dirtiest hack I've ever implemented... I should be ashamed of + # it. Regardless, that shit still doesn't work because each reconnect, + # /dev/hidraw* is recreated and has default permissions which breaks + # Solaar. Fuck this shit. + ExecStartPre = let + pkg = pkgs.writeShellApplication { + name = "solaar-pre"; + text = '' + for i in /dev/hidraw*; do + if [ -c "$i" ]; then + sudo chown root:input "$i" + sudo chmod 0660 "$i" + fi + done + ''; + }; + in "${pkg}/bin/solaar-pre"; + ExecStart = "${pkgs.solaar}/bin/solaar --window=hide"; + }; + Install.WantedBy = ["graphical-session.target"]; + }; + }; + + boot.kernelModules = ["hid_logitech_dj" "hid_logitech_hidpp"]; + + hardware.uinput.enable = true; + + my.extraGroups = ["uinput" "input"]; + }; +} diff --git a/modules/nixos/sonarr.nix b/modules/nixos/sonarr.nix new file mode 100644 index 0000000..8c79175 --- /dev/null +++ b/modules/nixos/sonarr.nix @@ -0,0 +1,28 @@ +{ + config, + lib, + ... +}: +with lib; let + cfg = config.nixfiles.modules.sonarr; +in { + options.nixfiles.modules.sonarr = { + enable = mkEnableOption "Sonarr"; + + domain = mkOption { + description = "Domain name sans protocol scheme."; + type = with types; str; + default = "sonarr.${config.networking.fqdn}"; + }; + }; + + config = mkIf cfg.enable { + nixfiles.modules.nginx = { + enable = true; + upstreams.sonarr.servers."127.0.0.1:8989" = {}; + virtualHosts.${cfg.domain}.locations."/".proxyPass = "http://sonarr"; + }; + + services.sonarr.enable = true; + }; +} diff --git a/modules/nixos/sound.nix b/modules/nixos/sound.nix new file mode 100644 index 0000000..ae35e44 --- /dev/null +++ b/modules/nixos/sound.nix @@ -0,0 +1,21 @@ +{ + config, + lib, + ... +}: +with lib; let + cfg = config.nixfiles.modules.sound; +in { + options.nixfiles.modules.sound.enable = + mkEnableOption "sound support"; + + config = mkIf cfg.enable { + services.pipewire = { + enable = true; + + alsa.enable = false; + jack.enable = false; + pulse.enable = true; + }; + }; +} diff --git a/modules/nixos/syncthing.nix b/modules/nixos/syncthing.nix new file mode 100644 index 0000000..b690ab4 --- /dev/null +++ b/modules/nixos/syncthing.nix @@ -0,0 +1,145 @@ +{ + config, + inputs, + lib, + pkgs, + this, + ... +}: +with lib; let + cfg = config.nixfiles.modules.syncthing; +in { + options.nixfiles.modules.syncthing = { + enable = mkEnableOption "Syncthing"; + + domain = mkOption { + description = "Domain name sans protocol scheme."; + type = with types; str; + default = "syncthing.${config.networking.fqdn}"; + }; + }; + + config = mkIf cfg.enable (mkMerge [ + { + secrets = { + "syncthing-cert-${this.hostname}" = with config.services.syncthing; { + file = "${inputs.self}/secrets/syncthing-cert-${this.hostname}"; + owner = user; + inherit group; + }; + + "syncthing-key-${this.hostname}" = with config.services.syncthing; { + file = "${inputs.self}/secrets/syncthing-key-${this.hostname}"; + owner = user; + inherit group; + }; + }; + + services.syncthing = { + enable = true; + + user = my.username; + inherit (config.my) group; + + dataDir = config.my.home; + + guiAddress = "127.0.0.1:8384"; + + cert = config.secrets."syncthing-cert-${this.hostname}".path; + key = config.secrets."syncthing-key-${this.hostname}".path; + + overrideDevices = true; + devices = mapAttrs (name: attr: + mkIf (attr.syncthing.id != null && hasAttr "wireguard" attr) { + inherit (attr.syncthing) id; + addresses = ["tcp://${name}.${config.networking.domain}:22000"]; + introducer = this.isHeadless; + }) + my.configurations; + + overrideFolders = true; + folders = let + filterDevices = f: + attrNames (filterAttrs (_: attr: + (attr.hostname != this.hostname) + && (attr.syncthing.id != null) + && f attr) + my.configurations); + all = filterDevices (_: true); + notHeadless = filterDevices (attr: !attr.isHeadless); + notOther = filterDevices (attr: !attr.isOther); + + simple = { + type = "simple"; + params.keep = "5"; + }; + trashcan = { + type = "trashcan"; + params.cleanoutDays = "7"; + }; + in + with config.hm.xdg.userDirs; { + share = { + path = publicShare; + devices = notHeadless; + versioning = trashcan; + }; + pass = { + path = config.hm.programs.password-store.settings.PASSWORD_STORE_DIR; + devices = notOther; + versioning = trashcan; + }; + org = { + path = "${documents}/org"; + devices = all; + versioning = simple; + }; + roam = { + path = "${documents}/roam"; + devices = notOther; + versioning = simple; + }; + elfeed = { + path = "${config.my.home}/.elfeed"; + devices = notOther; + versioning = trashcan; + }; + books = { + path = "${documents}/books"; + devices = notOther; + versioning = trashcan; + }; + }; + + extraOptions = { + gui = { + insecureAdminAccess = true; + insecureSkipHostcheck = this.isHeadless; + }; + options = { + autoUpgradeIntervalH = 0; + crashReportingEnabled = false; + globalAnnounceEnabled = false; + relaysEnabled = false; + setLowPriority = this.isHeadless; + stunKeepaliveMinS = 0; + stunKeepaliveStartS = 0; + urAccepted = -1; + }; + }; + }; + + systemd.services.syncthing.environment.STNODEFAULTFOLDER = "yes"; + } + (mkIf this.isHeadless { + nixfiles.modules.nginx = { + enable = true; + upstreams.syncthing.servers.${config.services.syncthing.guiAddress} = {}; + virtualHosts.${cfg.domain} = { + locations."/".proxyPass = "http://syncthing"; + extraConfig = nginxInternalOnly; + }; + }; + }) + ]); +} diff --git a/modules/nixos/throttled.nix b/modules/nixos/throttled.nix new file mode 100644 index 0000000..f182ee1 --- /dev/null +++ b/modules/nixos/throttled.nix @@ -0,0 +1,119 @@ +{ + config, + lib, + pkgs, + ... +}: +with lib; let + cfg = config.nixfiles.modules.throttled; +in { + options.nixfiles.modules.throttled.enable = mkEnableOption "Throttled"; + + config = mkIf cfg.enable { + # Disable the module we are trying to "override". + services.throttled.enable = mkForce false; + + environment.etc."throttled.conf".text = '' + [GENERAL] + # Enable or disable the script execution + Enabled: True + # SYSFS path for checking if the system is running on AC power + Sysfs_Power_Path: /sys/class/power_supply/AC*/online + # Auto reload config on changes + Autoreload: True + + ## Settings to apply while connected to Battery power + [BATTERY] + # Update the registers every this many seconds + Update_Rate_s: 30 + # Max package power for time window #1 + PL1_Tdp_W: 29 + # Time window #1 duration + PL1_Duration_s: 28 + # Max package power for time window #2 + PL2_Tdp_W: 44 + # Time window #2 duration + PL2_Duration_S: 0.002 + # Max allowed temperature before throttling + Trip_Temp_C: 85 + # Set cTDP to normal=0, down=1 or up=2 (EXPERIMENTAL) + cTDP: 0 + # Disable BDPROCHOT (EXPERIMENTAL) + Disable_BDPROCHOT: False + + ## Settings to apply while connected to AC power + [AC] + # Update the registers every this many seconds + Update_Rate_s: 5 + # Max package power for time window #1 + PL1_Tdp_W: 44 + # Time window #1 duration + PL1_Duration_s: 28 + # Max package power for time window #2 + PL2_Tdp_W: 44 + # Time window #2 duration + PL2_Duration_S: 0.002 + # Max allowed temperature before throttling + Trip_Temp_C: 95 + # Set HWP energy performance hints to 'performance' on high load (EXPERIMENTAL) + # Uncomment only if you really want to use it + # HWP_Mode: False + # Set cTDP to normal=0, down=1 or up=2 (EXPERIMENTAL) + cTDP: 0 + # Disable BDPROCHOT (EXPERIMENTAL) + Disable_BDPROCHOT: False + + # All voltage values are expressed in mV and *MUST* be negative (i.e. undervolt)! + [UNDERVOLT.BATTERY] + # CPU core voltage offset (mV) + CORE: 0 + # Integrated GPU voltage offset (mV) + GPU: 0 + # CPU cache voltage offset (mV) + CACHE: 0 + # System Agent voltage offset (mV) + UNCORE: 0 + # Analog I/O voltage offset (mV) + ANALOGIO: 0 + + # All voltage values are expressed in mV and *MUST* be negative (i.e. undervolt)! + [UNDERVOLT.AC] + # CPU core voltage offset (mV) + CORE: 0 + # Integrated GPU voltage offset (mV) + GPU: 0 + # CPU cache voltage offset (mV) + CACHE: 0 + # System Agent voltage offset (mV) + UNCORE: 0 + # Analog I/O voltage offset (mV) + ANALOGIO: 0 + + # [ICCMAX.AC] + # # CPU core max current (A) + # CORE: + # # Integrated GPU max current (A) + # GPU: + # # CPU cache max current (A) + # CACHE: + + # [ICCMAX.BATTERY] + # # CPU core max current (A) + # CORE: + # # Integrated GPU max current (A) + # GPU: + # # CPU cache max current (A) + # CACHE: + ''; + + systemd.services.throttled = { + description = "Stop Intel throttling"; + serviceConfig = { + Type = "simple"; + ExecStart = "${pkgs.throttled}/opt/throttled/throttled.py"; + }; + environment.PYTHONUNBUFFERED = "1"; + wantedBy = ["multi-user.target"]; + }; + }; +} diff --git a/modules/nixos/unbound.nix b/modules/nixos/unbound.nix new file mode 100644 index 0000000..8c40291 --- /dev/null +++ b/modules/nixos/unbound.nix @@ -0,0 +1,197 @@ +{ + config, + lib, + pkgs, + this, + ... +}: +with lib; let + cfg = config.nixfiles.modules.unbound; +in { + options.nixfiles.modules.unbound = { + enable = mkEnableOption "Unbound"; + + domain = mkOption { + description = "Domain name sans protocol scheme."; + type = with types; str; + default = config.networking.domain; + }; + }; + + config = let + adblock-conf = "${config.services.unbound.stateDir}/adblock.conf"; + in + mkIf cfg.enable { + services = { + unbound = { + enable = true; + + package = pkgs.unbound-with-systemd.override { + withRedis = true; + withTFO = true; + }; + + settings = { + server = { + interface = with this.wireguard; [ + "127.0.0.1" + "::1" + ipv4.address + ipv6.address + ]; + + local-zone = + concatLists + (mapAttrsToList (h: _: [''"${h}.${cfg.domain}" redirect'']) + my.configurations); + local-data = concatLists (mapAttrsToList (hostname: let + domain = "${hostname}.${cfg.domain}"; + in + attr: (optionals (hasAttr "wireguard" attr) (with attr.wireguard; + [ + ''"${domain} 604800 IN A ${ipv4.address}"'' + ''"${domain} 604800 IN AAAA ${ipv6.address}"'' + ''"${domain}. A ${ipv4.address}"'' + ''"${domain}. AAAA ${ipv6.address}"'' + ] + ++ concatMap (domain: [ + ''"${domain}. A ${ipv4.address}"'' + ''"${domain}. AAAA ${ipv6.address}"'' + ]) + attr.domains))) + my.configurations); + local-data-ptr = concatLists (mapAttrsToList (hostname: let + domain = "${hostname}.${cfg.domain}"; + in + attr: (optionals (hasAttr "wireguard" attr) (with attr.wireguard; + [ + ''"${ipv4.address} ${domain}"'' + ''"${ipv6.address} ${domain}"'' + ] + ++ concatMap (domain: [ + ''"${ipv4.address} ${domain}"'' + ''"${ipv6.address} ${domain}"'' + ]) + attr.domains))) + my.configurations); + + access-control = with config.nixfiles.modules.wireguard; [ + "0.0.0.0/0 refuse" + "::/0 refuse" + "127.0.0.0/8 allow" + "::1/128 allow" + "${ipv4.subnet} allow" + "${ipv6.subnet} allow" + ]; + + private-domain = cfg.domain; + private-address = with config.nixfiles.modules.wireguard; [ + ipv4.subnet + ipv6.subnet + ]; + + domain-insecure = cfg.domain; + + prefetch = true; + prefetch-key = true; + + hide-identity = true; + hide-version = true; + + extended-statistics = true; + + include = ''"${adblock-conf}"''; + }; + + forward-zone = [ + { + name = "."; + forward-tls-upstream = true; + forward-addr = let + mkDnsOverTls = ips: auth: map (ip: concatStrings [ip "@" auth]) ips; + in + mkDnsOverTls dns.const.quad9.default "853#dns.quad9.net"; + } + ]; + + cachedb = with config.services.redis.servers.unbound; { + backend = "redis"; + redis-server-host = bind; + redis-server-port = port; + }; + }; + + localControlSocketPath = "/run/unbound/unbound.socket"; + }; + + redis = { + servers.unbound = { + enable = true; + bind = "127.0.0.1"; + port = 6379; + }; + vmOverCommit = mkForce true; + }; + + prometheus.exporters = { + unbound = { + enable = true; + listenAddress = mkDefault this.wireguard.ipv4.address; + port = 9167; + fetchType = "uds"; + controlInterface = config.services.unbound.localControlSocketPath; + inherit (config.services.unbound) group user; + }; + + redis = { + enable = true; + listenAddress = mkDefault this.wireguard.ipv4.address; + port = mkDefault 9121; + extraFlags = with config.services.redis.servers.unbound; [ + "--redis.addr=redis://${bind}:${toString port}" + "--redis.user=${user}" + ]; + }; + }; + }; + + systemd = { + services = { + unbound.after = ["unbound-adblock-update.service"]; + + unbound-adblock-update = { + serviceConfig = with config.services.unbound; { + Type = "oneshot"; + User = user; + Group = group; + ExecStart = let + pkg = with pkgs; + writeShellApplication { + name = "unbound-adblock-update"; + runtimeInputs = [curl package]; + text = '' + curl \ + "https://pgl.yoyo.org/adservers/serverlist.php?hostformat=unbound&showintro=0&mimetype=plaintext" \ + >${adblock-conf} + + if [[ -f "${localControlSocketPath}" ]]; then + unbound-control reload + fi + ''; + }; + in "${pkg}/bin/unbound-adblock-update"; + }; + }; + }; + + timers.unbound-adblock-update = { + requires = ["network-online.target"]; + timerConfig = { + OnUnitActiveSec = "1d"; + Unit = "unbound-adblock-update.service"; + }; + wantedBy = ["timers.target"]; + }; + }; + }; +} diff --git a/modules/nixos/vaultwarden.nix b/modules/nixos/vaultwarden.nix new file mode 100644 index 0000000..7d51667 --- /dev/null +++ b/modules/nixos/vaultwarden.nix @@ -0,0 +1,134 @@ +{ + config, + inputs, + lib, + ... +}: +with lib; let + cfg = config.nixfiles.modules.vaultwarden; +in { + options.nixfiles.modules.vaultwarden = { + enable = mkEnableOption "Vaultwarden"; + + domain = mkOption { + description = "Domain name sans protocol scheme."; + type = with types; str; + default = "vaultwarden.${config.networking.domain}"; + }; + }; + + config = let + db = "vaultwarden"; + in + mkIf cfg.enable { + secrets.vaultwarden-environment = { + file = "${inputs.self}/secrets/vaultwarden-environment"; + owner = "vaultwarden"; + group = "vaultwarden"; + }; + + nixfiles.modules = { + nginx = { + enable = true; + upstreams = with config.services.vaultwarden.config; { + vaultwarden_rocket.servers."${ROCKET_ADDRESS}:${toString ROCKET_PORT}" = {}; + vaultwarden_websocket.servers."${WEBSOCKET_ADDRESS}:${toString WEBSOCKET_PORT}" = {}; + }; + virtualHosts.${cfg.domain} = { + locations."/" = { + proxyPass = "http://vaultwarden_rocket"; + proxyWebsockets = true; + }; + locations."/notifications/hub" = { + proxyPass = "http://vaultwarden_websocket"; + proxyWebsockets = true; + }; + locations."/notifications/hub/negotiate" = { + proxyPass = "http://vaultwarden_rocket"; + proxyWebsockets = true; + }; + }; + }; + postgresql = { + enable = true; + extraPostStart = [ + '' + $PSQL "${db}" -tAc 'GRANT ALL ON SCHEMA "public" TO "${db}"' + '' + ]; + }; + }; + + services = { + vaultwarden = { + enable = true; + config = { + TZ = config.time.timeZone; + + WEB_VAULT_ENABLED = true; + + DOMAIN = optionalString (cfg.domain != null) "http://${cfg.domain}"; + + SIGNUPS_ALLOWED = false; + INVITATIONS_ALLOWED = false; + + ORG_CREATION_USERS = "none"; + + PASSWORD_HINTS_ALLOWED = false; + SHOW_PASSWORD_HINT = false; + + ROCKET_ADDRESS = "127.0.0.1"; + ROCKET_PORT = 8812; + + WEBSOCKET_ENABLED = true; + WEBSOCKET_ADDRESS = "127.0.0.1"; + WEBSOCKET_PORT = 8813; + + LOG_LEVEL = "error"; + + DATABASE_URL = "postgresql://${db}@/${db}"; + }; + dbBackend = "postgresql"; + environmentFile = config.secrets.vaultwarden-environment.path; + }; + + postgresql = { + ensureDatabases = [db]; + ensureUsers = [ + { + name = db; + ensurePermissions."DATABASE \"${db}\"" = "ALL"; + } + ]; + }; + + fail2ban.jails = mkIf config.nixfiles.modules.fail2ban.enable { + vaultwarden = '' + enabled = true + filter = vaultwarden + port = http,https + ''; + vaultwarden-admin = '' + enabled = true + filter = vaultwarden-admin + port = http,https + ''; + }; + }; + + environment.etc = mkIf config.nixfiles.modules.fail2ban.enable { + "fail2ban/filter.d/vaultwarden.conf".text = '' + [Definition] + failregex = ^.*Username or password is incorrect\. Try again\. IP: <ADDR>\. Username:.*$ + ignoreregex = + journalmatch = _SYSTEMD_UNIT=vaultwarden.service + ''; + "fail2ban/filter.d/vaultwarden-admin.conf".text = '' + [Definition] + failregex = ^.*Invalid admin token\. IP: <ADDR>.*$ + ignoreregex = + journalmatch = _SYSTEMD_UNIT=vaultwarden.service + ''; + }; + }; +} diff --git a/modules/nixos/wireguard.nix b/modules/nixos/wireguard.nix new file mode 100644 index 0000000..d05c6ae --- /dev/null +++ b/modules/nixos/wireguard.nix @@ -0,0 +1,198 @@ +{ + config, + inputs, + lib, + pkgs, + this, + ... +}: +with lib; let + cfg = config.nixfiles.modules.wireguard; +in { + options.nixfiles.modules.wireguard = { + client = { + enable = mkEnableOption "WireGuard client"; + + enableTrafficRouting = mkOption { + description = "Whether to enable traffic routing through the sever."; + type = with types; bool; + default = !this.isHeadless; + }; + }; + + server = { + enable = mkEnableOption "WireGuard server"; + + ipv4.address = mkOption { + description = "IPv4 address to bind to."; + type = with types; str; + default = my.configurations.manwe.wireguard.ipv4.address; + }; + + ipv6.address = mkOption { + description = "IPv4 address to bind to."; + type = with types; str; + default = my.configurations.manwe.wireguard.ipv6.address; + }; + + address = mkOption { + description = "Endpoint address to use"; + type = with types; str; + default = my.configurations.manwe.ipv4.address; + }; + + port = mkOption { + description = "Endpoint port to use."; + type = with types; int; + default = 6969; + }; + + publicKey = mkOption { + description = "Server's public key."; + type = with types; str; + default = my.configurations.manwe.wireguard.publicKey; + }; + + peers = mkOption { + description = "List of peers."; + type = with types; listOf attrs; + default = mapAttrsToList (_: attr: + with attr; { + inherit (wireguard) publicKey; + allowedIPs = with wireguard; [ + "${ipv4.address}/32" + "${ipv6.address}/128" + ]; + }) (filterAttrs (_: attr: + attr.hostname != this.hostname && hasAttr "wireguard" attr) + my.configurations); + }; + }; + + interface = mkOption { + description = "Name of the interface to use WireGuard with."; + type = with types; str; + default = "wg69"; + }; + + ipv4.subnet = mkOption { + description = "CIDR notation for the IPv4 subnet to use over WireGuard."; + type = with types; str; + default = "10.69.0.0/16"; + }; + + ipv6.subnet = mkOption { + description = "CIDR notation for the IPv6 subnet to use over WireGuard."; + type = with types; str; + default = "fd69::/16"; + }; + }; + + config = + { + assertions = [ + { + assertion = config.security.sudo.enable; + message = "Sudo is not enabled."; + } + { + assertion = any (x: x == "wheel") config.my.extraGroups; + message = ''User is not in the "wheel" group.''; + } + ]; + } + // mkMerge [ + (mkIf (cfg.client.enable || cfg.server.enable) { + secrets."wireguard-private-key-${this.hostname}".file = "${inputs.self}/secrets/wireguard-private-key-${this.hostname}"; + + networking.firewall.trustedInterfaces = [cfg.interface]; + }) + (mkIf cfg.client.enable { + networking.wg-quick.interfaces.${cfg.interface} = mkMerge [ + (with this.wireguard; { + privateKeyFile = config.secrets."wireguard-private-key-${this.hostname}".path; + address = ["${ipv4.address}/16" "${ipv6.address}/16"]; + }) + (with cfg.server; { + peers = [ + { + inherit publicKey; + endpoint = "${address}:${toString port}"; + allowedIPs = + if cfg.client.enableTrafficRouting + then [ + "0.0.0.0/0" + "::/0" + ] + else [ + cfg.ipv4.subnet + cfg.ipv6.subnet + ]; + persistentKeepalive = 25; + } + ]; + dns = [ + ipv4.address + ipv6.address + ]; # This assumes that the host has Unbound running. + }) + ]; + + environment.systemPackages = with pkgs; [ + (writeShellApplication { + name = "wg-toggle"; + runtimeInputs = [iproute2 jq wireguard-tools]; + text = '' + ip46() { + sudo ip -4 "$@" + sudo ip -6 "$@" + } + + fwmark=$(sudo wg show ${cfg.interface} fwmark) || exit + if ip -j rule list lookup "$fwmark" | jq -e 'length > 0' >/dev/null; then + ip46 rule del lookup main suppress_prefixlength 0 + ip46 rule del lookup "$fwmark" + else + ip46 rule add not fwmark "$fwmark" lookup "$fwmark" + ip46 rule add lookup main suppress_prefixlength 0 + fi + ''; + }) + ]; + }) + (mkIf cfg.server.enable { + networking = { + wireguard = { + enable = true; + interfaces.${cfg.interface} = with cfg.server; { + privateKeyFile = config.secrets."wireguard-private-key-${this.hostname}".path; + ips = ["${ipv4.address}/16" "${ipv6.address}/16"]; + listenPort = port; + inherit peers; + allowedIPsAsRoutes = false; + }; + }; + + nat = { + enable = true; + enableIPv6 = true; + + externalInterface = mkDefault "eth0"; + + internalInterfaces = [cfg.interface]; + internalIPs = [cfg.ipv4.subnet]; + internalIPv6s = [cfg.ipv6.subnet]; + }; + + firewall.allowedUDPPorts = [cfg.server.port]; + }; + + services.prometheus.exporters.wireguard = { + enable = true; + listenAddress = mkDefault this.wireguard.ipv4.address; + withRemoteIp = true; + port = 9586; + }; + }) + ]; +} diff --git a/modules/nixos/x11.nix b/modules/nixos/x11.nix new file mode 100644 index 0000000..cd8dfbe --- /dev/null +++ b/modules/nixos/x11.nix @@ -0,0 +1,92 @@ +{ + config, + lib, + pkgs, + ... +}: +with lib; let + cfg = config.nixfiles.modules.x11; +in { + options.nixfiles.modules.x11.enable = mkEnableOption "X11"; + + config = mkIf cfg.enable { + nixfiles.modules.fonts.enable = true; + + hm = { + home.sessionVariables = with config.dirs; { + XCOMPOSEFILE = "${cache}/XComposeFile"; + XCOMPOSECACHE = "${cache}/XComposeCache"; + }; + + xsession.scriptPath = ".xinitrc"; + + xresources.properties = + (let + font = with config.fontScheme.monospaceFont; "${family}:style=${style}:size=${toString size}"; + in { + "*.font" = font; + + "Xft.antialias" = 1; + "Xft.autohint" = 0; + "Xft.dpi" = 96; + "Xft.hinting" = 1; + "Xft.hintstyle" = "hintslight"; + "Xft.lcdfilter" = "lcddefault"; + "Xft.rgba" = "rgb"; + }) + // (with config.colourScheme; { + "*.color0" = black; + "*.color8" = brightBlack; + "*.color1" = red; + "*.color9" = brightRed; + "*.color2" = green; + "*.color10" = brightGreen; + "*.color3" = yellow; + "*.color11" = brightYellow; + "*.color4" = blue; + "*.color12" = brightBlue; + "*.color5" = magenta; + "*.color13" = brightMagenta; + "*.color6" = cyan; + "*.color14" = brightCyan; + "*.color7" = white; + "*.color15" = brightWhite; + + "*.background" = background; + "*.foreground" = foreground; + }); + }; + + services.xserver = { + enable = true; + + tty = mkDefault 1; + + autoRepeatDelay = 200; + autoRepeatInterval = 25; + + libinput.enable = true; + + monitorSection = '' + Option "DPMS" "false" + ''; + + serverFlagsSection = '' + Option "BlankTime" "0" + Option "OffTime" "0" + Option "StandbyTime" "0" + Option "SuspendTime" "0" + ''; + + inputClassSections = [ + '' + Identifier "Mouse" + MatchIsPointer "yes" + Option "AccelerationNumerator" "2" + Option "AccelerationDenominator" "1" + Option "AccelerationThreshold" "4" + '' + ]; + }; + }; +} diff --git a/modules/nixos/xmonad.nix b/modules/nixos/xmonad.nix new file mode 100644 index 0000000..2cc7ad6 --- /dev/null +++ b/modules/nixos/xmonad.nix @@ -0,0 +1,28 @@ +{ + config, + inputs, + lib, + pkgs, + ... +}: +with lib; let + cfg = config.nixfiles.modules.xmonad; +in { + options.nixfiles.modules.xmonad.enable = mkEnableOption "XMonad"; + + config = mkIf cfg.enable { + nixfiles.modules.x11.enable = true; + + hm = { + xsession = { + enable = true; + + scriptPath = ".xinitrc"; + + windowManager.command = "${pkgs.xmonad-ng}/bin/xmonad-ng"; + }; + }; + + services.xserver.displayManager.startx.enable = true; + }; +} |