Jump to: navigation, search

Difference between revisions of "Network/LBaaS/docs/how-to-create-tls-loadbalancer"

(Create certificate chain and key.)
 
(45 intermediate revisions by 7 users not shown)
Line 8: Line 8:
 
* Certificate and key generation
 
* Certificate and key generation
 
* Barbican secret and container operations
 
* Barbican secret and container operations
* Required patchsets for neutron-lbaas
+
* Load balancer creation
 
* Verifying the setup
 
* Verifying the setup
 +
  
 
Neutron-LBaaS utilizes Barbican as its keystore and requires some setting up, let's get started.
 
Neutron-LBaaS utilizes Barbican as its keystore and requires some setting up, let's get started.
  
Firstly, this article assumes that the reader is familiar with Git, Openstack, Devstack and other related tools and technologies to get this going. This article will not go through setting up Devstack, Openstack, Git or any thing in this regard.
+
Firstly, this article assumes that the reader is familiar with Git, Openstack, Devstack and other related tools and technologies to get this going.  
 +
This article will not go through setting up Devstack, Openstack, Git or any thing in this regard.
  
With that said, assuming you don't have Devstack set up already use this script to get started with Devstack and Barbican: https://gist.github.com/rm-you/6feacb91182f5c011018
 
Alternatively, the reader can copy the two needed files and add 'barbican' to the enabled services in your localrc/local.conf as described below or follow the official instructions found here: https://wiki.openstack.org/wiki/BarbicanDevStack
 
 
TL;DR
 
<pre><nowiki>
 
$ git clone https://github.com/openstack/barbican.git
 
$ cp barbican/contrib/devstack/lib/barbican <devstack_dir>/lib/
 
$ cp barbican/contrib/devstack/extras.d/70-barbican.sh <devstack_dir>/extras.d/
 
</nowiki></pre>
 
  
Add 'barbican' to the end of 'enabled_services' in your localrc, then run stack.sh
+
==== Barbican Devstack: ====
 +
Add 'enable_plugin barbican https://review.openstack.org/openstack/barbican' to right above the lbaas plugins in your localrc, then run stack.sh
 
<pre><nowiki>
 
<pre><nowiki>
$ ./<devstack_dir>/stack.sh
+
./<devstack_dir>/stack.sh
 
</nowiki></pre>
 
</nowiki></pre>
  
===== Create certificate chain and key. =====
+
==== Create certificate chain and key. ====
 
*The following may be a little more verbose then whats actually needed, please feel free to create/retrieve cert/key/intermediates as you see fit.
 
*The following may be a little more verbose then whats actually needed, please feel free to create/retrieve cert/key/intermediates as you see fit.
  
 
<pre><nowiki>
 
<pre><nowiki>
$ openssl genrsa -des3 -out ca.key 1024  
+
openssl genrsa -des3 -out ca.key 1024  
$ openssl req -new -x509 -days 3650 -key ca.key -out ca.crt   
+
openssl req -new -x509 -days 3650 -key ca.key -out ca.crt   
$ openssl x509  -in  ca.crt -out ca.pem  
+
openssl x509  -in  ca.crt -out ca.pem  
$ openssl genrsa -des3 -out ca-int_encrypted.key 1024  
+
openssl genrsa -des3 -out ca-int_encrypted.key 1024  
$ openssl rsa -in ca-int_encrypted.key -out ca-int.key  
+
openssl rsa -in ca-int_encrypted.key -out ca-int.key  
$ openssl req -new -key ca-int.key -out ca-int.csr -subj "/CN=ca-int@acme.com"  
+
openssl req -new -key ca-int.key -out ca-int.csr -subj "/CN=ca-int@acme.com"  
$ openssl x509 -req -days 3650 -in ca-int.csr -CA ca.crt -CAkey ca.key -set_serial 01 -out ca-int.crt  
+
openssl x509 -req -days 3650 -in ca-int.csr -CA ca.crt -CAkey ca.key -set_serial 01 -out ca-int.crt  
$ openssl genrsa -des3 -out server_encrypted.key 1024  
+
openssl genrsa -des3 -out server_encrypted.key 1024  
$ openssl rsa -in server_encrypted.key -out server.key  
+
openssl rsa -in server_encrypted.key -out server.key  
$ openssl req -new -key server.key -out server.csr -subj "/CN=server@acme.com"  
+
openssl req -new -key server.key -out server.csr -subj "/CN=server@acme.com"  
$ openssl x509 -req -days 3650 -in server.csr -CA ca-int.crt -CAkey ca-int.key -set_serial 01 -out server.crt
+
openssl x509 -req -days 3650 -in server.csr -CA ca-int.crt -CAkey ca-int.key -set_serial 01 -out server.crt
 
