Add 2 new fields into Pool API for support re-encryption
Add tls_ca_container_id and crl_container_id into Pool API. Story: 2003858 Task: 26672 Co-Authored-By: Michael Johnson <johnsomor@gmail.com> Change-Id: I6cd6e2ca8e48a5df707a70d22505dec9d752c7ebchanges/47/614447/10
parent
aa7ac7ab73
commit
7aa115a553
|
@ -226,6 +226,24 @@ bytes_out:
|
|||
in: body
|
||||
required: true
|
||||
type: integer
|
||||
ca_tls_container_ref:
|
||||
description: |
|
||||
The reference of the `key manager service
|
||||
<https://docs.openstack.org/castellan/latest/>`__ secret containing a
|
||||
PEM format CA certificate bundle for ``tls_enabled`` pools.
|
||||
in: body
|
||||
min_version: 2.8
|
||||
required: true
|
||||
type: string
|
||||
ca_tls_container_ref-optional:
|
||||
description: |
|
||||
The reference of the `key manager service
|
||||
<https://docs.openstack.org/castellan/latest/>`__ secret containing a
|
||||
PEM format CA certificate bundle for ``tls_enabled`` pools.
|
||||
in: body
|
||||
min_version: 2.8
|
||||
required: false
|
||||
type: string
|
||||
cached-zone:
|
||||
description: |
|
||||
The availability zone of a compute instance, cached at create time. This
|
||||
|
@ -333,6 +351,22 @@ created_at:
|
|||
in: body
|
||||
required: true
|
||||
type: string
|
||||
crl_container_ref:
|
||||
description: |
|
||||
The reference of the `key manager service
|
||||
<https://docs.openstack.org/castellan/latest/>`__ secret containing a
|
||||
PEM format CA revocation list file for ``tls_enabled`` pools.
|
||||
in: body
|
||||
required: true
|
||||
type: string
|
||||
crl_container_ref-optional:
|
||||
description: |
|
||||
The reference of the `key manager service
|
||||
<https://docs.openstack.org/castellan/latest/>`__ secret containing a
|
||||
PEM format CA revocation list file for ``tls_enabled`` pools.
|
||||
in: body
|
||||
required: false
|
||||
type: string
|
||||
default_pool_id:
|
||||
description: |
|
||||
The ID of the pool used by the listener if no L7 policies match.
|
||||
|
|
|
@ -1 +1 @@
|
|||
curl -X POST -H "Content-Type: application/json" -H "X-Auth-Token: <token>" -d '{"pool":{"lb_algorithm":"ROUND_ROBIN","protocol":"HTTP","description":"Super Round Robin Pool","admin_state_up":true,"session_persistence":{"cookie_name":"ChocolateChip","type":"APP_COOKIE"},"listener_id":"023f2e34-7806-443b-bfae-16c324569a3d","name":"super-pool","tags":["test_tag"],"tls_container_ref":"http://198.51.100.10:9311/v1/containers/4073846f-1d5e-42e1-a4cf-a7046419d0e6"}}' http://198.51.100.10:9876/v2/lbaas/pools
|
||||
curl -X POST -H "Content-Type: application/json" -H "X-Auth-Token: <token>" -d '{"pool":{"lb_algorithm":"ROUND_ROBIN","protocol":"HTTP","description":"Super Round Robin Pool","admin_state_up":true,"session_persistence":{"cookie_name":"ChocolateChip","type":"APP_COOKIE"},"listener_id":"023f2e34-7806-443b-bfae-16c324569a3d","name":"super-pool","tags":["test_tag"],"tls_container_ref":"http://198.51.100.10:9311/v1/containers/4073846f-1d5e-42e1-a4cf-a7046419d0e6","ca_tls_container_ref":"http://198.51.100.10:9311/v1/containers/5f0d5540-fae6-4646-85d6-8a84883807fb","crl_container_ref":"http://198.51.100.10:9311/v1/containers/6faf0a01-6892-454c-aaac-650282820c0b"}}' http://198.51.100.10:9876/v2/lbaas/pools
|
||||
|
|
|
@ -11,6 +11,8 @@
|
|||
"listener_id": "023f2e34-7806-443b-bfae-16c324569a3d",
|
||||
"name": "super-pool",
|
||||
"tags": ["test_tag"],
|
||||
"tls_container_ref": "http://198.51.100.10:9311/v1/containers/4073846f-1d5e-42e1-a4cf-a7046419d0e6"
|
||||
"tls_container_ref": "http://198.51.100.10:9311/v1/containers/4073846f-1d5e-42e1-a4cf-a7046419d0e6",
|
||||
"ca_tls_container_ref": "http://198.51.100.10:9311/v1/containers/5f0d5540-fae6-4646-85d6-8a84883807fb",
|
||||
"crl_container_ref": "http://198.51.100.10:9311/v1/containers/6faf0a01-6892-454c-aaac-650282820c0b"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -28,6 +28,8 @@
|
|||
"operating_status": "ONLINE",
|
||||
"name": "super-pool",
|
||||
"tags": ["test_tag"],
|
||||
"tls_container_ref": "http://198.51.100.10:9311/v1/containers/4073846f-1d5e-42e1-a4cf-a7046419d0e6"
|
||||
"tls_container_ref": "http://198.51.100.10:9311/v1/containers/4073846f-1d5e-42e1-a4cf-a7046419d0e6",
|
||||
"ca_tls_container_ref": "http://198.51.100.10:9311/v1/containers/5f0d5540-fae6-4646-85d6-8a84883807fb",
|
||||
"crl_container_ref": "http://198.51.100.10:9311/v1/containers/6faf0a01-6892-454c-aaac-650282820c0b"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -28,6 +28,8 @@
|
|||
"operating_status": "ONLINE",
|
||||
"name": "super-pool",
|
||||
"tags": ["test_tag"],
|
||||
"tls_container_ref": "http://198.51.100.10:9311/v1/containers/4073846f-1d5e-42e1-a4cf-a7046419d0e6"
|
||||
"tls_container_ref": "http://198.51.100.10:9311/v1/containers/4073846f-1d5e-42e1-a4cf-a7046419d0e6",
|
||||
"ca_tls_container_ref": "http://198.51.100.10:9311/v1/containers/5f0d5540-fae6-4646-85d6-8a84883807fb",
|
||||
"crl_container_ref": "http://198.51.100.10:9311/v1/containers/6faf0a01-6892-454c-aaac-650282820c0b"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1 +1 @@
|
|||
curl -X PUT -H "Content-Type: application/json" -H "X-Auth-Token: <token>" -d '{"pool":{"lb_algorithm":"LEAST_CONNECTIONS","session_persistence":{"type":"SOURCE_IP"},"description":"second description","name":"second_name","tags":["updated_tag"],"tls_container_ref":"http://198.51.100.10:9311/v1/containers/c1cd501d-3cf9-4873-a11b-a74bebcde929"}}' http://198.51.100.10:9876/v2/lbaas/pools/4029d267-3983-4224-a3d0-afb3fe16a2cd
|
||||
curl -X PUT -H "Content-Type: application/json" -H "X-Auth-Token: <token>" -d '{"pool":{"lb_algorithm":"LEAST_CONNECTIONS","session_persistence":{"type":"SOURCE_IP"},"description":"second description","name":"second_name","tags":["updated_tag"],"tls_container_ref":"http://198.51.100.10:9311/v1/containers/c1cd501d-3cf9-4873-a11b-a74bebcde929","ca_tls_container_ref":null,"crl_container_ref":null}}' http://198.51.100.10:9876/v2/lbaas/pools/4029d267-3983-4224-a3d0-afb3fe16a2cd
|
||||
|
|
|
@ -7,6 +7,8 @@
|
|||
"description": "Super Least Connections Pool",
|
||||
"name": "super-least-conn-pool",
|
||||
"tags": ["updated_tag"],
|
||||
"tls_container_ref": "http://198.51.100.10:9311/v1/containers/c1cd501d-3cf9-4873-a11b-a74bebcde929"
|
||||
"tls_container_ref": "http://198.51.100.10:9311/v1/containers/c1cd501d-3cf9-4873-a11b-a74bebcde929",
|
||||
"ca_tls_container_ref": null,
|
||||
"crl_container_ref": null
|
||||
}
|
||||
}
|
||||
|
|
|
@ -28,6 +28,8 @@
|
|||
"operating_status": "ONLINE",
|
||||
"name": "super-least-conn-pool",
|
||||
"tags": ["updated_tag"],
|
||||
"tls_container_ref": "http://198.51.100.10:9311/v1/containers/c1cd501d-3cf9-4873-a11b-a74bebcde929"
|
||||
"tls_container_ref": "http://198.51.100.10:9311/v1/containers/c1cd501d-3cf9-4873-a11b-a74bebcde929",
|
||||
"ca_tls_container_ref": null,
|
||||
"crl_container_ref": null
|
||||
}
|
||||
}
|
||||
|
|
|
@ -34,7 +34,9 @@
|
|||
"operating_status": "ONLINE",
|
||||
"name": "round_robin_pool",
|
||||
"tags": ["test_tag"],
|
||||
"tls_container_ref": "http://198.51.100.10:9311/v1/containers/4073846f-1d5e-42e1-a4cf-a7046419d0e6"
|
||||
"tls_container_ref": "http://198.51.100.10:9311/v1/containers/4073846f-1d5e-42e1-a4cf-a7046419d0e6",
|
||||
"ca_tls_container_ref": "http://198.51.100.10:9311/v1/containers/5f0d5540-fae6-4646-85d6-8a84883807fb",
|
||||
"crl_container_ref": "http://198.51.100.10:9311/v1/containers/6faf0a01-6892-454c-aaac-650282820c0b"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
@ -46,7 +46,9 @@ Response Parameters
|
|||
.. rest_parameters:: ../parameters.yaml
|
||||
|
||||
- admin_state_up: admin_state_up
|
||||
- ca_tls_container_ref: ca_tls_container_ref
|
||||
- created_at: created_at
|
||||
- crl_container_ref: crl_container_ref
|
||||
- description: description
|
||||
- healthmonitor_id: healthmonitor-id
|
||||
- id: pool-id
|
||||
|
@ -161,6 +163,8 @@ Request
|
|||
.. rest_parameters:: ../parameters.yaml
|
||||
|
||||
- admin_state_up: admin_state_up-default-optional
|
||||
- ca_tls_container_ref: ca_tls_container_ref-optional
|
||||
- crl_container_ref: crl_container_ref-optional
|
||||
- description: description-optional
|
||||
- lb_algorithm: lb-algorithm
|
||||
- listener_id: listener-id-pool-optional
|
||||
|
@ -233,7 +237,9 @@ Response Parameters
|
|||
.. rest_parameters:: ../parameters.yaml
|
||||
|
||||
- admin_state_up: admin_state_up
|
||||
- ca_tls_container_ref: ca_tls_container_ref
|
||||
- created_at: created_at
|
||||
- crl_container_ref: crl_container_ref
|
||||
- description: description
|
||||
- healthmonitor_id: healthmonitor-id
|
||||
- id: pool-id
|
||||
|
@ -301,7 +307,9 @@ Response Parameters
|
|||
.. rest_parameters:: ../parameters.yaml
|
||||
|
||||
- admin_state_up: admin_state_up
|
||||
- ca_tls_container_ref: ca_tls_container_ref
|
||||
- created_at: created_at
|
||||
- crl_container_ref: crl_container_ref
|
||||
- description: description
|
||||
- healthmonitor_id: healthmonitor-id
|
||||
- id: pool-id
|
||||
|
@ -359,6 +367,8 @@ Request
|
|||
.. rest_parameters:: ../parameters.yaml
|
||||
|
||||
- admin_state_up: admin_state_up-default-optional
|
||||
- ca_tls_container_ref: ca_tls_container_ref-optional
|
||||
- crl_container_ref: crl_container_ref-optional
|
||||
- description: description-optional
|
||||
- lb_algorithm: lb-algorithm-optional
|
||||
- name: name-optional
|
||||
|
@ -385,7 +395,9 @@ Response Parameters
|
|||
.. rest_parameters:: ../parameters.yaml
|
||||
|
||||
- admin_state_up: admin_state_up
|
||||
- ca_tls_container_ref: ca_tls_container_ref
|
||||
- created_at: created_at
|
||||
- crl_container_ref: crl_container_ref
|
||||
- description: description
|
||||
- healthmonitor_id: healthmonitor-id
|
||||
- id: pool-id
|
||||
|
|
|
@ -648,6 +648,16 @@ contain the following:
|
|||
+=======================+========+==========================================+
|
||||
| admin_state_up | bool | Admin state: True if up, False if down. |
|
||||
+-----------------------+--------+------------------------------------------+
|
||||
| ca_tls_container_data | string | A PEM encoded certificate. |
|
||||
+-----------------------+--------+------------------------------------------+
|
||||
| ca_tls_container_ref | string | The reference to the secrets |
|
||||
| | | container. |
|
||||
+-----------------------+--------+------------------------------------------+
|
||||
| crl_container_data | string | A PEM encoded CRL file. |
|
||||
+-----------------------+--------+------------------------------------------+
|
||||
| crl_container_ref | string | The reference to the secrets |
|
||||
| | | container. |
|
||||
+-----------------------+--------+------------------------------------------+
|
||||
| description | string | A human-readable description for the |
|
||||
| | | pool. |
|
||||
+-----------------------+--------+------------------------------------------+
|
||||
|
@ -712,6 +722,16 @@ contain the following:
|
|||
+=======================+========+==========================================+
|
||||
| admin_state_up | bool | Admin state: True if up, False if down. |
|
||||
+-----------------------+--------+------------------------------------------+
|
||||
| ca_tls_container_data | string | A PEM encoded certificate. |
|
||||
+-----------------------+--------+------------------------------------------+
|
||||
| ca_tls_container_ref | string | The reference to the secrets |
|
||||
| | | container. |
|
||||
+-----------------------+--------+------------------------------------------+
|
||||
| crl_container_data | string | A PEM encoded CRL file. |
|
||||
+-----------------------+--------+------------------------------------------+
|
||||
| crl_container_ref | string | The reference to the secrets |
|
||||
| | | container. |
|
||||
+-----------------------+--------+------------------------------------------+
|
||||
| description | string | A human-readable description for the |
|
||||
| | | pool. |
|
||||
+-----------------------+--------+------------------------------------------+
|
||||
|
|
|
@ -349,6 +349,15 @@ class HaproxyAmphoraLoadBalancerDriver(
|
|||
self._apply(self._upload_cert, listener, None, pem, md5, name)
|
||||
pool_cert_dict['client_cert'] = os.path.join(
|
||||
CONF.haproxy_amphora.base_cert_dir, listener.id, name)
|
||||
if pool.ca_tls_certificate_id:
|
||||
name = self._process_secret(listener, pool.ca_tls_certificate_id)
|
||||
pool_cert_dict['ca_cert'] = os.path.join(
|
||||
CONF.haproxy_amphora.base_cert_dir, listener.id, name)
|
||||
if pool.crl_container_id:
|
||||
name = self._process_secret(listener, pool.crl_container_id)
|
||||
pool_cert_dict['crl'] = os.path.join(
|
||||
CONF.haproxy_amphora.base_cert_dir, listener.id, name)
|
||||
|
||||
return pool_cert_dict
|
||||
|
||||
def _upload_cert(self, amp, listener_id, pem, md5, name):
|
||||
|
|
|
@ -138,6 +138,15 @@ class AmphoraProviderDriver(driver_base.ProviderDriver):
|
|||
if 'tls_container_ref' in pool_dict:
|
||||
pool_dict['tls_container_id'] = pool_dict.pop('tls_container_ref')
|
||||
pool_dict.pop('tls_container_data', None)
|
||||
if 'ca_tls_container_ref' in pool_dict:
|
||||
pool_dict['ca_tls_certificate_id'] = pool_dict.pop(
|
||||
'ca_tls_container_ref')
|
||||
pool_dict.pop('ca_tls_container_data', None)
|
||||
if 'client_crl_container_ref' in pool_dict:
|
||||
pool_dict['client_crl_container_id'] = pool_dict.pop(
|
||||
'client_crl_container_ref')
|
||||
pool_dict.pop('client_crl_container_data', None)
|
||||
|
||||
payload = {consts.POOL_ID: pool_id,
|
||||
consts.POOL_UPDATES: pool_dict}
|
||||
self.client.cast({}, 'update_pool', **payload)
|
||||
|
|
|
@ -171,7 +171,9 @@ class Pool(BaseDataModel):
|
|||
loadbalancer_id=Unset, members=Unset, name=Unset,
|
||||
pool_id=Unset, listener_id=Unset, protocol=Unset,
|
||||
session_persistence=Unset, tls_container_ref=Unset,
|
||||
tls_container_data=Unset):
|
||||
tls_container_data=Unset, ca_tls_container_ref=Unset,
|
||||
ca_tls_container_data=Unset, crl_container_ref=Unset,
|
||||
crl_container_data=Unset):
|
||||
|
||||
self.admin_state_up = admin_state_up
|
||||
self.description = description
|
||||
|
@ -186,6 +188,10 @@ class Pool(BaseDataModel):
|
|||
self.session_persistence = session_persistence
|
||||
self.tls_container_ref = tls_container_ref
|
||||
self.tls_container_data = tls_container_data
|
||||
self.ca_tls_container_ref = ca_tls_container_ref
|
||||
self.ca_tls_container_data = ca_tls_container_data
|
||||
self.crl_container_ref = crl_container_ref
|
||||
self.crl_container_data = crl_container_data
|
||||
|
||||
|
||||
class Member(BaseDataModel):
|
||||
|
|
|
@ -155,12 +155,12 @@ def db_listener_to_provider_listener(db_listener):
|
|||
return provider_listener
|
||||
|
||||
|
||||
def _get_secret_data(cert_manager, listener, secret_ref):
|
||||
def _get_secret_data(cert_manager, project_id, secret_ref):
|
||||
"""Get the secret from the certificate manager and upload it to the amp.
|
||||
|
||||
:returns: The secret data.
|
||||
"""
|
||||
context = oslo_context.RequestContext(project_id=listener.project_id)
|
||||
context = oslo_context.RequestContext(project_id=project_id)
|
||||
return cert_manager.get_secret(context, secret_ref)
|
||||
|
||||
|
||||
|
@ -219,11 +219,11 @@ def listener_dict_to_provider_dict(listener_dict):
|
|||
new_listener_dict['sni_container_data'] = sni_data_list
|
||||
|
||||
if listener_obj.client_ca_tls_certificate_id:
|
||||
cert = _get_secret_data(cert_manager, listener_obj,
|
||||
cert = _get_secret_data(cert_manager, listener_obj.project_id,
|
||||
listener_obj.client_ca_tls_certificate_id)
|
||||
new_listener_dict['client_ca_tls_container_data'] = cert
|
||||
if listener_obj.client_crl_container_id:
|
||||
crl_file = _get_secret_data(cert_manager, listener_obj,
|
||||
crl_file = _get_secret_data(cert_manager, listener_obj.project_id,
|
||||
listener_obj.client_crl_container_id)
|
||||
new_listener_dict['client_crl_container_data'] = crl_file
|
||||
|
||||
|
@ -286,9 +286,16 @@ def pool_dict_to_provider_dict(pool_dict):
|
|||
if 'tls_certificate_id' in new_pool_dict:
|
||||
new_pool_dict['tls_container_ref'] = new_pool_dict.pop(
|
||||
'tls_certificate_id')
|
||||
if 'ca_tls_certificate_id' in new_pool_dict:
|
||||
new_pool_dict['ca_tls_container_ref'] = new_pool_dict.pop(
|
||||
'ca_tls_certificate_id')
|
||||
if 'crl_container_id' in new_pool_dict:
|
||||
new_pool_dict['crl_container_ref'] = new_pool_dict.pop(
|
||||
'crl_container_id')
|
||||
|
||||
pool_obj = data_models.Pool(**pool_dict)
|
||||
if pool_obj.tls_certificate_id:
|
||||
if (pool_obj.tls_certificate_id or pool_obj.ca_tls_certificate_id or
|
||||
pool_obj.crl_container_id):
|
||||
cert_manager = stevedore_driver.DriverManager(
|
||||
namespace='octavia.cert_manager',
|
||||
name=CONF.certificates.cert_manager,
|
||||
|
@ -300,6 +307,16 @@ def pool_dict_to_provider_dict(pool_dict):
|
|||
new_pool_dict['tls_container_data'] = (
|
||||
cert_dict['tls_cert'].to_dict())
|
||||
|
||||
if pool_obj.ca_tls_certificate_id:
|
||||
cert = _get_secret_data(cert_manager, pool_obj.project_id,
|
||||
pool_obj.ca_tls_certificate_id)
|
||||
new_pool_dict['ca_tls_container_data'] = cert
|
||||
|
||||
if pool_obj.crl_container_id:
|
||||
crl_file = _get_secret_data(cert_manager, pool_obj.project_id,
|
||||
pool_obj.crl_container_id)
|
||||
new_pool_dict['crl_container_data'] = crl_file
|
||||
|
||||
# Remove the DB back references
|
||||
if ('session_persistence' in new_pool_dict and
|
||||
new_pool_dict['session_persistence']):
|
||||
|
|
|
@ -12,9 +12,12 @@
|
|||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from cryptography.hazmat.backends import default_backend
|
||||
from cryptography import x509
|
||||
from oslo_config import cfg
|
||||
from oslo_log import log as logging
|
||||
from pecan import rest
|
||||
import pecan
|
||||
from stevedore import driver as stevedore_driver
|
||||
from wsme import types as wtypes
|
||||
|
||||
from octavia.common import constants
|
||||
|
@ -22,16 +25,23 @@ from octavia.common import data_models
|
|||
from octavia.common import exceptions
|
||||
from octavia.common import policy
|
||||
from octavia.db import repositories
|
||||
from octavia.i18n import _
|
||||
|
||||
CONF = cfg.CONF
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class BaseController(rest.RestController):
|
||||
class BaseController(pecan.rest.RestController):
|
||||
RBAC_TYPE = None
|
||||
|
||||
def __init__(self):
|
||||
super(BaseController, self).__init__()
|
||||
self.cert_manager = stevedore_driver.DriverManager(
|
||||
namespace='octavia.cert_manager',
|
||||
name=CONF.certificates.cert_manager,
|
||||
invoke_on_load=True,
|
||||
).driver
|
||||
|
||||
self.repositories = repositories.Repositories()
|
||||
|
||||
@staticmethod
|
||||
|
@ -228,3 +238,70 @@ class BaseController(rest.RestController):
|
|||
attrs = [attr for attr in dir(obj) if not callable(
|
||||
getattr(obj, attr)) and not attr.startswith("_")]
|
||||
return attrs
|
||||
|
||||
def _validate_tls_refs(self, tls_refs):
|
||||
context = pecan.request.context.get('octavia_context')
|
||||
bad_refs = []
|
||||
for ref in tls_refs:
|
||||
try:
|
||||
self.cert_manager.set_acls(context, ref)
|
||||
self.cert_manager.get_cert(context, ref, check_only=True)
|
||||
except Exception:
|
||||
bad_refs.append(ref)
|
||||
|
||||
if bad_refs:
|
||||
raise exceptions.CertificateRetrievalException(ref=bad_refs)
|
||||
|
||||
def _validate_client_ca_and_crl_refs(self, client_ca_ref, crl_ref):
|
||||
context = pecan.request.context.get('octavia_context')
|
||||
bad_refs = []
|
||||
try:
|
||||
self.cert_manager.set_acls(context, client_ca_ref)
|
||||
ca_pem = self.cert_manager.get_secret(context, client_ca_ref)
|
||||
except Exception:
|
||||
bad_refs.append(client_ca_ref)
|
||||
|
||||
pem_crl = None
|
||||
if crl_ref:
|
||||
try:
|
||||
self.cert_manager.set_acls(context, crl_ref)
|
||||
pem_crl = self.cert_manager.get_secret(context, crl_ref)
|
||||
except Exception:
|
||||
bad_refs.append(crl_ref)
|
||||
if bad_refs:
|
||||
raise exceptions.CertificateRetrievalException(ref=bad_refs)
|
||||
|
||||
ca_cert = None
|
||||
try:
|
||||
# Test if it needs to be UTF-8 encoded
|
||||
try:
|
||||
ca_pem = ca_pem.encode('utf-8')
|
||||
except AttributeError:
|
||||
pass
|
||||
ca_cert = x509.load_pem_x509_certificate(ca_pem, default_backend())
|
||||
except Exception as e:
|
||||
raise exceptions.ValidationException(detail=_(
|
||||
"The client authentication CA certificate is invalid. "
|
||||
"It must be a valid x509 PEM format certificate. "
|
||||
"Error: %s") % str(e))
|
||||
|
||||
# Validate the CRL is for the client CA
|
||||
if pem_crl:
|
||||
ca_pub_key = ca_cert.public_key()
|
||||
crl = None
|
||||
# Test if it needs to be UTF-8 encoded
|
||||
try:
|
||||
pem_crl = pem_crl.encode('utf-8')
|
||||
except AttributeError:
|
||||
pass
|
||||
try:
|
||||
crl = x509.load_pem_x509_crl(pem_crl, default_backend())
|
||||
except Exception as e:
|
||||
raise exceptions.ValidationException(detail=_(
|
||||
"The client authentication certificate revocation list "
|
||||
"is invalid. It must be a valid x509 PEM format "
|
||||
"certificate revocation list. Error: %s") % str(e))
|
||||
if not crl.is_signature_valid(ca_pub_key):
|
||||
raise exceptions.ValidationException(detail=_(
|
||||
"The CRL specified is not valid for client certificate "
|
||||
"authority reference supplied."))
|
||||
|
|
|
@ -13,14 +13,11 @@
|
|||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from cryptography.hazmat.backends import default_backend
|
||||
from cryptography import x509
|
||||
from oslo_config import cfg
|
||||
from oslo_db import exception as odb_exceptions
|
||||
from oslo_log import log as logging
|
||||
from oslo_utils import excutils
|
||||
import pecan
|
||||
from stevedore import driver as stevedore_driver
|
||||
from wsme import types as wtypes
|
||||
from wsmeext import pecan as wsme_pecan
|
||||
|
||||
|
@ -48,11 +45,6 @@ class ListenersController(base.BaseController):
|
|||
|
||||
def __init__(self):
|
||||
super(ListenersController, self).__init__()
|
||||
self.cert_manager = stevedore_driver.DriverManager(
|
||||
namespace='octavia.cert_manager',
|
||||
name=CONF.certificates.cert_manager,
|
||||
invoke_on_load=True,
|
||||
).driver
|
||||
|
||||
@wsme_pecan.wsexpose(listener_types.ListenerRootResponse, wtypes.text,
|
||||
[wtypes.text], ignore_extra_args=True)
|
||||
|
@ -129,73 +121,6 @@ class ListenersController(base.BaseController):
|
|||
"type UDP.") % constants.PROTOCOL_UDP
|
||||
raise exceptions.ValidationException(detail=msg)
|
||||
|
||||
def _validate_tls_refs(self, tls_refs):
|
||||
context = pecan.request.context.get('octavia_context')
|
||||
bad_refs = []
|
||||
for ref in tls_refs:
|
||||
try:
|
||||
self.cert_manager.set_acls(context, ref)
|
||||
self.cert_manager.get_cert(context, ref, check_only=True)
|
||||
except Exception:
|
||||
bad_refs.append(ref)
|
||||
|
||||
if bad_refs:
|
||||
raise exceptions.CertificateRetrievalException(ref=bad_refs)
|
||||
|
||||
def _validate_client_ca_and_crl_refs(self, client_ca_ref, crl_ref):
|
||||
context = pecan.request.context.get('octavia_context')
|
||||
bad_refs = []
|
||||
try:
|
||||
self.cert_manager.set_acls(context, client_ca_ref)
|
||||
ca_pem = self.cert_manager.get_secret(context, client_ca_ref)
|
||||
except Exception:
|
||||
bad_refs.append(client_ca_ref)
|
||||
|
||||
pem_crl = None
|
||||
if crl_ref:
|
||||
try:
|
||||
self.cert_manager.set_acls(context, crl_ref)
|
||||
pem_crl = self.cert_manager.get_secret(context, crl_ref)
|
||||
except Exception:
|
||||
bad_refs.append(crl_ref)
|
||||
if bad_refs:
|
||||
raise exceptions.CertificateRetrievalException(ref=bad_refs)
|
||||
|
||||
ca_cert = None
|
||||
try:
|
||||
# Test if it needs to be UTF-8 encoded
|
||||
try:
|
||||
ca_pem = ca_pem.encode('utf-8')
|
||||
except AttributeError:
|
||||
pass
|
||||
ca_cert = x509.load_pem_x509_certificate(ca_pem, default_backend())
|
||||
except Exception as e:
|
||||
raise exceptions.ValidationException(detail=_(
|
||||
"The client authentication CA certificate is invalid. "
|
||||
"It must be a valid x509 PEM format certificate. "
|
||||
"Error: %s") % str(e))
|
||||
|
||||
# Validate the CRL is for the client CA
|
||||
if pem_crl:
|
||||
ca_pub_key = ca_cert.public_key()
|
||||
crl = None
|
||||
# Test if it needs to be UTF-8 encoded
|
||||
try:
|
||||
pem_crl = pem_crl.encode('utf-8')
|
||||
except AttributeError:
|
||||
pass
|
||||
try:
|
||||
crl = x509.load_pem_x509_crl(pem_crl, default_backend())
|
||||
except Exception as e:
|
||||
raise exceptions.ValidationException(detail=_(
|
||||
"The client authentication certificate revocation list "
|
||||
"is invalid. It must be a valid x509 PEM format "
|
||||
"certificate revocation list. Error: %s") % str(e))
|
||||
if not crl.is_signature_valid(ca_pub_key):
|
||||
raise exceptions.ValidationException(detail=_(
|
||||
"The CRL specified is not valid for client certificate "
|
||||
"authority reference supplied."))
|
||||
|
||||
def _has_tls_container_refs(self, listener_dict):
|
||||
return (listener_dict.get('tls_certificate_id') or
|
||||
listener_dict.get('client_ca_tls_container_id') or
|
||||
|
|
|
@ -18,7 +18,6 @@ from oslo_db import exception as odb_exceptions
|
|||
from oslo_log import log as logging
|
||||
from oslo_utils import excutils
|
||||
import pecan
|
||||
from stevedore import driver as stevedore_driver
|
||||
from wsme import types as wtypes
|
||||
from wsmeext import pecan as wsme_pecan
|
||||
|
||||
|
@ -47,11 +46,6 @@ class PoolsController(base.BaseController):
|
|||
|
||||
def __init__(self):
|
||||
super(PoolsController, self).__init__()
|
||||
self.cert_manager = stevedore_driver.DriverManager(
|
||||
namespace='octavia.cert_manager',
|
||||
name=CONF.certificates.cert_manager,
|
||||
invoke_on_load=True,
|
||||
).driver
|
||||
|
||||
@wsme_pecan.wsexpose(pool_types.PoolRootResponse, wtypes.text,
|
||||
[wtypes.text], ignore_extra_args=True)
|
||||
|
@ -104,29 +98,30 @@ class PoolsController(base.BaseController):
|
|||
raise exceptions.ImmutableObject(resource=_('Load Balancer'),
|
||||
id=lb_id)
|
||||
|
||||
def _validate_tls_refs(self, tls_refs):
|
||||
context = pecan.request.context.get('octavia_context')
|
||||
bad_refs = []
|
||||
for ref in tls_refs:
|
||||
try:
|
||||
self.cert_manager.set_acls(context, ref)
|
||||
self.cert_manager.get_cert(context, ref, check_only=True)
|
||||
except Exception:
|
||||
bad_refs.append(ref)
|
||||
|
||||
if bad_refs:
|
||||
raise exceptions.CertificateRetrievalException(ref=bad_refs)
|
||||
|
||||
def _validate_create_pool(self, lock_session, pool_dict, listener_id=None):
|
||||
"""Validate creating pool on load balancer.
|
||||
|
||||
Update database for load balancer and (optional) listener based on
|
||||
provisioning status.
|
||||
"""
|
||||
# Make sure we have a client CA if they specify a CRL
|
||||
if (pool_dict.get('crl_container_id') and
|
||||
not pool_dict.get('ca_tls_certificate_id')):
|
||||
raise exceptions.ValidationException(detail=_(
|
||||
"A CA certificate reference is required to "
|
||||
"specify a revocation list."))
|
||||
|
||||
tls_certificate_id = pool_dict.get('tls_certificate_id', None)
|
||||
tls_refs = [tls_certificate_id] if tls_certificate_id else []
|
||||
self._validate_tls_refs(tls_refs)
|
||||
|
||||
# Validate the client CA cert and optional client CRL
|
||||
if pool_dict.get('ca_tls_certificate_id'):
|
||||
self._validate_client_ca_and_crl_refs(
|
||||
pool_dict.get('ca_tls_certificate_id'),
|
||||
pool_dict.get('crl_container_id', None))
|
||||
|
||||
try:
|
||||
tls_certificate_id = pool_dict.get('tls_certificate_id', None)
|
||||
tls_refs = [tls_certificate_id] if tls_certificate_id else []
|
||||
self._validate_tls_refs(tls_refs)
|
||||
return self.repositories.create_pool_on_load_balancer(
|
||||
lock_session, pool_dict,
|
||||
listener_id=listener_id)
|
||||
|
@ -318,23 +313,8 @@ class PoolsController(base.BaseController):
|
|||
db_pool.members = new_members
|
||||
return db_pool
|
||||
|
||||
@wsme_pecan.wsexpose(pool_types.PoolRootResponse, wtypes.text,
|
||||
body=pool_types.PoolRootPut, status_code=200)
|
||||
def put(self, id, pool_):
|
||||
"""Updates a pool on a load balancer."""
|
||||
pool = pool_.pool
|
||||
context = pecan.request.context.get('octavia_context')
|
||||
db_pool = self._get_db_pool(context.session, id, show_deleted=False)
|
||||
def _validate_pool_PUT(self, pool, db_pool):
|
||||
|
||||
project_id, provider = self._get_lb_project_id_provider(
|
||||
context.session, db_pool.load_balancer_id)
|
||||
|
||||
if (pool.session_persistence and
|
||||
not pool.session_persistence.type and
|
||||
db_pool.session_persistence and
|
||||
db_pool.session_persistence.type):
|
||||
pool.session_persistence.type = db_pool.session_persistence.type
|
||||
self._auth_validate_action(context, project_id, constants.RBAC_PUT)
|
||||
if db_pool.protocol == constants.PROTOCOL_UDP:
|
||||
self._validate_pool_request_for_udp(pool)
|
||||
else:
|
||||
|
@ -349,9 +329,62 @@ class PoolsController(base.BaseController):
|
|||
sp_dict = pool.session_persistence.to_dict(render_unsets=False)
|
||||
validate.check_session_persistence(sp_dict)
|
||||
|
||||
crl_ref = None
|
||||
if (pool.crl_container_ref and
|
||||
pool.crl_container_ref != wtypes.Unset):
|
||||
crl_ref = pool.crl_container_ref
|
||||
elif db_pool.crl_container_id:
|
||||
crl_ref = db_pool.crl_container_id
|
||||
|
||||
ca_ref = None
|
||||
db_ca_ref = db_pool.ca_tls_certificate_id
|
||||
if pool.ca_tls_container_ref != wtypes.Unset:
|
||||
if not pool.ca_tls_container_ref and db_ca_ref and crl_ref:
|
||||
raise exceptions.ValidationException(detail=_(
|
||||
"A CA reference cannot be removed when a "
|
||||
"certificate revocation list is present."))
|
||||
|
||||
if not pool.ca_tls_container_ref and not db_ca_ref and crl_ref:
|
||||
raise exceptions.ValidationException(detail=_(
|
||||
"A CA reference is required to "
|
||||
"specify a certificate revocation list."))
|
||||
if pool.ca_tls_container_ref:
|
||||
ca_ref = pool.ca_tls_container_ref
|
||||
elif db_ca_ref:
|
||||
ca_ref = db_ca_ref
|
||||
elif crl_ref and not db_ca_ref:
|
||||
raise exceptions.ValidationException(detail=_(
|
||||
"A CA reference is required to "
|
||||
"specify a certificate revocation list."))
|
||||
|
||||
if pool.tls_container_ref:
|
||||
self._validate_tls_refs([pool.tls_container_ref])
|
||||
|
||||
# Validate the client CA cert and optional client CRL
|
||||
if ca_ref:
|
||||
self._validate_client_ca_and_crl_refs(ca_ref, crl_ref)
|
||||
|
||||
@wsme_pecan.wsexpose(pool_types.PoolRootResponse, wtypes.text,
|
||||
body=pool_types.PoolRootPut, status_code=200)
|
||||
def put(self, id, pool_):
|
||||
"""Updates a pool on a load balancer."""
|
||||
pool = pool_.pool
|
||||
context = pecan.request.context.get('octavia_context')
|
||||
db_pool = self._get_db_pool(context.session, id, show_deleted=False)
|
||||
|
||||
project_id, provider = self._get_lb_project_id_provider(
|
||||
context.session, db_pool.load_balancer_id)
|
||||
|
||||
self._auth_validate_action(context, project_id, constants.RBAC_PUT)
|
||||
|
||||
if (pool.session_persistence and
|
||||
not pool.session_persistence.type and
|
||||
db_pool.session_persistence and
|
||||
db_pool.session_persistence.type):
|
||||
pool.session_persistence.type = db_pool.session_persistence.type
|
||||
|
||||
self._validate_pool_PUT(pool, db_pool)
|
||||
|
||||
# Load the driver early as it also provides validation
|
||||
driver = driver_factory.get_driver(provider)
|
||||
|
||||
|
|
|
@ -53,7 +53,9 @@ class BasePoolType(types.BaseType):
|
|||
_type_to_model_map = {'admin_state_up': 'enabled',
|
||||
'healthmonitor': 'health_monitor',
|
||||
'healthmonitor_id': 'health_monitor.id',
|
||||
'tls_container_ref': 'tls_certificate_id'}
|
||||
'tls_container_ref': 'tls_certificate_id',
|
||||
'ca_tls_container_ref': 'ca_tls_certificate_id',
|
||||
'crl_container_ref': 'crl_container_id'}
|
||||
|
||||
_child_map = {'health_monitor': {'id': 'healthmonitor_id'}}
|
||||
|
||||
|
@ -78,6 +80,8 @@ class PoolResponse(BasePoolType):
|
|||
members = wtypes.wsattr([types.IdOnlyType])
|
||||
tags = wtypes.wsattr(wtypes.ArrayType(wtypes.StringType()))
|
||||
tls_container_ref = wtypes.wsattr(wtypes.StringType())
|
||||
ca_tls_container_ref = wtypes.wsattr(wtypes.StringType())
|
||||
crl_container_ref = wtypes.wsattr(wtypes.StringType())
|
||||
|
||||
@classmethod
|
||||
def from_data_model(cls, data_model, children=False):
|
||||
|
@ -104,7 +108,6 @@ class PoolResponse(BasePoolType):
|
|||
member_model = types.IdOnlyType
|
||||
if data_model.health_monitor:
|
||||
pool.healthmonitor_id = data_model.health_monitor.id
|
||||
|
||||
pool.listeners = [
|
||||
types.IdOnlyType.from_data_model(i) for i in data_model.listeners]
|
||||
pool.members = [
|
||||
|
@ -151,6 +154,8 @@ class PoolPOST(BasePoolType):
|
|||
tags = wtypes.wsattr(wtypes.ArrayType(wtypes.StringType(max_length=255)))
|
||||
tls_container_ref = wtypes.wsattr(
|
||||
wtypes.StringType(max_length=255))
|
||||
ca_tls_container_ref = wtypes.wsattr(wtypes.StringType(max_length=255))
|
||||
crl_container_ref = wtypes.wsattr(wtypes.StringType(max_length=255))
|
||||
|
||||
|
||||
class PoolRootPOST(types.BaseType):
|
||||
|
@ -167,6 +172,8 @@ class PoolPUT(BasePoolType):
|
|||
session_persistence = wtypes.wsattr(SessionPersistencePUT)
|
||||
tags = wtypes.wsattr(wtypes.ArrayType(wtypes.StringType(max_length=255)))
|
||||
tls_container_ref = wtypes.wsattr(wtypes.StringType(max_length=255))
|
||||
ca_tls_container_ref = wtypes.wsattr(wtypes.StringType(max_length=255))
|
||||
crl_container_ref = wtypes.wsattr(wtypes.StringType(max_length=255))
|
||||
|
||||
|
||||
class PoolRootPut(types.BaseType):
|
||||
|
@ -186,6 +193,8 @@ class PoolSingleCreate(BasePoolType):
|
|||
members = wtypes.wsattr([member.MemberSingleCreate])
|
||||
tags = wtypes.wsattr(wtypes.ArrayType(wtypes.StringType(max_length=255)))
|
||||
tls_container_ref = wtypes.wsattr(wtypes.StringType(max_length=255))
|
||||
ca_tls_container_ref = wtypes.wsattr(wtypes.StringType(max_length=255))
|
||||
crl_container_ref = wtypes.wsattr(wtypes.StringType(max_length=255))
|
||||
|
||||
|
||||
class PoolStatusResponse(BasePoolType):
|
||||
|
|
|
@ -178,5 +178,4 @@ class BarbicanCertManager(cert_mgr.CertManager):
|
|||
except Exception as e:
|
||||
LOG.error("Failed to access secret for %s due to: %s.",
|
||||
secret_ref, str(e))
|
||||
raise exceptions.CertificateStorageException(
|
||||
msg="Secret could not be accessed.")
|
||||
raise exceptions.CertificateRetrievalException(ref=secret_ref)
|
||||
|
|
|
@ -79,6 +79,5 @@ class CastellanCertManager(cert_mgr.CertManager):
|
|||
except Exception as e:
|
||||
LOG.error("Failed to access secret for %s due to: %s.",
|
||||
secret_ref, str(e))
|
||||
raise exceptions.CertificateStorageException(
|
||||
msg="Secret could not be accessed.")
|
||||
raise exceptions.CertificateRetrievalException(ref=secret_ref)
|
||||
return certbag_data
|
||||
|
|
|
@ -194,7 +194,6 @@ class LocalCertManager(cert_mgr.CertManager):
|
|||
secret_data = secret_file.read()
|
||||
except IOError:
|
||||
LOG.error("Failed to read secret for %s.", secret_ref)
|
||||
raise exceptions.CertificateStorageException(
|
||||
msg="secret could not be read.")
|
||||
raise exceptions.CertificateRetrievalException(ref=secret_ref)
|
||||
|
||||
return secret_data
|
||||
|
|
|
@ -263,7 +263,8 @@ class Pool(BaseDataModel):
|
|||
session_persistence=None, load_balancer_id=None,
|
||||
load_balancer=None, listeners=None, l7policies=None,
|
||||
created_at=None, updated_at=None, provisioning_status=None,
|
||||
tags=None, tls_certificate_id=None):
|
||||
tags=None, tls_certificate_id=None,
|
||||
ca_tls_certificate_id=None, crl_container_id=None):
|
||||
self.id = id
|
||||
self.project_id = project_id
|
||||
self.name = name
|
||||
|
@ -284,6 +285,8 @@ class Pool(BaseDataModel):
|
|||
self.provisioning_status = provisioning_status
|
||||
self.tags = tags
|
||||
self.tls_certificate_id = tls_certificate_id
|
||||
self.ca_tls_certificate_id = ca_tls_certificate_id
|
||||
self.crl_container_id = crl_container_id
|
||||
|
||||
def update(self, update_dict):
|
||||
for key, value in update_dict.items():
|
||||
|
|
|
@ -295,7 +295,9 @@ class JinjaTemplater(object):
|
|||
'operating_status': pool.operating_status,
|
||||
'stick_size': CONF.haproxy_amphora.haproxy_stick_size,
|
||||
constants.HTTP_REUSE: feature_compatibility.get(
|
||||
constants.HTTP_REUSE, False)
|
||||
constants.HTTP_REUSE, False),
|
||||
'ca_tls_path': '',
|
||||
'crl_path': ''
|
||||
}
|
||||
members = [self._transform_member(x, feature_compatibility)
|
||||
for x in pool.members]
|
||||
|
@ -310,6 +312,12 @@ class JinjaTemplater(object):
|
|||
if (pool.tls_certificate_id and pool_tls_certs and
|
||||
pool_tls_certs.get('client_cert')):
|
||||
ret_value['client_cert'] = pool_tls_certs.get('client_cert')
|
||||
if (pool.ca_tls_certificate_id and pool_tls_certs and
|
||||
pool_tls_certs.get('ca_cert')):
|
||||
ret_value['ca_cert'] = pool_tls_certs.get('ca_cert')
|
||||
if (pool.crl_container_id and pool_tls_certs and
|
||||
pool_tls_certs.get('crl')):
|
||||
ret_value['crl'] = pool_tls_certs.get('crl')
|
||||
|
||||
return ret_value
|
||||
|
||||
|
|
|
@ -210,19 +210,34 @@ frontend {{ listener.id }}
|
|||
{% else %}
|
||||
{% set member_enabled_opt = " disabled" %}
|
||||
{% endif %}
|
||||
{% if pool.client_cert %}
|
||||
{% if pool.client_cert or pool.ca_cert %}
|
||||
{% set def_opt_prefix = " ssl" %}
|
||||
{% set def_crt_opt = " crt %s"|format(pool.client_cert) %}
|
||||
{% set def_verify_opt = " verify none" %}
|
||||
{% else %}
|
||||
{% set def_opt_prefix = "" %}
|
||||
{% set def_crt_opt = "" %}
|
||||
{% set def_verify_opt = "" %}
|
||||
{% endif %}
|
||||
{{ "server %s %s:%d weight %s%s%s%s%s%s%s%s%s"|e|format(
|
||||
{% if pool.client_cert %}
|
||||
{% set def_crt_opt = " crt %s"|format(pool.client_cert) %}
|
||||
{% else %}
|
||||
{% set def_crt_opt = "" %}
|
||||
{% endif %}
|
||||
{% if pool.ca_cert %}
|
||||
{% set ca_opt = " ca-file %s"|format(pool.ca_cert) %}
|
||||
{% set def_verify_opt = " verify required" %}
|
||||
{% if pool.crl %}
|
||||
{% set crl_opt = " crl-file %s"|format(pool.crl) %}
|
||||
{% else %}
|
||||
{% set crl_opt = "" %}
|
||||
{% endif %}
|
||||
{% else %}
|
||||
{% set ca_opt = "" %}
|
||||
{% set def_verify_opt = "" %}
|
||||
{% set crl_opt = "" %}
|
||||
{% endif %}
|
||||
{{ "server %s %s:%d weight %s%s%s%s%s%s%s%s%s%s%s"|e|format(
|
||||
member.id, member.address, member.protocol_port, member.weight,
|
||||
hm_opt, persistence_opt, proxy_protocol_opt, member_backup_opt,
|
||||
member_enabled_opt, def_opt_prefix, def_crt_opt, def_verify_opt)|trim() }}
|
||||
member_enabled_opt, def_opt_prefix, def_crt_opt, ca_opt, crl_opt,
|
||||
def_verify_opt)|trim() }}
|
||||
{% endmacro %}
|
||||
|
||||
|
||||
|
|
|
@ -0,0 +1,36 @@
|
|||
# Copyright 2019 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.
|
||||
#
|
||||
|
||||
"""extend pool for backend CA and CRL
|
||||
|
||||
Revision ID: 74aae261694c
|
||||
Revises: a1f689aecc1d
|
||||
Create Date: 2019-02-27 09:22:24.779576
|
||||
|
||||
"""
|
||||
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = '74aae261694c'
|
||||
down_revision = 'a1f689aecc1d'
|
||||
|
||||
|
||||
def upgrade():
|
||||
op.add_column(u'pool', sa.Column(u'ca_tls_certificate_id', sa.String(255),
|
||||
nullable=True))
|
||||
op.add_column(u'pool', sa.Column(u'crl_container_id', sa.String(255),
|
||||
nullable=True))
|
|
@ -329,6 +329,8 @@ class Pool(base_models.BASE, base_models.IdMixin, base_models.ProjectMixin,
|
|||
primaryjoin='and_(foreign(Tags.resource_id)==Pool.id)'
|
||||
)
|
||||
tls_certificate_id = sa.Column(sa.String(255), nullable=True)
|
||||
ca_tls_certificate_id = sa.Column(sa.String(255), nullable=True)
|
||||
crl_container_id = sa.Column(sa.String(255), nullable=True)
|
||||
|
||||
# This property should be a unique list of any listeners that reference
|
||||
# this pool as its default_pool and any listeners referenced by enabled
|
||||
|
|
|
@ -23,6 +23,7 @@ import octavia.common.context
|
|||
from octavia.common import data_models
|
||||
from octavia.common import exceptions
|
||||
from octavia.tests.functional.api.v2 import base
|
||||
from octavia.tests.unit.common.sample_configs import sample_certs
|
||||
|
||||
|
||||
class TestPool(base.BaseAPITest):
|
||||
|
@ -884,9 +885,42 @@ class TestPool(base.BaseAPITest):
|
|||
lb_id=self.lb_id, listener_id=self.listener_id,
|
||||
pool_id=api_pool.get('id'))
|
||||
|
||||
@mock.patch('octavia.common.tls_utils.cert_parser.load_certificates_data')
|
||||
def test_create_with_ca_and_crl(self, mock_cert_data):
|
||||
self.cert_manager_mock().get_secret.side_effect = [
|
||||
sample_certs.X509_CA_CERT, sample_certs.X509_CA_CRL,
|
||||
sample_certs.X509_CA_CERT, sample_certs.X509_CA_CRL]
|
||||
|
||||
ca_tls_container_ref = uuidutils.generate_uuid()
|
||||
crl_container_ref = uuidutils.generate_uuid()
|
||||
api_pool = self.create_pool(
|
||||
self.lb_id,
|
||||
constants.PROTOCOL_HTTP,
|
||||
constants.LB_ALGORITHM_ROUND_ROBIN,
|
||||
listener_id=self.listener_id,
|
||||
ca_tls_container_ref=ca_tls_container_ref,
|
||||
crl_container_ref=crl_container_ref).get(self.root_tag)
|
||||
self.assert_correct_status(
|
||||
lb_id=self.lb_id, listener_id=self.listener_id,
|
||||
pool_id=api_pool.get('id'),
|
||||
lb_prov_status=constants.PENDING_UPDATE,
|
||||
listener_prov_status=constants.PENDING_UPDATE,
|
||||
pool_prov_status=constants.PENDING_CREATE,
|
||||
pool_op_status=constants.OFFLINE)
|
||||
self.set_lb_status(self.lb_id)
|
||||
self.assertEqual(ca_tls_container_ref,
|
||||
api_pool.get('ca_tls_container_ref'))
|
||||
self.assertEqual(crl_container_ref,
|
||||
api_pool.get('crl_container_ref'))
|
||||
self.assert_correct_status(
|
||||
lb_id=self.lb_id, listener_id=self.listener_id,
|
||||
pool_id=api_pool.get('id'))
|
||||
|
||||
def test_create_with_bad_tls_container_ref(self):
|
||||
tls_container_ref = uuidutils.generate_uuid()
|
||||
self.cert_manager_mock().get_cert.side_effect = [Exception(
|
||||
"bad cert")]
|
||||
self.cert_manager_mock().get_secret.side_effect = [Exception(
|
||||
"bad secret")]
|
||||
api_pool = self.create_pool(
|
||||
self.lb_id, constants.PROTOCOL_HTTP,
|
||||
|
@ -895,6 +929,45 @@ class TestPool(base.BaseAPITest):
|
|||
tls_container_ref=tls_container_ref, status=400)
|
||||
self.assertIn(tls_container_ref, api_pool['faultstring'])
|
||||
|
||||
def test_create_with_bad_ca_tls_container_ref(self):
|
||||
ca_tls_container_ref = uuidutils.generate_uuid()
|
||||
self.cert_manager_mock().get_cert.side_effect = [Exception(
|
||||
"bad ca cert")]
|
||||
self.cert_manager_mock().get_secret.side_effect = [Exception(
|
||||
"bad ca secret")]
|
||||
api_pool = self.create_pool(
|
||||
self.lb_id, constants.PROTOCOL_HTTP,
|
||||
constants.LB_ALGORITHM_ROUND_ROBIN,
|
||||
listener_id=self.listener_id,
|
||||
ca_tls_container_ref=ca_tls_container_ref, status=400)
|
||||
self.assertIn(ca_tls_container_ref, api_pool['faultstring'])
|
||||
|
||||
def test_create_with_unreachable_crl(self):
|
||||
ca_tls_container_ref = uuidutils.generate_uuid()
|
||||
crl_container_ref = uuidutils.generate_uuid()
|
||||
self.cert_manager_mock().get_cert.side_effect = [
|
||||
'cert 1', Exception('unknow/bad cert')]
|
||||
self.cert_manager_mock().get_secret.side_effect = [Exception(
|
||||
'bad secret')]
|
||||
api_pool = self.create_pool(
|
||||
self.lb_id, constants.PROTOCOL_HTTP,
|
||||
constants.LB_ALGORITHM_ROUND_ROBIN,
|
||||
listener_id=self.listener_id,
|
||||
ca_tls_container_ref=ca_tls_container_ref,
|
||||
crl_container_ref=crl_container_ref, status=400)
|
||||
self.assertIn(crl_container_ref, api_pool['faultstring'])
|
||||
|
||||
def test_create_with_crl_only(self):
|
||||
crl_container_ref = uuidutils.generate_uuid()
|
||||
api_pool = self.create_pool(
|
||||
self.lb_id, constants.PROTOCOL_HTTP,
|
||||
constants.LB_ALGORITHM_ROUND_ROBIN,
|
||||
listener_id=self.listener_id,
|
||||
crl_container_ref=crl_container_ref, status=400)
|
||||
self.assertIn(
|
||||
'A CA certificate reference is required to specify a '
|
||||
'revocation list.', api_pool['faultstring'])
|
||||
|
||||
def test_negative_create_udp_case(self):
|
||||
# Error create pool with udp protocol but non-udp-type
|
||||
sp = {"type": constants.SESSION_PERSISTENCE_HTTP_COOKIE,
|
||||
|
@ -1337,11 +1410,231 @@ class TestPool(base.BaseAPITest):
|
|||
new_pool = {'tls_container_ref': tls_container_ref}
|
||||
|
||||
self.cert_manager_mock().get_cert.side_effect = [Exception(
|
||||
"bad cert")]
|
||||
self.cert_manager_mock().get_secret.side_effect = [Exception(
|
||||
"bad secret")]
|
||||
resp = self.put(self.POOL_PATH.format(pool_id=api_pool.get('id')),
|
||||
self._build_body(new_pool), status=400).json
|
||||
self.assertIn(tls_container_ref, resp['faultstring'])
|
||||
|
||||
@mock.patch('octavia.common.tls_utils.cert_parser.load_certificates_data')
|
||||
def test_update_with_ca_and_crl(self, mock_cert_data):
|
||||
self.cert_manager_mock().get_secret.side_effect = [
|
||||
sample_certs.X509_CA_CERT, sample_certs.X509_CA_CRL,
|
||||
sample_certs.X509_CA_CERT, sample_certs.X509_CA_CRL]
|
||||
ca_tls_container_ref = uuidutils.generate_uuid()
|
||||
crl_container_ref = uuidutils.generate_uuid()
|
||||
api_pool = self.create_pool(
|
||||
self.lb_id,
|
||||
constants.PROTOCOL_HTTP,
|
||||
constants.LB_ALGORITHM_ROUND_ROBIN,
|
||||
listener_id=self.listener_id).get(self.root_tag)
|
||||
self.set_lb_status(lb_id=self.lb_id)
|
||||
new_pool = {'ca_tls_container_ref': ca_tls_container_ref,
|
||||
'crl_container_ref': crl_container_ref}
|
||||
self.put(self.POOL_PATH.format(pool_id=api_pool.get('id')),
|
||||
self._build_body(new_pool))
|
||||
self.assert_correct_status(
|
||||
lb_id=self.lb_id, listener_id=self.listener_id,
|
||||
pool_id=api_pool.get('id'),
|
||||
lb_prov_status=constants.PENDING_UPDATE,
|
||||
listener_prov_status=constants.PENDING_UPDATE,
|
||||
pool_prov_status=constants.PENDING_UPDATE)
|
||||
self.set_lb_status(self.lb_id)
|
||||
response = self.get(self.POOL_PATH.format(
|
||||
pool_id=api_pool.get('id'))).json.get(self.root_tag)
|
||||
self.assertEqual(ca_tls_container_ref,
|
||||
response.get('ca_tls_container_ref'))
|
||||
self.assertEqual(crl_container_ref,
|
||||
response.get('crl_container_ref'))
|
||||
self.assert_correct_status(
|
||||
lb_id=self.lb_id, listener_id=self.listener_id,
|
||||
pool_id=response.get('id'))
|
||||
|
||||
def test_update_with_bad_ca_tls_container_ref(self):
|
||||
api_pool = self.create_pool(
|
||||
self.lb_id,
|
||||
constants.PROTOCOL_HTTP,
|
||||
constants.LB_ALGORITHM_ROUND_ROBIN,
|
||||
listener_id=self.listener_id).get(self.root_tag)
|
||||
self.set_lb_status(lb_id=self.lb_id)
|
||||
ca_tls_container_ref = uuidutils.generate_uuid()
|
||||
new_pool = {'ca_tls_container_ref': ca_tls_container_ref}
|
||||
self.cert_manager_mock().get_cert.side_effect = [Exception(
|
||||
"bad cert")]
|
||||
self.cert_manager_mock().get_secret.side_effect = [Exception(
|
||||
"bad secret")]
|
||||
resp = self.put(self.POOL_PATH.format(pool_id=api_pool.get('id')),
|
||||
self._build_body(new_pool), status=400).json
|
||||
self.assertIn(ca_tls_container_ref, resp['faultstring'])
|
||||
|
||||
@mock.patch('octavia.common.tls_utils.cert_parser.load_certificates_data')
|
||||
def test_update_with_crl(self, mock_cert_data):
|
||||
ca_tls_container_ref = uuidutils.generate_uuid()
|
||||
crl_container_ref = uuidutils.generate_uuid()
|
||||
self.cert_manager_mock().get_secret.side_effect = [
|
||||
sample_certs.X509_CA_CERT, sample_certs.X509_CA_CRL,
|
||||
sample_certs.X509_CA_CERT, sample_certs.X509_CA_CRL,
|
||||
sample_certs.X509_CA_CERT, sample_certs.X509_CA_CRL,
|
||||
sample_certs.X509_CA_CERT, sample_certs.X509_CA_CRL,
|
||||
sample_certs.X509_CA_CERT, sample_certs.X509_CA_CRL]
|
||||
api_pool = self.create_pool(
|
||||
self.lb_id,
|
||||
constants.PROTOCOL_HTTP,
|
||||
constants.LB_ALGORITHM_ROUND_ROBIN,
|
||||
listener_id=self.listener_id,
|
||||
ca_tls_container_ref=ca_tls_container_ref,
|
||||
crl_container_ref=crl_container_ref).get(self.root_tag)
|
||||
self.set_lb_status(lb_id=self.lb_id)
|
||||
new_crl_container_ref = uuidutils.generate_uuid()
|
||||
new_pool = {'crl_container_ref': new_crl_container_ref}
|
||||
self.put(self.POOL_PATH.format(pool_id=api_pool.get('id')),
|
||||
self._build_body(new_pool))
|
||||
self.assert_correct_status(
|
||||
lb_id=self.lb_id, listener_id=self.listener_id,
|
||||
pool_id=api_pool.get('id'),
|
||||
lb_prov_status=constants.PENDING_UPDATE,
|
||||
listener_prov_status=constants.PENDING_UPDATE,
|
||||
pool_prov_status=constants.PENDING_UPDATE)
|
||||
self.set_lb_status(self.lb_id)
|
||||
response = self.get(self.POOL_PATH.format(
|
||||
pool_id=api_pool.get('id'))).json.get(self.root_tag)
|
||||
self.assertEqual(new_crl_container_ref,
|
||||
response.get('crl_container_ref'))
|
||||
self.assert_correct_status(
|
||||
lb_id=self.lb_id, listener_id=self.listener_id,
|
||||
pool_id=response.get('id'))
|
||||
|
||||
def test_update_with_crl_only_negative_case(self):
|
||||
api_pool = self.create_pool(
|
||||
self.lb_id,
|
||||
constants.PROTOCOL_HTTP,
|
||||
constants.LB_ALGORITHM_ROUND_ROBIN,
|
||||
listener_id=self.listener_id).get(self.root_tag)
|
||||
self.set_lb_status(lb_id=self.lb_id)
|
||||
crl_container_ref = uuidutils.generate_uuid()
|
||||
new_pool = {'crl_container_ref': crl_container_ref}
|
||||
resp = self.put(self.POOL_PATH.format(pool_id=api_pool.get('id')),
|
||||
self._build_body(new_pool), status=400).json
|
||||
self.assertIn(
|
||||
'A CA reference is required to specify a certificate revocation '
|
||||
'list.', resp['faultstring'])
|
||||
|
||||
def test_update_with_crl_only_none_ca(self):
|
||||
api_pool = self.create_pool(
|
||||
self.lb_id,
|
||||
constants.PROTOCOL_HTTP,
|
||||
constants.LB_ALGORITHM_ROUND_ROBIN,
|
||||
listener_id=self.listener_id).get(self.root_tag)
|
||||
self.set_lb_status(lb_id=self.lb_id)
|
||||
crl_container_ref = uuidutils.generate_uuid()
|
||||
new_pool = {'ca_tls_container_ref': None,
|
||||
'crl_container_ref': crl_container_ref}
|
||||
resp = self.put(self.POOL_PATH.format(pool_id=api_pool.get('id')),
|
||||
self._build_body(new_pool), status=400).json
|
||||
self.assertIn(
|
||||
'A CA reference is required to specify a certificate revocation '
|
||||
'list.', resp['faultstring'])
|
||||
|
||||
@mock.patch('octavia.common.tls_utils.cert_parser.load_certificates_data')
|
||||
def test_update_with_unreachable_crl(self, mock_cert_data):
|
||||
crl_container_ref = uuidutils.generate_uuid()
|
||||
new_crl_container_ref = uuidutils.generate_uuid()
|
||||
ca_tls_container_ref = uuidutils.generate_uuid()
|
||||
self.cert_manager_mock().get_secret.side_effect = [
|
||||
sample_certs.X509_CA_CERT, sample_certs.X509_CA_CRL,
|
||||
sample_certs.X509_CA_CERT, sample_certs.X509_CA_CRL]
|
||||
|
||||
api_pool = self.create_pool(
|
||||
self.lb_id,
|
||||
constants.PROTOCOL_HTTP,
|
||||
constants.LB_ALGORITHM_ROUND_ROBIN,
|
||||
listener_id=self.listener_id,
|
||||
ca_tls_container_ref=ca_tls_container_ref,
|
||||
crl_container_ref=crl_container_ref).get(self.root_tag)
|
||||
self.set_lb_status(self.lb_id)
|
||||
new_crl_container_ref = uuidutils.generate_uuid()
|
||||
new_pool = {'crl_container_ref': new_crl_container_ref}
|
||||
self.cert_manager_mock().get_secret.side_effect = [
|
||||
exceptions.CertificateRetrievalException(
|
||||
ref=new_crl_container_ref)]
|
||||
resp = self.put(self.POOL_PATH.format(pool_id=api_pool.get('id')),
|
||||
self._build_body(new_pool), status=400).json
|
||||
self.assertIn(new_crl_container_ref, resp['faultstring'])
|
||||
|
||||
@mock.patch('octavia.common.tls_utils.cert_parser.load_certificates_data')
|
||||
def test_update_unset_ca_cert(self, mock_cert_data):
|
||||
self.cert_manager_mock().get_secret.return_value = (
|
||||
sample_certs.X509_CA_CERT)
|
||||
|
||||
ca_tls_uuid = uuidutils.generate_uuid()
|
||||
api_pool = self.create_pool(
|
||||
self.lb_id,
|
||||
constants.PROTOCOL_HTTP,
|
||||
constants.LB_ALGORITHM_ROUND_ROBIN,
|
||||
listener_id=self.listener_id,
|
||||
ca_tls_container_ref=ca_tls_uuid).get(self.root_tag)
|
||||
self.set_lb_status(self.lb_id)
|
||||
new_pool = {'ca_tls_container_ref': None}
|
||||
body = self._build_body(new_pool)
|
||||
listener_path = self.POOL_PATH.format(
|
||||
pool_id=api_pool['id'])
|
||||
api_pool = self.put(listener_path, body).json.get(self.root_tag)
|
||||
self.assertIsNone(api_pool.get('ca_tls_container_ref'))
|
||||
self.assertIsNone(api_pool.get('crl_container_ref'))
|
||||
|
||||
@mock.patch('octavia.common.tls_utils.cert_parser.load_certificates_data')
|
||||
def test_update_unset_ca_cert_with_crl(self, mock_cert_data):
|
||||
self.cert_manager_mock().get_secret.side_effect = [
|
||||
sample_certs.X509_CA_CERT, sample_certs.X509_CA_CRL,
|
||||
sample_certs.X509_CA_CERT, sample_certs.X509_CA_CRL,
|
||||
sample_certs.X509_CA_CERT, sample_certs.X509_CA_CRL,
|
||||
sample_certs.X509_CA_CERT, sample_certs.X509_CA_CRL]
|
||||
|
||||
ca_tls_uuid = uuidutils.generate_uuid()
|
||||
crl_uuid = uuidutils.generate_uuid()
|
||||
api_pool = self.create_pool(
|
||||
self.lb_id,
|
||||
constants.PROTOCOL_HTTP,
|
||||
constants.LB_ALGORITHM_ROUND_ROBIN,
|
||||
listener_id=self.listener_id,
|
||||
ca_tls_container_ref=ca_tls_uuid,
|
||||
crl_container_ref=crl_uuid).get(self.root_tag)
|
||||
self.set_lb_status(self.lb_id)
|
||||
new_pool = {'ca_tls_container_ref': None}
|
||||
body = self._build_body(new_pool)
|
||||
listener_path = self.POOL_PATH.format(
|
||||
pool_id=api_pool['id'])
|
||||
response = self.put(listener_path, body, status=400).json
|
||||
self.assertIn('A CA reference cannot be removed when a certificate '
|
||||
'revocation list is present.', response['faultstring'])
|
||||
|
||||
@mock.patch('octavia.common.tls_utils.cert_parser.load_certificates_data')
|
||||
def test_update_unset_crl(self, mock_cert_data):
|
||||
self.cert_manager_mock() |