blob: e58e513a60a9e6ee65ae68dcae5d99b04a8f9079 [file] [log] [blame]
Dean Troyerc83a7e12012-11-29 11:47:58 -06001# lib/tls
2# Functions to control the configuration and operation of the TLS proxy service
3
Dean Troyerc83a7e12012-11-29 11:47:58 -06004# !! source _before_ any services that use ``SERVICE_HOST``
Adam Spiers6a5aa7c2013-10-24 11:27:02 +01005#
6# Dependencies:
7#
8# - ``functions`` file
9# - ``DEST``, ``DATA_DIR`` must be defined
10# - ``HOST_IP``, ``SERVICE_HOST``
11# - ``KEYSTONE_TOKEN_FORMAT`` must be defined
Dean Troyerc83a7e12012-11-29 11:47:58 -060012
13# Entry points:
Adam Spiers6a5aa7c2013-10-24 11:27:02 +010014#
15# - configure_CA
16# - init_CA
Dean Troyerc83a7e12012-11-29 11:47:58 -060017
Adam Spiers6a5aa7c2013-10-24 11:27:02 +010018# - configure_proxy
19# - start_tls_proxy
Dean Troyerc83a7e12012-11-29 11:47:58 -060020
Stanislaw Pituchabd5dae02014-06-25 15:29:43 +010021# - stop_tls_proxy
22# - cleanup_CA
23
Stanislaw Pitucha2e0f0542014-06-27 16:05:53 +010024# - make_root_CA
25# - make_int_CA
26# - make_cert ca-dir cert-name "common-name" ["alt-name" ...]
Adam Spiers6a5aa7c2013-10-24 11:27:02 +010027# - start_tls_proxy HOST_IP 5000 localhost 5000
Jamie Lennoxbd24a8d2013-09-20 16:26:42 +100028# - ensure_certificates
29# - is_ssl_enabled_service
Dean Troyerc83a7e12012-11-29 11:47:58 -060030
Dean Troyercc6b4432013-04-08 15:38:03 -050031# Defaults
32# --------
33
Dean Troyerc83a7e12012-11-29 11:47:58 -060034if is_service_enabled tls-proxy; then
35 # TODO(dtroyer): revisit this below after the search for HOST_IP has been done
36 TLS_IP=${TLS_IP:-$SERVICE_IP}
37
38 # Set the default ``SERVICE_PROTOCOL`` for TLS
39 SERVICE_PROTOCOL=https
40fi
41
42# Make up a hostname for cert purposes
43# will be added to /etc/hosts?
44DEVSTACK_HOSTNAME=secure.devstack.org
45DEVSTACK_CERT_NAME=devstack-cert
46DEVSTACK_CERT=$DATA_DIR/$DEVSTACK_CERT_NAME.pem
47
48# CA configuration
49ROOT_CA_DIR=${ROOT_CA_DIR:-$DATA_DIR/CA/root-ca}
50INT_CA_DIR=${INT_CA_DIR:-$DATA_DIR/CA/int-ca}
51
52ORG_NAME="OpenStack"
53ORG_UNIT_NAME="DevStack"
54
55# Stud configuration
56STUD_PROTO="--tls"
57STUD_CIPHERS='TLSv1+HIGH:!DES:!aNULL:!eNULL:@STRENGTH'
58
59
60# CA Functions
61# ============
62
63# There may be more than one, get specific
64OPENSSL=${OPENSSL:-/usr/bin/openssl}
65
66# Do primary CA configuration
Ian Wienandaee18c72014-02-21 15:35:08 +110067function configure_CA {
Dean Troyerc83a7e12012-11-29 11:47:58 -060068 # build common config file
69
70 # Verify ``TLS_IP`` is good
71 if [[ -n "$HOST_IP" && "$HOST_IP" != "$TLS_IP" ]]; then
72 # auto-discover has changed the IP
73 TLS_IP=$HOST_IP
74 fi
75}
76
77# Creates a new CA directory structure
78# create_CA_base ca-dir
Ian Wienandaee18c72014-02-21 15:35:08 +110079function create_CA_base {
Dean Troyerc83a7e12012-11-29 11:47:58 -060080 local ca_dir=$1
81
82 if [[ -d $ca_dir ]]; then
83 # Bail out it exists
84 return 0
85 fi
86
87 for i in certs crl newcerts private; do
88 mkdir -p $ca_dir/$i
89 done
90 chmod 710 $ca_dir/private
91 echo "01" >$ca_dir/serial
92 cp /dev/null $ca_dir/index.txt
93}
94
95
96# Create a new CA configuration file
97# create_CA_config ca-dir common-name
Ian Wienandaee18c72014-02-21 15:35:08 +110098function create_CA_config {
Dean Troyerc83a7e12012-11-29 11:47:58 -060099 local ca_dir=$1
100 local common_name=$2
101
102 echo "
103[ ca ]
104default_ca = CA_default
105
106[ CA_default ]
107dir = $ca_dir
108policy = policy_match
109database = \$dir/index.txt
110serial = \$dir/serial
111certs = \$dir/certs
112crl_dir = \$dir/crl
113new_certs_dir = \$dir/newcerts
114certificate = \$dir/cacert.pem
115private_key = \$dir/private/cacert.key
116RANDFILE = \$dir/private/.rand
117default_md = default
118
119[ req ]
120default_bits = 1024
121default_md = sha1
122
123prompt = no
124distinguished_name = ca_distinguished_name
125
126x509_extensions = ca_extensions
127
128[ ca_distinguished_name ]
129organizationName = $ORG_NAME
130organizationalUnitName = $ORG_UNIT_NAME Certificate Authority
131commonName = $common_name
132
133[ policy_match ]
134countryName = optional
135stateOrProvinceName = optional
136organizationName = match
137organizationalUnitName = optional
138commonName = supplied
139
140[ ca_extensions ]
141basicConstraints = critical,CA:true
142subjectKeyIdentifier = hash
143authorityKeyIdentifier = keyid:always, issuer
144keyUsage = cRLSign, keyCertSign
145
146" >$ca_dir/ca.conf
147}
148
149# Create a new signing configuration file
150# create_signing_config ca-dir
Ian Wienandaee18c72014-02-21 15:35:08 +1100151function create_signing_config {
Dean Troyerc83a7e12012-11-29 11:47:58 -0600152 local ca_dir=$1
153
154 echo "
155[ ca ]
156default_ca = CA_default
157
158[ CA_default ]
159dir = $ca_dir
160policy = policy_match
161database = \$dir/index.txt
162serial = \$dir/serial
163certs = \$dir/certs
164crl_dir = \$dir/crl
165new_certs_dir = \$dir/newcerts
166certificate = \$dir/cacert.pem
167private_key = \$dir/private/cacert.key
168RANDFILE = \$dir/private/.rand
169default_md = default
170
171[ req ]
172default_bits = 1024
173default_md = sha1
174
175prompt = no
176distinguished_name = req_distinguished_name
177
178x509_extensions = req_extensions
179
180[ req_distinguished_name ]
181organizationName = $ORG_NAME
182organizationalUnitName = $ORG_UNIT_NAME Server Farm
183
184[ policy_match ]
185countryName = optional
186stateOrProvinceName = optional
187organizationName = match
188organizationalUnitName = optional
189commonName = supplied
190
191[ req_extensions ]
192basicConstraints = CA:false
193subjectKeyIdentifier = hash
194authorityKeyIdentifier = keyid:always, issuer
195keyUsage = digitalSignature, keyEncipherment, keyAgreement
196extendedKeyUsage = serverAuth, clientAuth
197subjectAltName = \$ENV::SUBJECT_ALT_NAME
198
199" >$ca_dir/signing.conf
200}
201
Dean Troyerca802172013-01-09 19:08:02 -0600202# Create root and intermediate CAs
Dean Troyerc83a7e12012-11-29 11:47:58 -0600203# init_CA
204function init_CA {
205 # Ensure CAs are built
206 make_root_CA $ROOT_CA_DIR
207 make_int_CA $INT_CA_DIR $ROOT_CA_DIR
208
209 # Create the CA bundle
210 cat $ROOT_CA_DIR/cacert.pem $INT_CA_DIR/cacert.pem >>$INT_CA_DIR/ca-chain.pem
Dean Troyerca802172013-01-09 19:08:02 -0600211}
Dean Troyerc83a7e12012-11-29 11:47:58 -0600212
Dean Troyerca802172013-01-09 19:08:02 -0600213# Create an initial server cert
214# init_cert
215function init_cert {
Dean Troyerc83a7e12012-11-29 11:47:58 -0600216 if [[ ! -r $DEVSTACK_CERT ]]; then
217 if [[ -n "$TLS_IP" ]]; then
218 # Lie to let incomplete match routines work
219 TLS_IP="DNS:$TLS_IP"
220 fi
221 make_cert $INT_CA_DIR $DEVSTACK_CERT_NAME $DEVSTACK_HOSTNAME "$TLS_IP"
222
223 # Create a cert bundle
224 cat $INT_CA_DIR/private/$DEVSTACK_CERT_NAME.key $INT_CA_DIR/$DEVSTACK_CERT_NAME.crt $INT_CA_DIR/cacert.pem >$DEVSTACK_CERT
225 fi
226}
227
228
229# make_cert creates and signs a new certificate with the given commonName and CA
230# make_cert ca-dir cert-name "common-name" ["alt-name" ...]
Ian Wienandaee18c72014-02-21 15:35:08 +1100231function make_cert {
Dean Troyerc83a7e12012-11-29 11:47:58 -0600232 local ca_dir=$1
233 local cert_name=$2
234 local common_name=$3
235 local alt_names=$4
236
237 # Generate a signing request
238 $OPENSSL req \
239 -sha1 \
240 -newkey rsa \
241 -nodes \
242 -keyout $ca_dir/private/$cert_name.key \
243 -out $ca_dir/$cert_name.csr \
244 -subj "/O=${ORG_NAME}/OU=${ORG_UNIT_NAME} Servers/CN=${common_name}"
245
246 if [[ -z "$alt_names" ]]; then
247 alt_names="DNS:${common_name}"
248 else
249 alt_names="DNS:${common_name},${alt_names}"
250 fi
251
252 # Sign the request valid for 1 year
253 SUBJECT_ALT_NAME="$alt_names" \
254 $OPENSSL ca -config $ca_dir/signing.conf \
255 -extensions req_extensions \
256 -days 365 \
257 -notext \
258 -in $ca_dir/$cert_name.csr \
259 -out $ca_dir/$cert_name.crt \
260 -subj "/O=${ORG_NAME}/OU=${ORG_UNIT_NAME} Servers/CN=${common_name}" \
261 -batch
262}
263
264
265# Make an intermediate CA to sign everything else
266# make_int_CA ca-dir signing-ca-dir
Ian Wienandaee18c72014-02-21 15:35:08 +1100267function make_int_CA {
Dean Troyerc83a7e12012-11-29 11:47:58 -0600268 local ca_dir=$1
269 local signing_ca_dir=$2
270
271 # Create the root CA
272 create_CA_base $ca_dir
273 create_CA_config $ca_dir 'Intermediate CA'
274 create_signing_config $ca_dir
275
276 # Create a signing certificate request
277 $OPENSSL req -config $ca_dir/ca.conf \
278 -sha1 \
279 -newkey rsa \
280 -nodes \
281 -keyout $ca_dir/private/cacert.key \
282 -out $ca_dir/cacert.csr \
283 -outform PEM
284
285 # Sign the intermediate request valid for 1 year
286 $OPENSSL ca -config $signing_ca_dir/ca.conf \
287 -extensions ca_extensions \
288 -days 365 \
289 -notext \
290 -in $ca_dir/cacert.csr \
291 -out $ca_dir/cacert.pem \
292 -batch
293}
294
295# Make a root CA to sign other CAs
296# make_root_CA ca-dir
Ian Wienandaee18c72014-02-21 15:35:08 +1100297function make_root_CA {
Dean Troyerc83a7e12012-11-29 11:47:58 -0600298 local ca_dir=$1
299
300 # Create the root CA
301 create_CA_base $ca_dir
302 create_CA_config $ca_dir 'Root CA'
303
304 # Create a self-signed certificate valid for 5 years
305 $OPENSSL req -config $ca_dir/ca.conf \
306 -x509 \
307 -nodes \
308 -newkey rsa \
309 -days 21360 \
310 -keyout $ca_dir/private/cacert.key \
311 -out $ca_dir/cacert.pem \
312 -outform PEM
313}
314
315
Jamie Lennoxbd24a8d2013-09-20 16:26:42 +1000316# Certificate Input Configuration
317# ===============================
318
319# check to see if the service(s) specified are to be SSL enabled.
320#
321# Multiple services specified as arguments are ``OR``'ed together; the test
322# is a short-circuit boolean, i.e it returns on the first match.
323#
324# Uses global ``SSL_ENABLED_SERVICES``
Ian Wienandaee18c72014-02-21 15:35:08 +1100325function is_ssl_enabled_service {
Sean Daguef0bd8db2014-07-23 15:14:07 -0400326 local services=$@
327 local service=""
Jamie Lennoxbd24a8d2013-09-20 16:26:42 +1000328 for service in ${services}; do
329 [[ ,${SSL_ENABLED_SERVICES}, =~ ,${service}, ]] && return 0
330 done
331 return 1
332}
333
334
335# Ensure that the certificates for a service are in place. This function does
336# not check that a service is SSL enabled, this should already have been
337# completed.
338#
339# The function expects to find a certificate, key and CA certificate in the
340# variables {service}_SSL_CERT, {service}_SSL_KEY and {service}_SSL_CA. For
341# example for keystone this would be KEYSTONE_SSL_CERT, KEYSTONE_SSL_KEY and
342# KEYSTONE_SSL_CA. If it does not find these certificates the program will
343# quit.
Ian Wienandaee18c72014-02-21 15:35:08 +1100344function ensure_certificates {
Jamie Lennoxbd24a8d2013-09-20 16:26:42 +1000345 local service=$1
346
347 local cert_var="${service}_SSL_CERT"
348 local key_var="${service}_SSL_KEY"
349 local ca_var="${service}_SSL_CA"
350
351 local cert=${!cert_var}
352 local key=${!key_var}
353 local ca=${!ca_var}
354
Solly Ross66115e52014-03-18 15:12:05 -0400355 if [[ -z "$cert" || -z "$key" || -z "$ca" ]]; then
Jamie Lennoxbd24a8d2013-09-20 16:26:42 +1000356 die $LINENO "Missing either the ${cert_var} ${key_var} or ${ca_var}" \
357 "variable to enable SSL for ${service}"
358 fi
359
360 cat $ca >> $SSL_BUNDLE_FILE
361}
362
363
Dean Troyerc83a7e12012-11-29 11:47:58 -0600364# Proxy Functions
365# ===============
366
367# Starts the TLS proxy for the given IP/ports
368# start_tls_proxy front-host front-port back-host back-port
Ian Wienandaee18c72014-02-21 15:35:08 +1100369function start_tls_proxy {
Dean Troyerc83a7e12012-11-29 11:47:58 -0600370 local f_host=$1
371 local f_port=$2
372 local b_host=$3
373 local b_port=$4
374
375 stud $STUD_PROTO -f $f_host,$f_port -b $b_host,$b_port $DEVSTACK_CERT 2>/dev/null
376}
Sean Dague584d90e2013-03-29 14:34:53 -0400377
Dean Troyercc6b4432013-04-08 15:38:03 -0500378
Stanislaw Pituchabd5dae02014-06-25 15:29:43 +0100379# Cleanup Functions
380# ===============
381
382
383# Stops all stud processes. This should be done only after all services
384# using tls configuration are down.
385function stop_tls_proxy {
386 killall stud
387}
388
389
390# Remove CA along with configuration, as well as the local server certificate
391function cleanup_CA {
392 rm -rf "$DATA_DIR/CA" "$DEVSTACK_CERT"
393}
394
Adam Spiers6a5aa7c2013-10-24 11:27:02 +0100395# Tell emacs to use shell-script-mode
396## Local variables:
397## mode: shell-script
398## End: