The current default probe config uses the unwrapped fping binary, which leads to an error because fping must be executed with elevated permissions. I fixed this by changing the path to the default binary to the setuid-wrapped version.
		
			
				
	
	
		
			305 lines
		
	
	
		
			9.3 KiB
		
	
	
	
		
			Nix
		
	
	
	
	
	
			
		
		
	
	
			305 lines
		
	
	
		
			9.3 KiB
		
	
	
	
		
			Nix
		
	
	
	
	
	
{ config, lib, pkgs, ... }:
 | 
						|
 | 
						|
with lib;
 | 
						|
let
 | 
						|
 | 
						|
  cfg = config.services.smokeping;
 | 
						|
  smokepingHome = "/var/lib/smokeping";
 | 
						|
  smokepingPidDir = "/run";
 | 
						|
  configFile =
 | 
						|
    if cfg.config == null
 | 
						|
      then
 | 
						|
        ''
 | 
						|
          *** General ***
 | 
						|
          owner = ${cfg.owner}
 | 
						|
          contact = ${cfg.ownerEmail}
 | 
						|
          ${lib.optionalString (cfg.mailHost != "") "mailhost = ${cfg.mailHost}"}
 | 
						|
          ${lib.optionalString (cfg.sendmail != null) "sendmail = ${cfg.sendmail}"}
 | 
						|
          imgcache = ${smokepingHome}/cache
 | 
						|
          imgurl   = http://${cfg.hostName}:${builtins.toString cfg.port}/cache
 | 
						|
          datadir  = ${smokepingHome}/data
 | 
						|
          pagedir = ${smokepingHome}/cache
 | 
						|
          piddir  = ${smokepingPidDir}
 | 
						|
          cgiurl   = http://${cfg.hostName}:${builtins.toString cfg.port}/smokeping.cgi
 | 
						|
          linkstyle = ${cfg.linkStyle}
 | 
						|
          smokemail = ${cfg.smokeMailTemplate}
 | 
						|
          *** Presentation ***
 | 
						|
          template = ${cfg.presentationTemplate}
 | 
						|
          ${cfg.presentationConfig}
 | 
						|
          *** Alerts ***
 | 
						|
          ${cfg.alertConfig}
 | 
						|
          *** Database ***
 | 
						|
          ${cfg.databaseConfig}
 | 
						|
          *** Probes ***
 | 
						|
          ${cfg.probeConfig}
 | 
						|
          *** Targets ***
 | 
						|
          ${cfg.targetConfig}
 | 
						|
          ${cfg.extraConfig}
 | 
						|
        ''
 | 
						|
      else
 | 
						|
        cfg.config;
 | 
						|
 | 
						|
  configPath = pkgs.writeText "smokeping.conf" configFile;
 | 
						|
  cgiHome = pkgs.writeScript "smokeping.fcgi" ''
 | 
						|
    #!${pkgs.bash}/bin/bash
 | 
						|
    ${cfg.package}/bin/smokeping_cgi ${configPath}
 | 
						|
  '';
 | 
						|
