Skip to content
Snippets Groups Projects
grafana.nix 4.43 KiB
Newer Older
  • Learn to ignore specific revisions
  • # Grafana Server
    #
    # Scope: Beautiful plots of time series data retrieved from Prometheus
    # See https://christine.website/blog/prometheus-grafana-loki-nixos-2020-11-20
    
    { config, lib, ... }:
    
    let
      cfg = config.services.private-storage.monitoring.grafana;
    
      grafanaAuth = if (cfg.googleOAuthClientID == "") then {
                      anonymous.enable = true;
                    } else {
                      google.enable = true;
    
                      # Grafana considers it "sign up" to let in a user it has
                      # never seen before.
                      google.allowSignUp = true;
    
    Florian Sesser's avatar
    Florian Sesser committed
                      google.clientSecretFile = cfg.googleOAuthClientSecretFile;
                      google.clientId = cfg.googleOAuthClientID;
    
    
    in {
      options.services.private-storage.monitoring.grafana = {
        domain = lib.mkOption
        { type = lib.types.str;
          example = lib.literalExample "grafana.grid.private.storage";
          description = "The FQDN of the Grafana host";
        };
        prometheusUrl = lib.mkOption
        { type = lib.types.str;
          example = lib.literalExample "http://prometheus:9090/";
    
          default = "http://localhost:9090/";
    
          description = "The URL of the Prometheus host to access";
        };
        lokiUrl = lib.mkOption
        { type = lib.types.str;
          example = lib.literalExample "http://loki:3100/";
    
          default = "http://localhost:3100/";
    
          description = "The URL of the Loki host to access";
        };
    
        letsEncryptAdminEmail = lib.mkOption
        { type = lib.types.str;
          description = ''
            An email address to give to Let's Encrypt as an
            operational contact for the service's TLS certificate.
          '';
        };
    
        googleOAuthClientID = lib.mkOption
        { type = lib.types.str;
          example = lib.literalExample "grafana-staging-345678";
          default = "replace-by-your-client-id-or-set-empty-string-for-anonymous-access";
          description = "The GSuite OAuth2 SSO Client ID.  Empty string turns SSO auth off and anonymous (free for all) access on.";
        };
        googleOAuthClientSecretFile = lib.mkOption
        { type = lib.types.path;
    
          example = lib.literalExample "/var/secret/monitoring-gsuite-client-secret";
    
          default = /run/keys/grafana-google-sso.secret;
    
          description = "The path to the GSuite SSO secret file.";
        };
    
        # Port 80 for ACME ssl retrieval only. 443 for nginx -> grafana.
        networking.firewall.allowedTCPPorts = [ 80 443 ];
    
    
        services.grafana = {
          enable = true;
          domain = cfg.domain;
          port = 2342;
          addr = "127.0.0.1";
    
    
          # No phoning home
          analytics.reporting.enable = false;
    
    
          # Force Grafana to believe it is reachable via https on the default port
          # number because that's where the nginx that forwards traffic to it is
          # listening.  Grafana's own server listens on an internal address that
          # doesn't matter to anyone except our nginx instance.
          rootUrl = "https://%(domain)s/";
    
        services.grafana.auth = {
          anonymous.org_role = "Admin";
          anonymous.org_name = "Main Org.";
        } // grafanaAuth;
    
    
        services.grafana.provision = {
          enable = true;
          # See https://grafana.com/docs/grafana/latest/administration/provisioning/#datasources
          datasources = [{
            name = "Prometheus";
            type = "prometheus";
            access = "proxy";
            url = cfg.prometheusUrl;
            isDefault = true;
          } {
            name = "Loki";
            type = "loki";
            access = "proxy";
            url = cfg.lokiUrl;
          }];
          # See https://grafana.com/docs/grafana/latest/administration/provisioning/#dashboards
          dashboards = [{
            name = "provisioned";
            options.path = ./grafana-config;
          }];
        };
    
        # nginx reverse proxy
    
        security.acme.email = cfg.letsEncryptAdminEmail;
        security.acme.acceptTerms = true;
    
        services.nginx = {
          enable = true;
    
          recommendedGzipSettings = true;
          recommendedOptimisation = true;
          recommendedProxySettings = true;
          recommendedTlsSettings = true;
    
          # Only allow PFS-enabled ciphers with AES256:
          sslCiphers = "AES256+EECDH:AES256+EDH:!aNULL";
    
          virtualHosts.${config.services.grafana.domain} = {
            enableACME = true;
    
            locations."/" = {
              proxyPass = "http://127.0.0.1:${toString config.services.grafana.port}";
              proxyWebsockets = true;
            };
    
    
        # Let Grafana read from keys, if necessary.
        users.users.grafana.extraGroups = [ "keys" ];