-keys
-log
+ca
+etc/hosts
+gnupg
+host
+publish
+*.new
--- /dev/null
+#! /bin/sh
+
+set -e
+. lib/func.sh
+
+## Check to see whether we're already set up.
+if [ -d ca ]; then
+ echo >&2 "$0: already set up: delete ca/ to restart"
+ exit 1
+fi
+
+## Clear out and recreate the old state directories.
+rm -rf gnupg ca ca.new publish publish.new
+mkdir -m700 gnupg ca.new
+
+## Generate the CA keys.
+for kt in $keytypes; do
+ case $kt in
+ *:*) bits=-b${kt#*:} kt=${kt%:*} ;;
+ *) bits= ;;
+ esac
+ ssh-keygen -fca.new/ca-$kt -t$kt $bits -C"$cacomment" -N ""
+done
+
+## Generate the GnuPG key.
+run_gpg --batch -q --gen-key <<EOF
+%echo Generating key ssh-ca; hold on tight...
+Key-Type: $gnupg_key_type
+Key-Length: $gnupg_key_length
+Name-Real: ${gnupg_key_realname_prefix}ssh-ca
+Name-Comment: ssh-ca
+Name-Email: ssh-ca@$gnupg_key_email_domain
+EOF
+
+## Done.
+mv ca.new ca
--- /dev/null
+#! /bin/sh
+
+set -e
+. lib/func.sh
+
+## The key types are adorned with bit lengths. Work out the raw key type
+## names.
+rawkeytypes=""
+for kt in $keytypes; do
+ rawkeytypes="$rawkeytypes ${kt%:*}"
+done
+
+## Start a new output directory.
+rm -rf publish.new
+mkdir publish.new
+mkdir publish.new/ssh-ca
+for kt in $rawkeytypes; do
+ cp ca/ca-$kt.pub publish.new/ssh-ca/
+ read pub <ca/ca-$kt.pub
+ echo "$@cert-authority $scope $pub" >publish.new/ssh-ca/ca-$kt.entry
+done
+
+## Sign the various host keys.
+exec 3<etc/hosts 4>publish.new/ssh-ca/hosts.list
+last=%%%
+while read line <&3; do
+
+ ## Ignore comments and empty lines.
+ case "$line" in
+ "#"* | "") continue ;;
+ ##*[! ]*) ;;
+ ##*) continue ;;
+ esac
+
+ ## Read the host line.
+ set -- $line
+ host=$1
+ names=""
+
+ ## If this is a different host, then start a new section of the list.
+ case "$host" in "$last") ;; *) { echo; echo "$host"; } >&4 ;; esac
+ last=$host
+
+ ## Build a list of names for the host.
+ for n in "$@"; do
+ names=${names:+$names,}$n
+ case "$n" in
+ *.* | *:*) ;;
+ *) names=${names:+$names,}$n.$domain ;;
+ esac
+ done
+
+ ## Sign certificates.
+ for kt in $rawkeytypes; do
+ if [ ! -f host/$host-$kt.pub ]; then continue; fi
+ cp host/$host-$kt.pub publish.new/ssh-ca/
+ ssh-keygen -q -sca/ca-$kt \
+ -h -I"$cacomment:$host.$domain" -n$names \
+ -V$validity \
+ publish.new/ssh-ca/$host-$kt.pub
+ mv publish.new/ssh-ca/$host-$kt-cert.pub \
+ publish.new/ssh-ca/$host-$kt.cert
+ ssh-keygen -lv -fpublish.new/ssh-ca/$host-$kt.pub | sed 's,^,| ,' >&4
+ done
+done
+exec 3>&- 4>&-
+
+## Sign the list.
+run_gpg --armor -o publish.new/ssh-ca/hosts.asc \
+ --clearsign publish.new/ssh-ca/hosts.list
+rm publish.new/ssh-ca/hosts.list
+
+## Include a copy of the public key.
+run_gpg --export --armor -o publish.new/ssh-ca/ca-gnupg.asc
+
+## Include a copy of the complete archive.
+(cd publish.new; tar czf ssh-ca.tar.gz ssh-ca/)
+mv publish.new/ssh-ca.tar.gz publish.new/ssh-ca/
+
+## Done.
+rm -rf publish
+mv publish.new publish
+++ /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
+#! /bin/sh
+
+set -e
+. lib/func.sh
+
+rsync \
+ --recursive --delete-after --delay-updates \
+ publish/ssh-ca $publish_target
+++ /dev/null
-### -*-sh-*-
-
-keytypes="rsa:3128 dsa:1024"
-domain=distorted.org.uk
-scope="*.distorted.org.uk,62.49.204.144,62.49.204.145,62.49.204.146,62.49.204.147,62.49.204.148,62.49.204.149,62.49.204.15?,172.29.198.*,172.29.199.*"
--- /dev/null
+### -*-sh-*-
+###
+### Configuration for ssh-ca.
+
+###--------------------------------------------------------------------------
+### General configuration.
+
+## General configuration defaults.
+keytypes="rsa:3072 dsa:1024"
+domain="distorted.org.uk"
+cacomment="ssh-ca@$domain"
+validity="-1d:+2d"
+publish_target="vampire.distorted.org.uk:/var/www/"
+
+## GnuPG defaults.
+gnupg_key_type=RSA gnupg_key_length=3072
+gnupg_key_realname_prefix="distorted.org.uk "
+gnupg_key_email_domain="$domain"
+
+###--------------------------------------------------------------------------
+### Scope for the CA.
+
+## Domain name.
+scope="*.$domain"
+
+## IPv4 addresses.
+for i in 144 145 146 147 148 149; do scope=$scope,"62.49.204.$i"; done
+scope=$scope,"62.49.204.15?"
+for i in 198 199; do scope=$scope,"172.29.198.*,172.29.199.*"; done
+
+## IPv6 addresses.
+scope=$scope,"2001:470:1f09:1b98:*,2001:470:9740:*"
+
+###----- That's all, folks --------------------------------------------------
--- /dev/null
+### GnuPG configuration
+
+## Annoying copyright notice.
+no-greeting
+
+## Algorithm selection
+s2k-cipher-algo AES256
+s2k-digest-algo SHA256
+personal-cipher-preferences AES256 AES TWOFISH 3DES BLOWFISH CAST5
+personal-digest-preferences SHA256 SHA1 RIPEMD160
+personal-compress-preferences ZLIB ZIP
+default-preference-list AES TWOFISH 3DES BLOWFISH CAST5 SHA256 SHA1 RIPEMD160 ZLIB ZIP
+++ /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
--- /dev/null
+### -*-sh-*-
+
+## Read user configuration.
+. etc/config.sh
+
+## GnuPG runes.
+run_gpg () {
+ gpg --no-permission-warning \
+ --homedir=gnupg --options=etc/gpg.conf \
+ "$@"
+}
\ No newline at end of file