From cd0bb14594c5e7ae1ea79825f865242b26bef857 Mon Sep 17 00:00:00 2001 From: Tom Weininger Date: Wed, 19 Apr 2023 12:22:15 +0200 Subject: [PATCH] Add support for HTTP Strict Transport Security Change-Id: I61882c844424a768d70b758e22d2aac979e3e3c6 --- octaviaclient/osc/v2/constants.py | 5 ++- octaviaclient/osc/v2/listener.py | 32 +++++++++++++++++++ octaviaclient/osc/v2/utils.py | 2 ++ octaviaclient/tests/unit/osc/v2/constants.py | 3 +- .../tests/unit/osc/v2/test_listener.py | 25 ++++++++++++--- .../add-hsts-support-f612ec171e28a3b3.yaml | 6 ++++ 6 files changed, 66 insertions(+), 7 deletions(-) create mode 100644 releasenotes/notes/add-hsts-support-f612ec171e28a3b3.yaml diff --git a/octaviaclient/osc/v2/constants.py b/octaviaclient/osc/v2/constants.py index 46b99ca..9a57932 100644 --- a/octaviaclient/osc/v2/constants.py +++ b/octaviaclient/osc/v2/constants.py @@ -82,7 +82,10 @@ LISTENER_ROWS = ( 'tls_ciphers', 'tls_versions', 'alpn_protocols', - 'tags') + 'tags', + 'hsts_enabled', + 'hsts_header_opts', +) LISTENER_COLUMNS = ( 'id', diff --git a/octaviaclient/osc/v2/listener.py b/octaviaclient/osc/v2/listener.py index 4f81f0a..a9b6a9e 100644 --- a/octaviaclient/osc/v2/listener.py +++ b/octaviaclient/osc/v2/listener.py @@ -201,6 +201,22 @@ class CreateListener(command.ShowOne): help="Set the ALPN protocol to be used " "by the listener (can be set multiple times)." ) + parser.add_argument( + '--enable-hsts', + dest='hsts_enabled', + action='store_true', + help="Enables HTTP Strict Transport Security (HSTS) for the " + "TLS-terminated listener." + ) + parser.add_argument( + '--hsts-header-opts', + dest='hsts_header_opts', + metavar='', + nargs='?', + help="Options of the Strict-Transport-Security header. E.g. " + "'max-age=16000000; includeSubDomains; preload;'. Note that " + "max-age is a mandatory field for using the HSTS feature." + ) _tag.add_tag_option_to_parser_for_create( parser, 'listener') @@ -528,6 +544,22 @@ class SetListener(command.Command): help="Set the ALPN protocol to be used " "by the listener (can be set multiple times)." ) + parser.add_argument( + '--enable-hsts', + dest='hsts_enabled', + action='store_true', + help="Enables HTTP Strict Transport Security (HSTS) for the " + "TLS-terminated listener." + ) + parser.add_argument( + '--hsts-header-opts', + dest='hsts_header_opts', + metavar='', + nargs='?', + help="Options of the Strict-Transport-Security header. E.g. " + "'max-age=16000000; includeSubDomains; preload;'. Note that " + "max-age is a mandatory field for using the HSTS feature." + ) _tag.add_tag_option_to_parser_for_set(parser, 'listener') diff --git a/octaviaclient/osc/v2/utils.py b/octaviaclient/osc/v2/utils.py index 637839c..80c6da2 100644 --- a/octaviaclient/osc/v2/utils.py +++ b/octaviaclient/osc/v2/utils.py @@ -322,6 +322,8 @@ def get_listener_attrs(client_manager, parsed_args): 'tls_ciphers': ('tls_ciphers', str), 'tls_versions': ('tls_versions', list), 'alpn_protocols': ('alpn_protocols', list), + 'hsts_enabled': ('hsts_enabled', bool), + 'hsts_header_opts': ('hsts_header_opts', str), } add_tags_attr_map(attr_map) diff --git a/octaviaclient/tests/unit/osc/v2/constants.py b/octaviaclient/tests/unit/osc/v2/constants.py index 39fcfaf..cf313ea 100644 --- a/octaviaclient/tests/unit/osc/v2/constants.py +++ b/octaviaclient/tests/unit/osc/v2/constants.py @@ -82,7 +82,8 @@ LISTENER_ATTRS = { 'tls_ciphers': "ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256", 'tls_versions': ['TLSv1.1', 'TLSv1.2'], 'alpn_protocols': ['h2', 'http/1.1'], - "tags": ["foo", "bar"] + "tags": ["foo", "bar"], + 'hsts_header_opts': "max-age=16000000; includeSubDomains; preload;", } LOADBALANCER_ATTRS = { diff --git a/octaviaclient/tests/unit/osc/v2/test_listener.py b/octaviaclient/tests/unit/osc/v2/test_listener.py index 2da8c65..4873fb9 100644 --- a/octaviaclient/tests/unit/osc/v2/test_listener.py +++ b/octaviaclient/tests/unit/osc/v2/test_listener.py @@ -255,7 +255,11 @@ class TestListenerCreate(TestListener): '--alpn-protocol', self._listener.alpn_protocols[0], '--alpn-protocol', - self._listener.alpn_protocols[1]] + self._listener.alpn_protocols[1], + '--enable-hsts', + '--hsts-header-opts', + self._listener.hsts_header_opts, + ] verifylist = [ ('loadbalancer', 'mock_lb_id'), @@ -276,6 +280,8 @@ class TestListenerCreate(TestListener): self._listener.tls_versions), ('alpn_protocols', self._listener.alpn_protocols), + ('hsts_enabled', True), + ('hsts_header_opts', self._listener.hsts_header_opts), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -386,7 +392,11 @@ class TestListenerSet(TestListener): '--alpn-protocol', self._listener.alpn_protocols[0], '--alpn-protocol', - self._listener.alpn_protocols[1]] + self._listener.alpn_protocols[1], + '--enable-hsts', + '--hsts-header-opts', + self._listener.hsts_header_opts, + ] verifylist = [ ('listener', self._listener.id), ('name', 'new_name'), @@ -402,7 +412,9 @@ class TestListenerSet(TestListener): ('allowed_cidrs', self._listener.allowed_cidrs), ('tls_ciphers', self._listener.tls_ciphers), ('tls_versions', self._listener.tls_versions), - ('alpn_protocols', self._listener.alpn_protocols) + ('alpn_protocols', self._listener.alpn_protocols), + ('hsts_enabled', True), + ('hsts_header_opts', self._listener.hsts_header_opts), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -424,6 +436,8 @@ class TestListenerSet(TestListener): 'tls_ciphers': self._listener.tls_ciphers, 'tls_versions': self._listener.tls_versions, 'alpn_protocols': self._listener.alpn_protocols, + 'hsts_enabled': True, + 'hsts_header_opts': self._listener.hsts_header_opts, }}) @mock.patch('osc_lib.utils.wait_for_status') @@ -438,7 +452,8 @@ class TestListenerSet(TestListener): parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.cmd.take_action(parsed_args) self.api_mock.listener_set.assert_called_with( - self._listener.id, json={'listener': {'name': 'new_name'}}) + self._listener.id, json={'listener': {'name': 'new_name', + 'hsts_enabled': False}}) mock_wait.assert_called_once_with( status_f=mock.ANY, res_id=self._listener.id, @@ -480,7 +495,7 @@ class TestListenerSet(TestListener): self.api_mock.listener_set.assert_called_once_with( self._listener.id, - json={"listener": {"tags": ['bar']}} + json={"listener": {"tags": ['bar'], 'hsts_enabled': False}} ) diff --git a/releasenotes/notes/add-hsts-support-f612ec171e28a3b3.yaml b/releasenotes/notes/add-hsts-support-f612ec171e28a3b3.yaml new file mode 100644 index 0000000..4fe971c --- /dev/null +++ b/releasenotes/notes/add-hsts-support-f612ec171e28a3b3.yaml @@ -0,0 +1,6 @@ +--- +features: + - | + Added support for HTTP Strict Transport Security (HSTS). This new feature + can be enabled and configured during listener creation using the new + ``--enable-hsts`` and ``--hsts-header-opts`` settings.