in
 | 
						|
 | 
						|
{
 | 
						|
  options = {
 | 
						|
    services.smokeping = {
 | 
						|
      enable = mkOption {
 | 
						|
        type = types.bool;
 | 
						|
        default = false;
 | 
						|
        description = "Enable the smokeping service";
 | 
						|
      };
 | 
						|
      webService = mkOption {
 | 
						|
        type = types.bool;
 | 
						|
        default = true;
 | 
						|
        description = "Enable a smokeping web interface";
 | 
						|
      };
 | 
						|
 | 
						|
      user = mkOption {
 | 
						|
        type = types.string;
 | 
						|
        default = "smokeping";
 | 
						|
        description = "User that runs smokeping and (optionally) thttpd";
 | 
						|
      };
 | 
						|
      mailHost = mkOption {
 | 
						|
        type = types.string;
 | 
						|
        default = "";
 | 
						|
        example = "localhost";
 | 
						|
        description = "Use this SMTP server to send alerts";
 | 
						|
      };
 | 
						|
      sendmail = mkOption {
 | 
						|
        type = types.nullOr types.path;
 | 
						|
        default = null;
 | 
						|
        example = "/var/setuid-wrappers/sendmail";
 | 
						|
        description = "Use this sendmail compatible script to deliver alerts";
 | 
						|
      };
 | 
						|
      smokeMailTemplate = mkOption {
 | 
						|
        type = types.string;
 | 
						|
        default = "${cfg.package}/etc/smokemail.dist";
 | 
						|
        description = "Specify the smokemail template for alerts.";
 | 
						|
      };
 | 
						|
 | 
						|
      package = mkOption {
 | 
						|
        type = types.package;
 | 
						|
        default = pkgs.smokeping;
 | 
						|
        defaultText = "pkgs.smokeping";
 | 
						|
        description = "Specify a custom smokeping package";
 | 
						|
      };
 | 
						|
      owner = mkOption {
 | 
						|
        type = types.string;
 | 
						|
        default = "nobody";
 | 
						|
        example = "Joe Admin";
 | 
						|
        description = "Real name of the owner of the instance";
 | 
						|
      };
 | 
						|
      hostName = mkOption {
 | 
						|
        type = types.string;
 | 
						|
        default = config.networking.hostName;
 | 
						|
        example = "somewhere.example.com";
 | 
						|
        description = "DNS name for the urls generated in the cgi.";
 | 
						|
      };
 | 
						|
      linkStyle = mkOption {
 | 
						|
        type = types.enum ["original" "absolute" "relative"];
 | 
						|
        default = "relative";
 | 
						|
        example = "absolute";
 | 
						|
        description = "DNS name for the urls generated in the cgi.";
 | 
						|
      };
 | 
						|
      port = mkOption {
 | 
						|
        type = types.int;
 | 
						|
        default = 8081;
 | 
						|
        example = 8081;
 | 
						|
        description = "TCP port to use for the web server.";
 | 
						|
      };
 | 
						|
      ownerEmail = mkOption {
 | 
						|
        type = types.string;
 | 
						|
        default = "no-reply@${cfg.hostName}";
 | 
						|
        example = "no-reply@yourdomain.com";
 | 
						|
        description = "Email contact for owner";
 | 
						|
      };
 | 
						|
 | 
						|
      databaseConfig = mkOption {
 | 
						|
        type = types.string;
 | 
						|
        default = ''
 | 
						|
          step     = 300
 | 
						|
          pings    = 20
 | 
						|
          # consfn mrhb steps total
 | 
						|
          AVERAGE  0.5   1  1008
 | 
						|
          AVERAGE  0.5  12  4320
 | 
						|
              MIN  0.5  12  4320
 | 
						|
              MAX  0.5  12  4320
 | 
						|
          AVERAGE  0.5 144   720
 | 
						|
              MAX  0.5 144   720
 | 
						|
              MIN  0.5 144   720
 | 
						|
 | 
						|
        '';
 | 
						|
        example = literalExample ''
 | 
						|
          # near constant pings.
 | 
						|
					step     = 30
 | 
						|
					pings    = 20
 | 
						|
					# consfn mrhb steps total
 | 
						|
					AVERAGE  0.5   1  10080
 | 
						|
					AVERAGE  0.5  12  43200
 | 
						|
							MIN  0.5  12  43200
 | 
						|
							MAX  0.5  12  43200
 | 
						|
					AVERAGE  0.5 144   7200
 | 
						|
							MAX  0.5 144   7200
 | 
						|
							MIN  0.5 144   7200
 | 
						|
				'';
 | 
						|
        description = ''Configure the ping frequency and retention of the rrd files.
 | 
						|
          Once set, changing the interval will require deletion or migration of all
 | 
						|
          the collected data.'';
 | 
						|
      };
 | 
						|
      alertConfig = mkOption {
 | 
						|
        type = types.string;
 | 
						|
        default = ''
 | 
						|
          to = root@localhost
 | 
						|
          from = smokeping@localhost
 | 
						|
        '';
 | 
						|
        example = literalExample ''
 | 
						|
          to = alertee@address.somewhere
 | 
						|
          from = smokealert@company.xy
 | 
						|
 | 
						|
          +someloss
 | 
						|
          type = loss
 | 
						|
          # in percent
 | 
						|
          pattern = >0%,*12*,>0%,*12*,>0%
 | 
						|
          comment = loss 3 times  in a row;
 | 
						|
        '';
 | 
						|
        description = "Configuration for alerts.";
 | 
						|
      };
 | 
						|
      presentationTemplate = mkOption {
 | 
						|
        type = types.string;
 | 
						|
        default = "${pkgs.smokeping}/etc/basepage.html.dist";
 | 
						|
        description = "Default page layout for the web UI.";
 | 
						|
      };
 | 
						|
 | 
						|
      presentationConfig = mkOption {
 | 
						|
        type = types.string;
 | 
						|
        default = ''
 | 
						|
          + charts
 | 
						|
          menu = Charts
 | 
						|
          title = The most interesting destinations
 | 
						|
          ++ stddev
 | 
						|
          sorter = StdDev(entries=>4)
 | 
						|
          title = Top Standard Deviation
 | 
						|
          menu = Std Deviation
 | 
						|
          format = Standard Deviation %f
 | 
						|
          ++ max
 | 
						|
          sorter = Max(entries=>5)
 | 
						|
          title = Top Max Roundtrip Time
 | 
						|
          menu = by Max
 | 
						|
          format = Max Roundtrip Time %f seconds
 | 
						|
          ++ loss
 | 
						|
          sorter = Loss(entries=>5)
 | 
						|
          title = Top Packet Loss
 | 
						|
          menu = Loss
 | 
						|
          format = Packets Lost %f
 | 
						|
          ++ median
 | 
						|
          sorter = Median(entries=>5)
 | 
						|
          title = Top Median Roundtrip Time
 | 
						|
          menu = by Median
 | 
						|
          format = Median RTT %f seconds
 | 
						|
          + overview
 | 
						|
          width = 600
 | 
						|
          height = 50
 | 
						|
          range = 10h
 | 
						|
          + detail
 | 
						|
          width = 600
 | 
						|
          height = 200
 | 
						|
          unison_tolerance = 2
 | 
						|
          "Last 3 Hours"    3h
 | 
						|
          "Last 30 Hours"   30h
 | 
						|
          "Last 10 Days"    10d
 | 
						|
          "Last 360 Days"   360d
 | 
						|
        '';
 | 
						|
        description = "presentation graph style";
 | 
						|
      };
 | 
						|
      probeConfig = mkOption {
 | 
						|
        type = types.string;
 | 
						|
        default = ''
 | 
						|
          + FPing
 | 
						|
          binary = ${config.security.wrapperDir}/fping
 | 
						|
        '';
 | 
						|
        description = "Probe configuration";
 | 
						|
      };
 | 
						|
      targetConfig = mkOption {
 | 
						|
        type = types.string;
 | 
						|
        default = ''
 | 
						|
					probe = FPing
 | 
						|
					menu = Top
 | 
						|
					title = Network Latency Grapher
 | 
						|
					remark = Welcome to the SmokePing website of xxx Company. \
 | 
						|
									 Here you will learn all about the latency of our network.
 | 
						|
					+ Local
 | 
						|
					menu = Local
 | 
						|
					title = Local Network
 | 
						|
					++ LocalMachine
 | 
						|
					menu = Local Machine
 | 
						|
					title = This host
 | 
						|
					host = localhost
 | 
						|
        '';
 | 
						|
        description = "Target configuration";
 | 
						|
      };
 | 
						|
      extraConfig = mkOption {
 | 
						|
        type = types.lines;
 | 
						|
        default = "";
 | 
						|
        description = "Any additional customization not already included.";
 | 
						|
      };
 | 
						|
      config = mkOption {
 | 
						|
        type = types.nullOr types.string;
 | 
						|
        default = null;
 | 
						|
        description = "Full smokeping config supplied by the user. Overrides " +
 | 
						|
          "and replaces any other configuration supplied.";
 | 
						|
      };
 | 
						|
 | 
						|
    };
 | 
						|
 | 
						|
  };
 | 
						|
 | 
						|
  config = mkIf cfg.enable {
 | 
						|
    assertions = [
 | 
						|
      {
 | 
						|
        assertion = !(cfg.sendmail != null && cfg.mailHost != "");
 | 
						|
        message = "services.smokeping: sendmail and Mailhost cannot both be enabled.";
 | 
						|
      }
 | 
						|
    ];
 | 
						|
    security.setuidPrograms = [ "fping" ];
 | 
						|
    environment.systemPackages = [ pkgs.fping ];
 | 
						|
    users.extraUsers = singleton {
 | 
						|
      name = cfg.user;
 | 
						|
      isNormalUser = false;
 | 
						|
      isSystemUser = true;
 | 
						|
      uid = config.ids.uids.smokeping;
 | 
						|
      description = "smokeping daemon user";
 | 
						|
      home = smokepingHome;
 | 
						|
    };
 | 
						|
    systemd.services.smokeping = {
 | 
						|
      wantedBy = [ "multi-user.target"];
 | 
						|
      serviceConfig.User = cfg.user;
 | 
						|
      serviceConfig.PermissionsStartOnly = true;
 | 
						|
      preStart = ''
 | 
						|
        mkdir -m 0755 -p ${smokepingHome}/cache ${smokepingHome}/data
 | 
						|
        rm -f ${smokepingHome}/cropper
 | 
						|
        ln -s ${cfg.package}/htdocs/cropper ${smokepingHome}/cropper
 | 
						|
        cp ${cgiHome} ${smokepingHome}/smokeping.fcgi
 | 
						|
        ${cfg.package}/bin/smokeping --check --config=${configPath}
 | 
						|
        ${cfg.package}/bin/smokeping --static --config=${configPath}
 | 
						|
        chown -R ${cfg.user} ${smokepingHome}
 | 
						|
      '';
 | 
						|
      script = ''${cfg.package}/bin/smokeping --config=${configPath} --nodaemon'';
 | 
						|
    };
 | 
						|
    systemd.services.thttpd = mkIf cfg.webService {
 | 
						|
      wantedBy = [ "multi-user.target"];
 | 
						|
      requires = [ "smokeping.service"];
 | 
						|
      partOf = [ "smokeping.service"];
 | 
						|
      path = with pkgs; [ bash rrdtool smokeping thttpd ];
 | 
						|
      script = ''thttpd -u ${cfg.user} -c "**.fcgi" -d ${smokepingHome} -p ${builtins.toString cfg.port} -D -nos'';
 | 
						|
      serviceConfig.Restart = "always";
 | 
						|
    };
 | 
						|
  };
 | 
						|
}
 | 
						|
 |