</nowiki></pre>
 
</nowiki></pre>
  
===== Barbican secrets and containers: =====
+
If youd like to also test SNI create atleast one more cert chain with different CN:
  
 
<pre><nowiki>
 
<pre><nowiki>
$ barbican secret store --payload-content-type='application/octet-stream' --payload-content-encoding='base64' --name='certificate' --payload="$(cat serverb64.crt)"
+
openssl genrsa -des3 -out ca2.key 1024
$ barbican secret store --payload-content-type='application/octet-stream' --payload-content-encoding='base64' --name='private_key' --payload="$(cat serverb64.key)"
+
openssl req -new -x509 -days 3650 -key ca2.key -out ca2.crt 
$ barbican container create --name='tls_container' --type='certificate' --secret="certificate=$(barbican secret list | awk '/ certificate / {print $2}')" --secret="private_key=$(barbican secret list | awk '/ private_key / {print $2}')"
+
openssl x509  -in  ca2.crt -out ca2.pem
 +
openssl genrsa -des3 -out ca-int_encrypted2.key 1024
 +
openssl rsa -in ca-int_encrypted2.key -out ca-int2.key
 +
openssl req -new -key ca-int2.key -out ca-int2.csr -subj "/CN=ca-int-test2@stacme.com"
 +
openssl x509 -req -days 3650 -in ca-int2.csr -CA ca2.crt -CAkey ca2.key -set_serial 01 -out ca-int2.crt
 +
openssl genrsa -des3 -out server_encrypted2.key 1024
 +
openssl rsa -in server_encrypted2.key -out server2.key
 +
openssl req -new -key server2.key -out server2.csr -subj "/CN=test2@stacme.com"  
 +
openssl x509 -req -days 3650 -in server2.csr --CA ca-int2.crt -CAkey ca-int2.key -set_serial 01 -out server2.crt
 
</nowiki></pre>
 
</nowiki></pre>
  
*Note: above is written with a Barbican client bug work around and expects the cert, key, intermediates to be Base64 encoded files.
+
*Note the CN for both chains as well use them to verify SNI later
An example of using OpenSSL to this:
+
 
 +
==== Barbican secrets and containers: ====
  
 
<pre><nowiki>
 
<pre><nowiki>
$ openssl base64 -in <infile> -out <outfile>
+
barbican secret store --payload-content-type='text/plain' --name='certificate' --payload="$(cat server.crt)"
</nowiki></pre>
 
  
Otherwise, leaving the files how they are generated, removing the payload-content-encoding and specifying 'text/plain' for the content-type will work just fine:
+
barbican secret store --payload-content-type='text/plain' --name='private_key' --payload="$(cat server.key)"
  
<pre><nowiki>
+
barbican secret container create --name='tls_container' --type='certificate' --secret="certificate=$(barbican secret list | awk '/ certificate / {print $2}')" --secret="private_key=$(barbican secret list | awk '/ private_key / {print $2}')"
$ barbican secret store --payload-content-type='text/plain' --name='certificate' --payload="$(cat server.crt)"
 
$ barbican secret store --payload-content-type='text/plain' --name='private_key' --payload="$(cat server.key)"
 
$ barbican container create --name='tls_container' --type='certificate' --secret="certificate=$(barbican secret list | awk '/ certificate / {print $2}')" --secret="private_key=$(barbican secret list | awk '/ private_key / {print $2}')"
 
 
</nowiki></pre>
 
</nowiki></pre>
  
