From daae6d1fcd432d0885880709dcbfd06140715644 Mon Sep 17 00:00:00 2001 From: Luke Tollefson <lukewtollefson@gmail.com> Date: Wed, 8 Apr 2020 15:36:59 -0500 Subject: [PATCH] Add ciphers options for listeners and pools Added an option to specify the TLS ciphers used by listeners and pools. It is specifed through a string in OpenSSL syntax. Change-Id: Icead9106b06ae7610301a121e51dc5fe1a0e4056 Co-authored-by: Steven Glasford <stevenglasford@gmail.com> Story: 2006627 Task: 37193 --- octavia_dashboard/api/rest/lbaasv2.py | 16 +++++-- .../lbaasv2/listeners/details/detail.html | 3 +- .../lbaasv2/listeners/listeners.module.js | 3 +- .../project/lbaasv2/pools/details/detail.html | 2 +- .../project/lbaasv2/pools/pools.module.js | 3 +- .../workflow/listener/listener.controller.js | 1 + .../workflow/listener/listener.help.html | 10 +++++ .../lbaasv2/workflow/listener/listener.html | 17 +++++++ .../project/lbaasv2/workflow/model.service.js | 9 +++- .../lbaasv2/workflow/model.service.spec.js | 4 +- .../lbaasv2/workflow/pool/pool.controller.js | 45 +++++++++++++++++++ .../workflow/pool/pool.controller.spec.js | 40 +++++++++++++++++ .../lbaasv2/workflow/pool/pool.help.html | 10 +++++ .../project/lbaasv2/workflow/pool/pool.html | 19 +++++++- ...-listeners-and-pools-8440eaf5a551c44c.yaml | 5 +++ 15 files changed, 174 insertions(+), 13 deletions(-) create mode 100644 octavia_dashboard/static/dashboard/project/lbaasv2/workflow/pool/pool.controller.js create mode 100644 octavia_dashboard/static/dashboard/project/lbaasv2/workflow/pool/pool.controller.spec.js create mode 100644 releasenotes/notes/add-ciphers-options-for-listeners-and-pools-8440eaf5a551c44c.yaml diff --git a/octavia_dashboard/api/rest/lbaasv2.py b/octavia_dashboard/api/rest/lbaasv2.py index ff1349c1..d8a6d94b 100644 --- a/octavia_dashboard/api/rest/lbaasv2.py +++ b/octavia_dashboard/api/rest/lbaasv2.py @@ -185,7 +185,9 @@ def create_listener(request, **kwargs): timeout_member_connect=data['listener'].get('timeout_member_connect'), timeout_member_data=data['listener'].get('timeout_member_data'), timeout_tcp_inspect=data['listener'].get('timeout_tcp_inspect'), - allowed_cidrs=data['listener'].get('allowed_cidrs') + allowed_cidrs=data['listener'].get('allowed_cidrs'), + # Replace empty string by None (uses default tls cipher string) + tls_ciphers=data['listener'].get('tls_ciphers') or None, ) if data.get('pool'): @@ -252,7 +254,9 @@ def create_pool(request, **kwargs): loadbalancer_id=kwargs['loadbalancer_id'], name=data['pool'].get('name'), description=data['pool'].get('description'), - admin_state_up=data['pool'].get('admin_state_up') + admin_state_up=data['pool'].get('admin_state_up'), + # Replace empty string by None (uses default tls cipher string) + tls_ciphers=data['pool'].get('tls_ciphers') or None, ) if data.get('members'): @@ -458,7 +462,9 @@ def update_listener(request, **kwargs): timeout_member_connect=data['listener'].get('timeout_member_connect'), timeout_member_data=data['listener'].get('timeout_member_data'), timeout_tcp_inspect=data['listener'].get('timeout_tcp_inspect'), - allowed_cidrs=data['listener'].get('allowed_cidrs') + allowed_cidrs=data['listener'].get('allowed_cidrs'), + # Replace empty string by None (uses default tls cipher string) + tls_ciphers=data['listener'].get('tls_ciphers') or None, ) if data.get('pool'): @@ -527,7 +533,9 @@ def update_pool(request, **kwargs): session_persistence=data['pool'].get('session_persistence'), name=data['pool'].get('name'), description=data['pool'].get('description'), - admin_state_up=data['pool'].get('admin_state_up') + admin_state_up=data['pool'].get('admin_state_up'), + # Replace empty string by None (uses default tls cipher string) + tls_ciphers=data['pool'].get('tls_ciphers') or None, ) # Assemble the lists of member id's to add and remove, if any exist diff --git a/octavia_dashboard/static/dashboard/project/lbaasv2/listeners/details/detail.html b/octavia_dashboard/static/dashboard/project/lbaasv2/listeners/details/detail.html index 522ba568..ab933e37 100644 --- a/octavia_dashboard/static/dashboard/project/lbaasv2/listeners/details/detail.html +++ b/octavia_dashboard/static/dashboard/project/lbaasv2/listeners/details/detail.html @@ -53,7 +53,8 @@ 'id', 'name', 'description', 'project_id', 'created_at', 'updated_at', 'connection_limit', 'insert_headers', 'default_pool_id', 'timeout_client_data', 'timeout_member_connect', - 'timeout_member_data', 'timeout_tcp_inspect', 'allowed_cidrs' + 'timeout_member_data', 'timeout_tcp_inspect', 'allowed_cidrs', + 'tls_ciphers' ]]"> </hz-resource-property-list> </div> diff --git a/octavia_dashboard/static/dashboard/project/lbaasv2/listeners/listeners.module.js b/octavia_dashboard/static/dashboard/project/lbaasv2/listeners/listeners.module.js index fb9be478..befa6ede 100644 --- a/octavia_dashboard/static/dashboard/project/lbaasv2/listeners/listeners.module.js +++ b/octavia_dashboard/static/dashboard/project/lbaasv2/listeners/listeners.module.js @@ -185,7 +185,8 @@ timeout_member_connect: gettext('Member Connect Timeout'), timeout_member_data: gettext('Member Data Timeout'), timeout_tcp_inspect: gettext('TCP Inspect Timeout'), - load_balancers: gettext('Load Balancers') + load_balancers: gettext('Load Balancers'), + tls_ciphers: gettext('TLS Cipher String') }; } diff --git a/octavia_dashboard/static/dashboard/project/lbaasv2/pools/details/detail.html b/octavia_dashboard/static/dashboard/project/lbaasv2/pools/details/detail.html index 1b57f3d4..13a5be12 100644 --- a/octavia_dashboard/static/dashboard/project/lbaasv2/pools/details/detail.html +++ b/octavia_dashboard/static/dashboard/project/lbaasv2/pools/details/detail.html @@ -52,7 +52,7 @@ item="ctrl.pool" property-groups="[[ 'id', 'name', 'description', 'project_id', 'created_at', 'updated_at', - 'session_persistence', 'health_monitor_id']]"> + 'session_persistence', 'health_monitor_id', 'tls_ciphers']]"> </hz-resource-property-list> </div> </uib-tab> diff --git a/octavia_dashboard/static/dashboard/project/lbaasv2/pools/pools.module.js b/octavia_dashboard/static/dashboard/project/lbaasv2/pools/pools.module.js index 33f4e139..544380c3 100644 --- a/octavia_dashboard/static/dashboard/project/lbaasv2/pools/pools.module.js +++ b/octavia_dashboard/static/dashboard/project/lbaasv2/pools/pools.module.js @@ -175,7 +175,8 @@ }, loadbalancers: gettext('Load Balancers'), listeners: gettext('Listeners'), - members: gettext('Members') + members: gettext('Members'), + tls_ciphers: gettext('TLS Cipher String') }; } diff --git a/octavia_dashboard/static/dashboard/project/lbaasv2/workflow/listener/listener.controller.js b/octavia_dashboard/static/dashboard/project/lbaasv2/workflow/listener/listener.controller.js index c377dcef..736f3e65 100644 --- a/octavia_dashboard/static/dashboard/project/lbaasv2/workflow/listener/listener.controller.js +++ b/octavia_dashboard/static/dashboard/project/lbaasv2/workflow/listener/listener.controller.js @@ -48,6 +48,7 @@ 'The connection limit must be a number greater than or equal to -1.' ); ctrl.timeoutError = gettext('The timeout must be a number between 0 and 31536000000.'); + ctrl.tls_ciphersError = gettext('The cipher string must conform to OpenSSL syntax.'); //////////// diff --git a/octavia_dashboard/static/dashboard/project/lbaasv2/workflow/listener/listener.help.html b/octavia_dashboard/static/dashboard/project/lbaasv2/workflow/listener/listener.help.html index 862d9606..ee5df7a3 100644 --- a/octavia_dashboard/static/dashboard/project/lbaasv2/workflow/listener/listener.help.html +++ b/octavia_dashboard/static/dashboard/project/lbaasv2/workflow/listener/listener.help.html @@ -69,3 +69,13 @@ An empty list means allow from any. </translate> </p> +<p> + <strong translate>TLS Cipher String:</strong> + <translate> + A string of the allowed ciphers using the OpenSSL syntax. The syntax + is a colon separated list of the chiphers, ex. + "TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256:TLS_AES_128_GCM_SHA256" + Note, don't include quotation marks. An empty string sets the default TLS + Cipher String configured in Octavia. + </translate> +</p> diff --git a/octavia_dashboard/static/dashboard/project/lbaasv2/workflow/listener/listener.html b/octavia_dashboard/static/dashboard/project/lbaasv2/workflow/listener/listener.html index ac1ee680..bae57227 100644 --- a/octavia_dashboard/static/dashboard/project/lbaasv2/workflow/listener/listener.html +++ b/octavia_dashboard/static/dashboard/project/lbaasv2/workflow/listener/listener.html @@ -167,6 +167,23 @@ </div> </div> + <div class="row" ng-if="model.spec.listener.protocol === 'TERMINATED_HTTPS'"> + + <div class="col-xs-12 col-sm-8 col-md-6"> + <div class="form-group" + ng-class="{ 'has-error': listenerDetailsForm.tls_ciphers.$invalid && listenerDetailsForm.tls_ciphers.$dirty }"> + <label translate class="control-label" for="tls_ciphers">TLS Cipher String</label> + <textarea name="tls_ciphers" id="tls_ciphers" class="form-control" + ng-model="model.spec.listener.tls_ciphers" ng-pattern="/^([A-Z0-9_-]+:)*[A-Z0-9_-]+$/"> + </textarea> + <span class="help-block" ng-show="listenerDetailsForm.tls_ciphers.$invalid && listenerDetailsForm.tls_ciphers.$dirty"> + {$ ::ctrl.tls_ciphersError $} + </span> + </div> + </div> + + </div> + <h4 translate ng-if="model.spec.listener.protocol === 'HTTP' || model.spec.listener.protocol === 'TERMINATED_HTTPS'">Insert Headers</h4> <div class="row form-group" ng-if="model.spec.listener.protocol === 'HTTP' || model.spec.listener.protocol === 'TERMINATED_HTTPS'"> diff --git a/octavia_dashboard/static/dashboard/project/lbaasv2/workflow/model.service.js b/octavia_dashboard/static/dashboard/project/lbaasv2/workflow/model.service.js index 1ee3d6f8..c430803c 100644 --- a/octavia_dashboard/static/dashboard/project/lbaasv2/workflow/model.service.js +++ b/octavia_dashboard/static/dashboard/project/lbaasv2/workflow/model.service.js @@ -170,7 +170,8 @@ timeout_member_connect: 5000, timeout_member_data: 50000, timeout_tcp_inspect: 0, - allowed_cidrs: null + allowed_cidrs: null, + tls_ciphers: null }, l7policy: { id: null, @@ -201,7 +202,8 @@ type: null, cookie_name: null }, - admin_state_up: true + admin_state_up: true, + tls_ciphers: null }, monitor: { id: null, @@ -516,6 +518,7 @@ if (finalSpec.listener.protocol !== 'TERMINATED_HTTPS') { // Remove certificate containers if not using TERMINATED_HTTPS delete finalSpec.certificates; + delete finalSpec.listener.tls_ciphers; } else { var containers = []; angular.forEach(finalSpec.certificates, function(cert) { @@ -803,6 +806,7 @@ spec.timeout_member_data = listener.timeout_member_data; spec.timeout_tcp_inspect = listener.timeout_tcp_inspect; spec.allowed_cidrs = listener.allowed_cidrs; + spec.tls_ciphers = listener.tls_ciphers; } function setL7PolicySpec(l7policy) { @@ -837,6 +841,7 @@ spec.lb_algorithm = pool.lb_algorithm; spec.admin_state_up = pool.admin_state_up; spec.session_persistence = pool.session_persistence; + spec.tls_ciphers = pool.tls_ciphers; } function setMembersSpec(membersList) { diff --git a/octavia_dashboard/static/dashboard/project/lbaasv2/workflow/model.service.spec.js b/octavia_dashboard/static/dashboard/project/lbaasv2/workflow/model.service.spec.js index f38a0183..9b047c72 100644 --- a/octavia_dashboard/static/dashboard/project/lbaasv2/workflow/model.service.spec.js +++ b/octavia_dashboard/static/dashboard/project/lbaasv2/workflow/model.service.spec.js @@ -1298,10 +1298,10 @@ it('has the right number of properties', function() { expect(Object.keys(model.spec).length).toBe(11); expect(Object.keys(model.spec.loadbalancer).length).toBe(7); - expect(Object.keys(model.spec.listener).length).toBe(15); + expect(Object.keys(model.spec.listener).length).toBe(16); expect(Object.keys(model.spec.l7policy).length).toBe(8); expect(Object.keys(model.spec.l7rule).length).toBe(7); - expect(Object.keys(model.spec.pool).length).toBe(7); + expect(Object.keys(model.spec.pool).length).toBe(8); expect(Object.keys(model.spec.monitor).length).toBe(11); expect(model.spec.members).toEqual([]); }); diff --git a/octavia_dashboard/static/dashboard/project/lbaasv2/workflow/pool/pool.controller.js b/octavia_dashboard/static/dashboard/project/lbaasv2/workflow/pool/pool.controller.js new file mode 100644 index 00000000..d57867f9 --- /dev/null +++ b/octavia_dashboard/static/dashboard/project/lbaasv2/workflow/pool/pool.controller.js @@ -0,0 +1,45 @@ +/* + * Copyright 2020 Red Hat, Inc. + * + * 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. + */ +(function () { + 'use strict'; + + angular + .module('horizon.dashboard.project.lbaasv2') + .controller('PoolDetailsController', PoolDetailsController); + + PoolDetailsController.$inject = [ + '$scope', + 'horizon.framework.util.i18n.gettext' + ]; + + /** + * @ngdoc controller + * @name PoolDetailsController + * @description + * The `PoolDetailsController` controller provides functions for + * configuring the pool details step of the LBaaS wizard. + * @param $scope The angular scope object. + * @param gettext The horizon gettext function for translation. + * @returns undefined + */ + + function PoolDetailsController($scope, gettext) { + var ctrl = this; + + // Error text for invalid fields + ctrl.tls_ciphersError = gettext('The cipher string must conform to OpenSSL syntax.'); + } +})(); diff --git a/octavia_dashboard/static/dashboard/project/lbaasv2/workflow/pool/pool.controller.spec.js b/octavia_dashboard/static/dashboard/project/lbaasv2/workflow/pool/pool.controller.spec.js new file mode 100644 index 00000000..a96b975d --- /dev/null +++ b/octavia_dashboard/static/dashboard/project/lbaasv2/workflow/pool/pool.controller.spec.js @@ -0,0 +1,40 @@ +/* + * Copyright 2020 Red Hat, Inc. + * + * 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. + */ +(function() { + 'use strict'; + + describe('Pool Details Step', function() { + + beforeEach(module('horizon.framework.util.i18n')); + beforeEach(module('horizon.dashboard.project.lbaasv2')); + + describe('PoolDetailsController', function() { + var ctrl, scope; + + beforeEach(inject(function($controller, $rootScope) { + scope = $rootScope.$new(); + ctrl = $controller('PoolDetailsController', { + $scope: scope + }); + })); + + it('should define error messages for invalid fields', function() { + expect(ctrl.tls_ciphersError).toBeDefined(); + }); + + }); + }); +})(); diff --git a/octavia_dashboard/static/dashboard/project/lbaasv2/workflow/pool/pool.help.html b/octavia_dashboard/static/dashboard/project/lbaasv2/workflow/pool/pool.help.html index 6df334d5..3d04a07a 100644 --- a/octavia_dashboard/static/dashboard/project/lbaasv2/workflow/pool/pool.help.html +++ b/octavia_dashboard/static/dashboard/project/lbaasv2/workflow/pool/pool.help.html @@ -42,3 +42,13 @@ </li> </ul> </p> +<p> + <strong translate>TLS Cipher String:</strong> + <translate> + A string of the allowed ciphers using the OpenSSL syntax. The syntax + is a colon separated list of the chiphers, ex. + "TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256:TLS_AES_128_GCM_SHA256" + Note, don't include quotation marks. An empty string sets the default TLS + Cipher String configured in Octavia. + </translate> +</p> \ No newline at end of file diff --git a/octavia_dashboard/static/dashboard/project/lbaasv2/workflow/pool/pool.html b/octavia_dashboard/static/dashboard/project/lbaasv2/workflow/pool/pool.html index a30cb788..4f46eb69 100644 --- a/octavia_dashboard/static/dashboard/project/lbaasv2/workflow/pool/pool.html +++ b/octavia_dashboard/static/dashboard/project/lbaasv2/workflow/pool/pool.html @@ -1,4 +1,4 @@ -<div> +<div ng-controller="PoolDetailsController as ctrl"> <p translate>Provide the details for the pool.</p> <div class="row"> @@ -84,6 +84,23 @@ </div> + <div class="row"> + + <div class="col-xs-12 col-sm-8 col-md-6"> + <div class="form-group" + ng-class="{ 'has-error': poolDetailsForm.tls_ciphers.$invalid && poolDetailsForm.tls_ciphers.$dirty }"> + <label translate class="control-label" for="tls_ciphers">TLS Cipher String</label> + <textarea name="tls_ciphers" id="tls_ciphers" class="form-control" + ng-model="model.spec.pool.tls_ciphers" ng-pattern="/^([A-Z0-9_-]+:)*[A-Z0-9_-]+$/"> + </textarea> + <span class="help-block" ng-show="poolDetailsForm.tls_ciphers.$invalid && poolDetailsForm.tls_ciphers.$dirty"> + {$ ::ctrl.tls_ciphersError $} + </span> + </div> + </div> + + </div> + <div class="row"> <div class="col-xs-12 col-sm-8 col-md-6"> diff --git a/releasenotes/notes/add-ciphers-options-for-listeners-and-pools-8440eaf5a551c44c.yaml b/releasenotes/notes/add-ciphers-options-for-listeners-and-pools-8440eaf5a551c44c.yaml new file mode 100644 index 00000000..63edb659 --- /dev/null +++ b/releasenotes/notes/add-ciphers-options-for-listeners-and-pools-8440eaf5a551c44c.yaml @@ -0,0 +1,5 @@ +--- +features: + - | + Added option to specify TLS ciphers for listeners and pools. + The ciphers are represented in OpenSSL syntax.