2023-10-23 13:25:38 +02:00
|
|
|
{ config
|
|
|
|
, lib
|
|
|
|
, pkgs
|
|
|
|
, ... }:
|
|
|
|
|
|
|
|
let
|
|
|
|
cfg = config.services.certspotter;
|
|
|
|
|
|
|
|
configDir = pkgs.linkFarm "certspotter-config" (
|
|
|
|
lib.toList {
|
|
|
|
name = "watchlist";
|
|
|
|
path = pkgs.writeText "certspotter-watchlist" (builtins.concatStringsSep "\n" cfg.watchlist);
|
|
|
|
}
|
|
|
|
++ lib.optional (cfg.emailRecipients != [ ]) {
|
|
|
|
name = "email_recipients";
|
|
|
|
path = pkgs.writeText "certspotter-email_recipients" (builtins.concatStringsSep "\n" cfg.emailRecipients);
|
|
|
|
}
|
|
|
|
# always generate hooks dir when no emails are provided to allow running cert spotter with no hooks/emails
|
|
|
|
++ lib.optional (cfg.emailRecipients == [ ] || cfg.hooks != [ ]) {
|
|
|
|
name = "hooks.d";
|
|
|
|
path = pkgs.linkFarm "certspotter-hooks" (lib.imap1 (i: path: {
|
|
|
|
inherit path;
|
|
|
|
name = "hook${toString i}";
|
|
|
|
}) cfg.hooks);
|
|
|
|
});
|
|
|
|
in
|
|
|
|
{
|
|
|
|
options.services.certspotter = {
|
|
|
|
enable = lib.mkEnableOption "Cert Spotter, a Certificate Transparency log monitor";
|
|
|
|
|
2023-11-30 19:03:14 +01:00
|
|
|
package = lib.mkPackageOption pkgs "certspotter" { };
|
2023-10-23 13:25:38 +02:00
|
|
|
|
|
|
|
startAtEnd = lib.mkOption {
|
|
|
|
type = lib.types.bool;
|
|
|
|
description = ''
|
|
|
|
Whether to skip certificates issued before the first launch of Cert Spotter.
|
|
|
|
Setting this to `false` will cause Cert Spotter to download tens of terabytes of data.
|
|
|
|
'';
|
|
|
|
default = true;
|
|
|
|
};
|
|
|
|
|
|
|
|
sendmailPath = lib.mkOption {
|
|
|
|
type = with lib.types; nullOr path;
|
|
|
|
description = ''
|
|
|
|
Path to the `sendmail` binary. By default, the local sendmail wrapper is used
|
|
|
|
(see {option}`services.mail.sendmailSetuidWrapper`}).
|
|
|
|
'';
|
|
|
|
example = lib.literalExpression ''"''${pkgs.system-sendmail}/bin/sendmail"'';
|
|
|
|
};
|
|
|
|
|
|
|
|
watchlist = lib.mkOption {
|
|
|
|
type = with lib.types; listOf str;
|
|
|
|
description = "Domain names to watch. To monitor a domain with all subdomains, prefix its name with `.` (e.g. `.example.org`).";
|
|
|
|
default = [ ];
|
|
|
|
example = [ ".example.org" "another.example.com" ];
|
|
|
|
};
|
|
|
|
|
|
|
|
emailRecipients = lib.mkOption {
|
|
|
|
type = with lib.types; listOf str;
|
|
|
|
description = "A list of email addresses to send certificate updates to.";
|
|
|
|
default = [ ];
|
|
|
|
};
|
|
|
|
|
|
|
|
hooks = lib.mkOption {
|
|
|
|
type = with lib.types; listOf path;
|
|
|
|
description = ''
|
|
|
|
Scripts to run upon the detection of a new certificate. See `man 8 certspotter-script` or
|
|
|
|
[the GitHub page](https://github.com/SSLMate/certspotter/blob/${pkgs.certspotter.src.rev or "master"}/man/certspotter-script.md)
|
|
|
|
for more info.
|
|
|
|
'';
|
|
|
|
default = [ ];
|
|
|
|
example = lib.literalExpression ''
|
|
|
|
[
|
|
|
|
(pkgs.writeShellScript "certspotter-hook" '''
|
|
|
|
echo "Event summary: $SUMMARY."
|
|
|
|
''')
|
|
|
|
]
|
|
|
|
'';
|
|
|
|
};
|
|
|
|
|
|
|
|
extraFlags = lib.mkOption {
|
|
|
|
type = with lib.types; listOf str;
|
|
|
|
description = "Extra command-line arguments to pass to Cert Spotter";
|
|
|
|
example = [ "-no_save" ];
|
|
|
|
default = [ ];
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
config = lib.mkIf cfg.enable {
|
|
|
|
assertions = [
|
|
|
|
{
|
|
|
|
assertion = (cfg.emailRecipients != [ ]) -> (cfg.sendmailPath != null);
|
|
|
|
message = ''
|
|
|
|
You must configure the sendmail setuid wrapper (services.mail.sendmailSetuidWrapper)
|
|
|
|
or services.certspotter.sendmailPath
|
|
|
|
'';
|
|
|
|
}
|
|
|
|
];
|
|
|
|
|
|
|
|
services.certspotter.sendmailPath = let
|
|
|
|
inherit (config.security) wrapperDir;
|
|
|
|
inherit (config.services.mail) sendmailSetuidWrapper;
|
|
|
|
in lib.mkMerge [
|
|
|
|
(lib.mkIf (sendmailSetuidWrapper != null) (lib.mkOptionDefault "${wrapperDir}/${sendmailSetuidWrapper.program}"))
|
|
|
|
(lib.mkIf (sendmailSetuidWrapper == null) (lib.mkOptionDefault null))
|
|
|
|
];
|
|
|
|
|
|
|
|
users.users.certspotter = {
|
|
|
|
description = "Cert Spotter user";
|
|
|
|
group = "certspotter";
|
|
|
|
home = "/var/lib/certspotter";
|
|
|
|
isSystemUser = true;
|
|
|
|
};
|
|
|
|
users.groups.certspotter = { };
|
|
|
|
|
|
|
|
systemd.services.certspotter = {
|
|
|
|
description = "Cert Spotter - Certificate Transparency Monitor";
|
|
|
|
after = [ "network.target" ];
|
|
|
|
wantedBy = [ "multi-user.target" ];
|
|
|
|
environment.CERTSPOTTER_CONFIG_DIR = configDir;
|
|
|
|
environment.SENDMAIL_PATH = if cfg.sendmailPath != null then cfg.sendmailPath else "/run/current-system/sw/bin/false";
|
|
|
|
script = ''
|
|
|
|
export CERTSPOTTER_STATE_DIR="$STATE_DIRECTORY"
|
|
|
|
cd "$CERTSPOTTER_STATE_DIR"
|
|
|
|
${lib.optionalString cfg.startAtEnd ''
|
|
|
|
if [[ ! -d logs ]]; then
|
|
|
|
# Don't download certificates issued before the first launch
|
|
|
|
exec ${cfg.package}/bin/certspotter -start_at_end ${lib.escapeShellArgs cfg.extraFlags}
|
|
|
|
fi
|
|
|
|
''}
|
|
|
|
exec ${cfg.package}/bin/certspotter ${lib.escapeShellArgs cfg.extraFlags}
|
|
|
|
'';
|
|
|
|
serviceConfig = {
|
|
|
|
User = "certspotter";
|
|
|
|
Group = "certspotter";
|
|
|
|
StateDirectory = "certspotter";
|
|
|
|
};
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
meta.maintainers = with lib.maintainers; [ chayleaf ];
|
|
|
|
meta.doc = ./certspotter.md;
|
|
|
|
}
|