===== Patch the code: =====
+
Add the second certificate chain to test SNI:
At this point we should have our certificate container created in Barbican using the certificates we created and are now ready to create the TLS load balancer.
 
Before we can dive into lbaas commands we will need to checkout a few patches and restart a few services to get everything up to date with the un-merged code.
 
  
* python-neutronclient to allow the new TLS settings
+
<pre><nowiki>
* neutron-lbaas TLS extension, plugin and other related code and Cert manager fixes
+
barbican secret store --payload-content-type='text/plain' --name='certificate2' --payload="$(cat server2.crt)"
  
Checkout the latest patch of neutron-lbaas from:
+
barbican secret store --payload-content-type='text/plain' --name='private_key2' --payload="$(cat server2.key)"
<pre><nowiki>https://review.openstack.org/#/c/145151/</nowiki></pre>
 
Cherry-pick, also neutron-lbaas, from:
 
<pre><nowiki>https://review.openstack.org/#/c/164063/</nowiki></pre>
 
  
Cherry-pick into the python-neutronclient
+
barbican secret container create --name='tls_container2' --type='certificate' --secret="certificate=$(barbican secret list | awk '/ certificate2 / {print $2}')" --secret="private_key=$(barbican secret list | awk '/ private_key2 / {print $2}')"
<pre><nowiki>https://review.openstack.org/#/c/161404/</nowiki></pre>
+
</nowiki></pre>
  
===== Update neutron config. =====
+
==== Update neutron config ====
Add:
+
Add or update the 'service_auth' group in /etc/neutron/neutron_lbaas.conf:
 
<pre><nowiki>
 
<pre><nowiki>
admin_project_name = admin
+
auth_uri = http://localhost:35357/v2.0
auth_version = v2
+
admin_tenant_name = admin
 +
admin_user = admin
 +
admin_password = password
 +
auth_version = 2
 
</nowiki></pre>
 
</nowiki></pre>
to the 'keystone_authtoken' group.
+
*The needed values may differ from your particular devstack install, ensure to use those values.
 +
* Neutron-server and neutron-lbaasv2-agent will need to be restarted to pick up the change
 +
** Note: at time of writing, this config is not loaded correctly by the devstack plugin, so you will have to add the command-line arg to load it when restarting the service.
  
===== Setup neutron-lbaas and neutronclient: =====
+
==== Update octavia config. ====
<pre><nowiki>$ sudo python setup.py install</nowiki></pre>
+
Change in the 'certificates' group the cert_manager:
within both neutron-lbaas and python-neutronclient to get things set up.
+
<pre><nowiki>
 
+
cert_manager=barbican_cert_manager
Restart:
+
</nowiki></pre>
Restart neutron-server from the Devstack screen session, or any other means of running the server.
+
*The needed values may differ from your particular devstack install, ensure to use those values.
 +
* Octavia controller worker ('o-cw') will need to be restarted to pick up the change
  
===== Create member for testing (basic devstack cirros instance): =====
+
==== Create nova instances: ====
 +
Create member for simple testing (basic devstack cirros instance):
 
<pre><nowiki>
 
<pre><nowiki>
 
nova keypair-add default --pub-key ~/.ssh/id_rsa.pub  
 
nova keypair-add default --pub-key ~/.ssh/id_rsa.pub  
 
nova secgroup-add-rule default tcp 22 22 0.0.0.0/0
 
nova secgroup-add-rule default tcp 22 22 0.0.0.0/0
 +
nova secgroup-add-rule default tcp 80 80 0.0.0.0/0
 
nova secgroup-add-rule default icmp -1 -1 0.0.0.0/0
 
nova secgroup-add-rule default icmp -1 -1 0.0.0.0/0
nova boot --image cirros-0.3.0-x86_64-disk --flavor 2 --nic net-id=$(neutron net-list | awk '/ private / {print $2}') member1 --security_groups default --key-name default
+
nova boot --image $(nova image-list | grep -o cirros.*-disk) --flavor 2 --nic net-id=$(neutron net-list | awk '/ private / {print $2}') member1 --security-groups default --key-name default
 +
