Switch to using PKCS12 for TLS Term certs

*NOT* deprecating the old way of storing these, as I believe that would
create a huge mess for anyone already using it.

Change-Id: I1fee174d8b8956f3d2053781a7f18c2940b21765
This commit is contained in:
Adam Harwell 2017-09-14 13:17:37 -06:00 committed by Michael Johnson
parent d7cc05be39
commit 8934a629df
22 changed files with 801 additions and 389 deletions

View File

@ -245,17 +245,21 @@ default_pool_id-optional:
type: string type: string
default_tls_container_ref: default_tls_container_ref:
description: | description: |
The URI to the `key manager service The URI of the `key manager service
<https://docs.openstack.org/barbican/latest/>`__ secrets container <https://docs.openstack.org/castellan/latest/>`__ secret containing a
containing the certificate and key for ``TERMINATED_TLS`` listeners. PKCS12 format certificate/key bundle for ``TERMINATED_TLS`` listeners.
DEPRECATED: A secret container of type "certificate" containing the
certificate and key for ``TERMINATED_TLS`` listeners.
in: body in: body
required: true required: true
type: string type: string
default_tls_container_ref-optional: default_tls_container_ref-optional:
description: | description: |
The URI to the `key manager service The URI of the `key manager service
<https://docs.openstack.org/barbican/latest/>`__ secrets container <https://docs.openstack.org/castellan/latest/>`__ secret containing a
containing the certificate and key for ``TERMINATED_TLS`` listeners. PKCS12 format certificate/key bundle for ``TERMINATED_TLS`` listeners.
DEPRECATED: A secret container of type "certificate" containing the
certificate and key for ``TERMINATED_TLS`` listeners.
in: body in: body
required: false required: false
type: string type: string
@ -963,18 +967,20 @@ session_persistence_type:
sni_container_refs: sni_container_refs:
description: | description: |
A list of URIs to the `key manager service A list of URIs to the `key manager service
<https://docs.openstack.org/barbican/latest/>`__ secrets containers <https://docs.openstack.org/barbican/latest/>`__ secrets containing
containing the certificates and keys for ``TERMINATED_TLS`` the listener PKCS12 format certificate/key bundles for ``TERMINATED_TLS`` listeners.
using Server Name Indication. (DEPRECATED) Secret containers of type "certificate" containing the
certificates and keys for ``TERMINATED_TLS`` listeners.
in: body in: body
required: true required: true
type: array type: array
sni_container_refs-optional: sni_container_refs-optional:
description: | description: |
A list of URIs to the `key manager service A list of URIs to the `key manager service
<https://docs.openstack.org/barbican/latest/>`__ secrets containers <https://docs.openstack.org/barbican/latest/>`__ secrets containing
containing the certificates and keys for ``TERMINATED_TLS`` the listener PKCS12 format certificate/key bundles for ``TERMINATED_TLS`` listeners.
using Server Name Indication. (DEPRECATED) Secret containers of type "certificate" containing the
certificates and keys for ``TERMINATED_TLS`` listeners.
in: body in: body
required: false required: false
type: array type: array

View File

@ -1,5 +1,5 @@
Octavia API v1 (SUPORTED) Octavia API v1 (SUPPORTED)
========================= ==========================
Authentication Authentication
-------------- --------------

View File

