Major change of approach and rewrite.
authorMark Wooding <mdw@distorted.org.uk>
Sat, 13 Aug 2011 22:45:48 +0000 (23:45 +0100)
committerMark Wooding <mdw@distorted.org.uk>
Sat, 13 Aug 2011 22:45:48 +0000 (23:45 +0100)
Fetching keys from the various hosts is silly: we must actually already
have them, otherwise SSH will complain.  Instead, assume that someone
has already arranged to collect the keys and put them in the host/
directory.  There's now a script to sign new certificates for them and
stash them in publish/.  There's another script to upload the publish/
directory to a webserver (or whatever).

.gitignore
bin/setup [new file with mode: 0755]
bin/sign [new file with mode: 0755]
bin/update-ssh-certs [deleted file]
bin/upload [new file with mode: 0755]
etc/config [deleted file]
etc/config.sh [new file with mode: 0644]
etc/gpg.conf [new file with mode: 0644]
etc/hosts [deleted file]
lib/func.sh [new file with mode: 0644]

index 9bcbb6e..07a1eb3 100644 (file)
@@ -1,2 +1,6 @@
-keys
-log
+ca
+etc/hosts
+gnupg
+host
+publish
+*.new
diff --git a/bin/setup b/bin/setup
new file mode 100755 (executable)
index 0000000..7bcbafa
--- /dev/null
+++ b/bin/setup
@@ -0,0 +1,36 @@
+#! /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
diff --git a/bin/sign b/bin/sign
new file mode 100755 (executable)
index 0000000..e3ac0c9
--- /dev/null
+++ b/bin/sign
@@ -0,0 +1,82 @@
+#! /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
diff --git a/bin/update-ssh-certs b/bin/update-ssh-certs
deleted file mode 100755 (executable)
index cb202cd..0000000
+++ /dev/null
@@ -1,116 +0,0 @@
-#! /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
diff --git a/bin/upload b/bin/upload
new file mode 100755 (executable)
index 0000000..ce8578d
--- /dev/null
@@ -0,0 +1,8 @@
+#! /bin/sh
+
+set -e
+. lib/func.sh
+
+rsync \
+  --recursive --delete-after --delay-updates \
+  publish/ssh-ca $publish_target
diff --git a/etc/config b/etc/config
deleted file mode 100644 (file)
index 425bf9a..0000000
+++ /dev/null
@@ -1,5 +0,0 @@
-### -*-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.*"
diff --git a/etc/config.sh b/etc/config.sh
new file mode 100644 (file)
index 0000000..b27ec96
--- /dev/null
@@ -0,0 +1,34 @@
+### -*-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 --------------------------------------------------
diff --git a/etc/gpg.conf b/etc/gpg.conf
new file mode 100644 (file)
index 0000000..c5fa96c
--- /dev/null
@@ -0,0 +1,12 @@
+### 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
diff --git a/etc/hosts b/etc/hosts
deleted file mode 100644 (file)
index 866315c..0000000
--- a/etc/hosts
+++ /dev/null
@@ -1,17 +0,0 @@
-### -*-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
diff --git a/lib/func.sh b/lib/func.sh
new file mode 100644 (file)
index 0000000..0b6afdc
--- /dev/null
@@ -0,0 +1,11 @@
+### -*-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