From a91e8fcb8d75260a28340c756894e9f507288fa3 Mon Sep 17 00:00:00 2001 From: Mark Wooding Date: Sat, 13 Aug 2011 23:45:48 +0100 Subject: [PATCH] Major change of approach and rewrite. 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 | 8 +++- bin/setup | 36 ++++++++++++++++ bin/sign | 82 ++++++++++++++++++++++++++++++++++++ bin/update-ssh-certs | 116 --------------------------------------------------- bin/upload | 8 ++++ etc/config | 5 --- etc/config.sh | 34 +++++++++++++++ etc/gpg.conf | 12 ++++++ etc/hosts | 17 -------- lib/func.sh | 11 +++++ 10 files changed, 189 insertions(+), 140 deletions(-) create mode 100755 bin/setup create mode 100755 bin/sign delete mode 100755 bin/update-ssh-certs create mode 100755 bin/upload delete mode 100644 etc/config create mode 100644 etc/config.sh create mode 100644 etc/gpg.conf delete mode 100644 etc/hosts create mode 100644 lib/func.sh diff --git a/.gitignore b/.gitignore index 9bcbb6e..07a1eb3 100644 --- a/.gitignore +++ b/.gitignore @@ -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 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 <publish.new/ssh-ca/ca-$kt.entry +done + +## Sign the various host keys. +exec 3publish.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 index cb202cd..0000000 --- a/bin/update-ssh-certs +++ /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.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 index 0000000..ce8578d --- /dev/null +++ b/bin/upload @@ -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 index 425bf9a..0000000 --- a/etc/config +++ /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 index 0000000..b27ec96 --- /dev/null +++ b/etc/config.sh @@ -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 index 0000000..c5fa96c --- /dev/null +++ b/etc/gpg.conf @@ -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 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 index 0000000..0b6afdc --- /dev/null +++ b/lib/func.sh @@ -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 -- 2.11.0