</nowiki></pre>
 +
 
 +
Add another member for load balanced testing (basic devstack cirros instance):
 +
<pre><nowiki>
 +
nova boot --image $(nova image-list | grep -o cirros.*-disk) --flavor 2 --nic net-id=$(neutron net-list | awk '/ private / {print $2}') member2 --security-groups default --key-name default
 
</nowiki></pre>
 
</nowiki></pre>
  
===== Create TLS enabled load balancer: =====
+
==== Create TLS enabled load balancer: ====
 
<pre><nowiki>
 
<pre><nowiki>
 
neutron lbaas-loadbalancer-create $(neutron subnet-list | awk '/ private-subnet / {print $2}') --name lb1
 
neutron lbaas-loadbalancer-create $(neutron subnet-list | awk '/ private-subnet / {print $2}') --name lb1
 
</nowiki></pre>
 
</nowiki></pre>
  
Create listener:  
+
Create listener with TLS no SNI:  
 
<pre><nowiki>
 
<pre><nowiki>
neutron lbaas-listener-create --loadbalancer lb1 --protocol-port 443 --protocol TERMINATED_HTTPS --name listener1 --default-tls-container=$(barbican container list | awk '/ tls_container / {print $2}')
+
neutron lbaas-listener-create --loadbalancer lb1 --protocol-port 443 --protocol TERMINATED_HTTPS --name listener1 --default-tls-container=$(barbican secret container list | awk '/ tls_container / {print $2}')
 +
</nowiki></pre>
 +
 
 +
Create listener with TLS and SNI:
 +
<pre><nowiki>
 +
neutron lbaas-listener-create --loadbalancer lb1 --protocol-port 443 --protocol TERMINATED_HTTPS --name listener1 --default-tls-container=$(barbican secret container list | awk '/ tls_container / {print $2}') --sni-container $(barbican secret container list | awk '/ tls_container2 / {print $2}')
 
</nowiki></pre>
 
</nowiki></pre>
  
Line 123: Line 135:
 
</nowiki></pre>
 
</nowiki></pre>
  
Create member:
+
Create members:
 
<pre><nowiki>
 
<pre><nowiki>
neutron lbaas-member-create pool1 --address <member_address> --protocol-port 80 --subnet $(neutron subnet-list | awk '/ private-subnet / {print $2}')  
+
neutron lbaas-member-create pool1 --address $(nova show member1 | awk '/private network/ {a = substr($5, 0, length($5)-1); if (a ~ "\\.") print a; else print $6}')  --protocol-port 80 --subnet $(neutron subnet-list | awk '/ private-subnet / {print $2}')
 +
 
 +
neutron lbaas-member-create pool1 --address $(nova show member2 | awk '/private network/ {a = substr($5, 0, length($5)-1); if (a ~ "\\.") print a; else print $6}') --protocol-port 80 --subnet $(neutron subnet-list | awk '/ private-subnet / {print $2}')  
 
</nowiki></pre>
 
</nowiki></pre>
  
===== Test it out: =====
+
==== Test it out: ====
*For a simple example, our member can run a simple web server on the instance:
+
*For a simple example, our members can run a simple web server :
 
<pre><nowiki>
 
<pre><nowiki>
while true; do echo -e 'HTTP/1.0 200 OK\r\n\r\nIt Works!' | sudo nc -l -p 80 ; done  
+
while true; do echo -e 'HTTP/1.0 200 OK\r\n\r\nIt Works member<N>!' | sudo nc -l -p 80 ; done  
 
</nowiki></pre>
 
</nowiki></pre>
  
  
===== Verify the response: =====
+
Verify the response:
 
<pre><nowiki>
 
<pre><nowiki>
$ curl -k https://<loadbalancer_address>
+
curl -k https://$(neutron lbaas-loadbalancer-list | awk '/ lb1 / {print $6}')
 
</nowiki></pre>
 
</nowiki></pre>
 
Should result in:
 
Should result in:
<pre><nowiki>It Works!</nowiki></pre>
+
<pre><nowiki>It Works member<N>!</nowiki></pre>
 +
 
 +
