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