From f71eb78f5c2b8ba088a5ffec34886373620060af Mon Sep 17 00:00:00 2001 From: Alejandro Soto Date: Sat, 10 Aug 2024 15:35:10 -0600 Subject: sys/jobs/pki-expiry: initial commit --- sys/default.nix | 1 + sys/jobs/default.nix | 5 ++ sys/jobs/pki-expiry/default.nix | 59 +++++++++++++++++++++ sys/jobs/pki-expiry/pki-expiry.sh | 108 ++++++++++++++++++++++++++++++++++++++ sys/preset/dmz.nix | 2 + 5 files changed, 175 insertions(+) create mode 100644 sys/jobs/default.nix create mode 100644 sys/jobs/pki-expiry/default.nix create mode 100644 sys/jobs/pki-expiry/pki-expiry.sh (limited to 'sys') diff --git a/sys/default.nix b/sys/default.nix index 800b6be..70134f9 100644 --- a/sys/default.nix +++ b/sys/default.nix @@ -16,6 +16,7 @@ with lib; { ./gitea ./hardware ./home-assistant + ./jobs ./kiosk ./mail ./mta diff --git a/sys/jobs/default.nix b/sys/jobs/default.nix new file mode 100644 index 0000000..24d6c73 --- /dev/null +++ b/sys/jobs/default.nix @@ -0,0 +1,5 @@ +{ + imports = [ + ./pki-expiry + ]; +} diff --git a/sys/jobs/pki-expiry/default.nix b/sys/jobs/pki-expiry/default.nix new file mode 100644 index 0000000..b61d6f5 --- /dev/null +++ b/sys/jobs/pki-expiry/default.nix @@ -0,0 +1,59 @@ +{ config, lib, pkgs, ... }: +with lib; let + cfg = config.local.jobs.pkiExpiry; + inherit (config.local) pki; +in +{ + options.local.jobs.pkiExpiry = { + enable = mkEnableOption "PKI expiration reminder"; + }; + + config = mkIf cfg.enable { + systemd = { + services.pki-expiry = { + after = [ "postfix.service" ]; + path = [ "/run/wrappers" ]; + + environment.PKI_PUBLIC = + let + mkdir = "mkdir -p $out/{ca,cert,crl}"; + + cas = mapAttrsToList (_: ca: "ln -s ${ca.cert} $out/ca/${ca.path}") pki.ca; + crls = mapAttrsToList (_: ca: "ln -s ${ca.crl} $out/crl/${ca.path}") pki.ca; + + certs = mapAttrsToList + (path: leaf: "ln -s ${leaf.cert} $out/cert/${path}") + (filterAttrs (_: object: ! object ? leaves) pki.byPath); + + pkiPublic = pkgs.runCommandNoCCLocal "pki-public" { } (concatLines ([ mkdir ] ++ cas ++ crls ++ certs)); + in + "${pkiPublic}"; + + serviceConfig = { + Type = "oneshot"; + StateDirectory = "pki-expiry"; + WorkingDirectory = "/var/lib/pki-expiry"; + + ExecStart = + let + script = pkgs.writeShellApplication { + name = "pki-expiry"; + text = readFile ./pki-expiry.sh; + runtimeInputs = with pkgs; [ diffutils openssl ]; + }; + in + "${getExe script}"; + }; + }; + + timers.pki-expiry = { + wantedBy = [ "timers.target" ]; + + timerConfig = { + OnStartupSec = "10m"; + OnUnitInactiveSec = "3d"; + }; + }; + }; + }; +} diff --git a/sys/jobs/pki-expiry/pki-expiry.sh b/sys/jobs/pki-expiry/pki-expiry.sh new file mode 100644 index 0000000..0e95a26 --- /dev/null +++ b/sys/jobs/pki-expiry/pki-expiry.sh @@ -0,0 +1,108 @@ +#!/usr/bin/env bash +# +function will_expire() { + expiry_status="" + expiry_vars="$(openssl "$openssl_cmd" -in "$object_path" -noout "${openssl_var_opts[@]}")" + + expiry_date="$(echo "$expiry_vars" | grep "^$openssl_expiry_var=" | sed 's/^.\+=//g')" + if [ -z "$expiry_date" ]; then + return 1 + fi + + expiry_secs="$(date +%s -d "$expiry_date")" + diff="$((expiry_secs - now))" + + if [ "$diff" -gt "$1" ]; then + return 1 + elif [ "$diff" -lt 0 ]; then + remaining=0 + else + remaining="$((diff / 86400))" + fi + + total_matches="$((total_matches + 1))" + + if [ -z "$min_expiry" ]; then + min_expiry="$remaining" + elif [ "$remaining" -lt "$min_expiry" ]; then + min_expiry="$remaining" + fi +} + +function has_expired() { + if ! will_expire 0; then + return 1 + fi + + expiry_status="has expired" +} + +function will_expire_days() { + if ! will_expire "$(($1 * 86400))"; then + return 1 + fi + + expiry_status="will expire in $remaining days" +} + +function check_object() { + object_id="$(basename "$1")" + object_path="$1" + + if has_expired || will_expire_days 3 || will_expire_days 7 || will_expire_days 15 || will_expire_days 30; then + { + echo + echo "$object_repr '$object_id' $expiry_status" + echo "$expiry_vars" + } >>"$mail_out" + fi +} + +function check_dir() { + object_repr="$2" + + for path in "$PKI_PUBLIC/$1"/*; do + check_object "$path" + done +} + +if [ -z "$PKI_PUBLIC" ]; then + echo "$0: \$PKI_PUBLIC not set" + exit 1 +elif [ ! -d "$PKI_PUBLIC" ]; then + echo "$0: invalid \$PKI_PUBLIC: $PKI_PUBLIC" + exit 1 +fi + +mail_out="$(mktemp)" +trap 'rm -f -- "$mail_out"' EXIT + +now="$(date +%s)" +min_expiry="" +total_matches=0 + +openssl_cmd=x509 +openssl_var_opts=(-startdate -enddate) +openssl_expiry_var="notAfter" + +check_dir ca "CA" +check_dir cert "Certificate" + +openssl_cmd=crl +openssl_var_opts=(-lastupdate -nextupdate) +openssl_expiry_var="nextUpdate" + +check_dir crl "CRL for CA" + +if [ -s "$mail_out" ] && ! cmp -s last-mail "$mail_out"; then + sendmail -t <<- EOF + From: PKI expiration reminder + To: sysadmin + Subject: $total_matches PKI objects will expire in $min_expiry days + + The following PKI objects are due for renewal: + $(<"$mail_out") + EOF + + mv -- "$mail_out" last-mail +fi diff --git a/sys/preset/dmz.nix b/sys/preset/dmz.nix index a64a5b7..eae58cc 100644 --- a/sys/preset/dmz.nix +++ b/sys/preset/dmz.nix @@ -27,6 +27,8 @@ in }; }; + jobs.pkiExpiry.enable = mkDefault config.local.mta.enable; + mta.enable = mkDefault true; net = { -- cgit v1.2.3