Verify SNI:
 +
<pre><nowiki>
 +
openssl s_client -servername test2@stacme.com -connect $(neutron lbaas-loadbalancer-list | awk '/ lb1 / {print $6}'):443
 +
</nowiki></pre>
 +
The certificate information should print to screen, verify the CN matches the CN you passed to '-servername'

Latest revision as of 21:54, 19 January 2017

How To Create A TLS Enabled Load Balancer

The following article will walk through the steps required to set up a load balancer to serve TLS terminated traffic. This article is geared towards the current state of the project and will evolve along with the project itself.

Some of the items to be discussed are:

  • Barbican devstack setup
  • Certificate and key generation
  • Barbican secret and container operations
  • Load balancer creation
  • Verifying the setup


Neutron-LBaaS utilizes Barbican as its keystore and requires some setting up, let's get started.

Firstly, this article assumes that the reader is familiar with Git, Openstack, Devstack and other related tools and technologies to get this going. This article will not go through setting up Devstack, Openstack, Git or any thing in this regard.


Barbican Devstack:

Add 'enable_plugin barbican https://review.openstack.org/openstack/barbican' to right above the lbaas plugins in your localrc, then run stack.sh

./<devstack_dir>/stack.sh

Create certificate chain and key.

  • The following may be a little more verbose then whats actually needed, please feel free to create/retrieve cert/key/intermediates as you see fit.
openssl genrsa -des3 -out ca.key 1024 
openssl req -new -x509 -days 3650 -key ca.key -out ca.crt  
openssl x509  -in  ca.crt -out ca.pem 
openssl genrsa -des3 -out ca-int_encrypted.key 1024 
openssl rsa -in ca-int_encrypted.key -out ca-int.key 
openssl req -new -key ca-int.key -out ca-int.csr -subj "/CN=ca-int@acme.com" 
openssl x509 -req -days 3650 -in ca-int.csr -CA ca.crt -CAkey ca.key -set_serial 01 -out ca-int.crt 
openssl genrsa -des3 -out server_encrypted.key 1024 
openssl rsa -in server_encrypted.key -out server.key 
openssl req -new -key server.key -out server.csr -subj "/CN=server@acme.com" 
openssl x509 -req -days 3650 -in server.csr  -CA ca-int.crt -CAkey ca-int.key -set_serial 01 -out server.crt

If youd like to also test SNI create atleast one more cert chain with different CN:

openssl genrsa -des3 -out ca2.key 1024 
openssl req -new -x509 -days 3650 -key ca2.key -out ca2.crt  
openssl x509  -in  ca2.crt -out ca2.pem 
openssl genrsa -des3 -out ca-int_encrypted2.key 1024 
openssl rsa -in ca-int_encrypted2.key -out ca-int2.key 
openssl req -new -key ca-int2.key -out ca-int2.csr -subj "/CN=ca-int-test2@stacme.com" 
openssl x509 -req -days 3650 -in ca-int2.csr -CA ca2.crt -CAkey ca2.key -set_serial 01 -out ca-int2.crt 
openssl genrsa -des3 -out server_encrypted2.key 1024 
openssl rsa -in server_encrypted2.key -out server2.key 
openssl req -new -key server2.key -out server2.csr -subj "/CN=test2@stacme.com" 
openssl x509 -req -days 3650 -in server2.csr --CA ca-int2.crt -CAkey ca-int2.key -set_serial 01 -out server2.crt
  • Note the CN for both chains as well use them to verify SNI later

Barbican secrets and containers:

barbican secret store --payload-content-type='text/plain' --name='certificate' --payload="$(cat server.crt)"

barbican secret store --payload-content-type='text/plain' --name='private_key' --payload="$(cat server.key)"

barbican secret container create --name='tls_container' --type='certificate' --secret="certificate=$(barbican secret list | awk '/ certificate / {print $2}')" --secret="private_key=$(barbican secret list | awk '/ private_key / {print $2}')"

Add the second certificate chain to test SNI:

barbican secret store --payload-content-type='text/plain' --name='certificate2' --payload="$(cat server2.crt)"

