summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore2
-rw-r--r--Makefile11
-rw-r--r--cb.conf13
-rw-r--r--rotate-keys.in127
-rw-r--r--rotate-keys.service.in6
-rw-r--r--rotate-keys.timer11
-rw-r--r--sign-ca.timer4
7 files changed, 170 insertions, 4 deletions
diff --git a/.gitignore b/.gitignore
index 628da3e..7798a7d 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,5 @@
+rotate-keys
+rotate-keys.service
sign-ca
sign-ca.service
sign-request
diff --git a/Makefile b/Makefile
index be82bbf..c5d6633 100644
--- a/Makefile
+++ b/Makefile
@@ -27,7 +27,7 @@ MANDIR = /usr/share/man
VERSION = 0.0
-all: sign-ca sign-request sign-ca.service
+all: rotate-keys rotate-keys.service sign-ca sign-ca.service sign-request
%: %.in
sed " \
@@ -37,8 +37,9 @@ all: sign-ca sign-request sign-ca.service
s@#HELPTEXT#\(\s\+\)#@ --help \1display this help and exit\n --version\1display version and exit@; \
" $< > $@
[ "$@" = "sign-ca" ] && chmod +x "$@" || true
+ [ "$@" = "rotate-keys" ] && chmod +x "$@" || true
-.PHONY: install dist clean
+.PHONY: install-ca install-cb dist clean
install-ca:
install -D -m0755 -t $(DESTDIR)$(BINDIR) sign-ca sign-request
@@ -46,6 +47,12 @@ install-ca:
install -d -m0700 $(DESTDIR)$(ETCDIR)/simple-pki/keys
install -D -m0644 -t $(DESTDIR)$(LIBDIR)/systemd/system sign-ca.service sign-ca.timer
+install-cb:
+ install -D -m0755 -t $(DESTDIR)$(BINDIR) rotate-keys
+ install -D -m0644 -t $(DESTDIR)$(ETCDIR)/simple-pki cb.conf
+ install -d -m0700 $(DESTDIR)$(ETCDIR)/simple-pki/keys
+ install -D -m0644 -t $(DESTDIR)$(LIBDIR)/systemd/system rotate-keys.service rotate-keys.timer
+
clean:
ls -A | \
grep "^\($(shell sed 's|\.|\\.|; s|\*|.*|; s|$$|\\|' .gitignore | tr '\n' '\|')\)\$$" | \
diff --git a/cb.conf b/cb.conf
new file mode 100644
index 0000000..21187c3
--- /dev/null
+++ b/cb.conf
@@ -0,0 +1,13 @@
+#!/bin/bash
+
+# where do we belong? (must match the setting of the ca)
+subject_prefix='/C=DE/ST=Thuringia/L=Jena/O=Eckner/OU=Net'
+
+# ignore these hosts
+ignore_hosts=('localhost')
+
+# where should the certificates be requested?
+ca_host='user@ca.example.com'
+
+# which user owns the certificates (not root)
+certificate_user='http'
diff --git a/rotate-keys.in b/rotate-keys.in
new file mode 100644
index 0000000..f965928
--- /dev/null
+++ b/rotate-keys.in
@@ -0,0 +1,127 @@
+#!/bin/bash
+
+key_dir='#ETCDIR#/simple-pki/keys'
+
+if [ -r '#ETCDIR#/simple-pki/cb.conf' ]; then
+ . '#ETCDIR#/simple-pki/cb.conf'
+fi
+
+hosts=$(
+ find '#ETCDIR#/nginx/' \
+ -name keys -prune , \
+ -name sites-available -prune , \
+ \( -type f -o -type l \) \
+ -exec sed -n '
+ s/^\s*//
+ /^server_name\s.*;/ p
+ /^server_name[^;]*$/,/;/ p
+ ' {} \; 2>/dev/null \
+ | tr '\n' ' ' \
+ | sed '
+ s/\s\+/ /g
+ s/;\s*/;\n/g
+ '"$(
+ printf 's/\\s%s\\(;\\|\\s\)//\n' "${ignore_hosts[@]}"
+ )"'
+ ' \
+ | sed -n '
+ s/^server_name //
+ T
+ s/;$//
+ T
+ p
+ ' \
+ | sort -u
+)
+
+host_key_files=$(
+ printf '%s\n' "${hosts}" \
+ | cut -d' ' -f1
+)
+
+if [ "$(whoami)" != "${certificate_user}" ]; then
+
+ if [ "$(whoami)" = 'root' ]; then
+ updated_something=false
+ for host_key_file in ${host_key_files}; do
+ if [ -f "${key_dir}/${host_key_file}.key.new" ] \
+ && [ -f "${key_dir}/${host_key_file}.crt.new" ]; then
+ if [ "$(stat -c%Y "${key_dir}/${host_key_file}.key.new")" -ge "$(($(date +%s)-60*60*24*30))" ] \
+ && [ -f "${key_dir}/${host_key_file}.key" ] \
+ && [ "$(stat -c%Y "${key_dir}/${host_key_file}.crt.new")" -ge "$(($(date +%s)-60*60*24*30))" ] \
+ && [ -f "${key_dir}/${host_key_file}.crt" ]; then
+ continue
+ fi
+ mv "${key_dir}/${host_key_file}.key"{.new,}
+ mv "${key_dir}/${host_key_file}.crt"{.new,}
+ updated_something=true
+ fi
+ done
+ if ${updated_something}; then
+ systemctl try-restart nginx
+ fi
+
+ su "${certificate_user}" -s /bin/bash -c "$0"
+ fi
+
+ exit
+fi
+
+if [ -n "$(trap)" ]; then
+ >&2 echo 'outer traps set - those will be forgotten!'
+ exit 1
+fi
+
+tmp_dir=$(mktemp -d '/srv/http/httpdocs/.csr/tmp.XXXXXXXXXX')
+trap 'rm -rf --one-file-system "${tmp_dir}"' EXIT
+
+printf '%s\n' "${hosts}" \
+| while read -r host other_hosts; do
+ if [ -f "${key_dir}/${host}.key.new" ] \
+ && [ -f "${key_dir}/${host}.crt.new" ]; then
+ continue
+ fi
+ if [ -n "${other_hosts}" ]; then
+ extensions="-addext subjectAltName=$(
+ printf ',DNS:%s' \
+ "${host}" \
+ ${other_hosts} \
+ | sed 's/^,//'
+ )"
+ else
+ extensions=''
+ fi
+ openssl req -newkey rsa:4096 \
+ -keyout "${key_dir}/${host}.key.new" \
+ -out "${tmp_dir}/${host}.csr" \
+ -nodes -subj "${subject_prefix}"'/CN='"${host}" -sha256 \
+ ${extensions}
+ printf 'https://%s/.csr/%s/%s.csr %s/CN=%s %s\n' \
+ "${host}" \
+ "${tmp_dir##*/}" \
+ "${host}" \
+ "${subject_prefix}" \
+ "${host}" \
+ "${extensions}" \
+ >> "${tmp_dir}/commands"
+done
+
+if [ ! -s "${tmp_dir}/commands" ]; then
+ >&2 echo 'nothing to do.'
+ exit
+fi
+
+cd "${tmp_dir}"
+
+cut -d' ' -f1 \
+< 'commands' \
+| ssh -T "${ca_host}" \
+| tar -xzf -
+
+for host_key_file in ${host_key_files}; do
+ if [ ! -f "${tmp_dir}/${host_key_file}.crt" ]; then
+ continue
+ fi
+ cat "${tmp_dir}/${host_key_file}.crt" \
+ > "${key_dir}/${host_key_file}.crt.new"
+done
diff --git a/rotate-keys.service.in b/rotate-keys.service.in
new file mode 100644
index 0000000..7c933d8
--- /dev/null
+++ b/rotate-keys.service.in
@@ -0,0 +1,6 @@
+[Unit]
+Description=rotate keys
+
+[Service]
+Type=oneshot
+ExecStart=#BINDIR#/rotate-keys
diff --git a/rotate-keys.timer b/rotate-keys.timer
new file mode 100644
index 0000000..d447063
--- /dev/null
+++ b/rotate-keys.timer
@@ -0,0 +1,11 @@
+[Unit]
+Description=regularly rotate keys
+
+[Timer]
+OnCalendar=* 00:00:00
+AccuracySec=1us
+RandomizeDelaySec=10000000
+Persistent=true
+
+[Install]
+WantedBy=timers.target
diff --git a/sign-ca.timer b/sign-ca.timer
index 308a8e6..b9f9ac6 100644
--- a/sign-ca.timer
+++ b/sign-ca.timer
@@ -1,8 +1,8 @@
[Unit]
-Description=generate and upload self-signed keys twice a year
+Description=regularly generate new ca keys
[Timer]
-OnCalendar=*-01,05,09-01 00:00:00
+OnCalendar=* 00:00:00
AccuracySec=1us
RandomizeDelaySec=10000000
Persistent=true