1#!/bin/sh 2# 3# Licensed to the Apache Software Foundation (ASF) under one or more 4# contributor license agreements. See the NOTICE file distributed with 5# this work for additional information regarding copyright ownership. 6# The ASF licenses this file to You under the Apache License, Version 2.0 7# (the "License"); you may not use this file except in compliance with 8# the License. You may obtain a copy of the License at 9# 10# http://www.apache.org/licenses/LICENSE-2.0 11# 12# Unless required by applicable law or agreed to in writing, software 13# distributed under the License is distributed on an "AS IS" BASIS, 14# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15# See the License for the specific language governing permissions and 16# limitations under the License. 17# 18# This script will populate a directory 'sni' with 3 sites, httpd.conf 19# and certificates as to facilitate testing of TLS server name 20# indication support (RFC 4366) or SNI. 21# 22# 23OPENSSL=${OPENSSL:-openssl} 24DOMAIN=${DOMAIN:-my-sni-test.org} 25DIR=${DIR:-$PWD/sni} 26 27# List of hostnames automatically created by default. 28NAMES=${NAMES:-ape nut pear apple banana} 29 30# IP address these hostnames are bound to. 31IP=${IP:-127.0.0.1} 32 33# A certificate password for the .p12 files of the client 34# authentication test. Normally not set. However some browsers 35# require a password of at least 4 characters. 36# 37PASSWD=${PASSWD:-} 38 39args=`getopt a:fd:D:p: $*` 40if [ $? != 0 ]; then 41 echo "Syntax: $0 [-f] [-a IPaddress] [-d outdir] [-D domain ] [two or more vhost names ]" 42 echo " -f Force overwriting of outdir (default is $DIR)" 43 echo " -d dir Directory to create the SNI test server in (default is $DIR)" 44 echo " -D domain Domain name to use for this test (default is $DOMAIN)" 45 echo " -a IP IP address to use for this virtual host (default is $IP)" 46 echo " -p str Password for the client certificate test (some browsers require a set password)" 47 echo " [names] List of optional vhost names (default is $NAMES)" 48 echo 49 echo "Example:" 50 echo " $0 -D SecureBlogsAreUs.com peter fred mary jane ardy" 51 echo 52 echo "Which will create peter.SecureBlogsAreUs.com, fred.SecureBlogsAreUs.com and" 53 echo "so on. Note that the _first_ FQDN is also the default for non SNI hosts. It" 54 echo "may make sense to give this host a generic name - and allow each of the real" 55 echo "SNI site as sub directories/URI's of this generic name; thus allowing the " 56 echo "few non-SNI browsers access." 57 exit 1 58fi 59set -- $args 60for i 61do 62 case "$i" 63 in 64 -f) 65 FORCE=1 66 shift;; 67 -a) 68 IP=$2; shift 69 shift;; 70 -d) 71 DIR=$2; shift 72 shift;; 73 -p) 74 PASSWD=$2; shift 75 shift;; 76 -D) 77 DOMAIN=$2; shift 78 shift;; 79 --) 80 shift; break; 81 esac 82done 83 84if [ $# = 1 ]; then 85 echo "Aborted - just specifying one vhost makes no sense for SNI testing. Go wild !" 86 exit 1 87fi 88 89if [ $# -gt 0 ]; then 90 NAMES=$* 91fi 92 93if ! openssl version | grep -q OpenSSL; then 94 echo Aborted - your openssl is very old or misconfigured. 95 exit 1 96fi 97 98set `openssl version` 99if test "0$2" \< "00.9"; then 100 echo Aborted - version of openssl too old, 0.9 or up required. 101 exit 1 102fi 103 104if test -d ${DIR} -a "x$FORCE" != "x1"; then 105 echo Aborted - already an ${DIR} directory. Use the -f flag to overwrite. 106 exit 1 107fi 108 109mkdir -p ${DIR} || exit 1 110mkdir -p ${DIR}/ssl ${DIR}/htdocs ${DIR}/logs || exit 1 111 112# Create a 'CA' - keep using different serial numbers 113# as the browsers get upset if they see an identical 114# serial with a different pub-key. 115# 116# Note that we're not relying on the 'v3_ca' section as 117# in the default openssl.conf file - so the certificate 118# will be without the basicConstraints = CA:true and 119# keyUsage = cRLSign, keyCertSign values. This is fine 120# for most browsers. 121# 122serial=$RANDOM$$ 123 124openssl req -new -nodes -batch \ 125 -x509 \ 126 -days 10 -subj '/CN=Da Root/O=SNI testing/' -set_serial $serial \ 127 -keyout ${DIR}/root.key -out ${DIR}/root.pem \ 128 || exit 2 129 130CDIR=${DIR}/client-xs-control 131mkdir -p ${CDIR} 132# Create some certificate authorities for testing client controls 133# 134openssl req -new -nodes -batch \ 135 -x509 \ 136 -days 10 -subj '/CN=Da Second Root/O=SNI user access I/' -set_serial 2$serial$$\ 137 -keyout ${CDIR}/xs-root-1.key -out ${CDIR}/xs-root-1.pem \ 138 || exit 2 139 140openssl req -new -nodes -batch \ 141 -x509 \ 142 -days 10 -subj '/CN=Da Second Root/O=SNI user access II/' -set_serial 3$serial$$ \ 143 -keyout ${CDIR}/xs-root-2.key -out ${CDIR}/xs-root-2.pem \ 144 || exit 2 145 146# Create a chain of just the two access authorities: 147cat ${CDIR}/xs-root-2.pem ${CDIR}/xs-root-1.pem > ${CDIR}/xs-root-chain.pem 148 149# And likewise a directory with the same information (using the 150# required 'hash' naming format 151# 152mkdir -p ${CDIR}/xs-root-dir || exit 1 153rm -f {$CDIR}/*.0 154ln ${CDIR}/xs-root-1.pem ${CDIR}/xs-root-dir/`openssl x509 -noout -hash -in ${CDIR}/xs-root-1.pem`.0 155ln ${CDIR}/xs-root-2.pem ${CDIR}/xs-root-dir/`openssl x509 -noout -hash -in ${CDIR}/xs-root-2.pem`.0 156 157# Use the above two client certificate authorities to make a few users 158for i in 1 2 159do 160 # Create a certificate request for a test user. 161 # 162 openssl req -new -nodes -batch \ 163 -days 9 -subj "/CN=User $i/O=SNI Test Crash Dummy Dept/" \ 164 -keyout ${CDIR}/client-$i.key -out ${CDIR}/client-$i.req -batch \ 165 || exit 3 166 167 # And get it signed by either our client cert issuing root authority. 168 # 169 openssl x509 -text -req \ 170 -CA ${CDIR}/xs-root-$i.pem -CAkey ${CDIR}/xs-root-$i.key \ 171 -set_serial 3$serial$$ -in ${CDIR}/client-$i.req -out ${CDIR}/client-$i.pem \ 172 || exit 4 173 174 # And create a pkcs#12 version for easy browser import. 175 # 176 openssl pkcs12 -export \ 177 -inkey ${CDIR}/client-$i.key -in ${CDIR}/client-$i.pem -name "Client $i" \ 178 -caname "Issuing client root $i" -certfile ${CDIR}/xs-root-$i.pem \ 179 -out ${CDIR}/client.p12 -passout pass:"$PASSWD" || exit 5 180 181 rm ${CDIR}/client-$i.req 182done 183 184# Create the header for the example '/etc/hosts' file. 185# 186echo '# To append to your hosts file' > ${DIR}/hosts 187 188# Create a header for the httpd.conf snipped. 189# 190cat > ${DIR}/httpd-sni.conf << EOM 191# To append to your httpd.conf file' 192Listen ${IP}:443 193NameVirtualHost ${IP}:443 194 195LoadModule ssl_module modules/mod_ssl.so 196 197SSLRandomSeed startup builtin 198SSLRandomSeed connect builtin 199 200LogLevel debug 201TransferLog ${DIR}/logs/access_log 202ErrorLog ${DIR}/logs/error_log 203 204# You'll get a warning about this. 205# 206SSLSessionCache none 207 208# Note that this SSL configuration is far 209# from complete - you probably will want 210# to configure SSLSession Caches at the 211# very least. 212 213<Directory /> 214 Options None 215 AllowOverride None 216 Require all denied 217</Directory> 218 219<Directory "${DIR}/htdocs"> 220 allow from all 221 Require all granted 222</Directory> 223 224# This first entry is also the default for non SNI 225# supporting clients. 226# 227EOM 228 229# Create the header of a sample BIND zone file. 230# 231( 232 echo "; Configuration sample to be added to the $DOMAIN zone file of BIND." 233 echo "\$ORIGIN $DOMAIN." 234) > ${DIR}/zone-file 235 236ZADD="IN A $IP" 237INFO="and also the site you see when the browser does not support SNI." 238 239set -- ${NAMES} 240DEFAULT=$1 241 242for n in ${NAMES} 243do 244 FQDN=$n.$DOMAIN 245 serial=`expr $serial + 1` 246 247 # Create a certificate request for this host. 248 # 249 openssl req -new -nodes -batch \ 250 -days 9 -subj "/CN=$FQDN/O=SNI Testing/" \ 251 -keyout ${DIR}/$n.key -out ${DIR}/$n.req -batch \ 252 || exit 3 253 254 # And get it signed by our root authority. 255 # 256 openssl x509 -text -req \ 257 -CA ${DIR}/root.pem -CAkey ${DIR}/root.key \ 258 -set_serial $serial -in ${DIR}/$n.req -out ${DIR}/$n.pem \ 259 || exit 4 260 261 # Combine the key and certificate in one file. 262 # 263 cat ${DIR}/$n.pem ${DIR}/$n.key > ${DIR}/ssl/$n.crt 264 rm ${DIR}/$n.req ${DIR}/$n.key ${DIR}/$n.pem 265 266 LST="$LST 267 https://$FQDN/index.html" 268 269 # Create a /etc/host and bind-zone file example 270 # 271 echo "${IP} $FQDN $n" >> ${DIR}/hosts 272 echo "$n $ZADD" >> ${DIR}/zone-file 273 ZADD="IN CNAME $DEFAULT" 274 275 # Create and populate a docroot for this host. 276 # 277 mkdir -p ${DIR}/htdocs/$n || exit 1 278 echo We are $FQDN $INFO > ${DIR}/htdocs/$n/index.html || exit 1 279 280 # And change the info text - so that only the default/fallback site 281 # gets marked as such. 282 # 283 INFO="and you'd normally only see this site when there is proper SNI support." 284 285 # And create a configuration snipped. 286 # 287 cat >> ${DIR}/httpd-sni.conf << EOM 288<VirtualHost ${IP}:443> 289 SSLEngine On 290 ServerName $FQDN:443 291 DocumentRoot ${DIR}/htdocs/$n 292 SSLCertificateChainFile ${DIR}/root.pem 293 SSLCertificateFile ${DIR}/ssl/$n.crt 294 295 # Uncomment the following lines if you 296 # want to only allow access to clients with 297 # a certificate issued/signed by some 298 # selection of the issuing authorities 299 # 300 # SSLCACertificate ${CDIR}/xs-root-1.pem # just root 1 301 # SSLCACertificate ${CDIR}/xs-root-2.pem # just root 2 302 # SSLCACertificate ${CDIR}/xs-root-chain.pem # 1 & 2 303 # SSLCACertificateDir ${CDIR}/xs-root-dir # 1 & 2 - but as a directory. 304 # 305 # SSLVerifyClient require 306 # SSLVerifyDepth 2 307 # 308 TransferLog ${DIR}/logs/access_$n 309</VirtualHost> 310 311EOM 312 313done 314 315cat << EOM 316SNI Files generated 317=================== 318 319The directory ${DIR}/sni has been populated with the following 320 321- root.key|pem Certificate authority root and key. (You could 322 import the root.pem key into your browser to 323 quell warnings about an unknown authority). 324 325- hosts /etc/hosts file with fake entries for the hosts 326 327- htdocs directory with one docroot for each domain, 328 each with a small sample file. 329 330- ssl directory with an ssl cert (signed by root) 331 for each of the domains). 332 333- logs logfiles, one for each domain and an 334 access_log for any misses. 335 336The directory ${CDIR} contains optional test files to allow client 337authentication testing: 338 339- client*pem/p12 Files for client authentication testing. These 340 need to be imported into the browser. 341 342- xs-root-1/2 Certificate authority which has issued above 343 client authentication certificates. 344 345- xs-root-dir A directory specific for the SSLCACertificateDir 346 directive. 347 348- xs-root-chain A chain of the two client xs authorities for the 349 SSLCACertificate directive. 350 351SNI Test 352======== 353 354A directory ${DIR}/sni has been created. Run an apache 355server against it with 356 357 .../httpd -f ${DIR}/httpd-sni.conf 358 359and keep an eye on ${DIR}/logs/error_log. When everything 360is fine you will see entries like: 361 362 Feb 11 16:12:26 2008] [debug] Init: 363 SSL server IP/port overlap: ape.*:443 (httpd-sni.conf:24) vs. jane.*:443 (httpd-sni.conf:42) 364 365for each vhost configured and a concluding warning: 366 367 [Mon Feb 11 16:12:26 2008] [warn] Init: 368 Name-based SSL virtual hosts only work for clients with TLS server name indication support (RFC 4366) 369 370HOWEVER - If you see an entry like: 371 372 [Mon Feb 11 15:41:41 2008] [warn] Init: 373 You should not use name-based virtual hosts in conjunction with SSL!! 374 375then you are either using an OpenSSL which is too old and/or you need to ensure that the 376TLS Extensions are compiled into openssl with the 'enable-tlsext' flag. Once you have 377recompiled or reinstalled OpenSSL with TLS Extensions you will have to recompile mod_ssl 378to allow it to recognize SNI support. 379 380Meanwhile add 'hosts' to your c:\windows\system32\drivers\etc\hosts 381or /etc/hosts file as to point the various URL's to your server: 382$LST 383 384and verify that each returns its own name (and an entry in its 385own ${DIR}/logs) file). 386 387NOTE 388==== 389 390Note that in the generated example the 'first' domain is special - and is the 391catch all for non-SNI browsers. Depending on your circumstances it may make 392sense to use a generic name - and have each of the SNI domains as subdirectories 393(and hence URI's under this generic name). Thus allowing non SNI browsers also 394access to those sites. 395EOM 396exit 0 397