summaryrefslogtreecommitdiff
path: root/sys
diff options
context:
space:
mode:
authorAlejandro Soto <alejandro@34project.org>2024-08-10 15:35:10 -0600
committerAlejandro Soto <alejandro@34project.org>2024-08-10 16:10:18 -0600
commitf71eb78f5c2b8ba088a5ffec34886373620060af (patch)
tree1455bcd8c4b46b57dbcb4c903bd654dedda4d4e6 /sys
parent95982afebf37e838bd0ab871732966aca98c1744 (diff)
sys/jobs/pki-expiry: initial commit
Diffstat (limited to '')
-rw-r--r--sys/default.nix1
-rw-r--r--sys/jobs/default.nix5
-rw-r--r--sys/jobs/pki-expiry/default.nix59
-rw-r--r--sys/jobs/pki-expiry/pki-expiry.sh108
-rw-r--r--sys/preset/dmz.nix2
5 files changed, 175 insertions, 0 deletions
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 <pki-expiry>
+ 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 = {