@ -365,34 +365,30 @@ balancer features, like Layer 7 features and header manipulation.
* Back-end servers 192.0.2.10 and 192.0.2.11 on subnet *private-subnet* have * Back-end servers 192.0.2.10 and 192.0.2.11 on subnet *private-subnet* have
been configured with regular HTTP application on TCP port 80. been configured with regular HTTP application on TCP port 80.
* These back-end servers have been configured with a health check at the URL
path "/healthcheck". See :ref:`http-heath-monitors` below.
* Subnet *public-subnet* is a shared external subnet created by the cloud * Subnet *public-subnet* is a shared external subnet created by the cloud
operator which is reachable from the internet. operator which is reachable from the internet.
* A TLS certificate, key, and intermediate certificate chain for * A TLS certificate, key, and intermediate certificate chain for
www.example.com have been obtained from an external certificate authority. www.example.com have been obtained from an external certificate authority.
These now exist in the files server.crt, server.key, and ca-chain.p7b in the These now exist in the files server.crt, server.key, and ca-chain.crt in the
current directory. The key and certificate are PEM-encoded, and the current directory. The key and certificate are PEM-encoded, and the
intermediate certificate chain is PKCS7 PEM encoded. The key is not encrypted intermediate certificate chain is multiple PEM-encoded certs concatenated
with a passphrase. together. The key is not encrypted with a passphrase.
* The *admin* user on this cloud installation has keystone ID *admin_id* * The *admin* user on this cloud installation has keystone ID *admin_id*
* We want to configure a TLS-terminated HTTPS load balancer that is accessible * We want to configure a TLS-terminated HTTPS load balancer that is accessible
from the internet using the key and certificate mentioned above, which from the internet using the key and certificate mentioned above, which
distributes requests to the back-end servers over the non-encrypted HTTP distributes requests to the back-end servers over the non-encrypted HTTP
protocol. protocol.
* Octavia is configured to use barbican for key management.
**Solution**: **Solution**:
1. Create barbican *secret* resources for the certificate, key, and 1. Combine the individual cert/key/intermediates to a single PKCS12 file.
intermediate certificate chain. We will call these *cert1*, *key1*, and 2. Create a barbican *secret* resource for the PKCS12 file. We will call
*intermediates1* respectively. this *tls_secret1*.
2. Create a *secret container* resource combining all of the above. We will 3. Grant the *admin* user access to the *tls_secret1* barbican resource.
call this *tls_container1*.
3. Grant the *admin* user access to all the *secret* and *secret container*
barbican resources above.
4. Create load balancer *lb1* on subnet *public-subnet*. 4. Create load balancer *lb1* on subnet *public-subnet*.
5. Create listener *listener1* as a TERMINATED_HTTPS listener referencing 5. Create listener *listener1* as a TERMINATED_HTTPS listener referencing
*tls_container1* as its default TLS container. *tls_secret1* as its default TLS container.
6. Create pool *pool1* as *listener1*'s default pool. 6. Create pool *pool1* as *listener1*'s default pool.
7. Add members 192.0.2.10 and 192.0.2.11 on *private-subnet* to *pool1*. 7. Add members 192.0.2.10 and 192.0.2.11 on *private-subnet* to *pool1*.
@ -400,21 +396,16 @@ balancer features, like Layer 7 features and header manipulation.
:: ::
openstack secret store --name='cert1' --payload-content-type='text/plain' --payload="$(cat server.crt)" openssl pkcs12 -export -inkey server.key -in server.crt -certfile ca-chain.crt -passout pass: -out server.p12
openstack secret store --name='key1' --payload-content-type='text/plain' --payload="$(cat server.key)" openstack secret store --name='tls_secret1' -t 'application/octet-stream' -e 'base64' --payload="$(base64 < server.p12)"
openstack secret store --name='intermediates1' --payload-content-type='text/plain' --payload="$(cat ca-chain.p7b)" openstack acl user add -u admin_id $(openstack secret list | awk '/ tls_secret1 / {print $2}')
openstack secret container create --name='tls_container1' --type='certificate' --secret="certificate=$(openstack secret list | awk '/ cert1 / {print $2}')" --secret="private_key=$(openstack secret list | awk '/ key1 / {print $2}')" --secret="intermediates=$(openstack secret list | awk '/ intermediates1 / {print $2}')" openstack loadbalancer create --name lb1 --vip-subnet-id public-subnet
openstack acl user add -u admin_id $(openstack secret list | awk '/ cert1 / {print $2}')
openstack acl user add -u admin_id $(openstack secret list | awk '/ key1 / {print $2}')
openstack acl user add -u admin_id $(openstack secret list | awk '/ intermediates1 / {print $2}')
openstack acl user add -u admin_id $(openstack secret list | awk '/ tls_container1 / {print $2}')
neutron lbaas-loadbalancer-create --name lb1 public-subnet
# Re-run the following until lb1 shows ACTIVE and ONLINE statuses: # Re-run the following until lb1 shows ACTIVE and ONLINE statuses:
neutron lbaas-loadbalancer-show lb1 openstack loadbalancer show lb1
neutron lbaas-listener-create --loadbalancer lb1 --protocol-port 443 --protocol TERMINATED_HTTPS --name listener1 --default-tls-container=$(openstack secret container list | awk '/ tls_container1 / {print $2}') openstack loadbalancer listener create --protocol-port 443 --protocol TERMINATED_HTTPS --name listener1 --default-tls-container=$(openstack secret list | awk '/ tls_secret1 / {print $2}' lb1
neutron lbaas-pool-create --name pool1 --lb-algorithm ROUND_ROBIN --listener listener1 --protocol HTTP openstack loadbalancer pool create --name pool1 --lb-algorithm ROUND_ROBIN --listener listener1 --protocol HTTP
neutron lbaas-member-create --subnet private-subnet --address 192.0.2.10 --protocol-port 80 pool1 openstack loadbalancer member create --subnet-id private-subnet --address 192.0.2.10 --protocol-port 80 pool1
neutron lbaas-member-create --subnet private-subnet --address 192.0.2.11 --protocol-port 80 pool1 openstack loadbalancer member create --subnet-id private-subnet --address 192.0.2.11 --protocol-port 80 pool1
Deploy a TLS-terminated HTTPS load balancer with SNI Deploy a TLS-terminated HTTPS load balancer with SNI
@ -427,18 +418,15 @@ listener using Server Name Indication (SNI) technology.
* Back-end servers 192.0.2.10 and 192.0.2.11 on subnet *private-subnet* have * Back-end servers 192.0.2.10 and 192.0.2.11 on subnet *private-subnet* have
been configured with regular HTTP application on TCP port 80. been configured with regular HTTP application on TCP port 80.
* These back-end servers have been configured with a health check at the URL
path "/healthcheck". See :ref:`http-heath-monitors` below.
* Subnet *public-subnet* is a shared external subnet created by the cloud * Subnet *public-subnet* is a shared external subnet created by the cloud
operator which is reachable from the internet. operator which is reachable from the internet.
* TLS certificates, keys, and intermediate certificate chains for * TLS certificates, keys, and intermediate certificate chains for
www.example.com and www2.example.com have been obtained from an external www.example.com and www2.example.com have been obtained from an external
certificate authority. These now exist in the files server.crt, server.key, certificate authority. These now exist in the files server.crt, server.key,
ca-chain.p7b, server2.crt, server2-encrypted.key, and ca-chain2.p7b in the ca-chain.crt, server2.crt, server2.key, and ca-chain2.crt in the
current directory. The keys and certificates are PEM-encoded, and the current directory. The keys and certificates are PEM-encoded, and the
intermediate certificate chains are PKCS7 PEM encoded. intermediate certificate chains are multiple certs PEM-encoded and
* The key for www.example.com is not encrypted with a passphrase. concatenated together. Neither key is encrypted with a passphrase.
* The key for www2.example.com is encrypted with the passphrase "abc123".
* The *admin* user on this cloud installation has keystone ID *admin_id* * The *admin* user on this cloud installation has keystone ID *admin_id*
* We want to configure a TLS-terminated HTTPS load balancer that is accessible * We want to configure a TLS-terminated HTTPS load balancer that is accessible
from the internet using the keys and certificates mentioned above, which from the internet using the keys and certificates mentioned above, which
@ -449,50 +437,34 @@ listener using Server Name Indication (SNI) technology.
**Solution**: **Solution**:
1. Create barbican *secret* resources for the certificates, keys, and 1. Combine the individual cert/key/intermediates to single PKCS12 files.
intermediate certificate chains. We will call these *cert1*, *key1*, 2. Create barbican *secret* resources for the PKCS12 files. We will call them
*intermediates1*, *cert2*, *key2* and *intermediates2* respectively. *tls_secret1* and *tls_secret2*.
2. Create a barbican *secret* resource *passphrase2* for the passphrase for 3. Grant the *admin* user access to both *tls_secret* barbican resources.
*key2* 4. Create load balancer *lb1* on subnet *public-subnet*.
3. Create *secret container* resources combining the above appropriately. We 5. Create listener *listener1* as a TERMINATED_HTTPS listener referencing
will call these *tls_container1* and *tls_container2*. *tls_secret1* as its default TLS container, and referencing both
4. Grant the *admin* user access to all the *secret* and *secret container* *tls_secret1* and *tls_secret2* using SNI.
barbican resources above. 6. Create pool *pool1* as *listener1*'s default pool.
5. Create load balancer *lb1* on subnet *public-subnet*. 7. Add members 192.0.2.10 and 192.0.2.11 on *private-subnet* to *pool1*.
6. Create listener *listener1* as a TERMINATED_HTTPS listener referencing
*tls_container1* as its default TLS container, and referencing both
*tls_container1* and *tls_container2* using SNI.
7. Create pool *pool1* as *listener1*'s default pool.
8. Add members 192.0.2.10 and 192.0.2.11 on *private-subnet* to *pool1*.
**CLI commands**: **CLI commands**:
:: ::
openstack secret store --name='cert1' --payload-content-type='text/plain' --payload="$(cat server.crt)" openssl pkcs12 -export -inkey server.key -in server.crt -certfile ca-chain.crt -passout pass: -out server.p12
openstack secret store --name='key1' --payload-content-type='text/plain' --payload="$(cat server.key)" openssl pkcs12 -export -inkey server2.key -in server2.crt -certfile ca-chain2.crt -passout pass: -out server2.p12
openstack secret store --name='intermediates1' --payload-content-type='text/plain' --payload="$(cat ca-chain.p7b)" openstack secret store --name='tls_secret1' -t 'application/octet-stream' -e 'base64' --payload="$(base64 < server.p12)"
openstack secret container create --name='tls_container1' --type='certificate' --secret="certificate=$(openstack secret list | awk '/ cert1 / {print $2}')" --secret="private_key=$(openstack secret list | awk '/ key1 / {print $2}')" --secret="intermediates=$(openstack secret list | awk '/ intermediates1 / {print $2}')" openstack secret store --name='tls_secret2' -t 'application/octet-stream' -e 'base64' --payload="$(base64 < server2.p12)"
openstack secret store --name='cert2' --payload-content-type='text/plain' --payload="$(cat server2.crt)" openstack acl user add -u admin_id $(openstack secret list | awk '/ tls_secret1 / {print $2}')
openstack secret store --name='key2' --payload-content-type='text/plain' --payload="$(cat server2-encrypted.key)" openstack acl user add -u admin_id $(openstack secret list | awk '/ tls_secret2 / {print $2}')
openstack secret store --name='intermediates2' --payload-content-type='text/plain' --payload="$(cat ca-chain2.p7b)" openstack loadbalancer create --name lb1 --vip-subnet-id public-subnet
openstack secret store --name='passphrase2' --payload-content-type='text/plain' --payload="abc123"
openstack secret container create --name='tls_container2' --type='certificate' --secret="certificate=$(openstack secret list | awk '/ cert2 / {print $2}')" --secret="private_key=$(openstack secret list | awk '/ key2 / {print $2}')" --secret="intermediates=$(openstack secret list | awk '/ intermediates2 / {print $2}')" --secret="private_key_passphrase=$(openstack secret list | awk '/ passphrase2 / {print $2}')"
openstack acl user add -u admin_id $(openstack secret list | awk '/ cert1 / {print $2}')
openstack acl user add -u admin_id $(openstack secret list | awk '/ key1 / {print $2}')
openstack acl user add -u admin_id $(openstack secret list | awk '/ intermediates1 / {print $2}')
openstack acl user add -u admin_id $(openstack secret list | awk '/ tls_container1 / {print $2}')
openstack acl user add -u admin_id $(openstack secret list | awk '/ cert2 / {print $2}')
openstack acl user add -u admin_id $(openstack secret list | awk '/ key2 / {print $2}')
openstack acl user add -u admin_id $(openstack secret list | awk '/ intermediates2 / {print $2}')
openstack acl user add -u admin_id $(openstack secret list | awk '/ tls_container2 / {print $2}')
neutron lbaas-loadbalancer-create --name lb1 public-subnet
# Re-run the following until lb1 shows ACTIVE and ONLINE statuses: # Re-run the following until lb1 shows ACTIVE and ONLINE statuses:
neutron lbaas-loadbalancer-show lb1 openstack loadbalancer show lb1
neutron lbaas-listener-create --loadbalancer lb1 --protocol-port 443 --protocol TERMINATED_HTTPS --name listener1 --default-tls-container=$(openstack secret container list | awk '/ tls_container1 / {print $2}') --sni-container_refs $(openstack secret container list | awk '/ tls_container1 / {print $2}') $(openstack secret container list | awk '/ tls_container2 / {print $2}') openstack loadbalancer listener create --protocol-port 443 --protocol TERMINATED_HTTPS --name listener1 --default-tls-container=$(openstack secret list | awk '/ tls_secret1 / {print $2}' --sni-container_refs $(openstack secret list | awk '/ tls_secret1 / {print $2}') $(openstack secret list | awk '/ tls_secret2 / {print $2}') lb1
neutron lbaas-pool-create --name pool1 --lb-algorithm ROUND_ROBIN --listener listener1 --protocol HTTP openstack loadbalancer pool create --name pool1 --lb-algorithm ROUND_ROBIN --listener listener1 --protocol HTTP
neutron lbaas-member-create --subnet private-subnet --address 192.0.2.10 --protocol-port 80 pool1 openstack loadbalancer member create --subnet-id private-subnet --address 192.0.2.10 --protocol-port 80 pool1
neutron lbaas-member-create --subnet private-subnet --address 192.0.2.11 --protocol-port 80 pool1 openstack loadbalancer member create --subnet-id private-subnet --address 192.0.2.11 --protocol-port 80 pool1
Deploy HTTP and TLS-terminated HTTPS load balancing on the same IP and backend Deploy HTTP and TLS-terminated HTTPS load balancing on the same IP and backend
@ -512,16 +484,14 @@ HTTP just get redirected to the HTTPS listener), then please see `the example
* Back-end servers 192.0.2.10 and 192.0.2.11 on subnet *private-subnet* have * Back-end servers 192.0.2.10 and 192.0.2.11 on subnet *private-subnet* have
been configured with regular HTTP application on TCP port 80. been configured with regular HTTP application on TCP port 80.
* These back-end servers have been configured with a health check at the URL
path "/healthcheck". See :ref:`http-heath-monitors` below.
* Subnet *public-subnet* is a shared external subnet created by the cloud * Subnet *public-subnet* is a shared external subnet created by the cloud
operator which is reachable from the internet. operator which is reachable from the internet.
* A TLS certificate, key, and intermediate certificate chain for * A TLS certificate, key, and intermediate certificate chain for
www.example.com have been obtained from an external certificate authority. www.example.com have been obtained from an external certificate authority.
These now exist in the files server.crt, server.key, and ca-chain.p7b in the These now exist in the files server.crt, server.key, and ca-chain.crt in the
current directory. The key and certificate are PEM-encoded, and the current directory. The key and certificate are PEM-encoded, and the
intermediate certificate chain is PKCS7 PEM encoded. The key is not encrypted intermediate certificate chain is multiple PEM-encoded certs concatenated
with a passphrase. together. The key is not encrypted with a passphrase.
* The *admin* user on this cloud installation has keystone ID *admin_id* * The *admin* user on this cloud installation has keystone ID *admin_id*
* We want to configure a TLS-terminated HTTPS load balancer that is accessible * We want to configure a TLS-terminated HTTPS load balancer that is accessible
from the internet using the key and certificate mentioned above, which from the internet using the key and certificate mentioned above, which
@ -533,16 +503,13 @@ HTTP just get redirected to the HTTPS listener), then please see `the example
**Solution**: **Solution**:
1. Create barbican *secret* resources for the certificate, key, and 1. Combine the individual cert/key/intermediates to a single PKCS12 file.
intermediate certificate chain. We will call these *cert1*, *key1*, and 2. Create a barbican *secret* resource for the PKCS12 file. We will call
*intermediates1* respectively. this *tls_secret1*.
2. Create a *secret container* resource combining all of the above. We will 3. Grant the *admin* user access to the *tls_secret1* barbican resource.
call this *tls_container1*.
3. Grant the *admin* user access to all the *secret* and *secret container*
barbican resources above.
4. Create load balancer *lb1* on subnet *public-subnet*. 4. Create load balancer *lb1* on subnet *public-subnet*.
5. Create listener *listener1* as a TERMINATED_HTTPS listener referencing 5. Create listener *listener1* as a TERMINATED_HTTPS listener referencing
*tls_container1* as its default TLS container. *tls_secret1* as its default TLS container.
6. Create pool *pool1* as *listener1*'s default pool. 6. Create pool *pool1* as *listener1*'s default pool.
7. Add members 192.0.2.10 and 192.0.2.11 on *private-subnet* to *pool1*. 7. Add members 192.0.2.10 and 192.0.2.11 on *private-subnet* to *pool1*.
8. Create listener *listener2* as an HTTP listener with *pool1* as its 8. Create listener *listener2* as an HTTP listener with *pool1* as its
@ -552,22 +519,18 @@ HTTP just get redirected to the HTTPS listener), then please see `the example
:: ::
openstack secret store --name='cert1' --payload-content-type='text/plain' --payload="$(cat server.crt)" openssl pkcs12 -export -inkey server.key -in server.crt -certfile ca-chain.crt -passout pass: -out server.p12
openstack secret store --name='key1' --payload-content-type='text/plain' --payload="$(cat server.key)" openstack secret store --name='tls_secret1' -t 'application/octet-stream' -e 'base64' --payload="$(base64 < server.p12)"
openstack secret store --name='intermediates1' --payload-content-type='text/plain' --payload="$(cat ca-chain.p7b)" openstack acl user add -u admin_id $(openstack secret list | awk '/ tls_secret1 / {print $2}')
openstack secret container create --name='tls_container1' --type='certificate' --secret="certificate=$(openstack secret list | awk '/ cert1 / {print $2}')" --secret="private_key=$(openstack secret list | awk '/ key1 / {print $2}')" --secret="intermediates=$(openstack secret list | awk '/ intermediates1 / {print $2}')" openstack loadbalancer create --name lb1 --vip-subnet-id public-subnet
openstack acl user add -u admin_id $(openstack secret list | awk '/ cert1 / {print $2}')
openstack acl user add -u admin_id $(openstack secret list | awk '/ key1 / {print $2}')
openstack acl user add -u admin_id $(openstack secret list | awk '/ intermediates1 / {print $2}')
openstack acl user add -u admin_id $(openstack secret list | awk '/ tls_container1 / {print $2}')
neutron lbaas-loadbalancer-create --name lb1 public-subnet
# Re-run the following until lb1 shows ACTIVE and ONLINE statuses: # Re-run the following until lb1 shows ACTIVE and ONLINE statuses:
neutron lbaas-loadbalancer-show lb1 openstack loadbalancer show lb1
neutron lbaas-listener-create --loadbalancer lb1 --protocol-port 443 --protocol TERMINATED_HTTPS --name listener1 --default-tls-container=$(openstack secret container list | awk '/ tls_container1 / {print $2}') openstack loadbalancer listener create --protocol-port 443 --protocol TERMINATED_HTTPS --name listener1 --default-tls-container=$(openstack secret list | awk '/ tls_secret1 / {print $2}' lb1
neutron lbaas-pool-create --name pool1 --lb-algorithm ROUND_ROBIN --listener listener1 --protocol HTTP openstack loadbalancer pool create --name pool1 --lb-algorithm ROUND_ROBIN --listener listener1 --protocol HTTP
neutron lbaas-member-create --subnet private-subnet --address 192.0.2.10 --protocol-port 80 pool1 openstack loadbalancer member create --subnet-id private-subnet --address 192.0.2.10 --protocol-port 80 pool1
neutron lbaas-member-create --subnet private-subnet --address 192.0.2.11 --protocol-port 80 pool1 openstack loadbalancer member create --subnet-id private-subnet --address 192.0.2.11 --protocol-port 80 pool1
neutron lbaas-listener-create --name listener2 --loadbalancer lb1 --protocol HTTP --protocol-port 80 --default-pool pool1 openstack secret store --name='tls_secret1' --payload-content-type='text/plain' --payload="$(cat server.crt)"
openstack loadbalancer listener create --protocol-port 80 --protocol HTTP --name listener2 --default-pool pool1 lb1
.. _heath-monitor-best-practices: .. _heath-monitor-best-practices:
@ -691,39 +654,33 @@ blocks of 64-character lines of ASCII text (that will look like gobbedlygook to
a human). These files are also typically named with a ``.crt`` or ``.pem`` a human). These files are also typically named with a ``.crt`` or ``.pem``
extension. extension.
To upload this type of intermediates chain to barbican, run a command similar
to the following (assuming "intermediates-chain.pem" is the name of the file):
::
openstack secret store --name='intermediates1' --payload-content-type='text/plain' --payload="$(cat intermediates-chain.pem)"
DER-encoded chains DER-encoded chains
------------------ ------------------
If the intermediates chain provided to you is a file that contains what appears If the intermediates chain provided to you is a file that contains what appears
to be random binary data, it is likely that it is a PKCS7 chain in DER format. to be random binary data, it is likely that it is a PKCS7 chain in DER format.
These files also may be named with a ``.p7b`` extension. In order to use this These files also may be named with a ``.p7b`` extension.
intermediates chain, you can either convert it to a series of PEM-encoded
certificates with the following command: You may use the binary DER file as-is when building your PKCS12 bundle:
:: ::
openssl pkcs7 -in intermediates-chain.p7b -inform DER -print_certs -out intermediates-chain.pem openssl pkcs12 -export -inkey server.key -in server.crt -certfile ca-chain.p7b -passout pass: -out server.p12
...or convert it into a PEM-encoded PKCS7 bundle with the following command: ... or you can convert it to a series of PEM-encoded certificates:
:: ::
openssl pkcs7 -in intermediates-chain.p7b -inform DER -outform PEM -out intermediates-chain.pem openssl pkcs7 -in intermediates-chain.p7b -inform DER -print_certs -out intermediates-chain.crt
...or simply upload the binary DER file to barbican without conversion: ... or you can convert it to a PEM-encoded PKCS7 bundle:
:: ::
openstack secret store --name='intermediates1' --payload-content-type='application/octet-stream' --payload-content-encoding='base64' --payload="$(cat intermediates-chain.p7b | base64)" openssl pkcs7 -in intermediates-chain.p7b -inform DER -outform PEM -out intermediates-chain.crt
In any case, if the file is not a PKCS7 DER bundle, then either of the above
two openssl commands will fail. If the file is not a PKCS7 DER bundle, either of the two ``openssl pkcs7``
commands will fail.
Further reading Further reading
=============== ===============

View File

@ -108,6 +108,7 @@
# For the TLS management # For the TLS management
# Certificate Manager options are local_cert_manager # Certificate Manager options are local_cert_manager
# barbican_cert_manager # barbican_cert_manager
# castellan_cert_manager
# cert_manager = barbican_cert_manager # cert_manager = barbican_cert_manager
# For Barbican authentication (if using any Barbican based cert class) # For Barbican authentication (if using any Barbican based cert class)
# barbican_auth = barbican_acl_auth # barbican_auth = barbican_acl_auth

View File

@ -19,7 +19,7 @@ Common classes for Barbican certificate handling
import abc import abc
from barbicanclient import client as barbican_client from barbicanclient.v1 import containers
from oslo_utils import encodeutils from oslo_utils import encodeutils
import six import six
@ -31,8 +31,7 @@ from octavia.i18n import _
class BarbicanCert(cert.Cert): class BarbicanCert(cert.Cert):
"""Representation of a Cert based on the Barbican CertificateContainer.""" """Representation of a Cert based on the Barbican CertificateContainer."""
def __init__(self, cert_container): def __init__(self, cert_container):
if not isinstance(cert_container, if not isinstance(cert_container, containers.CertificateContainer):
barbican_client.containers.CertificateContainer):
raise TypeError(_("Retrieved Barbican Container is not of the " raise TypeError(_("Retrieved Barbican Container is not of the "
"correct type (certificate).")) "correct type (certificate)."))
self._cert_container = cert_container self._cert_container = cert_container

View File

@ -0,0 +1,54 @@
# Copyright (c) 2017 GoDaddy
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
"""
Common classes for pkcs12 based certificate handling
"""
from cryptography.hazmat.primitives import serialization
from OpenSSL import crypto
from octavia.certificates.common import cert
class PKCS12Cert(cert.Cert):
"""Representation of a Cert for local storage."""
def __init__(self, certbag):
p12 = crypto.load_pkcs12(certbag)
self.certificate = p12.get_certificate()
self.intermediates = p12.get_ca_certificates()
self.private_key = p12.get_privatekey()
def get_certificate(self):
return self.certificate.to_cryptography().public_bytes(
encoding=serialization.Encoding.PEM).strip()
def get_intermediates(self):
if self.intermediates:
int_data = [
ic.to_cryptography().public_bytes(
encoding=serialization.Encoding.PEM).strip()
for ic in self.intermediates
]
return int_data
def get_private_key(self):
return self.private_key.to_cryptography_key().private_bytes(
encoding=serialization.Encoding.PEM,
format=serialization.PrivateFormat.TraditionalOpenSSL,
encryption_algorithm=serialization.NoEncryption()).strip()
def get_private_key_passphrase(self):
return None

View File

@ -1,4 +1,5 @@
# Copyright (c) 2014 Rackspace US, Inc # Copyright (c) 2014 Rackspace US, Inc
# Copyright (c) 2017 GoDaddy
# All Rights Reserved. # All Rights Reserved.
# #
# Licensed under the Apache License, Version 2.0 (the "License"); you may # Licensed under the Apache License, Version 2.0 (the "License"); you may
@ -14,15 +15,21 @@
# under the License. # under the License.
""" """
Cert manager implementation for Barbican Cert manager implementation for Barbican using a single PKCS12 secret
""" """
from OpenSSL import crypto
from oslo_config import cfg from oslo_config import cfg
from oslo_log import log as logging from oslo_log import log as logging
from oslo_utils import encodeutils
from oslo_utils import excutils from oslo_utils import excutils
from stevedore import driver as stevedore_driver from stevedore import driver as stevedore_driver
from octavia.certificates.common import barbican as barbican_common from octavia.certificates.common import pkcs12
from octavia.certificates.manager import barbican_legacy
from octavia.certificates.manager import cert_mgr from octavia.certificates.manager import cert_mgr
from octavia.common import exceptions
from octavia.common.tls_utils import cert_parser
LOG = logging.getLogger(__name__) LOG = logging.getLogger(__name__)
@ -38,11 +45,12 @@ class BarbicanCertManager(cert_mgr.CertManager):
invoke_on_load=True, invoke_on_load=True,
).driver ).driver
def store_cert(self, project_id, certificate, private_key, def store_cert(self, context, certificate, private_key, intermediates=None,
intermediates=None, private_key_passphrase=None, private_key_passphrase=None, expiration=None,
expiration=None, name='Octavia TLS Cert'): name="PKCS12 Certificate Bundle"):
"""Stores a certificate in the certificate manager. """Stores a certificate in the certificate manager.
:param context: Oslo context of the request
:param certificate: PEM encoded TLS certificate :param certificate: PEM encoded TLS certificate
:param private_key: private key for the supplied certificate :param private_key: private key for the supplied certificate
:param intermediates: ordered and concatenated intermediate certs :param intermediates: ordered and concatenated intermediate certs
@ -53,68 +61,42 @@ class BarbicanCertManager(cert_mgr.CertManager):
:returns: the container_ref of the stored cert :returns: the container_ref of the stored cert
:raises Exception: if certificate storage fails :raises Exception: if certificate storage fails
""" """
connection = self.auth.get_barbican_client(project_id) connection = self.auth.get_barbican_client(context.project_id)
LOG.info("Storing certificate container '%s' in Barbican.", name) LOG.info("Storing certificate secret '%s' in Barbican.", name)
p12 = crypto.PKCS12()
certificate_secret = None p12.set_friendlyname(encodeutils.to_utf8(name))
private_key_secret = None x509_cert = crypto.load_certificate(crypto.FILETYPE_PEM, certificate)
intermediates_secret = None p12.set_certificate(x509_cert)
pkp_secret = None x509_pk = crypto.load_privatekey(crypto.FILETYPE_PEM, private_key)
p12.set_privatekey(x509_pk)
if intermediates:
cert_ints = list(cert_parser.get_intermediates_pems(intermediates))
x509_ints = [
crypto.load_certificate(crypto.FILETYPE_PEM, ci)
for ci in cert_ints]
p12.set_ca_certificates(x509_ints)
if private_key_passphrase:
raise exceptions.CertificateStorageException(
"Passphrase protected PKCS12 certificates are not supported.")
try: try:
certificate_secret = connection.secrets.create( certificate_secret = connection.secrets.create(
payload=certificate, payload=p12.export(),
expiration=expiration, expiration=expiration,
name="Certificate" name=name
) )
private_key_secret = connection.secrets.create( certificate_secret.store()
payload=private_key, return certificate_secret.secret_ref
expiration=expiration,
name="Private Key"
)
certificate_container = connection.containers.create_certificate(
name=name,
certificate=certificate_secret,
private_key=private_key_secret
)
if intermediates:
intermediates_secret = connection.secrets.create(
payload=intermediates,
expiration=expiration,
name="Intermediates"
)
certificate_container.intermediates = intermediates_secret
if private_key_passphrase:
pkp_secret = connection.secrets.create(
payload=private_key_passphrase,
expiration=expiration,
name="Private Key Passphrase"
)
certificate_container.private_key_passphrase = pkp_secret
certificate_container.store()
return certificate_container.container_ref
except Exception as e: except Exception as e:
for i in [certificate_secret, private_key_secret,
intermediates_secret, pkp_secret]:
if i and i.secret_ref:
old_ref = i.secret_ref
try:
i.delete()
LOG.info('Deleted secret %s (%s) during rollback.',
i.name, old_ref)
except Exception:
LOG.warning('Failed to delete %s (%s) during '
'rollback. This might not be a problem.',
i.name, old_ref)
with excutils.save_and_reraise_exception(): with excutils.save_and_reraise_exception():
LOG.error('Error storing certificate data: %s', e) LOG.error('Error storing certificate data: %s', e)
def get_cert(self, project_id, cert_ref, resource_ref=None, def get_cert(self, context, cert_ref, resource_ref=None, check_only=False,
check_only=False, service_name='Octavia'): service_name=None):
"""Retrieves the specified cert and registers as a consumer. """Retrieves the specified cert and registers as a consumer.
:param context: Oslo context of the request
:param cert_ref: the UUID of the cert to retrieve :param cert_ref: the UUID of the cert to retrieve
:param resource_ref: Full HATEOAS reference to the consuming resource :param resource_ref: Full HATEOAS reference to the consuming resource
:param check_only: Read Certificate data without registering :param check_only: Read Certificate data without registering
@ -124,45 +106,40 @@ class BarbicanCertManager(cert_mgr.CertManager):
certificate data certificate data
:raises Exception: if certificate retrieval fails :raises Exception: if certificate retrieval fails
""" """
connection = self.auth.get_barbican_client(project_id) connection = self.auth.get_barbican_client(context.project_id)
LOG.info('Loading certificate container %s from Barbican.', cert_ref) LOG.info('Loading certificate secret %s from Barbican.', cert_ref)
try: try:
if check_only: cert_secret = connection.secrets.get(secret_ref=cert_ref)
cert_container = connection.containers.get( return pkcs12.PKCS12Cert(cert_secret.payload)
container_ref=cert_ref except Exception:
# If our get fails, try with the legacy driver.
# TODO(rm_work): Remove this code when the deprecation cycle for
# the legacy driver is complete.
legacy_mgr = barbican_legacy.BarbicanCertManager()
legacy_cert = legacy_mgr.get_cert(
context, cert_ref, resource_ref=resource_ref,
check_only=check_only, service_name=service_name
) )
else: return legacy_cert
cert_container = connection.containers.register_consumer(
container_ref=cert_ref,
name=service_name,
url=resource_ref
)
return barbican_common.BarbicanCert(cert_container)
except Exception as e:
with excutils.save_and_reraise_exception():
LOG.error('Error getting %s: %s', cert_ref, e)
def delete_cert(self, project_id, cert_ref, resource_ref=None, def delete_cert(self, context, cert_ref, resource_ref, service_name=None):
service_name='Octavia'):
"""Deregister as a consumer for the specified cert. """Deregister as a consumer for the specified cert.
:param context: Oslo context of the request
:param cert_ref: the UUID of the cert to retrieve :param cert_ref: the UUID of the cert to retrieve
:param resource_ref: Full HATEOAS reference to the consuming resource :param resource_ref: Full HATEOAS reference to the consuming resource
:param service_name: Friendly name for the consuming service :param service_name: Friendly name for the consuming service
:raises Exception: if deregistration fails :raises Exception: if deregistration fails
""" """
connection = self.auth.get_barbican_client(project_id) # TODO(rm_work): We won't take any action on a delete in this driver,
# but for now try the legacy driver's delete and ignore failure.
LOG.info('Deregistering as a consumer of %s in Barbican.', cert_ref)
try: try:
connection.containers.remove_consumer( legacy_mgr = barbican_legacy.BarbicanCertManager(auth=self.auth)
container_ref=cert_ref, legacy_mgr.delete_cert(
name=service_name, context, cert_ref, resource_ref, service_name=service_name)
url=resource_ref except Exception:
) # If the delete failed, it was probably because it isn't legacy
except Exception as e: # (this will be fixed once Secrets have Consumer registration).
with excutils.save_and_reraise_exception(): pass
LOG.error('Error deregistering as a consumer of %s: %s',
cert_ref, e)

View File

@ -0,0 +1,172 @@
# Copyright (c) 2014 Rackspace US, Inc
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
"""
Legacy cert manager implementation for Barbican (container+secrets)
"""
from oslo_config import cfg
from oslo_log import log as logging
from oslo_utils import excutils
from stevedore import driver as stevedore_driver
from octavia.certificates.common import barbican as barbican_common
from octavia.certificates.manager import cert_mgr
LOG = logging.getLogger(__name__)
class BarbicanCertManager(cert_mgr.CertManager):
"""Certificate Manager that wraps the Barbican client API."""
def __init__(self, auth=None):
super(BarbicanCertManager, self).__init__()
if auth:
self.auth = auth
else:
self.auth = stevedore_driver.DriverManager(
namespace='octavia.barbican_auth',
name=cfg.CONF.certificates.barbican_auth,
invoke_on_load=True,
).driver
def store_cert(self, context, certificate, private_key, intermediates=None,
private_key_passphrase=None, expiration=None, name=None):
"""Stores a certificate in the certificate manager.
:param context: Oslo context of the request
:param certificate: PEM encoded TLS certificate
:param private_key: private key for the supplied certificate
:param intermediates: ordered and concatenated intermediate certs
:param private_key_passphrase: optional passphrase for the supplied key
:param expiration: the expiration time of the cert in ISO 8601 format
:param name: a friendly name for the cert
:returns: the container_ref of the stored cert
:raises Exception: if certificate storage fails
"""
connection = self.auth.get_barbican_client(context.project_id)
LOG.info("Storing certificate container '%s' in Barbican.", name)
certificate_secret = None
private_key_secret = None
intermediates_secret = None
pkp_secret = None
try:
certificate_secret = connection.secrets.create(
payload=certificate,
expiration=expiration,
name="Certificate"
)
private_key_secret = connection.secrets.create(
payload=private_key,
expiration=expiration,
name="Private Key"
)
certificate_container = connection.containers.create_certificate(
name=name,
certificate=certificate_secret,
private_key=private_key_secret
)
if intermediates:
intermediates_secret = connection.secrets.create(
payload=intermediates,
expiration=expiration,
name="Intermediates"
)
certificate_container.intermediates = intermediates_secret
if private_key_passphrase:
pkp_secret = connection.secrets.create(
payload=private_key_passphrase,
expiration=expiration,
name="Private Key Passphrase"
)
certificate_container.private_key_passphrase = pkp_secret
certificate_container.store()
return certificate_container.container_ref
except Exception as e:
for i in [certificate_secret, private_key_secret,
intermediates_secret, pkp_secret]:
if i and i.secret_ref:
old_ref = i.secret_ref
try:
i.delete()
LOG.info('Deleted secret %s (%s) during rollback.',
i.name, old_ref)
except Exception:
LOG.warning('Failed to delete %s (%s) during '
'rollback. This might not be a problem.',
i.name, old_ref)
with excutils.save_and_reraise_exception():
LOG.error('Error storing certificate data: %s', e)
def get_cert(self, context, cert_ref, resource_ref=None, check_only=False,
service_name=None):
"""Retrieves the specified cert and registers as a consumer.
:param context: Oslo context of the request
:param cert_ref: the UUID of the cert to retrieve
:param resource_ref: Full HATEOAS reference to the consuming resource
:param check_only: Read Certificate data without registering
:param service_name: Friendly name for the consuming service
:return: octavia.certificates.common.Cert representation of the
certificate data
:raises Exception: if certificate retrieval fails
"""
connection = self.auth.get_barbican_client(context.project_id)
LOG.info('Loading certificate container %s from Barbican.', cert_ref)
try:
if check_only:
cert_container = connection.containers.get(
container_ref=cert_ref
)
else:
cert_container = connection.containers.register_consumer(
container_ref=cert_ref,
name=service_name,
url=resource_ref
)
return barbican_common.BarbicanCert(cert_container)
except Exception as e:
with excutils.save_and_reraise_exception():
LOG.error('Error getting %s: %s', cert_ref, e)
def delete_cert(self, context, cert_ref, resource_ref, service_name=None):
"""Deregister as a consumer for the specified cert.
:param context: Oslo context of the request
:param cert_ref: the UUID of the cert to retrieve
:param resource_ref: Full HATEOAS reference to the consuming resource
:param service_name: Friendly name for the consuming service
:raises Exception: if deregistration fails
"""
connection = self.auth.get_barbican_client(context.project_id)
LOG.info('Deregistering as a consumer of %s in Barbican.', cert_ref)
try:
connection.containers.remove_consumer(
container_ref=cert_ref,
name=service_name,
url=resource_ref
)
except Exception as e:
with excutils.save_and_reraise_exception():
LOG.error('Error deregistering as a consumer of %s: %s',
cert_ref, e)

View File

@ -0,0 +1,63 @@
# Copyright (c) 2017 GoDaddy
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
"""
Cert manager implementation for Castellan
"""
from castellan.common.objects import opaque_data
from castellan import key_manager
from OpenSSL import crypto
from oslo_log import log as logging
from octavia.certificates.common import pkcs12
from octavia.certificates.manager import cert_mgr
from octavia.common import exceptions
LOG = logging.getLogger(__name__)
class CastellanCertManager(cert_mgr.CertManager):
"""Certificate Manager for the Castellan library."""
def __init__(self):
super(CastellanCertManager, self).__init__()
self.manager = key_manager.API()
def store_cert(self, context, certificate, private_key, intermediates=None,
private_key_passphrase=None, expiration=None,
name="PKCS12 Certificate Bundle"):
p12 = crypto.PKCS12()
p12.set_certificate(certificate)
p12.set_privatekey(private_key)
if intermediates:
p12.set_ca_certificates(intermediates)
if private_key_passphrase:
raise exceptions.CertificateStorageException(
"Passphrases protected PKCS12 certificates are not supported.")
p12_data = opaque_data.OpaqueData(p12.export(), name=name)
self.manager.store(context, p12_data)
def get_cert(self, context, cert_ref, resource_ref=None, check_only=False,
service_name=None):
certbag = self.manager.get(context, cert_ref)
certbag_data = certbag.get_encoded()
cert = pkcs12.PKCS12Cert(certbag_data)
return cert
def delete_cert(self, context, cert_ref, resource_ref, service_name=None):
# Delete is not a great name for this -- we don't delete anything
# in reality, we just do cleanup here. For castellan, none is required
pass

View File

@ -29,9 +29,8 @@ class CertManager(object):
""" """
@abc.abstractmethod @abc.abstractmethod
def store_cert(self, project_id, certificate, private_key, def store_cert(self, context, certificate, private_key, intermediates=None,
intermediates=None, private_key_passphrase=None, private_key_passphrase=None, expiration=None, name=None):
expiration=None, name=None):
"""Stores (i.e., registers) a cert with the cert manager. """Stores (i.e., registers) a cert with the cert manager.
This method stores the specified cert and returns its UUID that This method stores the specified cert and returns its UUID that
@ -42,8 +41,8 @@ class CertManager(object):
pass pass
@abc.abstractmethod @abc.abstractmethod
def get_cert(self, project_id, cert_ref, resource_ref=None, def get_cert(self, context, cert_ref, resource_ref=None, check_only=False,
check_only=False, service_name=None): service_name=None):
"""Retrieves the specified cert. """Retrieves the specified cert.
If check_only is True, don't perform any sort of registration. If check_only is True, don't perform any sort of registration.
@ -53,8 +52,7 @@ class CertManager(object):
pass pass
@abc.abstractmethod @abc.abstractmethod
def delete_cert(self, project_id, cert_ref, resource_ref, def delete_cert(self, context, cert_ref, resource_ref, service_name=None):
service_name=None):
"""Deletes the specified cert. """Deletes the specified cert.
If the specified cert does not exist, a CertificateStorageException If the specified cert does not exist, a CertificateStorageException

View File

@ -31,14 +31,14 @@ class LocalCertManager(cert_mgr.CertManager):
"""Cert Manager Interface that stores data locally.""" """Cert Manager Interface that stores data locally."""
@staticmethod @staticmethod
def store_cert(project_id, certificate, private_key, intermediates=None, def store_cert(context, certificate, private_key, intermediates=None,
private_key_passphrase=None, **kwargs): private_key_passphrase=None, **kwargs):
"""Stores (i.e., registers) a cert with the cert manager. """Stores (i.e., registers) a cert with the cert manager.
This method stores the specified cert to the filesystem and returns This method stores the specified cert to the filesystem and returns
a UUID that can be used to retrieve it. a UUID that can be used to retrieve it.
:param project_id: Ignored in this implementation :param context: Ignored in this implementation
:param certificate: PEM encoded TLS certificate :param certificate: PEM encoded TLS certificate
:param private_key: private key for the supplied certificate :param private_key: private key for the supplied certificate
:param intermediates: ordered and concatenated intermediate certs :param intermediates: ordered and concatenated intermediate certs
@ -82,10 +82,10 @@ class LocalCertManager(cert_mgr.CertManager):
return cert_ref return cert_ref
@staticmethod @staticmethod
def get_cert(project_id, cert_ref, **kwargs): def get_cert(context, cert_ref, **kwargs):
"""Retrieves the specified cert. """Retrieves the specified cert.
:param project_id: Ignored in this implementation :param context: Ignored in this implementation
:param cert_ref: the UUID of the cert to retrieve :param cert_ref: the UUID of the cert to retrieve
:return: octavia.certificates.common.Cert representation of the :return: octavia.certificates.common.Cert representation of the
@ -134,10 +134,10 @@ class LocalCertManager(cert_mgr.CertManager):
return local_common.LocalCert(**cert_data) return local_common.LocalCert(**cert_data)
@staticmethod @staticmethod
def delete_cert(project_id, cert_ref, **kwargs): def delete_cert(context, cert_ref, **kwargs):
"""Deletes the specified cert. """Deletes the specified cert.
:param project_id: Ignored in this implementation :param context: Ignored in this implementation
:param cert_ref: the UUID of the cert to delete :param cert_ref: the UUID of the cert to delete
:raises CertificateStorageException: if certificate deletion fails :raises CertificateStorageException: if certificate deletion fails

View File

@ -18,6 +18,7 @@ import base64
from cryptography.hazmat import backends from cryptography.hazmat import backends
from cryptography.hazmat.primitives import serialization from cryptography.hazmat.primitives import serialization
from cryptography import x509 from cryptography import x509
from oslo_context import context as oslo_context
from oslo_log import log as logging from oslo_log import log as logging
from pyasn1.codec.der import decoder as der_decoder from pyasn1.codec.der import decoder as der_decoder
from pyasn1.codec.der import encoder as der_encoder from pyasn1.codec.der import encoder as der_encoder
@ -332,23 +333,25 @@ def build_pem(tls_container):
return b'\n'.join(pem) + b'\n' return b'\n'.join(pem) + b'\n'
def load_certificates_data(cert_mngr, listener): def load_certificates_data(cert_mngr, listener, context=None):
"""Load TLS certificate data from the listener. """Load TLS certificate data from the listener.
return TLS_CERT and SNI_CERTS return TLS_CERT and SNI_CERTS
""" """
tls_cert = None tls_cert = None
sni_certs = [] sni_certs = []
if not context:
context = oslo_context.RequestContext(project_id=listener.project_id)
if listener.tls_certificate_id: if listener.tls_certificate_id:
tls_cert = _map_cert_tls_container( tls_cert = _map_cert_tls_container(
cert_mngr.get_cert(listener.project_id, cert_mngr.get_cert(context,
listener.tls_certificate_id, listener.tls_certificate_id,
check_only=True)) check_only=True))
if listener.sni_containers: if listener.sni_containers:
for sni_cont in listener.sni_containers: for sni_cont in listener.sni_containers:
cert_container = _map_cert_tls_container( cert_container = _map_cert_tls_container(
cert_mngr.get_cert(listener.project_id, cert_mngr.get_cert(context,
sni_cont.tls_container_id, sni_cont.tls_container_id,
check_only=True)) check_only=True))
sni_certs.append(cert_container) sni_certs.append(cert_container)

View File

@ -12,7 +12,8 @@
# License for the specific language governing permissions and limitations # License for the specific language governing permissions and limitations
# under the License. # under the License.
from barbicanclient import client as barbican_client from barbicanclient.v1 import containers
from barbicanclient.v1 import secrets
import mock import mock
import six import six
@ -24,19 +25,19 @@ import octavia.tests.unit.common.sample_configs.sample_certs as sample
class TestBarbicanCert(base.TestCase): class TestBarbicanCert(base.TestCase):
def _prepare(self): def _prepare(self):
self.certificate_secret = barbican_client.secrets.Secret( self.certificate_secret = secrets.Secret(
api=mock.MagicMock(), api=mock.MagicMock(),
payload=self.certificate payload=self.certificate
) )
self.intermediates_secret = barbican_client.secrets.Secret( self.intermediates_secret = secrets.Secret(
api=mock.MagicMock(), api=mock.MagicMock(),
payload=sample.X509_IMDS payload=sample.X509_IMDS
) )
self.private_key_secret = barbican_client.secrets.Secret( self.private_key_secret = secrets.Secret(
api=mock.MagicMock(), api=mock.MagicMock(),
payload=self.private_key payload=self.private_key
) )
self.private_key_passphrase_secret = barbican_client.secrets.Secret( self.private_key_passphrase_secret = secrets.Secret(
api=mock.MagicMock(), api=mock.MagicMock(),
payload=self.private_key_passphrase payload=self.private_key_passphrase
) )
@ -49,7 +50,7 @@ class TestBarbicanCert(base.TestCase):
self.private_key_passphrase = sample.X509_CERT_KEY_PASSPHRASE self.private_key_passphrase = sample.X509_CERT_KEY_PASSPHRASE
self._prepare() self._prepare()
container = barbican_client.containers.CertificateContainer( container = containers.CertificateContainer(
api=mock.MagicMock(), api=mock.MagicMock(),
certificate=self.certificate_secret, certificate=self.certificate_secret,
intermediates=self.intermediates_secret, intermediates=self.intermediates_secret,
@ -78,7 +79,7 @@ class TestBarbicanCert(base.TestCase):
sample.X509_CERT_KEY_PASSPHRASE) sample.X509_CERT_KEY_PASSPHRASE)
self._prepare() self._prepare()
container = barbican_client.containers.CertificateContainer( container = containers.CertificateContainer(
api=mock.MagicMock(), api=mock.MagicMock(),
certificate=self.certificate_secret, certificate=self.certificate_secret,
intermediates=self.intermediates_secret, intermediates=self.intermediates_secret,

View File

@ -14,10 +14,8 @@
import uuid import uuid
from barbicanclient import containers from barbicanclient.v1 import secrets
from barbicanclient import secrets
import mock import mock
import six
import octavia.certificates.common.barbican as barbican_common import octavia.certificates.common.barbican as barbican_common
import octavia.certificates.common.cert as cert import octavia.certificates.common.cert as cert
@ -32,47 +30,21 @@ PROJECT_ID = "12345"
class TestBarbicanManager(base.TestCase): class TestBarbicanManager(base.TestCase):
def setUp(self): def setUp(self):
# Make a fake Container and contents # Make a fake Secret and contents
self.barbican_endpoint = 'http://localhost:9311/v1' self.barbican_endpoint = 'http://localhost:9311/v1'
self.container_uuid = uuid.uuid4() self.secret_uuid = uuid.uuid4()
self.container_ref = '{0}/containers/{1}'.format( self.secret_ref = '{0}/secrets/{1}'.format(
self.barbican_endpoint, self.container_uuid self.barbican_endpoint, self.secret_uuid
) )
self.name = 'My Fancy Cert' self.name = 'My Fancy Cert'
self.certificate = secrets.Secret( self.secret = secrets.Secret(
api=mock.MagicMock(), api=mock.MagicMock(),
payload=sample.X509_CERT payload=sample.PKCS12_BUNDLE
)
self.intermediates = secrets.Secret(
api=mock.MagicMock(),
payload=sample.X509_IMDS
)
self.private_key = secrets.Secret(
api=mock.MagicMock(),
payload=sample.X509_CERT_KEY_ENCRYPTED
)
self.private_key_passphrase = secrets.Secret(
api=mock.MagicMock(),
payload=sample.X509_CERT_KEY_PASSPHRASE
) )
container = mock.Mock(spec=containers.CertificateContainer) self.empty_secret = mock.Mock(spec=secrets.Secret)
container.container_ref = self.container_ref
container.name = self.name
container.private_key = self.private_key
container.certificate = self.certificate
container.intermediates = self.intermediates
container.private_key_passphrase = self.private_key_passphrase
self.container = container
self.empty_container = mock.Mock(spec=containers.CertificateContainer)
self.secret1 = mock.Mock(spec=secrets.Secret)
self.secret2 = mock.Mock(spec=secrets.Secret)
self.secret3 = mock.Mock(spec=secrets.Secret)
self.secret4 = mock.Mock(spec=secrets.Secret)
# Mock out the client # Mock out the client
self.bc = mock.Mock() self.bc = mock.Mock()
@ -82,159 +54,97 @@ class TestBarbicanManager(base.TestCase):
self.cert_manager = barbican_cert_mgr.BarbicanCertManager() self.cert_manager = barbican_cert_mgr.BarbicanCertManager()
self.cert_manager.auth = barbican_auth self.cert_manager.auth = barbican_auth
self.context = mock.Mock()
self.context.project_id = PROJECT_ID
super(TestBarbicanManager, self).setUp() super(TestBarbicanManager, self).setUp()
def test_store_cert(self): def test_store_cert(self):
# Mock out the client # Mock out the client
self.bc.containers.create_certificate.return_value = ( self.bc.secrets.create.return_value = (
self.empty_container) self.empty_secret)
# Attempt to store a cert # Attempt to store a cert
container_ref = self.cert_manager.store_cert( secret_ref = self.cert_manager.store_cert(
project_id=PROJECT_ID, context=self.context,
certificate=self.certificate, certificate=sample.X509_CERT,
private_key=self.private_key, private_key=sample.X509_CERT_KEY,
intermediates=self.intermediates, intermediates=sample.X509_IMDS,
private_key_passphrase=self.private_key_passphrase,
name=self.name name=self.name
) )
self.assertEqual(self.empty_container.container_ref, container_ref) self.assertEqual(secret_ref, self.empty_secret.secret_ref)
# create_secret should be called four times with our data # create_secret should be called once with our data
calls = [ calls = [
mock.call(payload=self.certificate, expiration=None, mock.call(payload=mock.ANY, expiration=None,
name=mock.ANY), name=self.name)
mock.call(payload=self.private_key, expiration=None,
name=mock.ANY),
mock.call(payload=self.intermediates, expiration=None,
name=mock.ANY),
mock.call(payload=self.private_key_passphrase, expiration=None,
name=mock.ANY)
] ]
self.bc.secrets.create.assert_has_calls(calls, any_order=True) self.bc.secrets.create.assert_has_calls(calls)
# create_certificate should be called once
self.assertEqual(1, self.bc.containers.create_certificate.call_count)
# Container should be stored once # Container should be stored once
self.empty_container.store.assert_called_once_with() self.empty_secret.store.assert_called_once_with()
def test_store_cert_failure(self): def test_store_cert_failure(self):
# Mock out the client # Mock out the client
self.bc.containers.create_certificate.return_value = ( self.bc.secrets.create.return_value = (
self.empty_container) self.empty_secret)
test_secrets = [
self.secret1, self.empty_secret.store.side_effect = ValueError()
self.secret2,
self.secret3,
self.secret4
]
self.bc.secrets.create.side_effect = test_secrets
self.empty_container.store.side_effect = ValueError()
# Attempt to store a cert # Attempt to store a cert
self.assertRaises( self.assertRaises(
ValueError, ValueError,
self.cert_manager.store_cert, self.cert_manager.store_cert,
project_id=PROJECT_ID, context=self.context,
certificate=self.certificate, certificate=sample.X509_CERT,
private_key=self.private_key, private_key=sample.X509_CERT_KEY,
intermediates=self.intermediates, intermediates=sample.X509_IMDS,
private_key_passphrase=self.private_key_passphrase,
name=self.name name=self.name
) )
# create_secret should be called four times with our data
calls = [
mock.call(payload=self.certificate, expiration=None,
name=mock.ANY),
mock.call(payload=self.private_key, expiration=None,
name=mock.ANY),
mock.call(payload=self.intermediates, expiration=None,
name=mock.ANY),
mock.call(payload=self.private_key_passphrase, expiration=None,
name=mock.ANY)
]
self.bc.secrets.create.assert_has_calls(calls, any_order=True)
# create_certificate should be called once # create_certificate should be called once
self.assertEqual(1, self.bc.containers.create_certificate.call_count) self.assertEqual(1, self.bc.secrets.create.call_count)
# Container should be stored once # Container should be stored once
self.empty_container.store.assert_called_once_with() self.empty_secret.store.assert_called_once_with()
# All secrets should be deleted (or at least an attempt made)
for s in test_secrets:
s.delete.assert_called_once_with()
def test_get_cert(self): def test_get_cert(self):
# Mock out the client # Mock out the client
self.bc.containers.register_consumer.return_value = self.container self.bc.secrets.get.return_value = self.secret
# Get the container data # Get the secret data
data = self.cert_manager.get_cert( data = self.cert_manager.get_cert(
project_id=PROJECT_ID, context=self.context,
cert_ref=self.container_ref, cert_ref=self.secret_ref,
resource_ref=self.container_ref, resource_ref=self.secret_ref,
service_name='Octavia' service_name='Octavia'
) )
# 'register_consumer' should be called once with the container_ref # 'get_secret' should be called once with the secret_ref
self.bc.containers.register_consumer.assert_called_once_with( self.bc.secrets.get.assert_called_once_with(
container_ref=self.container_ref, secret_ref=self.secret_ref
url=self.container_ref,
name='Octavia'
) )
# The returned data should be a Cert object with the correct values # The returned data should be a Cert object with the correct values
self.assertIsInstance(data, cert.Cert) self.assertIsInstance(data, cert.Cert)
self.assertEqual(data.get_private_key(), self.assertEqual(sample.X509_CERT_KEY, data.get_private_key())
self.private_key.payload) self.assertEqual(sample.X509_CERT, data.get_certificate())
self.assertEqual(data.get_certificate(), self.assertItemsEqual(sample.X509_IMDS_LIST, data.get_intermediates())
self.certificate.payload) self.assertIsNone(data.get_private_key_passphrase())
self.assertEqual(data.get_intermediates(),
sample.X509_IMDS_LIST)
self.assertEqual(data.get_private_key_passphrase(),
six.b(self.private_key_passphrase.payload))
def test_get_cert_no_registration(self): def test_delete_cert_legacy(self):
self.bc.containers.get.return_value = self.container
# Get the container data
data = self.cert_manager.get_cert(
project_id=PROJECT_ID,
cert_ref=self.container_ref, check_only=True
)
# 'get' should be called once with the container_ref
self.bc.containers.get.assert_called_once_with(
container_ref=self.container_ref
)
# The returned data should be a Cert object with the correct values
self.assertIsInstance(data, cert.Cert)
self.assertEqual(data.get_private_key(),
self.private_key.payload)
self.assertEqual(data.get_certificate(),
self.certificate.payload)
self.assertEqual(data.get_intermediates(),
sample.X509_IMDS_LIST)
self.assertEqual(data.get_private_key_passphrase(),
six.b(self.private_key_passphrase.payload))
def test_delete_cert(self):
# Attempt to deregister as a consumer # Attempt to deregister as a consumer
self.cert_manager.delete_cert( self.cert_manager.delete_cert(
project_id=PROJECT_ID, context=self.context,
cert_ref=self.container_ref, cert_ref=self.secret_ref,
resource_ref=self.container_ref, resource_ref=self.secret_ref,
service_name='Octavia' service_name='Octavia'
) )
# remove_consumer should be called once with the container_ref # remove_consumer should be called once with the container_ref (legacy)
self.bc.containers.remove_consumer.assert_called_once_with( self.bc.containers.remove_consumer.assert_called_once_with(
container_ref=self.container_ref, container_ref=self.secret_ref,
url=self.container_ref, url=self.secret_ref,
name='Octavia' name='Octavia'
) )

View File

@ -0,0 +1,242 @@
# Copyright 2014 Rackspace
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import uuid
from barbicanclient.v1 import containers
from barbicanclient.v1 import secrets
import mock
import six
import octavia.certificates.common.barbican as barbican_common
import octavia.certificates.common.cert as cert
import octavia.certificates.manager.barbican_legacy as barbican_cert_mgr
import octavia.tests.unit.base as base
import octavia.tests.unit.common.sample_configs.sample_certs as sample
PROJECT_ID = "12345"
class TestBarbicanManager(base.TestCase):
def setUp(self):
# Make a fake Container and contents
self.barbican_endpoint = 'http://localhost:9311/v1'
self.container_uuid = uuid.uuid4()
self.container_ref = '{0}/containers/{1}'.format(
self.barbican_endpoint, self.container_uuid
)
self.name = 'My Fancy Cert'
self.certificate = secrets.Secret(
api=mock.MagicMock(),
payload=sample.X509_CERT
)
self.intermediates = secrets.Secret(
api=mock.MagicMock(),
payload=sample.X509_IMDS
)
self.private_key = secrets.Secret(
api=mock.MagicMock(),
payload=sample.X509_CERT_KEY_ENCRYPTED
)
self.private_key_passphrase = secrets.Secret(
api=mock.MagicMock(),
payload=sample.X509_CERT_KEY_PASSPHRASE
)
container = mock.Mock(spec=containers.CertificateContainer)
container.container_ref = self.container_ref
container.name = self.name
container.private_key = self.private_key
container.certificate = self.certificate
container.intermediates = self.intermediates
container.private_key_passphrase = self.private_key_passphrase
self.container = container
self.empty_container = mock.Mock(spec=containers.CertificateContainer)
self.secret1 = mock.Mock(spec=secrets.Secret)
self.secret2 = mock.Mock(spec=secrets.Secret)
self.secret3 = mock.Mock(spec=secrets.Secret)
self.secret4 = mock.Mock(spec=secrets.Secret)
# Mock out the client
self.bc = mock.Mock()
barbican_auth = mock.Mock(spec=barbican_common.BarbicanAuth)
barbican_auth.get_barbican_client.return_value = self.bc
self.cert_manager = barbican_cert_mgr.BarbicanCertManager()
self.cert_manager.auth = barbican_auth
self.context = mock.Mock()
self.context.project_id = PROJECT_ID
super(TestBarbicanManager, self).setUp()
def test_store_cert(self):
# Mock out the client
self.bc.containers.create_certificate.return_value = (
self.empty_container)
# Attempt to store a cert
container_ref = self.cert_manager.store_cert(
context=self.context,
certificate=self.certificate,
private_key=self.private_key,
intermediates=self.intermediates,
private_key_passphrase=self.private_key_passphrase,
name=self.name
)
self.assertEqual(self.empty_container.container_ref, container_ref)
# create_secret should be called four times with our data
calls = [
mock.call(payload=self.certificate, expiration=None,
name=mock.ANY),
mock.call(payload=self.private_key, expiration=None,
name=mock.ANY),
mock.call(payload=self.intermediates, expiration=None,
name=mock.ANY),
mock.call(payload=self.private_key_passphrase, expiration=None,
name=mock.ANY)
]
self.bc.secrets.create.assert_has_calls(calls, any_order=True)
# create_certificate should be called once
self.assertEqual(1, self.bc.containers.create_certificate.call_count)
# Container should be stored once
self.empty_container.store.assert_called_once_with()
def test_store_cert_failure(self):
# Mock out the client
self.bc.containers.create_certificate.return_value = (
self.empty_container)
test_secrets = [
self.secret1,
self.secret2,
self.secret3,
self.secret4
]
self.bc.secrets.create.side_effect = test_secrets
self.empty_container.store.side_effect = ValueError()
# Attempt to store a cert
self.assertRaises(
ValueError,
self.cert_manager.store_cert,
context=self.context,
certificate=self.certificate,
private_key=self.private_key,
intermediates=self.intermediates,
private_key_passphrase=self.private_key_passphrase,
name=self.name
)
# create_secret should be called four times with our data
calls = [
mock.call(payload=self.certificate, expiration=None,
name=mock.ANY),
mock.call(payload=self.private_key, expiration=None,
name=mock.ANY),
mock.call(payload=self.intermediates, expiration=None,
name=mock.ANY),
mock.call(payload=self.private_key_passphrase, expiration=None,
name=mock.ANY)
]
self.bc.secrets.create.assert_has_calls(calls, any_order=True)
# create_certificate should be called once
self.assertEqual(1, self.bc.containers.create_certificate.call_count)
# Container should be stored once
self.empty_container.store.assert_called_once_with()
# All secrets should be deleted (or at least an attempt made)
for s in test_secrets:
s.delete.assert_called_once_with()
def test_get_cert(self):
# Mock out the client
self.bc.containers.register_consumer.return_value = self.container
# Get the container data
data = self.cert_manager.get_cert(
context=self.context,
cert_ref=self.container_ref,
resource_ref=self.container_ref,
service_name='Octavia'
)
# 'register_consumer' should be called once with the container_ref
self.bc.containers.register_consumer.assert_called_once_with(
container_ref=self.container_ref,
url=self.container_ref,
name='Octavia'
)
# The returned data should be a Cert object with the correct values
self.assertIsInstance(data, cert.Cert)
self.assertEqual(data.get_private_key(),
self.private_key.payload)
self.assertEqual(data.get_certificate(),
self.certificate.payload)
self.assertEqual(data.get_intermediates(),
sample.X509_IMDS_LIST)
self.assertEqual(data.get_private_key_passphrase(),
six.b(self.private_key_passphrase.payload))
def test_get_cert_no_registration(self):
self.bc.containers.get.return_value = self.container
# Get the container data
data = self.cert_manager.get_cert(
context=self.context,
cert_ref=self.container_ref, check_only=True
)
# 'get' should be called once with the container_ref
self.bc.containers.get.assert_called_once_with(
container_ref=self.container_ref
)
# The returned data should be a Cert object with the correct values
self.assertIsInstance(data, cert.Cert)
self.assertEqual(data.get_private_key(),
self.private_key.payload)
self.assertEqual(data.get_certificate(),
self.certificate.payload)
self.assertEqual(data.get_intermediates(),
sample.X509_IMDS_LIST)
self.assertEqual(data.get_private_key_passphrase(),
six.b(self.private_key_passphrase.payload))
def test_delete_cert(self):
# Attempt to deregister as a consumer
self.cert_manager.delete_cert(
context=self.context,
cert_ref=self.container_ref,
resource_ref=self.container_ref,
service_name='Octavia'
)
# remove_consumer should be called once with the container_ref
self.bc.containers.remove_consumer.assert_called_once_with(
container_ref=self.container_ref,
url=self.container_ref,
name='Octavia'
)

View File

@ -44,7 +44,7 @@ class TestLocalManager(base.TestCase):
with mock.patch('os.open', open_mock), mock.patch.object( with mock.patch('os.open', open_mock), mock.patch.object(
os, 'fdopen', fd_mock): os, 'fdopen', fd_mock):
cert_id = local_cert_mgr.LocalCertManager.store_cert( cert_id = local_cert_mgr.LocalCertManager.store_cert(
None, context=None,
certificate=self.certificate, certificate=self.certificate,
intermediates=self.intermediates, intermediates=self.intermediates,
private_key=self.private_key, private_key=self.private_key,

View File

@ -15,6 +15,7 @@
import base64 import base64
import pkg_resources
import six import six
@ -813,3 +814,6 @@ zfJ3Bo+P7In9fsHbyDAqIhMwDQYJKoZIhvcNAQELBQADQQBenkZ2k7RgZqgj+dxA
D7BF8MN1oUAOpyYqAjkGddSEuMyNmwtHKZI1dyQ0gBIQdiU9yAG2oTbUIK4msbBV D7BF8MN1oUAOpyYqAjkGddSEuMyNmwtHKZI1dyQ0gBIQdiU9yAG2oTbUIK4msbBV
uJIQ uJIQ
-----END CERTIFICATE-----""" -----END CERTIFICATE-----"""
PKCS12_BUNDLE = pkg_resources.resource_string(
'octavia.tests.unit.common.sample_configs', 'sample_pkcs12.p12')

View File

@ -134,18 +134,20 @@ class TestTLSParseUtils(base.TestCase):
def test_load_certificates(self): def test_load_certificates(self):
listener = sample_configs.sample_listener_tuple(tls=True, sni=True) listener = sample_configs.sample_listener_tuple(tls=True, sni=True)
client = mock.MagicMock() client = mock.MagicMock()
context = mock.Mock()
context.project_id = '12345'
with mock.patch.object(cert_parser, with mock.patch.object(cert_parser,
'get_host_names') as cp: 'get_host_names') as cp:
with mock.patch.object(cert_parser, with mock.patch.object(cert_parser,
'_map_cert_tls_container'): '_map_cert_tls_container'):
cp.return_value = {'cn': 'fakeCN'} cp.return_value = {'cn': 'fakeCN'}
cert_parser.load_certificates_data(client, listener) cert_parser.load_certificates_data(client, listener, context)
# Ensure upload_cert is called three times # Ensure upload_cert is called three times
calls_cert_mngr = [ calls_cert_mngr = [
mock.call.get_cert('12345', 'cont_id_1', check_only=True), mock.call.get_cert(context, 'cont_id_1', check_only=True),
mock.call.get_cert('12345', 'cont_id_2', check_only=True), mock.call.get_cert(context, 'cont_id_2', check_only=True),
mock.call.get_cert('12345', 'cont_id_3', check_only=True) mock.call.get_cert(context, 'cont_id_3', check_only=True)
] ]
client.assert_has_calls(calls_cert_mngr) client.assert_has_calls(calls_cert_mngr)

View File

@ -0,0 +1,21 @@
---
features:
- |
Users can now use a reference to a single PKCS12 bundle as their
`default_tls_container_ref` instead of a Barbican container with
individual secret objects. PKCS12 supports bundling a private key,
certificate, and intermediates. Private keys can no longer be passphrase
protected when using PKCS12 bundles.
No configuration change is necessary to enable this feature. Users may
simply begin using this. Any use of the old style containers will be
detected and automatically fall back to using the old Barbican driver.
- |
Certificate bundles can now be stored in any backend Castellan supports,
and can be retrieved via a Castellan driver, even if Barbican is not
deployed.
security:
- |
Private keys can no longer be password protected, as PKCS12 does not
support storing a passphrase in an explicitly defined way. Note that this
is not noticeably less secure than storing a passphrase protected private
key in the same place as the passphrase, as was the case with Barbican.

View File

@ -38,6 +38,7 @@ Jinja2!=2.9.0,!=2.9.1,!=2.9.2,!=2.9.3,!=2.9.4,>=2.8 # BSD License (3 clause)
taskflow>=2.7.0 # Apache-2.0 taskflow>=2.7.0 # Apache-2.0
diskimage-builder!=1.6.0,!=1.7.0,!=1.7.1,>=1.1.2 # Apache-2.0 diskimage-builder!=1.6.0,!=1.7.0,!=1.7.1,>=1.1.2 # Apache-2.0
futures>=3.0.0;python_version=='2.7' or python_version=='2.6' # BSD futures>=3.0.0;python_version=='2.7' or python_version=='2.6' # BSD
castellan>=0.16.0 # Apache-2.0
#for the amphora api #for the amphora api
Flask!=0.11,<1.0,>=0.10 # BSD Flask!=0.11,<1.0,>=0.10 # BSD

View File

@ -94,6 +94,7 @@ octavia.cert_generator =
octavia.cert_manager = octavia.cert_manager =
local_cert_manager = octavia.certificates.manager.local:LocalCertManager local_cert_manager = octavia.certificates.manager.local:LocalCertManager
barbican_cert_manager = octavia.certificates.manager.barbican:BarbicanCertManager barbican_cert_manager = octavia.certificates.manager.barbican:BarbicanCertManager
castellan_cert_manager = octavia.certificates.manager.castellan_mgr:CastellanCertManager
octavia.barbican_auth = octavia.barbican_auth =
barbican_acl_auth = octavia.certificates.common.auth.barbican_acl:BarbicanACLAuth barbican_acl_auth = octavia.certificates.common.auth.barbican_acl:BarbicanACLAuth
octavia.plugins = octavia.plugins =