#!/bin/sh # # Licensed to the Apache Software Foundation (ASF) under one or more # contributor license agreements. See the NOTICE file distributed with # this work for additional information regarding copyright ownership. # The ASF licenses this file to You under the Apache License, Version 2.0 # (the "License"); you may not use this file except in compliance with # the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # This script will populate a directory 'sni' with 3 sites, httpd.conf # and certificates as to facilitate testing of TLS server name # indication support (RFC 4366) or SNI. # # OPENSSL=${OPENSSL:-openssl} DOMAIN=${DOMAIN:-my-sni-test.org} DIR=${DIR:-$PWD/sni} # List of hostnames automatically created by default. NAMES=${NAMES:-ape nut pear apple banana} # IP address these hostnames are bound to. IP=${IP:-127.0.0.1} # A certificate password for the .p12 files of the client # authentication test. Normally not set. However some browsers # require a password of at least 4 characters. # 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 [ $# = 1 ]; then echo "Aborted - just specifying one vhost makes no sense for SNI testing. Go wild !" exit 1 fi if [ $# -gt 0 ]; then 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 # Create a 'CA' - keep using different serial numbers # as the browsers get upset if they see an identical # serial with a different pub-key. # # Note that we're not relying on the 'v3_ca' section as # in the default openssl.conf file - so the certificate # will be without the basicConstraints = CA:true and # keyUsage = cRLSign, keyCertSign values. This is fine # for most browsers. # 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} # Create some certificate authorities for testing client controls # 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 # Create a chain of just the two access authorites: cat ${CDIR}/xs-root-2.pem ${CDIR}/xs-root-1.pem > ${CDIR}/xs-root-chain.pem # And likewise a directory with the same information (using the # required 'hash' naming format # 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 # Use the above two client certificate authorities to make a few users for i in 1 2 do # Create a certificate request for a test user. # 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 # And get it signed by either our client cert issuing root authority. # 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 # And create a pkcs#12 version for easy browser import. # 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 # Create the header for the example '/etc/hosts' file. # echo '# To append to your hosts file' > ${DIR}/hosts # Create a header for the httpd.conf snipped. # cat > ${DIR}/httpd-sni.conf << EOM # To append to your httpd.conf file' 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 # You'll get a warning about this. # SSLSessionCache none # Note that this SSL configuration is far # from complete - you propably will want # to configure SSLSession Caches at the # very least. Options None AllowOverride None Require all denied allow from all Require all granted # This first entry is also the default for non SNI # supporting clients. # EOM # Create the header of a sample BIND zone file. # ( 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` # Create a certificate request for this host. # openssl req -new -nodes -batch \ -days 9 -subj "/CN=$FQDN/O=SNI Testing/" \ -keyout ${DIR}/$n.key -out ${DIR}/$n.req -batch \ || exit 3 # And get it signed by our root authority. # 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 # Combine the key and certificate in one file. # 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" # Create a /etc/host and bind-zone file example # echo "${IP} $FQDN $n" >> ${DIR}/hosts echo "$n $ZADD" >> ${DIR}/zone-file ZADD="IN CNAME $DEFAULT" # Create and populate a docroot for this host. # mkdir -p ${DIR}/htdocs/$n || exit 1 echo We are $FQDN $INFO > ${DIR}/htdocs/$n/index.html || exit 1 # And change the info text - so that only the default/fallback site # gets marked as such. # INFO="and you'd normally only see this site when there is proper SNI support." # And create a configuration snipped. # cat >> ${DIR}/httpd-sni.conf << EOM SSLEngine On ServerName $FQDN:443 DocumentRoot ${DIR}/htdocs/$n SSLCertificateChainFile ${DIR}/root.pem SSLCertificateFile ${DIR}/ssl/$n.crt # Uncomment the following lines if you # want to only allow access to clients with # a certificate issued/signed by some # selection of the issuing authorites # # SSLCACertificate ${CDIR}/xs-root-1.pem # just root 1 # SSLCACertificate ${CDIR}/xs-root-2.pem # just root 2 # SSLCACertificate ${CDIR}/xs-root-chain.pem # 1 & 2 # SSLCACertificateDir ${CDIR}/xs-root-dir # 1 & 2 - but as a directory. # # SSLVerifyClient require # SSLVerifyDepth 2 # TransferLog ${DIR}/logs/access_$n 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