barbican secret store --payload-content-type='text/plain' --name='private_key2' --payload="$(cat server2.key)"

barbican secret container create --name='tls_container2' --type='certificate' --secret="certificate=$(barbican secret list | awk '/ certificate2 / {print $2}')" --secret="private_key=$(barbican secret list | awk '/ private_key2 / {print $2}')"

Update neutron config

Add or update the 'service_auth' group in /etc/neutron/neutron_lbaas.conf:

auth_uri = http://localhost:35357/v2.0
admin_tenant_name = admin
admin_user = admin
admin_password = password
auth_version = 2
  • The needed values may differ from your particular devstack install, ensure to use those values.
  • Neutron-server and neutron-lbaasv2-agent will need to be restarted to pick up the change
    • Note: at time of writing, this config is not loaded correctly by the devstack plugin, so you will have to add the command-line arg to load it when restarting the service.

Update octavia config.

Change in the 'certificates' group the cert_manager:

cert_manager=barbican_cert_manager
  • The needed values may differ from your particular devstack install, ensure to use those values.
  • Octavia controller worker ('o-cw') will need to be restarted to pick up the change

Create nova instances:

Create member for simple testing (basic devstack cirros instance):

nova keypair-add default --pub-key ~/.ssh/id_rsa.pub 
nova secgroup-add-rule default tcp 22 22 0.0.0.0/0
nova secgroup-add-rule default tcp 80 80 0.0.0.0/0
nova secgroup-add-rule default icmp -1 -1 0.0.0.0/0
nova boot --image $(nova image-list | grep -o cirros.*-disk) --flavor 2 --nic net-id=$(neutron net-list | awk '/ private / {print $2}') member1 --security-groups default --key-name default

Add another member for load balanced testing (basic devstack cirros instance):

nova boot --image $(nova image-list | grep -o cirros.*-disk) --flavor 2 --nic net-id=$(neutron net-list | awk '/ private / {print $2}') member2 --security-groups default --key-name default

Create TLS enabled load balancer:

neutron lbaas-loadbalancer-create $(neutron subnet-list | awk '/ private-subnet / {print $2}') --name lb1

Create listener with TLS no SNI:

neutron lbaas-listener-create --loadbalancer lb1 --protocol-port 443 --protocol TERMINATED_HTTPS --name listener1 --default-tls-container=$(barbican secret container list | awk '/ tls_container / {print $2}')

Create listener with TLS and SNI:

neutron lbaas-listener-create --loadbalancer lb1 --protocol-port 443 --protocol TERMINATED_HTTPS --name listener1 --default-tls-container=$(barbican secret container list | awk '/ tls_container / {print $2}') --sni-container $(barbican secret container list | awk '/ tls_container2 / {print $2}')

Create pool:

neutron lbaas-pool-create --name pool1 --protocol HTTP --listener listener1 --lb-algorithm ROUND_ROBIN

Create members:

neutron lbaas-member-create pool1 --address $(nova show member1 | awk '/private network/ {a = substr($5, 0, length($5)-1); if (a ~ "\\.") print a; else print $6}')  --protocol-port 80 --subnet $(neutron subnet-list | awk '/ private-subnet / {print $2}') 

neutron lbaas-member-create pool1 --address $(nova show member2 | awk '/private network/ {a = substr($5, 0, length($5)-1); if (a ~ "\\.") print a; else print $6}')  --protocol-port 80 --subnet $(neutron subnet-list | awk '/ private-subnet / {print $2}') 

Test it out:

  • For a simple example, our members can run a simple web server :
while true; do echo -e 'HTTP/1.0 200 OK\r\n\r\nIt Works member<N>!' | sudo nc -l -p 80 ; done 


Verify the response:

curl -k https://$(neutron lbaas-loadbalancer-list | awk '/ lb1 / {print $6}')

Should result in:

It Works member<N>!

Verify SNI:

openssl s_client -servername test2@stacme.com -connect $(neutron lbaas-loadbalancer-list | awk '/ lb1 / {print $6}'):443

The certificate information should print to screen, verify the CN matches the CN you passed to '-servername'