blob: 061c1cabbfb93b9883b9946e3a18b3304a1bb547 [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
Dean Troyerb1e3d0f2014-07-25 14:57:54 -050087 local i
Dean Troyerc83a7e12012-11-29 11:47:58 -060088 for i in certs crl newcerts private; do
89 mkdir -p $ca_dir/$i
90 done
91 chmod 710 $ca_dir/private
92 echo "01" >$ca_dir/serial
93 cp /dev/null $ca_dir/index.txt
94}
95
96
97# Create a new CA configuration file
98# create_CA_config ca-dir common-name
Ian Wienandaee18c72014-02-21 15:35:08 +110099function create_CA_config {
Dean Troyerc83a7e12012-11-29 11:47:58 -0600100 local ca_dir=$1
101 local common_name=$2
102
103 echo "
104[ ca ]
105default_ca = CA_default
106
107[ CA_default ]
108dir = $ca_dir
109policy = policy_match
110database = \$dir/index.txt
111serial = \$dir/serial
112certs = \$dir/certs
113crl_dir = \$dir/crl
114new_certs_dir = \$dir/newcerts
115certificate = \$dir/cacert.pem
116private_key = \$dir/private/cacert.key
117RANDFILE = \$dir/private/.rand
118default_md = default
119
120[ req ]
121default_bits = 1024
122default_md = sha1
123
124prompt = no
125distinguished_name = ca_distinguished_name
126
127x509_extensions = ca_extensions
128
129[ ca_distinguished_name ]
130organizationName = $ORG_NAME
131organizationalUnitName = $ORG_UNIT_NAME Certificate Authority
132commonName = $common_name
133
134[ policy_match ]
135countryName = optional
136stateOrProvinceName = optional
137organizationName = match
138organizationalUnitName = optional
139commonName = supplied
140
141[ ca_extensions ]
142basicConstraints = critical,CA:true
143subjectKeyIdentifier = hash
144authorityKeyIdentifier = keyid:always, issuer
145keyUsage = cRLSign, keyCertSign
146
147" >$ca_dir/ca.conf
148}
149
150# Create a new signing configuration file
151# create_signing_config ca-dir
Ian Wienandaee18c72014-02-21 15:35:08 +1100152function create_signing_config {
Dean Troyerc83a7e12012-11-29 11:47:58 -0600153 local ca_dir=$1
154
155 echo "
156[ ca ]
157default_ca = CA_default
158
159[ CA_default ]
160dir = $ca_dir
161policy = policy_match
162database = \$dir/index.txt
163serial = \$dir/serial
164certs = \$dir/certs
165crl_dir = \$dir/crl
166new_certs_dir = \$dir/newcerts
167certificate = \$dir/cacert.pem
168private_key = \$dir/private/cacert.key
169RANDFILE = \$dir/private/.rand
170default_md = default
171
172[ req ]
173default_bits = 1024
174default_md = sha1
175
176prompt = no
177distinguished_name = req_distinguished_name
178
179x509_extensions = req_extensions
180
181[ req_distinguished_name ]
182organizationName = $ORG_NAME
183organizationalUnitName = $ORG_UNIT_NAME Server Farm
184
185[ policy_match ]
186countryName = optional
187stateOrProvinceName = optional
188organizationName = match
189organizationalUnitName = optional
190commonName = supplied
191
192[ req_extensions ]
193basicConstraints = CA:false
194subjectKeyIdentifier = hash
195authorityKeyIdentifier = keyid:always, issuer
196keyUsage = digitalSignature, keyEncipherment, keyAgreement
197extendedKeyUsage = serverAuth, clientAuth
198subjectAltName = \$ENV::SUBJECT_ALT_NAME
199
200" >$ca_dir/signing.conf
201}
202
Dean Troyerca802172013-01-09 19:08:02 -0600203# Create root and intermediate CAs
Dean Troyerc83a7e12012-11-29 11:47:58 -0600204# init_CA
205function init_CA {
206 # Ensure CAs are built
207 make_root_CA $ROOT_CA_DIR
208 make_int_CA $INT_CA_DIR $ROOT_CA_DIR
209
210 # Create the CA bundle
211 cat $ROOT_CA_DIR/cacert.pem $INT_CA_DIR/cacert.pem >>$INT_CA_DIR/ca-chain.pem
Dean Troyerca802172013-01-09 19:08:02 -0600212}
Dean Troyerc83a7e12012-11-29 11:47:58 -0600213
Dean Troyerca802172013-01-09 19:08:02 -0600214# Create an initial server cert
215# init_cert
216function init_cert {
Dean Troyerc83a7e12012-11-29 11:47:58 -0600217 if [[ ! -r $DEVSTACK_CERT ]]; then
218 if [[ -n "$TLS_IP" ]]; then
219 # Lie to let incomplete match routines work
220 TLS_IP="DNS:$TLS_IP"
221 fi
222 make_cert $INT_CA_DIR $DEVSTACK_CERT_NAME $DEVSTACK_HOSTNAME "$TLS_IP"
223
224 # Create a cert bundle
225 cat $INT_CA_DIR/private/$DEVSTACK_CERT_NAME.key $INT_CA_DIR/$DEVSTACK_CERT_NAME.crt $INT_CA_DIR/cacert.pem >$DEVSTACK_CERT
226 fi
227}
228
229
230# make_cert creates and signs a new certificate with the given commonName and CA
231# make_cert ca-dir cert-name "common-name" ["alt-name" ...]
Ian Wienandaee18c72014-02-21 15:35:08 +1100232function make_cert {
Dean Troyerc83a7e12012-11-29 11:47:58 -0600233 local ca_dir=$1
234 local cert_name=$2
235 local common_name=$3
236 local alt_names=$4
237
Stanislaw Pitucha2f69c6b2014-06-25 15:07:48 +0100238 # Only generate the certificate if it doesn't exist yet on the disk
239 if [ ! -r "$ca_dir/$cert_name.crt" ]; then
240 # Generate a signing request
241 $OPENSSL req \
242 -sha1 \
243 -newkey rsa \
244 -nodes \
245 -keyout $ca_dir/private/$cert_name.key \
246 -out $ca_dir/$cert_name.csr \
247 -subj "/O=${ORG_NAME}/OU=${ORG_UNIT_NAME} Servers/CN=${common_name}"
Dean Troyerc83a7e12012-11-29 11:47:58 -0600248
Stanislaw Pitucha2f69c6b2014-06-25 15:07:48 +0100249 if [[ -z "$alt_names" ]]; then
250 alt_names="DNS:${common_name}"
251 else
252 alt_names="DNS:${common_name},${alt_names}"
253 fi
254
255 # Sign the request valid for 1 year
256 SUBJECT_ALT_NAME="$alt_names" \
257 $OPENSSL ca -config $ca_dir/signing.conf \
258 -extensions req_extensions \
259 -days 365 \
260 -notext \
261 -in $ca_dir/$cert_name.csr \
262 -out $ca_dir/$cert_name.crt \
263 -subj "/O=${ORG_NAME}/OU=${ORG_UNIT_NAME} Servers/CN=${common_name}" \
264 -batch
Dean Troyerc83a7e12012-11-29 11:47:58 -0600265 fi
Dean Troyerc83a7e12012-11-29 11:47:58 -0600266}
267
268
269# Make an intermediate CA to sign everything else
270# make_int_CA ca-dir signing-ca-dir
Ian Wienandaee18c72014-02-21 15:35:08 +1100271function make_int_CA {
Dean Troyerc83a7e12012-11-29 11:47:58 -0600272 local ca_dir=$1
273 local signing_ca_dir=$2
274
275 # Create the root CA
276 create_CA_base $ca_dir
277 create_CA_config $ca_dir 'Intermediate CA'
278 create_signing_config $ca_dir
279
Stanislaw Pitucha2f69c6b2014-06-25 15:07:48 +0100280 if [ ! -r "$ca_dir/cacert.pem" ]; then
281 # Create a signing certificate request
282 $OPENSSL req -config $ca_dir/ca.conf \
283 -sha1 \
284 -newkey rsa \
285 -nodes \
286 -keyout $ca_dir/private/cacert.key \
287 -out $ca_dir/cacert.csr \
288 -outform PEM
Dean Troyerc83a7e12012-11-29 11:47:58 -0600289
Stanislaw Pitucha2f69c6b2014-06-25 15:07:48 +0100290 # Sign the intermediate request valid for 1 year
291 $OPENSSL ca -config $signing_ca_dir/ca.conf \
292 -extensions ca_extensions \
293 -days 365 \
294 -notext \
295 -in $ca_dir/cacert.csr \
296 -out $ca_dir/cacert.pem \
297 -batch
298 fi
Dean Troyerc83a7e12012-11-29 11:47:58 -0600299}
300
301# Make a root CA to sign other CAs
302# make_root_CA ca-dir
Ian Wienandaee18c72014-02-21 15:35:08 +1100303function make_root_CA {
Dean Troyerc83a7e12012-11-29 11:47:58 -0600304 local ca_dir=$1
305
306 # Create the root CA
307 create_CA_base $ca_dir
308 create_CA_config $ca_dir 'Root CA'
309
310 # Create a self-signed certificate valid for 5 years
311 $OPENSSL req -config $ca_dir/ca.conf \
312 -x509 \
313 -nodes \
314 -newkey rsa \
315 -days 21360 \
316 -keyout $ca_dir/private/cacert.key \
317 -out $ca_dir/cacert.pem \
318 -outform PEM
319}
320
321
Jamie Lennoxbd24a8d2013-09-20 16:26:42 +1000322# Certificate Input Configuration
323# ===============================
324
325# check to see if the service(s) specified are to be SSL enabled.
326#
327# Multiple services specified as arguments are ``OR``'ed together; the test
328# is a short-circuit boolean, i.e it returns on the first match.
329#
330# Uses global ``SSL_ENABLED_SERVICES``
Ian Wienandaee18c72014-02-21 15:35:08 +1100331function is_ssl_enabled_service {
Sean Daguef0bd8db2014-07-23 15:14:07 -0400332 local services=$@
333 local service=""
Jamie Lennoxbd24a8d2013-09-20 16:26:42 +1000334 for service in ${services}; do
335 [[ ,${SSL_ENABLED_SERVICES}, =~ ,${service}, ]] && return 0
336 done
337 return 1
338}
339
340
341# Ensure that the certificates for a service are in place. This function does
342# not check that a service is SSL enabled, this should already have been
343# completed.
344#
345# The function expects to find a certificate, key and CA certificate in the
346# variables {service}_SSL_CERT, {service}_SSL_KEY and {service}_SSL_CA. For
347# example for keystone this would be KEYSTONE_SSL_CERT, KEYSTONE_SSL_KEY and
348# KEYSTONE_SSL_CA. If it does not find these certificates the program will
349# quit.
Ian Wienandaee18c72014-02-21 15:35:08 +1100350function ensure_certificates {
Jamie Lennoxbd24a8d2013-09-20 16:26:42 +1000351 local service=$1
352
353 local cert_var="${service}_SSL_CERT"
354 local key_var="${service}_SSL_KEY"
355 local ca_var="${service}_SSL_CA"
356
357 local cert=${!cert_var}
358 local key=${!key_var}
359 local ca=${!ca_var}
360
Solly Ross66115e52014-03-18 15:12:05 -0400361 if [[ -z "$cert" || -z "$key" || -z "$ca" ]]; then
Jamie Lennoxbd24a8d2013-09-20 16:26:42 +1000362 die $LINENO "Missing either the ${cert_var} ${key_var} or ${ca_var}" \
363 "variable to enable SSL for ${service}"
364 fi
365
366 cat $ca >> $SSL_BUNDLE_FILE
367}
368
369
Dean Troyerc83a7e12012-11-29 11:47:58 -0600370# Proxy Functions
371# ===============
372
373# Starts the TLS proxy for the given IP/ports
374# start_tls_proxy front-host front-port back-host back-port
Ian Wienandaee18c72014-02-21 15:35:08 +1100375function start_tls_proxy {
Dean Troyerc83a7e12012-11-29 11:47:58 -0600376 local f_host=$1
377 local f_port=$2
378 local b_host=$3
379 local b_port=$4
380
381 stud $STUD_PROTO -f $f_host,$f_port -b $b_host,$b_port $DEVSTACK_CERT 2>/dev/null
382}
Sean Dague584d90e2013-03-29 14:34:53 -0400383
Dean Troyercc6b4432013-04-08 15:38:03 -0500384
Stanislaw Pituchabd5dae02014-06-25 15:29:43 +0100385# Cleanup Functions
386# ===============
387
388
389# Stops all stud processes. This should be done only after all services
390# using tls configuration are down.
391function stop_tls_proxy {
392 killall stud
393}
394
395
396# Remove CA along with configuration, as well as the local server certificate
397function cleanup_CA {
398 rm -rf "$DATA_DIR/CA" "$DEVSTACK_CERT"
399}
400
Adam Spiers6a5aa7c2013-10-24 11:27:02 +0100401# Tell emacs to use shell-script-mode
402## Local variables:
403## mode: shell-script
404## End: