From 325d497fb8281bbe7df049548da99629581ee03c Mon Sep 17 00:00:00 2001 From: Tim Rupp Date: Tue, 23 Aug 2016 11:07:15 -0700 Subject: [PATCH] Addition of bigip_device_sshd module This module can be used as part of the bootstrapping of a BIG-IP. It allows one to configure the various SSHD settings that are part of a BIG-IP. Tests for this module can be found here https://github.com/F5Networks/f5-ansible/blob/master/roles/__bigip_device_sshd/tasks/main.yaml Platforms this was tested on are 11.6.0 12.0.0 12.1.0 12.1.0 HF1 --- network/f5/bigip_device_sshd.py | 344 ++++++++++++++++++++++++++++++++ 1 file changed, 344 insertions(+) create mode 100644 network/f5/bigip_device_sshd.py diff --git a/network/f5/bigip_device_sshd.py b/network/f5/bigip_device_sshd.py new file mode 100644 index 00000000000..fd27127f897 --- /dev/null +++ b/network/f5/bigip_device_sshd.py @@ -0,0 +1,344 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +# +# This file is part of Ansible +# +# Ansible is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Ansible is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Ansible. If not, see . + +DOCUMENTATION = ''' +--- +module: bigip_device_sshd +short_description: Manage the SSHD settings of a BIG-IP +description: + - Manage the SSHD settings of a BIG-IP +version_added: "2.2" +options: + allow: + description: + - Specifies, if you have enabled SSH access, the IP address or address + range for other systems that can use SSH to communicate with this + system. + choices: + - all + - IP address, such as 172.27.1.10 + - IP range, such as 172.27.*.* or 172.27.0.0/255.255.0.0 + banner: + description: + - Whether to enable the banner or not. + required: false + choices: + - enabled + - disabled + banner_text: + description: + - Specifies the text to include on the pre-login banner that displays + when a user attempts to login to the system using SSH. + required: false + inactivity_timeout: + description: + - Specifies the number of seconds before inactivity causes an SSH + session to log out. + required: false + log_level: + description: + - Specifies the minimum SSHD message level to include in the system log. + choices: + - debug + - debug1 + - debug2 + - debug3 + - error + - fatal + - info + - quiet + - verbose + login: + description: + - Specifies, when checked C(enabled), that the system accepts SSH + communications. + choices: + - enabled + - disabled + required: false + port: + description: + - Port that you want the SSH daemon to run on. + required: false +notes: + - Requires the f5-sdk Python package on the host This is as easy as pip + install f5-sdk. + - Requires BIG-IP version 12.0.0 or greater +extends_documentation_fragment: f5 +requirements: + - f5-sdk +author: + - Tim Rupp (@caphrim007) +''' + +EXAMPLES = ''' +- name: Set the banner for the SSHD service from a string + bigip_device_sshd: + banner: "enabled" + banner_text: "banner text goes here" + password: "secret" + server: "lb.mydomain.com" + user: "admin" + delegate_to: localhost + +- name: Set the banner for the SSHD service from a file + bigip_device_sshd: + banner: "enabled" + banner_text: "{{ lookup('file', '/path/to/file') }}" + password: "secret" + server: "lb.mydomain.com" + user: "admin" + delegate_to: localhost + +- name: Set the SSHD service to run on port 2222 + bigip_device_sshd: + password: "secret" + port: 2222 + server: "lb.mydomain.com" + user: "admin" + delegate_to: localhost +''' + +RETURN = ''' +allow: + description: | + Specifies, if you have enabled SSH access, the IP address or address + range for other systems that can use SSH to communicate with this + system. + returned: changed + type: list + sample: "192.168.*.*" +banner: + description: Whether the banner is enabled or not. + returned: changed + type: string + sample: "true" +banner_text: + description: | + Specifies the text included on the pre-login banner that + displays when a user attempts to login to the system using SSH. + returned: changed and success + type: string + sample: "This is a corporate device. Connecting to it without..." +inactivity_timeout: + description: | + The number of seconds before inactivity causes an SSH. + session to log out + returned: changed + type: int + sample: "10" +log_level: + description: The minimum SSHD message level to include in the system log. + returned: changed + type: string + sample: "debug" +login: + description: Specifies that the system accepts SSH communications or not. + return: changed + type: bool + sample: true +port: + description: Port that you want the SSH daemon to run on. + return: changed + type: int + sample: 22 +''' + +try: + from f5.bigip import ManagementRoot + from icontrol.session import iControlUnexpectedHTTPError + HAS_F5SDK = True +except ImportError: + HAS_F5SDK = False + +CHOICES = ['enabled', 'disabled'] +LEVELS = ['debug', 'debug1', 'debug2', 'debug3', 'error', 'fatal', 'info', + 'quiet', 'verbose'] + + +class BigIpDeviceSshd(object): + def __init__(self, *args, **kwargs): + if not HAS_F5SDK: + raise F5ModuleError("The python f5-sdk module is required") + + # The params that change in the module + self.cparams = dict() + + # Stores the params that are sent to the module + self.params = kwargs + self.api = ManagementRoot(kwargs['server'], + kwargs['user'], + kwargs['password'], + port=kwargs['server_port']) + + def update(self): + changed = False + current = self.read() + params = dict() + + allow = self.params['allow'] + banner = self.params['banner'] + banner_text = self.params['banner_text'] + timeout = self.params['inactivity_timeout'] + log_level = self.params['log_level'] + login = self.params['login'] + port = self.params['port'] + check_mode = self.params['check_mode'] + + if allow: + if 'allow' in current: + items = set(allow) + if items != current['allow']: + params['allow'] = list(items) + else: + params['allow'] = allow + + if banner: + if 'banner' in current: + if banner != current['banner']: + params['banner'] = banner + else: + params['banner'] = banner + + if banner_text: + if 'banner_text' in current: + if banner_text != current['banner_text']: + params['bannerText'] = banner_text + else: + params['bannerText'] = banner_text + + if timeout: + if 'inactivity_timeout' in current: + if timeout != current['inactivity_timeout']: + params['inactivityTimeout'] = timeout + else: + params['inactivityTimeout'] = timeout + + if log_level: + if 'log_level' in current: + if log_level != current['log_level']: + params['logLevel'] = log_level + else: + params['logLevel'] = log_level + + if login: + if 'login' in current: + if login != current['login']: + params['login'] = login + else: + params['login'] = login + + if port: + if 'port' in current: + if port != current['port']: + params['port'] = port + else: + params['port'] = port + + if params: + changed = True + if check_mode: + return changed + self.cparams = camel_dict_to_snake_dict(params) + else: + return changed + + r = self.api.tm.sys.sshd.load() + r.update(**params) + r.refresh() + + return changed + + def read(self): + """Read information and transform it + + The values that are returned by BIG-IP in the f5-sdk can have encoding + attached to them as well as be completely missing in some cases. + + Therefore, this method will transform the data from the BIG-IP into a + format that is more easily consumable by the rest of the class and the + parameters that are supported by the module. + """ + p = dict() + r = self.api.tm.sys.sshd.load() + + if hasattr(r, 'allow'): + # Deliberately using sets to supress duplicates + p['allow'] = set([str(x) for x in r.allow]) + if hasattr(r, 'banner'): + p['banner'] = str(r.banner) + if hasattr(r, 'bannerText'): + p['banner_text'] = str(r.bannerText) + if hasattr(r, 'inactivityTimeout'): + p['inactivity_timeout'] = str(r.inactivityTimeout) + if hasattr(r, 'logLevel'): + p['log_level'] = str(r.logLevel) + if hasattr(r, 'login'): + p['login'] = str(r.login) + if hasattr(r, 'port'): + p['port'] = int(r.port) + return p + + def flush(self): + result = dict() + changed = False + + try: + changed = self.update() + except iControlUnexpectedHTTPError as e: + raise F5ModuleError(str(e)) + + result.update(**self.cparams) + result.update(dict(changed=changed)) + return result + + +def main(): + argument_spec = f5_argument_spec() + + meta_args = dict( + allow=dict(required=False, default=None, type='list'), + banner=dict(required=False, default=None, choices=CHOICES), + banner_text=dict(required=False, default=None), + inactivity_timeout=dict(required=False, default=None, type='int'), + log_level=dict(required=False, default=None, choices=LEVELS), + login=dict(required=False, default=None, choices=CHOICES), + port=dict(required=False, default=None, type='int'), + state=dict(default='present', choices=['present']) + ) + argument_spec.update(meta_args) + + module = AnsibleModule( + argument_spec=argument_spec, + supports_check_mode=True + ) + + try: + obj = BigIpDeviceSshd(check_mode=module.check_mode, **module.params) + result = obj.flush() + + module.exit_json(**result) + except F5ModuleError as e: + module.fail_json(msg=str(e)) + +from ansible.module_utils.basic import * +from ansible.module_utils.ec2 import camel_dict_to_snake_dict +from ansible.module_utils.f5 import * + +if __name__ == '__main__': + main()