Minimal SSH certificate authority.
[ssh-ca] / bin / update-ssh-certs
1 #! /bin/bash
2 ###
3 ### Update the site's SSH certificates.
4
5 set -e
6 cd "${0%/*}/.."
7
8 ###--------------------------------------------------------------------------
9 ### General setup stuff.
10
11 ## Read in a configuration file.
12 if [ -f etc/config ]; then . etc/config; fi
13 : ${keytypes="rsa:3072 dsa:1024"}
14 : ${domain="your.site.example"}
15 : ${cacomment="ssh-ca@$domain"}
16 : ${scope="*.$domain"}
17 : ${validity="-1d:+7d"}
18
19 ## The key types are adorned with bit lengths. Work out the raw key type
20 ## names.
21 cakeytypes=""
22 for kt in $keytypes; do
23 cakeytypes="$cakeytypes ${kt%:*}"
24 done
25
26 ## Make the keys if necessary.
27 mkdir -p keys
28 for kt in $keytypes; do
29 case $kt in
30 *:*) bits=-b${kt#*:} kt=${kt%:*} ;;
31 *) bits="" ;;
32 esac
33 if [ ! -f keys/ca-$kt ]; then
34 ssh-keygen -fkeys/ca-$kt -t$kt $bits -C"$cacomment" -N ""
35 fi
36 read pub <keys/ca-$kt.pub
37 echo "@cert-authority $scope $pub" >keys/ca-$kt.entry
38 done
39
40 ## Functions for managing concurrency.
41 kids=""
42 mkdir -p log
43 run () {
44 tag=$1; shift
45 "$@" >log/$tag 2>&1&
46 kids="$kids $tag:$!"
47 }
48
49 reap () {
50 outcome=0
51 for kid in $kids; do
52 tag=${kid%:*}
53 set +e; wait ${kid#*:}; rc=$?; set -e
54 case $rc in
55 0) ;;
56 *)
57 echo >&2 "$0: $tag failed (rc = $rc)"
58 sed 's,^,| ,' log/$tag
59 outcome=1
60 ;;
61 esac
62 done
63 return $outcome
64 }
65
66 ## Read the hosts.
67 dohost () {
68 host=$1; shift
69
70 set -x
71 hostkeytypes=$(
72 ssh $host "
73 cd /etc/ssh
74 for kt in $cakeytypes; do
75 if [ -f ssh_host_\${kt}_key.pub ]; then echo \$kt; fi
76 done"
77 )
78 names=""
79 for n in "$host" "$@"; do
80 names=${names:+$names,}$n
81 case "$n" in ".") ;; *) names=${names:+$names,}$n.$domain ;; esac
82 done
83 any=nil
84 for kt in $hostkeytypes; do
85 scp $host:/etc/ssh/ssh_host_${kt}_key.pub keys/$host-$kt.pub
86 ssh-keygen -skeys/ca-$kt \
87 -h -I"$cacomment:$host.$domain" -n$names \
88 -V$validity \
89 keys/$host-$kt.pub
90 scp keys/$host-$kt-cert.pub $host:/etc/ssh/ssh_host_${kt}_key-cert.pub
91 any=t
92 done
93 case "$any" in nil) echo >&2 "no matching key types"; exit 1 ;; esac
94 }
95
96 dotry () {
97 host=$1; shift
98 ping -c5 -q $host >/dev/null 2>&1 || return 0
99 dohost "$host" "$@"
100 }
101
102 must () { run "$1" dohost "$@"; }
103 try () { run "$1" dotry "$@"; }
104
105 . etc/hosts
106 reap
107
108 last=%%%
109 for i in keys/*.pub; do
110 case "$i" in *-cert.pub) continue ;; esac
111 host=${i%-*}
112 case "$host" in "$last") ;; *) echo; echo "$host" ;; esac
113 last=$host
114 ssh-keygen -lv -f "$i" | sed 's,^,| ,'
115 done >distorted-host-keys.new
116 mv distorted-host-keys.new distorted-host-keys