From 79153b95e56e8cc9130ba56604fdc4488b92e4b2 Mon Sep 17 00:00:00 2001 From: Tim Rupp Date: Fri, 24 Aug 2018 15:51:24 -0400 Subject: [PATCH] Adds params to the profile-client-ssl module (#44656) This patch adds new parameters to the client ssl module. --- lib/ansible/module_utils/network/f5/bigiq.py | 6 +- lib/ansible/module_utils/network/f5/common.py | 15 +- .../network/f5/bigip_profile_client_ssl.py | 225 +++++++++++++++++- 3 files changed, 231 insertions(+), 15 deletions(-) diff --git a/lib/ansible/module_utils/network/f5/bigiq.py b/lib/ansible/module_utils/network/f5/bigiq.py index 6ff374b4db1..44f75507328 100644 --- a/lib/ansible/module_utils/network/f5/bigiq.py +++ b/lib/ansible/module_utils/network/f5/bigiq.py @@ -63,9 +63,7 @@ class F5Client(F5BaseClient): class F5RestClient(F5BaseClient): def __init__(self, *args, **kwargs): - params = kwargs.get('module').params - module = kwargs.pop('module') - super(F5RestClient, self).__init__(module=module, **params) + super(F5RestClient, self).__init__(*args, **kwargs) self.provider = self.merge_provider_params() @property @@ -89,7 +87,7 @@ class F5RestClient(F5BaseClient): if response.status not in [200]: raise F5ModuleError('Status code: {0}. Unexpected Error: {1} for uri: {2}\nText: {3}'.format( - response.status, response.reason, response.url, response._content + response.status, response.reason, response.url, response.content )) session.headers['X-F5-Auth-Token'] = response.json()['token']['token'] diff --git a/lib/ansible/module_utils/network/f5/common.py b/lib/ansible/module_utils/network/f5/common.py index ad0ffbdeff0..1c2d175a344 100644 --- a/lib/ansible/module_utils/network/f5/common.py +++ b/lib/ansible/module_utils/network/f5/common.py @@ -54,6 +54,7 @@ f5_provider_spec = { default='rest' ), 'timeout': dict(type='int'), + 'auth_provider': dict() } f5_argument_spec = { @@ -88,6 +89,9 @@ f5_top_spec = { 'transport': dict( removed_in_version=2.9, choices=['cli', 'rest'] + ), + 'auth_provider': dict( + default=None ) } f5_argument_spec.update(f5_top_spec) @@ -105,6 +109,13 @@ def load_params(params): params[key] = value +def is_empty_list(seq): + if len(seq) == 1: + if seq[0] == '' or seq[0] == 'none': + return True + return False + + # Fully Qualified name (with the partition) def fqdn_name(partition, value): """This method is not used @@ -204,7 +215,9 @@ def flatten_boolean(value): return 'no' -def cleanup_tokens(client): +def cleanup_tokens(client=None): + if client is None: + return try: # isinstance cannot be used here because to import it creates a # circular dependency with teh module_utils.network.f5.bigip file. diff --git a/lib/ansible/modules/network/f5/bigip_profile_client_ssl.py b/lib/ansible/modules/network/f5/bigip_profile_client_ssl.py index b4c9e3f671f..b3c80d0e775 100644 --- a/lib/ansible/modules/network/f5/bigip_profile_client_ssl.py +++ b/lib/ansible/modules/network/f5/bigip_profile_client_ssl.py @@ -7,7 +7,6 @@ from __future__ import absolute_import, division, print_function __metaclass__ = type - ANSIBLE_METADATA = {'metadata_version': '1.1', 'status': ['preview'], 'supported_by': 'community'} @@ -42,8 +41,7 @@ options: type of each certificate/key type. This means that you can only have one RSA, one DSA, and one ECDSA per profile. If you attempt to assign two RSA, DSA, or ECDSA certificate/key combo, the device will reject this. - - This list is a complex list that specifies a number of keys. There are - several supported keys. + - This list is a complex list that specifies a number of keys. suboptions: cert: description: @@ -69,6 +67,59 @@ options: - Device partition to manage resources on. default: Common version_added: 2.5 + options: + description: + - Options that the system uses for SSL processing in the form of a list. When + creating a new profile, the list is provided by the parent profile. + - When a C('') or C(none) value is provided all options for SSL processing are disabled. + choices: + - netscape-reuse-cipher-change-bug + - microsoft-big-sslv3-buffer + - msie-sslv2-rsa-padding + - ssleay-080-client-dh-bug + - tls-d5-bug + - tls-block-padding-bug + - dont-insert-empty-fragments + - no-ssl + - no-dtls + - no-session-resumption-on-renegotiation + - no-tlsv1.1 + - no-tlsv1.2 + - single-dh-use + - ephemeral-rsa + - cipher-server-preference + - tls-rollback-bug + - no-sslv2 + - no-sslv3 + - no-tls + - no-tlsv1 + - pkcs1-check-1 + - pkcs1-check-2 + - netscape-ca-dn-bug + - netscape-demo-cipher-change-bug + version_added: 2.7 + secure_renegotiation: + description: + - Specifies the method of secure renegotiations for SSL connections. When + creating a new profile, the setting is provided by the parent profile. + - When C(request) is set the ssystem request secure renegotation of SSL + connections. + - C(require) is a default setting and when set the system permits initial SSL + handshakes from clients but terminates renegotiations from unpatched clients. + - The C(require-strict) setting the system requires strict renegotiation of SSL + connections. In this mode the system refuses connections to insecure servers, + and terminates existing SSL connections to insecure servers. + choices: + - require + - require-strict + - request + version_added: 2.7 + allow_non_ssl: + description: + - Enables or disables acceptance of non-SSL connections. + - When creating a new profile, the setting is provided by the parent profile. + type: bool + version_added: 2.7 state: description: - When C(present), ensures that the profile exists. @@ -83,6 +134,7 @@ notes: extends_documentation_fragment: f5 author: - Tim Rupp (@caphrim007) + - Wojciech Wypior (@wojtek0806) ''' EXAMPLES = r''' @@ -105,6 +157,28 @@ EXAMPLES = r''' ciphers: "!SSLv3:!SSLv2:ECDHE+AES-GCM+SHA256:ECDHE-RSA-AES128-CBC-SHA" delegate_to: localhost +- name: Create client SSL profile with specific SSL options + bigip_profile_client_ssl: + state: present + server: lb.mydomain.com + user: admin + password: secret + name: my_profile + options: + - no-sslv2 + - no-sslv3 + delegate_to: localhost + +- name: Create client SSL profile require secure renegotiation + bigip_profile_client_ssl: + state: present + server: lb.mydomain.com + user: admin + password: secret + name: my_profile + secure_renegotation: request + delegate_to: localhost + - name: Create a client SSL profile with a cert/key/chain setting bigip_profile_client_ssl: state: present @@ -125,6 +199,21 @@ ciphers: returned: changed type: string sample: "!SSLv3:!SSLv2:ECDHE+AES-GCM+SHA256:ECDHE-RSA-AES128-CBC-SHA" +options: + description: The list of options for SSL processing. + returned: changed + type: list + sample: ['no-sslv2', 'no-sslv3'] +secure_renegotation: + description: The method of secure SSL renegotiation. + returned: changed + type: string + sample: request +allow_non_ssl: + description: Acceptance of non-SSL connections. + returned: changed + type: bool + sample: yes ''' import os @@ -141,6 +230,8 @@ try: from library.module_utils.network.f5.common import cleanup_tokens from library.module_utils.network.f5.common import fq_name from library.module_utils.network.f5.common import f5_argument_spec + from library.module_utils.network.f5.common import flatten_boolean + from library.module_utils.network.f5.common import is_empty_list try: from library.module_utils.network.f5.common import iControlUnexpectedHTTPError except ImportError: @@ -153,6 +244,8 @@ except ImportError: from ansible.module_utils.network.f5.common import cleanup_tokens from ansible.module_utils.network.f5.common import fq_name from ansible.module_utils.network.f5.common import f5_argument_spec + from ansible.module_utils.network.f5.common import flatten_boolean + from ansible.module_utils.network.f5.common import is_empty_list try: from ansible.module_utils.network.f5.common import iControlUnexpectedHTTPError except ImportError: @@ -162,20 +255,26 @@ except ImportError: class Parameters(AnsibleF5Parameters): api_map = { 'certKeyChain': 'cert_key_chain', - 'defaultsFrom': 'parent' + 'defaultsFrom': 'parent', + 'allowNonSsl': 'allow_non_ssl', + 'secureRenegotiation': 'secure_renegotiation', + 'tmOptions': 'options', } api_attributes = [ 'ciphers', 'certKeyChain', - 'defaultsFrom' + 'defaultsFrom', 'tmOptions', + 'secureRenegotiation', 'allowNonSsl', ] returnables = [ - 'ciphers' + 'ciphers', 'allow_non_ssl', 'options', + 'secure_renegotiation', ] updatables = [ - 'ciphers', 'cert_key_chain' + 'ciphers', 'cert_key_chain', 'allow_non_ssl', + 'options', 'secure_renegotiation', ] @@ -237,6 +336,57 @@ class ModuleParameters(Parameters): result = sorted(result, key=lambda x: x['name']) return result + @property + def allow_non_ssl(self): + result = flatten_boolean(self._values['allow_non_ssl']) + if result is None: + return None + if result == 'yes': + return 'enabled' + return 'disabled' + + @property + def options(self): + choices = [ + 'netscape-reuse-cipher-change-bug', + 'microsoft-big-sslv3-buffer', + 'msie-sslv2-rsa-padding', + 'ssleay-080-client-dh-bug', + 'tls-d5-bug', + 'tls-block-padding-bug', + 'dont-insert-empty-fragments', + 'no-ssl', + 'no-dtls', + 'no-session-resumption-on-renegotiation', + 'no-tlsv1.1', + 'no-tlsv1.2', + 'single-dh-use', + 'ephemeral-rsa', + 'cipher-server-preference', + 'tls-rollback-bug', + 'no-sslv2', + 'no-sslv3', + 'no-tls', + 'no-tlsv1', + 'pkcs1-check-1', + 'pkcs1-check-2', + 'netscape-ca-dn-bug', + 'netscape-demo-cipher-change-bug' + ] + options = self._values['options'] + + if options is None: + return None + + if is_empty_list(options): + return [] + + if set(options).issubset(set(choices)): + return options + else: + offenders = set(options).difference(set(choices)) + raise F5ModuleError('Invalid options specified: {0}'.format(offenders)) + class ApiParameters(Parameters): @property @@ -275,7 +425,13 @@ class UsableChanges(Changes): class ReportableChanges(Changes): - pass + @property + def allow_non_ssl(self): + if self._values['allow_non_ssl'] is None: + return None + elif self._values['allow_non_ssl'] == 'enabled': + return 'yes' + return 'no' class Difference(object): @@ -331,6 +487,22 @@ class Difference(object): result = self._diff_complex_items(self.want.cert_key_chain, self.have.cert_key_chain) return result + @property + def options(self): + if self.want.options is None: + return None + if not self.want.options: + if self.have.options is None: + return None + if not self.have.options: + return None + if self.have.options is not None: + return self.want.options + if self.have.options is None: + return self.want.options + if set(self.want.options) != set(self.have.options): + return self.want.options + class ModuleManager(object): def __init__(self, *args, **kwargs): @@ -482,6 +654,39 @@ class ArgumentSpec(object): name=dict(required=True), parent=dict(default='/Common/clientssl'), ciphers=dict(), + allow_non_ssl=dict(type='bool'), + secure_renegotiation=dict( + choices=['require', 'require-strict', 'request'] + ), + options=dict( + type='list', + choices=[ + 'netscape-reuse-cipher-change-bug', + 'microsoft-big-sslv3-buffer', + 'msie-sslv2-rsa-padding', + 'ssleay-080-client-dh-bug', + 'tls-d5-bug', + 'tls-block-padding-bug', + 'dont-insert-empty-fragments', + 'no-ssl', + 'no-dtls', + 'no-session-resumption-on-renegotiation', + 'no-tlsv1.1', + 'no-tlsv1.2', + 'single-dh-use', + 'ephemeral-rsa', + 'cipher-server-preference', + 'tls-rollback-bug', + 'no-sslv2', + 'no-sslv3', + 'no-tls', + 'no-tlsv1', + 'pkcs1-check-1', + 'pkcs1-check-2', + 'netscape-ca-dn-bug', + 'netscape-demo-cipher-change-bug', + ] + ), cert_key_chain=dict( type='list', options=dict( @@ -507,7 +712,6 @@ class ArgumentSpec(object): def main(): spec = ArgumentSpec() - module = AnsibleModule( argument_spec=spec.argument_spec, supports_check_mode=spec.supports_check_mode @@ -515,8 +719,9 @@ def main(): if not HAS_F5SDK: module.fail_json(msg="The python f5-sdk module is required") + client = F5Client(**module.params) + try: - client = F5Client(**module.params) mm = ModuleManager(module=module, client=client) results = mm.exec_module() cleanup_tokens(client)