#!/bin/sh
OPENSSL=${OPENSSL:-openssl}
DOMAIN=${DOMAIN:-my-sni-test.org}
DIR=${DIR:-$PWD/sni}
NAMES=${NAMES:-ape nut pear apple banana}
IP=${IP:-127.0.0.1}
PASSWD=${PASSWD:-}
args=`getopt a:fd:D:p: $*`
if [ $? != 0 ]; then
echo "Syntax: $0 [-f] [-a IPaddress] [-d outdir] [-D domain ] [two or more vhost names ]"
echo " -f Force overwriting of outdir (default is $DIR)"
echo " -d dir Directory to create the SNI test server in (default is $DIR)"
echo " -D domain Domain name to use for this test (default is $DOMAIN)"
echo " -a IP IP address to use for this virtual host (default is $IP)"
echo " -p str Password for the client certificate test (some browsers require a set password)"
echo " [names] List of optional vhost names (default is $NAMES)"
echo
echo "Example:"
echo " $0 -D SecureBlogsAreUs.com peter fred mary jane ardy"
echo
echo "Which will create peter.SecureBlogsAreUs.com, fred.SecureBlogsAreUs.com and"
echo "so on. Note that the _first_ FQDN is also the default for non SNI hosts. It"
echo "may make sense to give this host a generic name - and allow each of the real"
echo "SNI site as sub directories/URI's of this generic name; thus allowing the "
echo "few non-SNI browsers access."
exit 1
fi
set -- $args
for i
do
case "$i"
in
-f)
FORCE=1
shift;;
-a)
IP=$2; shift
shift;;
-d)
DIR=$2; shift
shift;;
-p)
PASSWD=$2; shift
shift;;
-D)
DOMAIN=$2; shift
shift;;
--)
shift; break;
esac
done
if [ $ echo "Aborted - just specifing one vhost makes no sense for SNI testing. Go wild !"
exit 1
fi
if [ $ NAMES=$*
fi
if ! openssl version | grep -q OpenSSL; then
echo Aborted - your openssl is very old or misconfigured.
exit 1
fi
set `openssl version`
if test "0$2" \< "00.9"; then
echo Aborted - version of openssl too old, 0.9 or up required.
exit 1
fi
if test -d ${DIR} -a "x$FORCE" != "x1"; then
echo Aborted - already an ${DIR} directory. Use the -f flag to overwrite.
exit 1
fi
mkdir -p ${DIR} || exit 1
mkdir -p ${DIR}/ssl ${DIR}/htdocs ${DIR}/logs || exit 1
serial=$RANDOM$$
openssl req -new -nodes -batch \
-x509 \
-days 10 -subj '/CN=Da Root/O=SNI testing/' -set_serial $serial \
-keyout ${DIR}/root.key -out ${DIR}/root.pem \
|| exit 2
CDIR=${DIR}/client-xs-control
mkdir -p ${CDIR}
openssl req -new -nodes -batch \
-x509 \
-days 10 -subj '/CN=Da Second Root/O=SNI user access I/' -set_serial 2$serial$$\
-keyout ${CDIR}/xs-root-1.key -out ${CDIR}/xs-root-1.pem \
|| exit 2
openssl req -new -nodes -batch \
-x509 \
-days 10 -subj '/CN=Da Second Root/O=SNI user access II/' -set_serial 3$serial$$ \
-keyout ${CDIR}/xs-root-2.key -out ${CDIR}/xs-root-2.pem \
|| exit 2
cat ${CDIR}/xs-root-2.pem ${CDIR}/xs-root-1.pem > ${CDIR}/xs-root-chain.pem
mkdir -p ${CDIR}/xs-root-dir || exit 1
rm -f {$CDIR}/*.0
ln ${CDIR}/xs-root-1.pem ${CDIR}/xs-root-dir/`openssl x509 -noout -hash -in ${CDIR}/xs-root-1.pem`.0
ln ${CDIR}/xs-root-2.pem ${CDIR}/xs-root-dir/`openssl x509 -noout -hash -in ${CDIR}/xs-root-2.pem`.0
for i in 1 2
do
openssl req -new -nodes -batch \
-days 9 -subj "/CN=User $i/O=SNI Test Crash Dummy Dept/" \
-keyout ${CDIR}/client-$i.key -out ${CDIR}/client-$i.req -batch \
|| exit 3
openssl x509 -text -req \
-CA ${CDIR}/xs-root-$i.pem -CAkey ${CDIR}/xs-root-$i.key \
-set_serial 3$serial$$ -in ${CDIR}/client-$i.req -out ${CDIR}/client-$i.pem \
|| exit 4
openssl pkcs12 -export \
-inkey ${CDIR}/client-$i.key -in ${CDIR}/client-$i.pem -name "Client $i" \
-caname "Issuing client root $i" -certfile ${CDIR}/xs-root-$i.pem \
-out ${CDIR}/client.p12 -passout pass:"$PASSWD" || exit 5
rm ${CDIR}/client-$i.req
done
echo '
cat > ${DIR}/httpd-sni.conf << EOM
Listen ${IP}:443
NameVirtualHost ${IP}:443
LoadModule ssl_module modules/mod_ssl.so
SSLRandomSeed startup builtin
SSLRandomSeed connect builtin
LogLevel debug
TransferLog ${DIR}/logs/access_log
ErrorLog ${DIR}/logs/error_log
SSLSessionCache none
<Directory />
Options None
AllowOverride None
Require all denied
</Directory>
<Directory "${DIR}/htdocs">
allow from all
Require all granted
</Directory>
EOM
(
echo "; Configuration sample to be added to the $DOMAIN zone file of BIND."
echo "\$ORIGIN $DOMAIN."
) > ${DIR}/zone-file
ZADD="IN A $IP"
INFO="and also the site you see when the browser does not support SNI."
set -- ${NAMES}
DEFAULT=$1
for n in ${NAMES}
do
FQDN=$n.$DOMAIN
serial=`expr $serial + 1`
openssl req -new -nodes -batch \
-days 9 -subj "/CN=$FQDN/O=SNI Testing/" \
-keyout ${DIR}/$n.key -out ${DIR}/$n.req -batch \
|| exit 3
openssl x509 -text -req \
-CA ${DIR}/root.pem -CAkey ${DIR}/root.key \
-set_serial $serial -in ${DIR}/$n.req -out ${DIR}/$n.pem \
|| exit 4
cat ${DIR}/$n.pem ${DIR}/$n.key > ${DIR}/ssl/$n.crt
rm ${DIR}/$n.req ${DIR}/$n.key ${DIR}/$n.pem
LST="$LST
https://$FQDN/index.html"
echo "${IP} $FQDN $n" >> ${DIR}/hosts
echo "$n $ZADD" >> ${DIR}/zone-file
ZADD="IN CNAME $DEFAULT"
mkdir -p ${DIR}/htdocs/$n || exit 1
echo We are $FQDN $INFO > ${DIR}/htdocs/$n/index.html || exit 1
INFO="and you'd normally only see this site when there is proper SNI support."
cat >> ${DIR}/httpd-sni.conf << EOM
<VirtualHost ${IP}:443>
SSLEngine On
ServerName $FQDN:443
DocumentRoot ${DIR}/htdocs/$n
SSLCertificateChainFile ${DIR}/root.pem
SSLCertificateFile ${DIR}/ssl/$n.crt
TransferLog ${DIR}/logs/access_$n
</VirtualHost>
EOM
done
cat << EOM
SNI Files generated
===================
The directory ${DIR}/sni has been populated with the following
- root.key|pem Certificate authority root and key. (You could
import the root.pem key into your browser to
quell warnings about an unknown authority).
- hosts /etc/hosts file with fake entries for the hosts
- htdocs directory with one docroot for each domain,
each with a small sample file.
- ssl directory with an ssl cert (signed by root)
for each of the domains).
- logs logfiles, one for each domain and an
access_log for any misses.
The directory ${CDIR} contains optional test files to allow client
authentication testing:
- client*pem/p12 Files for client authentication testing. These
need to be imported into the browser.
- xs-root-1/2 Certificate authority which has issued above
client authentication certificates.
- xs-root-dir A directory specific for the SSLCACertificateDir
directive.
- xs-root-chain A chain of the two client xs authorities for the
SSLCACertificate directive.
SNI Test
========
A directory ${DIR}/sni has been created. Run an apache
server against it with
.../httpd -f ${DIR}/httpd-sni.conf
and keep an eye on ${DIR}/logs/error_log. When everything
is fine you will see entries like:
Feb 11 16:12:26 2008] [debug] Init:
SSL server IP/port overlap: ape.*:443 (httpd-sni.conf:24) vs. jane.*:443 (httpd-sni.conf:42)
for each vhost configured and a concluding warning:
[Mon Feb 11 16:12:26 2008] [warn] Init:
Name-based SSL virtual hosts only work for clients with TLS server name indication support (RFC 4366)
HOWEVER - If you see an entry like:
[Mon Feb 11 15:41:41 2008] [warn] Init:
You should not use name-based virtual hosts in conjunction with SSL!!
then you are either using an OpenSSL which is too old and/or you need to ensure that the
TLS Extensions are compiled into openssl with the 'enable-tlsext' flag. Once you have
recompiled or reinstalled OpenSSL with TLS Extensions you will have to recompile mod_ssl
to allow it to recognize SNI support.
Meanwhile add 'hosts' to your c:\windows\system32\drivers\etc\hosts
or /etc/hosts file as to point the various URL's to your server:
$LST
and verify that each returns its own name (and an entry in its
own ${DIR}/logs) file).
NOTE
====
Note that in the generated example the 'first' domain is special - and is the
catch all for non-SNI browsers. Depending on your circumstances it may make
sense to use a generic name - and have each of the SNI domains as subdirectories
(and hence URI's under this generic name). Thus allowing non SNI browsers also
access to those sites.
EOM
exit 0