feat: dynamic selector registration #1
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:
parent
5f96c7d7da
commit
7ebfd568bc
2 changed files with 75 additions and 3 deletions
|
@ -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
|
||||
|
|
76
opendkimd.sh
76
opendkimd.sh
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue