ctucx.git: nixfiles

ctucx' nixfiles

commit 67f679aaa6b1327f866c596b1f241123be4e0350
parent ae494a36a6467b28f55fd5c96c9cec0868a0332c
Author: Leah (ctucx) <leah@ctu.cx>
Date: Thu, 27 Jan 2022 20:40:22 +0100

modules: add vnstati module
3 files changed, 188 insertions(+), 0 deletions(-)
M
modules/default.nix
|
1
+
A
modules/vnstati/default.nix
|
91
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
A
modules/vnstati/vnstati-html.nix
|
96
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
diff --git a/modules/default.nix b/modules/default.nix
@@ -2,6 +2,7 @@
 
   imports = [
     ./restic-backups.nix
+    ./vnstati
   ];
 
 }
diff --git a/modules/vnstati/default.nix b/modules/vnstati/default.nix
@@ -0,0 +1,91 @@
+{ config, lib, pkgs, ... }:
+
+let
+  cfg = config.services.vnstati;
+
+in {
+
+  options.services.vnstati = with lib; {
+
+    enable = mkEnableOption "just some fancy traffic pics";
+
+    title = mkOption {
+      type    = types.str;
+      default = "${if config.networking ? domain then config.networking.hostName else config.networking.fqdn}";
+    };
+
+    domain = mkOption {
+      type    = types.str;
+      default = "${config.networking.fqdn}";
+    };
+
+    subdirectory = mkOption {
+      type    = types.str;
+      default = "/traffic/";
+    };
+
+  };
+
+  config = lib.mkIf cfg.enable {
+
+    assertions = [
+      ({
+        assertion = config.services.vnstat.enable;
+        message   = "vnstati requires vnstat.enable == true";
+      })
+    ];
+
+
+    fileSystems."/var/lib/vnstati" = {
+      device  = "tmpfs";
+      fsType  = "tmpfs";
+      options = [ "rw" "size=30M" ];
+    };
+
+    services.nginx.virtualHosts.${cfg.domain} = {
+      locations.${cfg.subdirectory} = {
+        alias = "/var/lib/vnstati/";
+        index = "index.html";
+      };
+    };
+
+    systemd.services.vnstati = {
+      wantedBy      = [ "multi-user.target" ];
+      after         = [ "var-lib-vnstati.mount" "vnstat.service" ];
+      startAt       = "*-*-* *:0/10:00";
+      path          = with pkgs; [ vnstat jq nix ];
+
+      serviceConfig = {
+        User           = "vnstatd";
+        Group          = "vnstatd";
+        StateDirectory = "vnstati";
+        PrivateTmp     = true;
+        ProtectHome    = true;
+        ProtectSystem  = "strict";
+        Environment    = "NIX_PATH=/etc/src";
+      };
+
+      script = ''
+        set -x
+        ifaces=$(vnstat --json | jq -r .interfaces[].name | grep -v "^lo$")
+        echo $ifaces
+
+        nix eval --raw -f ${./vnstati-html.nix} html \
+          --argstr ifaces "$ifaces" \
+          --argstr hostname "${cfg.title}" \
+          > /var/lib/vnstati/index.html
+
+        for iface in $ifaces
+        do
+          vnstati -s -nh -i $iface -o /var/lib/vnstati/$iface-summary.png
+          vnstati -h -nh -i $iface -o /var/lib/vnstati/$iface-hourly.png
+          vnstati -d -nh -i $iface -o /var/lib/vnstati/$iface-daily.png
+          vnstati -m -nh -i $iface -o /var/lib/vnstati/$iface-monthly.png
+          vnstati -t -nh -i $iface -o /var/lib/vnstati/$iface-top10.png
+        done
+      '';
+    };
+
+  };
+
+}
diff --git a/modules/vnstati/vnstati-html.nix b/modules/vnstati/vnstati-html.nix
@@ -0,0 +1,96 @@
+{ ifaces, hostname }:
+
+let
+  pkgs    = import <nixpkgs> {};
+  lib     = pkgs.lib;
+  ifacesL = lib.splitString "\n" ifaces;
+
+  types = [
+    { name = "summary"; title = "Summary"; }
+    { name = "hourly"; title = "Hourly"; }
+    { name = "daily"; title = "Daily"; }
+    { name = "monthly"; title = "Monthly"; }
+    { name = "top10"; title = "Top 10"; }
+  ];
+
+in {
+
+  html = ''
+    <!DOCTYPE html>
+    <html lang="en">
+      <head>
+        <title>${hostname} - stats</title>
+        <meta charset="utf-8">
+        <meta name="robots" content="none">
+        <link rel="icon" href="static/favicon.ico" type="image/x-icon">
+        <style>
+    body {
+        padding: 0;
+        margin: 0;
+        background: #eee;
+        font-family: monospace;
+        min-height: 100vh;
+    }
+    img:not(:last-child) {
+        margin-bottom: 15px;
+    }
+    img {
+        flex-shrink: 0;
+    }
+    .ui.segment {
+        margin: 0 1em 10px 1em;
+        padding: 1em;
+        background-color: white;
+        box-shadow: 0 1px 2px 0 rgba(34,36,38,.15);
+        border-radius: .286rem;
+        border: 1px solid rgba(34,36,38,.15);
+        display: flex;
+        flex-direction: column;
+        align-items: center;
+        justify-content: center;
+
+    }
+    div.heading {
+        min-width: 100vw;
+        height: 70px;
+        display: flex;
+        flex-direction: column;
+        align-items: center;
+        justify-content: center;
+    }
+    div.table.vertical {
+        overflow-y: auto;
+        min-width: 100vw;
+        height: calc(100vh - 70px);
+        display: flex;
+        flex-direction: row;
+    }
+    div.iface {
+        display: flex;
+        flex-direction: column;
+        align-items: center;
+        flex-grow: 1;
+    }
+        </style>
+      </head>
+      <body>
+        <div class="heading">
+          <h1>${hostname}</h1>
+        </div>
+        <div class="table vertical">
+          ${lib.concatMapStringsSep "\n" (iface: ''
+            <div class="iface">
+              <h3>${iface}</h3>
+              ${lib.concatMapStrings (type: ''
+                <div class="ui segment">
+                  <img src="${iface}-${type.name}.png" alt="${type.title}">
+                </div>
+              '') types}
+            </div>
+          '') ifacesL}
+        </div>
+      </body>
+    </html>
+  '';
+
+}