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:
parent
d7cc05be39
commit
8934a629df
@ -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
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
Octavia API v1 (SUPORTED)
|
Octavia API v1 (SUPPORTED)
|
||||||
=========================
|
==========================
|
||||||
|
|
||||||
Authentication
|
Authentication
|
||||||
--------------
|
--------------
|
||||||
|
@ -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
|
||||||
===============
|
===============
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
54
octavia/certificates/common/pkcs12.py
Normal file
54
octavia/certificates/common/pkcs12.py
Normal 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
|
@ -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)
|
|
||||||
|
172
octavia/certificates/manager/barbican_legacy.py
Normal file
172
octavia/certificates/manager/barbican_legacy.py
Normal 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)
|
63
octavia/certificates/manager/castellan_mgr.py
Normal file
63
octavia/certificates/manager/castellan_mgr.py
Normal 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
|
@ -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
|
||||||
|
@ -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
|
||||||
|
@ -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)
|
||||||
|
@ -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,
|
||||||
|
@ -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'
|
||||||
)
|
)
|
||||||
|
242
octavia/tests/unit/certificates/manager/test_barbican_legacy.py
Normal file
242
octavia/tests/unit/certificates/manager/test_barbican_legacy.py
Normal 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'
|
||||||
|
)
|
@ -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,
|
||||||
|
@ -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')
|
||||||
|
BIN
octavia/tests/unit/common/sample_configs/sample_pkcs12.p12
Normal file
BIN
octavia/tests/unit/common/sample_configs/sample_pkcs12.p12
Normal file
Binary file not shown.
@ -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)
|
||||||
|
|
||||||
|
@ -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.
|
@ -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
|
||||||
|
@ -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 =
|
||||||
|
Loading…
Reference in New Issue
Block a user