--- /dev/null
+#! /bin/bash
+###
+### Update the site's SSH certificates.
+
+set -e
+cd "${0%/*}/.."
+
+###--------------------------------------------------------------------------
+### General setup stuff.
+
+## Read in a configuration file.
+if [ -f etc/config ]; then . etc/config; fi
+: ${keytypes="rsa:3072 dsa:1024"}
+: ${domain="your.site.example"}
+: ${cacomment="ssh-ca@$domain"}
+: ${scope="*.$domain"}
+: ${validity="-1d:+7d"}
+
+## The key types are adorned with bit lengths. Work out the raw key type
+## names.
+cakeytypes=""
+for kt in $keytypes; do
+ cakeytypes="$cakeytypes ${kt%:*}"
+done
+
+## Make the keys if necessary.
+mkdir -p keys
+for kt in $keytypes; do
+ case $kt in
+ *:*) bits=-b${kt#*:} kt=${kt%:*} ;;
+ *) bits="" ;;
+ esac
+ if [ ! -f keys/ca-$kt ]; then
+ ssh-keygen -fkeys/ca-$kt -t$kt $bits -C"$cacomment" -N ""
+ fi
+ read pub <keys/ca-$kt.pub
+ echo "@cert-authority $scope $pub" >keys/ca-$kt.entry
+done
+
+## Functions for managing concurrency.
+kids=""
+mkdir -p log
+run () {
+ tag=$1; shift
+ "$@" >log/$tag 2>&1&
+ kids="$kids $tag:$!"
+}
+
+reap () {
+ outcome=0
+ for kid in $kids; do
+ tag=${kid%:*}
+ set +e; wait ${kid#*:}; rc=$?; set -e
+ case $rc in
+ 0) ;;
+ *)
+ echo >&2 "$0: $tag failed (rc = $rc)"
+ sed 's,^,| ,' log/$tag
+ outcome=1
+ ;;
+ esac
+ done
+ return $outcome
+}
+
+## Read the hosts.
+dohost () {
+ host=$1; shift
+
+ set -x
+ hostkeytypes=$(
+ ssh $host "
+ cd /etc/ssh
+ for kt in $cakeytypes; do
+ if [ -f ssh_host_\${kt}_key.pub ]; then echo \$kt; fi
+ done"
+ )
+ names=""
+ for n in "$host" "$@"; do
+ names=${names:+$names,}$n
+ case "$n" in ".") ;; *) names=${names:+$names,}$n.$domain ;; esac
+ done
+ any=nil
+ for kt in $hostkeytypes; do
+ scp $host:/etc/ssh/ssh_host_${kt}_key.pub keys/$host-$kt.pub
+ ssh-keygen -skeys/ca-$kt \
+ -h -I"$cacomment:$host.$domain" -n$names \
+ -V$validity \
+ keys/$host-$kt.pub
+ scp keys/$host-$kt-cert.pub $host:/etc/ssh/ssh_host_${kt}_key-cert.pub
+ any=t
+ done
+ case "$any" in nil) echo >&2 "no matching key types"; exit 1 ;; esac
+}
+
+dotry () {
+ host=$1; shift
+ ping -c5 -q $host >/dev/null 2>&1 || return 0
+ dohost "$host" "$@"
+}
+
+must () { run "$1" dohost "$@"; }
+try () { run "$1" dotry "$@"; }
+
+. etc/hosts
+reap
+
+last=%%%
+for i in keys/*.pub; do
+ case "$i" in *-cert.pub) continue ;; esac
+ host=${i%-*}
+ case "$host" in "$last") ;; *) echo; echo "$host" ;; esac
+ last=$host
+ ssh-keygen -lv -f "$i" | sed 's,^,| ,'
+done >distorted-host-keys.new
+mv distorted-host-keys.new distorted-host-keys
--- /dev/null
+### -*-conf-*-
+
+## Servers.
+must ibanez 62.49.204.153 172.29.199.14
+must radius 62.49.204.146 172.29.199.2 172.29.198.1
+must roadstar 62.49.204.147 172.29.199.2
+must jem 62.49.204.148 172.29.199.3
+must artist 62.49.204.149 172.29.199.4
+must vampire git 62.49.204.150 172.29.199.5 172.29.198.3
+
+## Workstation and occasional guests.
+must gibson
+try marauder
+try invader
+
+## Mobile.
+try crybaby