From 8b2ca79507b1721056913be49810095f97977944 Mon Sep 17 00:00:00 2001 From: Ivan Bojer Date: Thu, 2 Mar 2017 08:42:54 -0800 Subject: [PATCH] panos new module used for security policy creation (#20778) * - new module used for security policy creation * - added support for API key authentication - added support for Panorama * documentation changes * documentation update * minor doc changes based on review comments * move ANSIBLE_METADATA * changes to docs per reviewer comments --- CHANGELOG.md | 2 + .../network/panos/panos_security_policy.py | 493 ++++++++++++++++++ 2 files changed, 495 insertions(+) create mode 100644 lib/ansible/modules/network/panos/panos_security_policy.py diff --git a/CHANGELOG.md b/CHANGELOG.md index 482e8a9ec1..2561a57ac0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -235,6 +235,8 @@ Ansible Changes By Release - runit - serverless - set_stats +- panos: + * panos_security_policy - smartos: * imgadm * vmadm diff --git a/lib/ansible/modules/network/panos/panos_security_policy.py b/lib/ansible/modules/network/panos/panos_security_policy.py new file mode 100644 index 0000000000..de4fd60919 --- /dev/null +++ b/lib/ansible/modules/network/panos/panos_security_policy.py @@ -0,0 +1,493 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +# +# Ansible module to manage PaloAltoNetworks Firewall +# (c) 2016, techbizdev +# +# 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 . + +ANSIBLE_METADATA = {'status': ['preview'], + 'supported_by': 'community', + 'version': '1.0'} + +DOCUMENTATION = ''' +--- +module: panos_security_policy +short_description: Create security rule policy on PanOS devices. +description: > + Security policies allow you to enforce rules and take action, and can be as general or specific as needed. + The policy rules are compared against the incoming traffic in sequence, and because the first rule that matches + the traffic is applied, the more specific rules must precede the more general ones. +author: "Ivan Bojer (@ivanbojer)" +version_added: "2.3" +requirements: + - pan-python can be obtained from PyPi U(https://pypi.python.org/pypi/pan-python) + - pandevice can be obtained from PyPi U(https://pypi.python.org/pypi/pandevice) +notes: + - Checkmode is not supported. + - Panorama is supported +options: + ip_address: + description: + - IP address (or hostname) of PAN-OS device being configured. + required: true + username: + description: + - Username credentials to use for auth unless I(api_key) is set. + default: "admin" + password: + description: + - Password credentials to use for auth unless I(api_key) is set. + required: true + api_key: + description: + - API key that can be used instead of I(username)/I(password) credentials. + rule_name: + description: + - Name of the security rule. + required: true + rule_type: + description: + - Type of security rule (version 6.1 of PanOS and above). + default: "universal" + description: + description: + - Description for the security rule. + default: "None" + tag: + description: + - Administrative tags that can be added to the rule. Note, tags must be already defined. + default: "None" + from_zone: + description: + - List of source zones. + default: "any" + to_zone: + description: + - List of destination zones. + default: "any" + source: + description: + - List of source addresses. + default: "any" + source_user: + description: + - Use users to enforce policy for individual users or a group of users. + default: "any" + hip_profiles: + description: > + If you are using GlobalProtect with host information profile (HIP) enabled, you can also base the policy + on information collected by GlobalProtect. For example, the user access level can be determined HIP that + notifies the firewall about the user's local configuration. + default: "any" + destination: + description: + - List of destination addresses. + default: "any" + application: + description: + - List of applications. + default: "any" + service: + description: + - List of services. + default: "application-default" + log_start: + description: + - Whether to log at session start. + default: false + log_end: + description: + - Whether to log at session end. + default: true + action: + description: + - Action to apply once rules maches. + default: "allow" + group_profile: + description: > + Security profile group that is already defined in the system. This property supersedes antivirus, + vulnerability, spyware, url_filtering, file_blocking, data_filtering, and wildfire_analysis properties. + default: None + antivirus: + description: + - Name of the already defined antivirus profile. + default: None + vulnerability: + description: + - Name of the already defined vulnerability profile. + default: None + spyware: + description: + - Name of the already defined spyware profile. + default: None + url_filtering: + description: + - Name of the already defined url_filtering profile. + default: None + file_blocking: + description: + - Name of the already defined file_blocking profile. + default: None + data_filtering: + description: + - Name of the already defined data_filtering profile. + default: None + wildfire_analysis: + description: + - Name of the already defined wildfire_analysis profile. + default: None + devicegroup: + description: > + Device groups are used for the Panorama interaction with Firewall(s). The group must exists on Panorama. + If device group is not define we assume that we are contacting Firewall. + default: None + commit: + description: + - Commit configuration if changed. + default: true +''' + +EXAMPLES = ''' +- name: permit ssh to 1.1.1.1 + panos_security_policy: + ip_address: '10.5.172.91' + username: 'admin' + password: 'paloalto' + rule_name: 'SSH permit' + description: 'SSH rule test' + from_zone: ['public'] + to_zone: ['private'] + source: ['any'] + source_user: ['any'] + destination: ['1.1.1.1'] + category: ['any'] + application: ['ssh'] + service: ['application-default'] + hip_profiles: ['any'] + action: 'allow' + commit: false + +- name: Allow HTTP multimedia only from CDNs + panos_security_policy: + ip_address: '10.5.172.91' + username: 'admin' + password: 'paloalto' + rule_name: 'HTTP Multimedia' + description: 'Allow HTTP multimedia only to host at 1.1.1.1' + from_zone: ['public'] + to_zone: ['private'] + source: ['any'] + source_user: ['any'] + destination: ['1.1.1.1'] + category: ['content-delivery-networks'] + application: ['http-video', 'http-audio'] + service: ['service-http', 'service-https'] + hip_profiles: ['any'] + action: 'allow' + commit: false + +- name: more complex fictitious rule that uses profiles + panos_security_policy: + ip_address: '10.5.172.91' + username: 'admin' + password: 'paloalto' + rule_name: 'Allow HTTP w profile' + log_start: false + log_end: true + action: 'allow' + antivirus: 'default' + vulnerability: 'default' + spyware: 'default' + url_filtering: 'default' + wildfire_analysis: 'default' + commit: false + +- name: deny all + panos_security_policy: + ip_address: '10.5.172.91' + username: 'admin' + password: 'paloalto' + rule_name: 'DenyAll' + log_start: true + log_end: true + action: 'deny' + rule_type: 'interzone' + commit: false + +# permit ssh to 1.1.1.1 using panorama and pushing the configuration to firewalls +# that are defined in 'DeviceGroupA' device group +- name: permit ssh to 1.1.1.1 through Panorama + panos_security_policy: + ip_address: '10.5.172.92' + password: 'paloalto' + rule_name: 'SSH permit' + description: 'SSH rule test' + from_zone: ['public'] + to_zone: ['private'] + source: ['any'] + source_user: ['any'] + destination: ['1.1.1.1'] + category: ['any'] + application: ['ssh'] + service: ['application-default'] + hip_profiles: ['any'] + action: 'allow' + devicegroup: 'DeviceGroupA' +''' + +RETURN = ''' +# Default return values +''' + +from ansible.module_utils.basic import AnsibleModule +from ansible.module_utils.basic import get_exception + +try: + import pan.xapi + from pan.xapi import PanXapiError + import pandevice + import pandevice.firewall + import pandevice.panorama + import pandevice.objects + import pandevice.policies + + HAS_LIB = True +except ImportError: + HAS_LIB = False + + +def security_rule_exists(device, rule_name): + if isinstance(device, pandevice.firewall.Firewall): + rule_base = pandevice.policies.Rulebase.refreshall(device) + elif isinstance(device, pandevice.panorama.Panorama): + # look for only pre-rulebase ATM + rule_base = pandevice.policies.PreRulebase.refreshall(device) + + if rule_base: + rule_base = rule_base[0] + security_rules = rule_base.findall(pandevice.policies.SecurityRule) + + if security_rules: + for r in security_rules: + if r.name == rule_name: + return True + + return False + + +def create_security_rule(**kwargs): + security_rule = pandevice.policies.SecurityRule( + name=kwargs['rule_name'], + description=kwargs['description'], + tozone=kwargs['to_zone'], + fromzone=kwargs['from_zone'], + source=kwargs['source'], + source_user=kwargs['source_user'], + destination=kwargs['destination'], + category=kwargs['category'], + application=kwargs['application'], + service=kwargs['service'], + hip_profiles=kwargs['hip_profiles'], + log_start=kwargs['log_start'], + log_end=kwargs['log_end'], + type=kwargs['rule_type'], + action=kwargs['action']) + + if 'tag' in kwargs: + security_rule.tag = kwargs['tag'] + + # profile settings + if 'group_profile' in kwargs: + security_rule.group = kwargs['group_profile'] + else: + if 'antivirus' in kwargs: + security_rule.virus = kwargs['antivirus'] + if 'vulnerability' in kwargs: + security_rule.vulnerability = kwargs['vulnerability'] + if 'spyware' in kwargs: + security_rule.spyware = kwargs['spyware'] + if 'url_filtering' in kwargs: + security_rule.url_filtering = kwargs['url_filtering'] + if 'file_blocking' in kwargs: + security_rule.file_blocking = kwargs['file_blocking'] + if 'data_filtering' in kwargs: + security_rule.data_filtering = kwargs['data_filtering'] + if 'wildfire_analysis' in kwargs: + security_rule.wildfire_analysis = kwargs['wildfire_analysis'] + + return security_rule + + +def add_security_rule(device, sec_rule): + if isinstance(device, pandevice.firewall.Firewall): + rule_base = pandevice.policies.Rulebase.refreshall(device) + elif isinstance(device, pandevice.panorama.Panorama): + # look for only pre-rulebase ATM + rule_base = pandevice.policies.PreRulebase.refreshall(device) + + if rule_base: + rule_base = rule_base[0] + + rule_base.add(sec_rule) + sec_rule.create() + + return True + else: + return False + + +def _commit(device, device_group=None): + """ + :param device: either firewall or panorama + :param device_group: panorama device group or if none then 'all' + :return: True if successful + """ + result = device.commit(sync=True) + + if isinstance(device, pandevice.panorama.Panorama): + result = device.commit_all(sync=True, sync_all=True, devicegroup=device_group) + + return result + + +def main(): + argument_spec = dict( + ip_address=dict(required=True), + password=dict(no_log=True), + username=dict(default='admin'), + api_key=dict(no_log=True), + rule_name=dict(required=True), + description=dict(default=''), + tag=dict(), + to_zone=dict(type='list', default=['any']), + from_zone=dict(type='list', default=['any']), + source=dict(type='list', default=["any"]), + source_user=dict(type='list', default=['any']), + destination=dict(type='list', default=["any"]), + category=dict(type='list', default=['any']), + application=dict(type='list', default=['any']), + service=dict(type='list', default=['application-default']), + hip_profiles=dict(type='list', default=['any']), + group_profile=dict(), + antivirus=dict(), + vulnerability=dict(), + spyware=dict(), + url_filtering=dict(), + file_blocking=dict(), + data_filtering=dict(), + wildfire_analysis=dict(), + log_start=dict(type='bool', default=False), + log_end=dict(type='bool', default=True), + rule_type=dict(default='universal'), + action=dict(default='allow'), + devicegroup=dict(), + commit=dict(type='bool', default=True) + ) + module = AnsibleModule(argument_spec=argument_spec, supports_check_mode=False, + required_one_of=[['api_key', 'password']]) + if not HAS_LIB: + module.fail_json(msg='Missing required pan-python and pandevice modules.') + + ip_address = module.params["ip_address"] + password = module.params["password"] + username = module.params['username'] + api_key = module.params['api_key'] + rule_name = module.params['rule_name'] + description = module.params['description'] + tag = module.params['tag'] + from_zone = module.params['from_zone'] + to_zone = module.params['to_zone'] + source = module.params['source'] + source_user = module.params['source_user'] + destination = module.params['destination'] + category = module.params['category'] + application = module.params['application'] + service = module.params['service'] + hip_profiles = module.params['hip_profiles'] + log_start = module.params['log_start'] + log_end = module.params['log_end'] + rule_type = module.params['rule_type'] + action = module.params['action'] + + group_profile = module.params['group_profile'] + antivirus = module.params['antivirus'] + vulnerability = module.params['vulnerability'] + spyware = module.params['spyware'] + url_filtering = module.params['url_filtering'] + file_blocking = module.params['file_blocking'] + data_filtering = module.params['data_filtering'] + wildfire_analysis = module.params['wildfire_analysis'] + + devicegroup = module.params['devicegroup'] + + commit = module.params['commit'] + + if devicegroup: + device = pandevice.panorama.Panorama(ip_address, username, password, api_key=api_key) + dev_grps = device.refresh_devices() + + for grp in dev_grps: + if grp.name == devicegroup: + break + module.fail_json(msg=' \'%s\' device group not found in Panorama. Is the name correct?' % devicegroup) + else: + device = pandevice.firewall.Firewall(ip_address, username, password, api_key=api_key) + + if security_rule_exists(device, rule_name): + module.fail_json(msg='Rule with the same name already exists.') + + try: + sec_rule = create_security_rule( + rule_name=rule_name, + description=description, + tag=tag, + from_zone=from_zone, + to_zone=to_zone, + source=source, + source_user=source_user, + destination=destination, + category=category, + application=application, + service=service, + hip_profiles=hip_profiles, + group_profile=group_profile, + antivirus=antivirus, + vulnerability=vulnerability, + spyware=spyware, + url_filtering=url_filtering, + file_blocking=file_blocking, + data_filtering=data_filtering, + wildfire_analysis=wildfire_analysis, + log_start=log_start, + log_end=log_end, + rule_type=rule_type, + action=action + ) + + changed = add_security_rule(device, sec_rule) + except PanXapiError: + exc = get_exception() + module.fail_json(msg=exc.message) + + if changed and commit: + result = _commit(device, devicegroup) + + module.exit_json(changed=changed, msg="okey dokey") + + +if __name__ == '__main__': + main()