From e0e9af3b51c744eabb28fc951b9769373e9a3673 Mon Sep 17 00:00:00 2001 From: ZhaoBo Date: Thu, 1 Nov 2018 14:18:58 +0800 Subject: [PATCH] Add boolean tls_enabled option into Pool Add "tls_enabled" option in Pool API. This option will work on cert cases or no cert cases. Story: 2003858 Task: 26672 Co-Authored-By: Michael Johnson Change-Id: I62e31aaa66748ba652dfd5dbfd5a8b06d9ba0dfe --- api-ref/source/parameters.yaml | 16 ++++ api-ref/source/v2/examples/pool-create-curl | 2 +- .../v2/examples/pool-create-request.json | 3 +- .../v2/examples/pool-create-response.json | 3 +- .../v2/examples/pool-show-response.json | 3 +- api-ref/source/v2/examples/pool-update-curl | 2 +- .../v2/examples/pool-update-request.json | 3 +- .../v2/examples/pool-update-response.json | 3 +- .../v2/examples/pools-list-response.json | 3 +- api-ref/source/v2/pool.inc | 6 ++ doc/source/contributor/guides/providers.rst | 6 ++ octavia/api/drivers/data_models.py | 3 +- octavia/api/v2/controllers/load_balancer.py | 10 +- octavia/api/v2/types/pool.py | 4 + octavia/common/data_models.py | 4 +- octavia/common/jinja/haproxy/jinja_cfg.py | 3 +- .../common/jinja/haproxy/templates/macros.j2 | 14 ++- ...f_add_tls_boolean_type_for_reencryption.py | 37 ++++++++ octavia/db/models.py | 1 + octavia/tests/functional/api/v2/test_pool.py | 93 +++++++++++++++++++ octavia/tests/functional/db/test_models.py | 3 +- .../tests/functional/db/test_repositories.py | 14 ++- .../amphora_driver/test_amphora_driver.py | 7 +- .../unit/api/drivers/sample_data_models.py | 6 +- .../common/jinja/haproxy/test_jinja_cfg.py | 8 +- .../common/sample_configs/sample_configs.py | 26 ++++-- ...Add-pool-tls_enabled-f189677c0e13c447.yaml | 5 + 27 files changed, 242 insertions(+), 46 deletions(-) create mode 100644 octavia/db/migration/alembic_migrations/versions/a7f187cd221f_add_tls_boolean_type_for_reencryption.py create mode 100644 releasenotes/notes/Add-pool-tls_enabled-f189677c0e13c447.yaml diff --git a/api-ref/source/parameters.yaml b/api-ref/source/parameters.yaml index 9c2fd4431f..ee9ff8be74 100644 --- a/api-ref/source/parameters.yaml +++ b/api-ref/source/parameters.yaml @@ -1374,6 +1374,22 @@ tls_container_ref-optional: min_version: 2.8 required: false type: string +tls_enabled: + description: | + When ``true`` connections to backend member servers will use TLS + encryption. Default is ``false``. + in: body + min_version: 2.8 + required: true + type: boolean +tls_enabled-optional: + description: | + When ``true`` connections to backend member servers will use TLS + encryption. Default is ``false``. + in: body + min_version: 2.8 + required: false + type: boolean total_connections: description: | The total connections handled. diff --git a/api-ref/source/v2/examples/pool-create-curl b/api-ref/source/v2/examples/pool-create-curl index 383dfb87c9..1996984b9d 100644 --- a/api-ref/source/v2/examples/pool-create-curl +++ b/api-ref/source/v2/examples/pool-create-curl @@ -1 +1 @@ -curl -X POST -H "Content-Type: application/json" -H "X-Auth-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 +curl -X POST -H "Content-Type: application/json" -H "X-Auth-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","tls_enabled":true}}' http://198.51.100.10:9876/v2/lbaas/pools diff --git a/api-ref/source/v2/examples/pool-create-request.json b/api-ref/source/v2/examples/pool-create-request.json index 35818ec68d..60ac35f6f5 100644 --- a/api-ref/source/v2/examples/pool-create-request.json +++ b/api-ref/source/v2/examples/pool-create-request.json @@ -13,6 +13,7 @@ "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" + "crl_container_ref": "http://198.51.100.10:9311/v1/containers/6faf0a01-6892-454c-aaac-650282820c0b", + "tls_enabled": true } } diff --git a/api-ref/source/v2/examples/pool-create-response.json b/api-ref/source/v2/examples/pool-create-response.json index 288030b357..7605186758 100644 --- a/api-ref/source/v2/examples/pool-create-response.json +++ b/api-ref/source/v2/examples/pool-create-response.json @@ -30,6 +30,7 @@ "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" + "crl_container_ref": "http://198.51.100.10:9311/v1/containers/6faf0a01-6892-454c-aaac-650282820c0b", + "tls_enabled": true } } diff --git a/api-ref/source/v2/examples/pool-show-response.json b/api-ref/source/v2/examples/pool-show-response.json index 288030b357..ed29cc0d9f 100644 --- a/api-ref/source/v2/examples/pool-show-response.json +++ b/api-ref/source/v2/examples/pool-show-response.json @@ -30,6 +30,7 @@ "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" + "crl_container_ref": "http://198.51.100.10:9311/v1/containers/6faf0a01-6892-454c-aaac-650282820c0b", + "tls_enabled": false } } diff --git a/api-ref/source/v2/examples/pool-update-curl b/api-ref/source/v2/examples/pool-update-curl index a68603c15e..01ae74257d 100644 --- a/api-ref/source/v2/examples/pool-update-curl +++ b/api-ref/source/v2/examples/pool-update-curl @@ -1 +1 @@ -curl -X PUT -H "Content-Type: application/json" -H "X-Auth-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 +curl -X PUT -H "Content-Type: application/json" -H "X-Auth-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,"tls_enabled":false}}' http://198.51.100.10:9876/v2/lbaas/pools/4029d267-3983-4224-a3d0-afb3fe16a2cd diff --git a/api-ref/source/v2/examples/pool-update-request.json b/api-ref/source/v2/examples/pool-update-request.json index 81d1140690..5e98da33c1 100644 --- a/api-ref/source/v2/examples/pool-update-request.json +++ b/api-ref/source/v2/examples/pool-update-request.json @@ -9,6 +9,7 @@ "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 + "crl_container_ref": null, + "tls_enabled": false } } diff --git a/api-ref/source/v2/examples/pool-update-response.json b/api-ref/source/v2/examples/pool-update-response.json index d095734f28..a35f8eb903 100644 --- a/api-ref/source/v2/examples/pool-update-response.json +++ b/api-ref/source/v2/examples/pool-update-response.json @@ -30,6 +30,7 @@ "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 + "crl_container_ref": null, + "tls_enabled": false } } diff --git a/api-ref/source/v2/examples/pools-list-response.json b/api-ref/source/v2/examples/pools-list-response.json index 371942b688..a41a14a8e0 100644 --- a/api-ref/source/v2/examples/pools-list-response.json +++ b/api-ref/source/v2/examples/pools-list-response.json @@ -36,7 +36,8 @@ "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" + "crl_container_ref": "http://198.51.100.10:9311/v1/containers/6faf0a01-6892-454c-aaac-650282820c0b", + "tls_enabled": true } ] } diff --git a/api-ref/source/v2/pool.inc b/api-ref/source/v2/pool.inc index 74358df996..f57ec983b2 100644 --- a/api-ref/source/v2/pool.inc +++ b/api-ref/source/v2/pool.inc @@ -64,6 +64,7 @@ Response Parameters - session_persistence: session_persistence - tags: tags - tls_container_ref: tls_container_ref + - tls_enabled: tls_enabled - updated_at: updated_at Response Example @@ -174,6 +175,7 @@ Request - protocol: protocol-pools - session_persistence: session_persistence-optional - tags: tags-optional + - tls_enabled: tls_enabled-optional - tls_container_ref: tls_container_ref-optional .. _session_persistence: @@ -254,6 +256,7 @@ Response Parameters - provisioning_status: provisioning_status - session_persistence: session_persistence - tags: tags + - tls_enabled: tls_enabled - tls_container_ref: tls_container_ref - updated_at: updated_at @@ -324,6 +327,7 @@ Response Parameters - provisioning_status: provisioning_status - session_persistence: session_persistence - tags: tags + - tls_enabled: tls_enabled - tls_container_ref: tls_container_ref - updated_at: updated_at @@ -375,6 +379,7 @@ Request - pool_id: path-pool-id - session_persistence: session_persistence-optional - tags: tags-optional + - tls_enabled: tls_enabled-optional - tls_container_ref: tls_container_ref-optional Request Example @@ -412,6 +417,7 @@ Response Parameters - provisioning_status: provisioning_status - session_persistence: session_persistence - tags: tags + - tls_enabled: tls_enabled - tls_container_ref: tls_container_ref - updated_at: updated_at diff --git a/doc/source/contributor/guides/providers.rst b/doc/source/contributor/guides/providers.rst index 163322a1ba..f0f439ed93 100644 --- a/doc/source/contributor/guides/providers.rst +++ b/doc/source/contributor/guides/providers.rst @@ -691,6 +691,9 @@ contain the following: | tls_container_ref | string | The reference to the secrets | | | | container. | +-----------------------+--------+------------------------------------------+ +| tls_enabled | bool | True when backend re-encryption is | +| | | enabled. | ++-----------------------+--------+------------------------------------------+ Delete ^^^^^^ @@ -754,6 +757,9 @@ contain the following: | tls_container_ref | string | The reference to the secrets | | | | container. | +-----------------------+--------+------------------------------------------+ +| tls_enabled | bool | True when backend re-encryption is | +| | | enabled. | ++-----------------------+--------+------------------------------------------+ The pool will be in the ``PENDING_UPDATE`` provisioning_status when it is passed to the driver. The driver will update the provisioning_status of the diff --git a/octavia/api/drivers/data_models.py b/octavia/api/drivers/data_models.py index d1e0be6494..678b9bf097 100644 --- a/octavia/api/drivers/data_models.py +++ b/octavia/api/drivers/data_models.py @@ -173,7 +173,7 @@ class Pool(BaseDataModel): session_persistence=Unset, tls_container_ref=Unset, tls_container_data=Unset, ca_tls_container_ref=Unset, ca_tls_container_data=Unset, crl_container_ref=Unset, - crl_container_data=Unset): + crl_container_data=Unset, tls_enabled=Unset): self.admin_state_up = admin_state_up self.description = description @@ -192,6 +192,7 @@ class Pool(BaseDataModel): self.ca_tls_container_data = ca_tls_container_data self.crl_container_ref = crl_container_ref self.crl_container_data = crl_container_data + self.tls_enabled = tls_enabled class Member(BaseDataModel): diff --git a/octavia/api/v2/controllers/load_balancer.py b/octavia/api/v2/controllers/load_balancer.py index 34afebb166..34ed71f521 100644 --- a/octavia/api/v2/controllers/load_balancer.py +++ b/octavia/api/v2/controllers/load_balancer.py @@ -432,8 +432,9 @@ class LoadBalancersController(base.BaseController): detail='Pools must be named when creating a fully ' 'populated loadbalancer.') # If a pool has more than a name, assume it's a full specification - # (but use >2 because it will also have "enabled" as default) - if default_pool and len(default_pool) > 2: + # (but use >3 because it will also have "enabled" and "tls_enabled" + # as default) + if default_pool and len(default_pool) > 3: pools.append(default_pool) l['default_pool'] = {'name': pool_name} # Otherwise, it's a reference and we record it and move on @@ -450,8 +451,9 @@ class LoadBalancersController(base.BaseController): detail='Pools must be named when creating a fully ' 'populated loadbalancer.') # If a pool has more than a name, assume it's a full spec - # (but use >2 because it will also have "enabled" as default) - if redirect_pool and len(redirect_pool) > 2: + # (but use >2 because it will also have "enabled" and + # "tls_enabled" as default) + if redirect_pool and len(redirect_pool) > 3: pool_name = redirect_pool['name'] policy['redirect_pool'] = {'name': pool_name} pools.append(redirect_pool) diff --git a/octavia/api/v2/types/pool.py b/octavia/api/v2/types/pool.py index d27ab1de42..0ddd9733d7 100644 --- a/octavia/api/v2/types/pool.py +++ b/octavia/api/v2/types/pool.py @@ -82,6 +82,7 @@ class PoolResponse(BasePoolType): tls_container_ref = wtypes.wsattr(wtypes.StringType()) ca_tls_container_ref = wtypes.wsattr(wtypes.StringType()) crl_container_ref = wtypes.wsattr(wtypes.StringType()) + tls_enabled = wtypes.wsattr(bool) @classmethod def from_data_model(cls, data_model, children=False): @@ -156,6 +157,7 @@ class PoolPOST(BasePoolType): 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)) + tls_enabled = wtypes.wsattr(bool, default=False) class PoolRootPOST(types.BaseType): @@ -174,6 +176,7 @@ class PoolPUT(BasePoolType): 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)) + tls_enabled = wtypes.wsattr(bool) class PoolRootPut(types.BaseType): @@ -195,6 +198,7 @@ class PoolSingleCreate(BasePoolType): 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)) + tls_enabled = wtypes.wsattr(bool, default=False) class PoolStatusResponse(BasePoolType): diff --git a/octavia/common/data_models.py b/octavia/common/data_models.py index ef93228853..300ec87b00 100644 --- a/octavia/common/data_models.py +++ b/octavia/common/data_models.py @@ -264,7 +264,8 @@ class Pool(BaseDataModel): load_balancer=None, listeners=None, l7policies=None, created_at=None, updated_at=None, provisioning_status=None, tags=None, tls_certificate_id=None, - ca_tls_certificate_id=None, crl_container_id=None): + ca_tls_certificate_id=None, crl_container_id=None, + tls_enabled=None): self.id = id self.project_id = project_id self.name = name @@ -287,6 +288,7 @@ class Pool(BaseDataModel): self.tls_certificate_id = tls_certificate_id self.ca_tls_certificate_id = ca_tls_certificate_id self.crl_container_id = crl_container_id + self.tls_enabled = tls_enabled def update(self, update_dict): for key, value in update_dict.items(): diff --git a/octavia/common/jinja/haproxy/jinja_cfg.py b/octavia/common/jinja/haproxy/jinja_cfg.py index c66c8dcf82..70e46e91e3 100644 --- a/octavia/common/jinja/haproxy/jinja_cfg.py +++ b/octavia/common/jinja/haproxy/jinja_cfg.py @@ -297,7 +297,8 @@ class JinjaTemplater(object): constants.HTTP_REUSE: feature_compatibility.get( constants.HTTP_REUSE, False), 'ca_tls_path': '', - 'crl_path': '' + 'crl_path': '', + 'tls_enabled': pool.tls_enabled } members = [self._transform_member(x, feature_compatibility) for x in pool.members] diff --git a/octavia/common/jinja/haproxy/templates/macros.j2 b/octavia/common/jinja/haproxy/templates/macros.j2 index 427bfb2c9e..50fbc8337e 100644 --- a/octavia/common/jinja/haproxy/templates/macros.j2 +++ b/octavia/common/jinja/haproxy/templates/macros.j2 @@ -210,28 +210,26 @@ frontend {{ listener.id }} {% else %} {% set member_enabled_opt = " disabled" %} {% endif %} - {% if pool.client_cert or pool.ca_cert %} + {% if pool.tls_enabled %} {% set def_opt_prefix = " ssl" %} {% else %} {% set def_opt_prefix = "" %} {% endif %} - {% if pool.client_cert %} + {% if pool.client_cert and pool.tls_enabled %} {% set def_crt_opt = " crt %s"|format(pool.client_cert) %} {% else %} {% set def_crt_opt = "" %} {% endif %} - {% if pool.ca_cert %} + {% if pool.ca_cert and pool.tls_enabled %} {% 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 = "" %} + {% set def_verify_opt = "" %} {% endif %} - {% else %} - {% set ca_opt = "" %} - {% set def_verify_opt = "" %} - {% set crl_opt = "" %} + {% elif pool.tls_enabled %} + {% set def_verify_opt = " verify none" %} {% 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, diff --git a/octavia/db/migration/alembic_migrations/versions/a7f187cd221f_add_tls_boolean_type_for_reencryption.py b/octavia/db/migration/alembic_migrations/versions/a7f187cd221f_add_tls_boolean_type_for_reencryption.py new file mode 100644 index 0000000000..9fddf0afea --- /dev/null +++ b/octavia/db/migration/alembic_migrations/versions/a7f187cd221f_add_tls_boolean_type_for_reencryption.py @@ -0,0 +1,37 @@ +# Copyright 2018 Huawei +# +# 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. +# + +"""Add tls boolean type for backend re-encryption + +Revision ID: a7f187cd221f +Revises: 74aae261694c +Create Date: 2018-11-01 20:47:52.405865 + +""" + + +from alembic import op +import sqlalchemy as sa + +# revision identifiers, used by Alembic. +revision = 'a7f187cd221f' +down_revision = '74aae261694c' + + +def upgrade(): + op.add_column(u'pool', + sa.Column(u'tls_enabled', sa.Boolean(), + server_default=sa.sql.expression.false(), + nullable=False)) diff --git a/octavia/db/models.py b/octavia/db/models.py index 4b352b4be0..42076ce131 100644 --- a/octavia/db/models.py +++ b/octavia/db/models.py @@ -331,6 +331,7 @@ class Pool(base_models.BASE, base_models.IdMixin, base_models.ProjectMixin, 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) + tls_enabled = sa.Column(sa.Boolean, default=False, nullable=False) # This property should be a unique list of any listeners that reference # this pool as its default_pool and any listeners referenced by enabled diff --git a/octavia/tests/functional/api/v2/test_pool.py b/octavia/tests/functional/api/v2/test_pool.py index 3ba601d990..154efb437e 100644 --- a/octavia/tests/functional/api/v2/test_pool.py +++ b/octavia/tests/functional/api/v2/test_pool.py @@ -859,6 +859,26 @@ class TestPool(base.BaseAPITest): pool_prov_status=constants.PENDING_CREATE, pool_op_status=constants.OFFLINE) + def test_create_with_tls_enabled_only(self): + api_pool = self.create_pool( + self.lb_id, + constants.PROTOCOL_HTTP, + constants.LB_ALGORITHM_ROUND_ROBIN, + listener_id=self.listener_id, + tls_enabled=True).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.assertTrue(api_pool.get('tls_enabled')) + self.assert_correct_status( + 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_tls_container_ref(self, mock_cert_data): tls_container_ref = uuidutils.generate_uuid() @@ -1315,6 +1335,79 @@ class TestPool(base.BaseAPITest): self.assert_correct_status( lb_id=self.udp_lb_id, listener_id=self.udp_listener_id) + def test_update_with_tls_enabled_only(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) + self.assertFalse(api_pool['tls_enabled']) + new_pool = {'tls_enabled': True} + 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.assertTrue(response.get('tls_enabled')) + self.assertIsNotNone(response.get('created_at')) + self.assertIsNotNone(response.get('updated_at')) + self.assert_correct_status( + lb_id=self.lb_id, listener_id=self.listener_id, + pool_id=response.get('id')) + + @mock.patch('octavia.common.tls_utils.cert_parser.load_certificates_data') + def test_update_with_tls_enabled_only_on_pool_certs_exist( + self, mock_cert_data): + tls_container_ref = uuidutils.generate_uuid() + ca_tls_container_ref = uuidutils.generate_uuid() + crl_container_ref = uuidutils.generate_uuid() + pool_cert = data_models.TLSContainer(certificate='pool cert') + mock_cert_data.return_value = {'tls_cert': pool_cert, + 'sni_certs': [], + 'client_ca_cert': None} + 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] + api_pool = self.create_pool( + self.lb_id, + constants.PROTOCOL_HTTP, + constants.LB_ALGORITHM_ROUND_ROBIN, + listener_id=self.listener_id, + tls_container_ref=tls_container_ref, + 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) + self.assertFalse(api_pool['tls_enabled']) + + new_pool = {'tls_enabled': True} + self.cert_manager_mock().get_cert.reset_mock() + self.cert_manager_mock().get_secret.reset_mock() + 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.assertTrue(response.get('tls_enabled')) + self.assertIsNotNone(response.get('created_at')) + self.assertIsNotNone(response.get('updated_at')) + self.assert_correct_status( + lb_id=self.lb_id, listener_id=self.listener_id, + pool_id=response.get('id')) + @mock.patch( 'octavia.common.tls_utils.cert_parser.load_certificates_data') def test_update_with_tls_container_ref(self, mock_cert_data): diff --git a/octavia/tests/functional/db/test_models.py b/octavia/tests/functional/db/test_models.py index 3b91b04037..c5df0a8811 100644 --- a/octavia/tests/functional/db/test_models.py +++ b/octavia/tests/functional/db/test_models.py @@ -87,7 +87,8 @@ class ModelTestMixin(object): 'lb_algorithm': constants.LB_ALGORITHM_LEAST_CONNECTIONS, 'provisioning_status': constants.ACTIVE, 'operating_status': constants.ONLINE, - 'enabled': True} + 'enabled': True, + 'tls_enabled': False} kwargs.update(overrides) return self._insert(session, models.Pool, kwargs) diff --git a/octavia/tests/functional/db/test_repositories.py b/octavia/tests/functional/db/test_repositories.py index 4efed12a8e..9059a40c3a 100644 --- a/octavia/tests/functional/db/test_repositories.py +++ b/octavia/tests/functional/db/test_repositories.py @@ -180,7 +180,8 @@ class AllRepositoriesTest(base.OctaviaDBTestBase): 'id': uuidutils.generate_uuid(), 'provisioning_status': constants.ACTIVE, 'tags': ['test_tag'], - 'tls_certificate_id': uuidutils.generate_uuid()} + 'tls_certificate_id': uuidutils.generate_uuid(), + 'tls_enabled': False} pool_dm = self.repos.create_pool_on_load_balancer( self.session, pool, listener_id=self.listener.id) pool_dm_dict = pool_dm.to_dict() @@ -211,7 +212,8 @@ class AllRepositoriesTest(base.OctaviaDBTestBase): 'id': uuidutils.generate_uuid(), 'provisioning_status': constants.ACTIVE, 'tags': ['test_tag'], - 'tls_certificate_id': uuidutils.generate_uuid()} + 'tls_certificate_id': uuidutils.generate_uuid(), + 'tls_enabled': False} sp = {'type': constants.SESSION_PERSISTENCE_HTTP_COOKIE, 'cookie_name': 'cookie_monster', 'pool_id': pool['id'], @@ -254,7 +256,7 @@ class AllRepositoriesTest(base.OctaviaDBTestBase): 'project_id': uuidutils.generate_uuid(), 'id': uuidutils.generate_uuid(), 'provisioning_status': constants.ACTIVE, - 'tags': ['test_tag']} + 'tags': ['test_tag'], 'tls_enabled': False} pool_dm = self.repos.create_pool_on_load_balancer( self.session, pool, listener_id=self.listener.id) update_pool = {'protocol': constants.PROTOCOL_TCP, 'name': 'up_pool'} @@ -288,7 +290,8 @@ class AllRepositoriesTest(base.OctaviaDBTestBase): 'id': uuidutils.generate_uuid(), 'provisioning_status': constants.ACTIVE, 'tags': ['test_tag'], - 'tls_certificate_id': uuidutils.generate_uuid()} + 'tls_certificate_id': uuidutils.generate_uuid(), + 'tls_enabled': False} sp = {'type': constants.SESSION_PERSISTENCE_HTTP_COOKIE, 'cookie_name': 'cookie_monster', 'pool_id': pool['id'], @@ -391,7 +394,8 @@ class AllRepositoriesTest(base.OctaviaDBTestBase): 'enabled': True, 'operating_status': constants.ONLINE, 'project_id': uuidutils.generate_uuid(), 'id': uuidutils.generate_uuid(), - 'provisioning_status': constants.ACTIVE} + 'provisioning_status': constants.ACTIVE, + 'tls_enabled': False} pool_dm = self.repos.create_pool_on_load_balancer( self.session, pool, listener_id=self.listener.id) update_pool = {'tls_certificate_id': uuidutils.generate_uuid()} diff --git a/octavia/tests/unit/api/drivers/amphora_driver/test_amphora_driver.py b/octavia/tests/unit/api/drivers/amphora_driver/test_amphora_driver.py index 4e981dbfb2..427542b8e6 100644 --- a/octavia/tests/unit/api/drivers/amphora_driver/test_amphora_driver.py +++ b/octavia/tests/unit/api/drivers/amphora_driver/test_amphora_driver.py @@ -178,8 +178,11 @@ class TestAmphoraDriver(base.TestRpc): old_provider_pool = driver_dm.Pool( pool_id=self.sample_data.pool1_id) provider_pool = driver_dm.Pool( - pool_id=self.sample_data.pool1_id, name='Great pool') - pool_dict = {'name': 'Great pool'} + pool_id=self.sample_data.pool1_id, name='Great pool', + admin_state_up=True, tls_enabled=True) + pool_dict = {'name': 'Great pool', + 'enabled': True, + 'tls_enabled': True} self.amp_driver.pool_update(old_provider_pool, provider_pool) payload = {consts.POOL_ID: self.sample_data.pool1_id, consts.POOL_UPDATES: pool_dict} diff --git a/octavia/tests/unit/api/drivers/sample_data_models.py b/octavia/tests/unit/api/drivers/sample_data_models.py index e9cfa1ceff..60edfd50ef 100644 --- a/octavia/tests/unit/api/drivers/sample_data_models.py +++ b/octavia/tests/unit/api/drivers/sample_data_models.py @@ -214,7 +214,8 @@ class SampleDriverDataModels(object): 'ca_tls_certificate_id': self.pool_ca_container_ref, 'crl_container_id': - self.pool_crl_container_ref} + self.pool_crl_container_ref, + 'tls_enabled': True} self.test_pool1_dict.update(self._common_test_dict) @@ -257,7 +258,8 @@ class SampleDriverDataModels(object): 'ca_tls_container_ref': self.pool_ca_container_ref, 'ca_tls_container_data': pool_ca_file_content, 'crl_container_ref': self.pool_crl_container_ref, - 'crl_container_data': pool_crl_file_content + 'crl_container_data': pool_crl_file_content, + 'tls_enabled': True } self.provider_pool2_dict = copy.deepcopy(self.provider_pool1_dict) diff --git a/octavia/tests/unit/common/jinja/haproxy/test_jinja_cfg.py b/octavia/tests/unit/common/jinja/haproxy/test_jinja_cfg.py index 7663f6aa24..845fbdee2d 100644 --- a/octavia/tests/unit/common/jinja/haproxy/test_jinja_cfg.py +++ b/octavia/tests/unit/common/jinja/haproxy/test_jinja_cfg.py @@ -768,10 +768,11 @@ class TestHaproxyCfg(base.TestCase): "check inter 30s fall 3 rise 2 cookie sample_member_id_2 " "{opts}\n\n").format( maxconn=constants.HAPROXY_MAX_MAXCONN, - opts="%s %s %s" % ("ssl", "crt", cert_file_path)) + opts="ssl crt %s verify none" % cert_file_path) rendered_obj = self.jinja_cfg.render_loadbalancer_obj( sample_configs.sample_amphora_tuple(), - sample_configs.sample_listener_tuple(pool_cert=True), + sample_configs.sample_listener_tuple( + pool_cert=True, tls_enabled=True), pool_tls_certs={ 'sample_pool_id_1': {'client_cert': cert_file_path, @@ -810,7 +811,8 @@ class TestHaproxyCfg(base.TestCase): rendered_obj = self.jinja_cfg.render_loadbalancer_obj( sample_configs.sample_amphora_tuple(), sample_configs.sample_listener_tuple( - pool_cert=True, pool_ca_cert=True, pool_crl=True), + pool_cert=True, pool_ca_cert=True, pool_crl=True, + tls_enabled=True), pool_tls_certs={ 'sample_pool_id_1': {'client_cert': pool_client_cert, diff --git a/octavia/tests/unit/common/sample_configs/sample_configs.py b/octavia/tests/unit/common/sample_configs/sample_configs.py index 533abfe501..47be649c11 100644 --- a/octavia/tests/unit/common/sample_configs/sample_configs.py +++ b/octavia/tests/unit/common/sample_configs/sample_configs.py @@ -121,7 +121,8 @@ RET_POOL_1 = { 'stick_size': '10k', constants.HTTP_REUSE: False, 'ca_tls_path': '', - 'crl_path': ''} + 'crl_path': '', + 'tls_enabled': False} RET_POOL_2 = { 'id': 'sample_pool_id_2', @@ -135,7 +136,8 @@ RET_POOL_2 = { 'stick_size': '10k', constants.HTTP_REUSE: False, 'ca_tls_path': '', - 'crl_path': ''} + 'crl_path': '', + 'tls_enabled': False} RET_DEF_TLS_CONT = {'id': 'cont_id_1', 'allencompassingpem': 'imapem', @@ -534,7 +536,8 @@ def sample_listener_tuple(proto=None, monitor=True, alloc_default_pool=True, timeout_tcp_inspect=0, client_ca_cert=False, client_crl_cert=False, ssl_type_l7=False, pool_cert=False, - pool_ca_cert=False, pool_crl=False): + pool_ca_cert=False, pool_crl=False, + tls_enabled=False): proto = 'HTTP' if proto is None else proto if be_proto is None: be_proto = 'HTTP' if proto is 'TERMINATED_HTTPS' else proto @@ -560,14 +563,14 @@ def sample_listener_tuple(proto=None, monitor=True, alloc_default_pool=True, persistence_cookie=persistence_cookie, monitor_ip_port=monitor_ip_port, monitor_proto=monitor_proto, pool_cert=pool_cert, pool_ca_cert=pool_ca_cert, - pool_crl=pool_crl), + pool_crl=pool_crl, tls_enabled=tls_enabled), sample_pool_tuple( proto=be_proto, monitor=monitor, persistence=persistence, persistence_type=persistence_type, persistence_cookie=persistence_cookie, sample_pool=2, monitor_ip_port=monitor_ip_port, monitor_proto=monitor_proto, pool_cert=pool_cert, pool_ca_cert=pool_ca_cert, - pool_crl=pool_crl)] + pool_crl=pool_crl, tls_enabled=tls_enabled)] l7policies = [ sample_l7policy_tuple('sample_l7policy_id_1', sample_policy=1), sample_l7policy_tuple('sample_l7policy_id_2', sample_policy=2), @@ -588,7 +591,7 @@ def sample_listener_tuple(proto=None, monitor=True, alloc_default_pool=True, monitor_ip_port=monitor_ip_port, monitor_proto=monitor_proto, backup_member=backup_member, disabled_member=disabled_member, pool_cert=pool_cert, pool_ca_cert=pool_ca_cert, - pool_crl=pool_crl)] + pool_crl=pool_crl, tls_enabled=tls_enabled)] l7policies = [] return in_listener( id='sample_listener_id_1', @@ -608,7 +611,8 @@ def sample_listener_tuple(proto=None, monitor=True, alloc_default_pool=True, monitor_proto=monitor_proto, pool_cert=pool_cert, pool_ca_cert=pool_ca_cert, - pool_crl=pool_crl + pool_crl=pool_crl, + tls_enabled=tls_enabled ) if alloc_default_pool else '', connection_limit=connection_limit, tls_certificate_id='cont_id_1' if tls else '', @@ -683,14 +687,15 @@ def sample_pool_tuple(proto=None, monitor=True, persistence=True, sample_pool=1, monitor_ip_port=False, monitor_proto=None, backup_member=False, disabled_member=False, has_http_reuse=True, - pool_cert=False, pool_ca_cert=False, pool_crl=False): + pool_cert=False, pool_ca_cert=False, pool_crl=False, + tls_enabled=False): proto = 'HTTP' if proto is None else proto monitor_proto = proto if monitor_proto is None else monitor_proto in_pool = collections.namedtuple( 'pool', 'id, protocol, lb_algorithm, members, health_monitor, ' 'session_persistence, enabled, operating_status, ' 'tls_certificate_id, ca_tls_certificate_id, ' - 'crl_container_id, ' + constants.HTTP_REUSE) + 'crl_container_id, tls_enabled, ' + constants.HTTP_REUSE) if (proto == constants.PROTOCOL_UDP and persistence_type == constants.SESSION_PERSISTENCE_SOURCE_IP): kwargs = {'persistence_type': persistence_type, @@ -729,7 +734,8 @@ def sample_pool_tuple(proto=None, monitor=True, persistence=True, operating_status='ACTIVE', has_http_reuse=has_http_reuse, tls_certificate_id='pool_cont_1' if pool_cert else None, ca_tls_certificate_id='pool_ca_1' if pool_ca_cert else None, - crl_container_id='pool_crl' if pool_crl else None) + crl_container_id='pool_crl' if pool_crl else None, + tls_enabled=tls_enabled) def sample_member_tuple(id, ip, enabled=True, operating_status='ACTIVE', diff --git a/releasenotes/notes/Add-pool-tls_enabled-f189677c0e13c447.yaml b/releasenotes/notes/Add-pool-tls_enabled-f189677c0e13c447.yaml new file mode 100644 index 0000000000..5b1f599df5 --- /dev/null +++ b/releasenotes/notes/Add-pool-tls_enabled-f189677c0e13c447.yaml @@ -0,0 +1,5 @@ +--- +features: + - | + You can now enable TLS backend re-encryption for connections to member + servers by enabling tls_enabled option on pools.