feat: dynamic selector registration #1
All checks were successful
ci/woodpecker/push/woodpecker/1 Pipeline was successful
ci/woodpecker/push/woodpecker/2 Pipeline was successful
ci/woodpecker/push/woodpecker/3 Pipeline was successful
ci/woodpecker/push/woodpecker/4 Pipeline was successful

regular key rotation is important because it prevents email
impersonation if private keys are lost.  once an email is verified,
there's no reason to have the private key lying around.

this implementation uses knot's dynamic dns features to allow every
opendkim node to issue its own key pairs and selectors, and publish them
on the _domainkey zone.

we don't need central coordination between dns and opendkim, and between
opendkim nodes. each works on its own and announces selectors
individually.
This commit is contained in:
f 2024-06-22 15:47:08 -03:00
parent 5f96c7d7da
commit 7ebfd568bc
No known key found for this signature in database
2 changed files with 75 additions and 3 deletions

View file

@ -3,7 +3,7 @@ ARG BASE_IMAGE=gitea.nulo.in/sutty/monit
FROM ${BASE_IMAGE}:${ALPINE_VERSION}
LABEL org.opencontainers.image.authors="f@sutty.nl"
RUN apk add --no-cache opendkim mini-sendmail
RUN apk add --no-cache opendkim opendkim-utils mini-sendmail drill
COPY ./monit.conf /etc/monit.d/opendkim.conf
COPY ./opendkimd.sh /usr/local/bin/opendkimd

View file

@ -1,6 +1,19 @@
#!/bin/sh
pid=/tmp/opendkim.pid
dir=/etc/opendkim
key=$dir/key_table
sig=$dir/signing_table
SUTTY="${SUTTY:-${DOMAIN#*.}}"
ok() {
logger -s -t $0 "ok - $@"
}
not_ok() {
logger -s -t $0 "not ok - $@"
}
case $1 in
stop)
@ -9,12 +22,71 @@ case $1 in
;;
start)
install -dm 2750 -o opendkim -g opendkim /run/opendkim
touch $key $sig
chown -R opendkim:opendkim /etc/opendkim
/usr/sbin/opendkim
;;
rotate)
test -f $pid || exit 0
test $pid -ot /etc/opendkim/dkim.private && cat $pid | xargs -r kill -USR1
node="${DOMAIN%%.*}"
date="`date +%Y%m`"
selc="${date}${node}"
pkey="${dir}/${selc}.private"
tkey="${dir}/${selc}.txt"
dkey="$selc._domainkey.$SUTTY"
# Do nothing if it already exists
test -f "$pkey" && exit 0
# Remove old keys later
prev="`find $dir -name "*.private" -o -name "*.txt"`"
ok "Generating new keys for selector ${selc}"
opendkim-genkey --directory=$dir --selector=$selc --subdomains --domain=$SUTTY
# Discard previous keys
echo "$dkey $SUTTY:$selc:$pkey" > $key
echo "$SUTTY $dkey" > $sig
txt="`cat $tkey | tr -d "\n" | cut -d '"' -f 2,4 | tr -d '"' | tr -d " "`"
# Craft record
cat >/tmp/dns <<DNS
server knot.$node
zone _domainkey.${SUTTY}.
origin _domainkey.${SUTTY}.
ttl 60
del ${dkey}. TXT
add ${dkey}. TXT "${txt}"
send
quit
DNS
# Register the TXT record on every nameserver. Log failures but
# continue anyway.
ok "Registering public key records"
for node in `drill -Q ns $SUTTY`; do
if knsupdate /tmp/dns; then
ok "Registered public key record at ${node}"
else
not_ok "Registered public key record at ${node}"
fi
if drill -Q @${node} txt ${dkey} | grep -q "${txt}"; then
ok "Registered correctly"
else
not_ok "Couldn't query ${dkey} at ${node}!"
fi
done
if test -n "$prev"; then
ok "Pruning old keypairs"
rm -f $prev
fi
if test -f $pid ; then
ok "Reloading"
cat $pid | xargs -r kill -USR1
fi
;;
esac