1
0
Fork 0
mirror of https://github.com/ansible-collections/community.general.git synced 2026-04-20 18:59:08 +00:00

Initial commit

This commit is contained in:
Ansible Core Team 2020-03-09 09:11:07 +00:00
commit aebc1b03fd
4861 changed files with 812621 additions and 0 deletions

View file

@ -0,0 +1,196 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
#
# Ansible module to manage PaloAltoNetworks Firewall
# (c) 2016, techbizdev <techbizdev@paloaltonetworks.com>
#
# 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 <http://www.gnu.org/licenses/>.
ANSIBLE_METADATA = {'metadata_version': '1.1',
'status': ['deprecated'],
'supported_by': 'community'}
DOCUMENTATION = '''
---
module: panos_admin
short_description: Add or modify PAN-OS user accounts password.
description:
- PanOS module that allows changes to the user account passwords by doing
API calls to the Firewall using pan-api as the protocol.
author: "Luigi Mori (@jtschichold), Ivan Bojer (@ivanbojer)"
requirements:
- pan-python
deprecated:
alternative: Use U(https://galaxy.ansible.com/PaloAltoNetworks/paloaltonetworks) instead.
removed_in: "2.12"
why: Consolidating code base.
options:
admin_username:
description:
- username for admin user
default: "admin"
admin_password:
description:
- password for admin user
required: true
role:
description:
- role for admin user
commit:
description:
- commit if changed
type: bool
default: 'yes'
extends_documentation_fragment:
- community.general.panos
'''
EXAMPLES = '''
# Set the password of user admin to "badpassword"
# Doesn't commit the candidate config
- name: set admin password
panos_admin:
ip_address: "192.168.1.1"
password: "admin"
admin_username: admin
admin_password: "badpassword"
commit: False
'''
RETURN = '''
status:
description: success status
returned: success
type: str
sample: "okey dokey"
'''
from ansible.module_utils.basic import AnsibleModule
try:
import pan.xapi
HAS_LIB = True
except ImportError:
HAS_LIB = False
_ADMIN_XPATH = "/config/mgt-config/users/entry[@name='%s']"
def admin_exists(xapi, admin_username):
xapi.get(_ADMIN_XPATH % admin_username)
e = xapi.element_root.find('.//entry')
return e
def admin_set(xapi, module, admin_username, admin_password, role):
if admin_password is not None:
xapi.op(cmd='request password-hash password "%s"' % admin_password,
cmd_xml=True)
r = xapi.element_root
phash = r.find('.//phash').text
if role is not None:
rbval = "yes"
if role != "superuser" and role != 'superreader':
rbval = ""
ea = admin_exists(xapi, admin_username)
if ea is not None:
# user exists
changed = False
if role is not None:
rb = ea.find('.//role-based')
if rb is not None:
if rb[0].tag != role:
changed = True
xpath = _ADMIN_XPATH % admin_username
xpath += '/permissions/role-based/%s' % rb[0].tag
xapi.delete(xpath=xpath)
xpath = _ADMIN_XPATH % admin_username
xpath += '/permissions/role-based'
xapi.set(xpath=xpath,
element='<%s>%s</%s>' % (role, rbval, role))
if admin_password is not None:
xapi.edit(xpath=_ADMIN_XPATH % admin_username + '/phash',
element='<phash>%s</phash>' % phash)
changed = True
return changed
# setup the non encrypted part of the monitor
exml = []
exml.append('<phash>%s</phash>' % phash)
exml.append('<permissions><role-based><%s>%s</%s>'
'</role-based></permissions>' % (role, rbval, role))
exml = ''.join(exml)
# module.fail_json(msg=exml)
xapi.set(xpath=_ADMIN_XPATH % admin_username, element=exml)
return True
def main():
argument_spec = dict(
ip_address=dict(),
password=dict(no_log=True),
username=dict(default='admin'),
admin_username=dict(default='admin'),
admin_password=dict(no_log=True),
role=dict(),
commit=dict(type='bool', default=True)
)
module = AnsibleModule(argument_spec=argument_spec, supports_check_mode=False)
if not HAS_LIB:
module.fail_json(msg='pan-python required for this module')
ip_address = module.params["ip_address"]
if not ip_address:
module.fail_json(msg="ip_address should be specified")
password = module.params["password"]
if not password:
module.fail_json(msg="password is required")
username = module.params['username']
xapi = pan.xapi.PanXapi(
hostname=ip_address,
api_username=username,
api_password=password
)
admin_username = module.params['admin_username']
if admin_username is None:
module.fail_json(msg="admin_username is required")
admin_password = module.params['admin_password']
role = module.params['role']
commit = module.params['commit']
changed = admin_set(xapi, module, admin_username, admin_password, role)
if changed and commit:
xapi.commit(cmd="<commit></commit>", sync=True, interval=1)
module.exit_json(changed=changed, msg="okey dokey")
if __name__ == '__main__':
main()

View file

@ -0,0 +1,205 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
#
# Ansible module to manage PaloAltoNetworks Firewall
# (c) 2016, techbizdev <techbizdev@paloaltonetworks.com>
#
# 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 <http://www.gnu.org/licenses/>.
DOCUMENTATION = '''
---
module: panos_admpwd
short_description: change admin password of PAN-OS device using SSH with SSH key
description:
- Change the admin password of PAN-OS via SSH using a SSH key for authentication.
- Useful for AWS instances where the first login should be done via SSH.
author: "Luigi Mori (@jtschichold), Ivan Bojer (@ivanbojer)"
requirements:
- paramiko
deprecated:
alternative: Use U(https://galaxy.ansible.com/PaloAltoNetworks/paloaltonetworks) instead.
removed_in: "2.12"
why: Consolidating code base.
options:
ip_address:
description:
- IP address (or hostname) of PAN-OS device
required: true
username:
description:
- username for initial authentication
required: false
default: "admin"
key_filename:
description:
- filename of the SSH Key to use for authentication
required: true
newpassword:
description:
- password to configure for admin on the PAN-OS device
required: true
'''
EXAMPLES = '''
# Tries for 10 times to set the admin password of 192.168.1.1 to "badpassword"
# via SSH, authenticating using key /tmp/ssh.key
- name: set admin password
panos_admpwd:
ip_address: "192.168.1.1"
username: "admin"
key_filename: "/tmp/ssh.key"
newpassword: "badpassword"
register: result
until: result is not failed
retries: 10
delay: 30
'''
RETURN = '''
status:
description: success status
returned: success
type: str
sample: "Last login: Fri Sep 16 11:09:20 2016 from 10.35.34.56.....Configuration committed successfully"
'''
ANSIBLE_METADATA = {'metadata_version': '1.1',
'status': ['deprecated'],
'supported_by': 'community'}
from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils.compat.paramiko import paramiko
import time
import sys
_PROMPTBUFF = 4096
def wait_with_timeout(module, shell, prompt, timeout=60):
now = time.time()
result = ""
while True:
if shell.recv_ready():
result += shell.recv(_PROMPTBUFF)
endresult = result.strip()
if len(endresult) != 0 and endresult[-1] == prompt:
break
if time.time() - now > timeout:
module.fail_json(msg="Timeout waiting for prompt")
return result
def set_panwfw_password(module, ip_address, key_filename, newpassword, username):
stdout = ""
ssh = paramiko.SSHClient()
# add policy to accept all host keys, I haven't found
# a way to retrieve the instance SSH key fingerprint from AWS
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh.connect(ip_address, username=username, key_filename=key_filename)
shell = ssh.invoke_shell()
# wait for the shell to start
buff = wait_with_timeout(module, shell, ">")
stdout += buff
# step into config mode
shell.send('configure\n')
# wait for the config prompt
buff = wait_with_timeout(module, shell, "#")
stdout += buff
if module.check_mode:
# exit and close connection
shell.send('exit\n')
ssh.close()
return False, 'Connection test successful. Password left intact.'
# set admin password
shell.send('set mgt-config users ' + username + ' password\n')
# wait for the password prompt
buff = wait_with_timeout(module, shell, ":")
stdout += buff
# enter password for the first time
shell.send(newpassword + '\n')
# wait for the password prompt
buff = wait_with_timeout(module, shell, ":")
stdout += buff
# enter password for the second time
shell.send(newpassword + '\n')
# wait for the config mode prompt
buff = wait_with_timeout(module, shell, "#")
stdout += buff
# commit !
shell.send('commit\n')
# wait for the prompt
buff = wait_with_timeout(module, shell, "#", 120)
stdout += buff
if 'success' not in buff:
module.fail_json(msg="Error setting " + username + " password: " + stdout)
# exit
shell.send('exit\n')
ssh.close()
return True, stdout
def main():
argument_spec = dict(
ip_address=dict(required=True),
username=dict(default='admin'),
key_filename=dict(required=True),
newpassword=dict(no_log=True, required=True)
)
module = AnsibleModule(argument_spec=argument_spec, supports_check_mode=True)
if paramiko is None:
module.fail_json(msg='paramiko is required for this module')
ip_address = module.params["ip_address"]
if not ip_address:
module.fail_json(msg="ip_address should be specified")
key_filename = module.params["key_filename"]
if not key_filename:
module.fail_json(msg="key_filename should be specified")
newpassword = module.params["newpassword"]
if not newpassword:
module.fail_json(msg="newpassword is required")
username = module.params['username']
try:
changed, stdout = set_panwfw_password(module, ip_address, key_filename, newpassword, username)
module.exit_json(changed=changed, stdout=stdout)
except Exception:
x = sys.exc_info()[1]
module.fail_json(msg=x)
if __name__ == '__main__':
main()

View file

@ -0,0 +1,194 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
#
# Ansible module to manage PaloAltoNetworks Firewall
# (c) 2016, techbizdev <techbizdev@paloaltonetworks.com>
#
# 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 <http://www.gnu.org/licenses/>.
DOCUMENTATION = '''
---
module: panos_cert_gen_ssh
short_description: generates a self-signed certificate using SSH protocol with SSH key
description:
- This module generates a self-signed certificate that can be used by GlobalProtect client, SSL connector, or
- otherwise. Root certificate must be preset on the system first. This module depends on paramiko for ssh.
author: "Luigi Mori (@jtschichold), Ivan Bojer (@ivanbojer)"
requirements:
- paramiko
deprecated:
alternative: Use U(https://galaxy.ansible.com/PaloAltoNetworks/paloaltonetworks) instead.
removed_in: "2.12"
why: Consolidating code base.
notes:
- Checkmode is not supported.
options:
ip_address:
description:
- IP address (or hostname) of PAN-OS device being configured.
required: true
key_filename:
description:
- Location of the filename that is used for the auth. Either I(key_filename) or I(password) is required.
required: true
password:
description:
- Password credentials to use for auth. Either I(key_filename) or I(password) is required.
required: true
cert_friendly_name:
description:
- Human friendly certificate name (not CN but just a friendly name).
required: true
cert_cn:
description:
- Certificate CN (common name) embedded in the certificate signature.
required: true
signed_by:
description:
- Undersigning authority (CA) that MUST already be presents on the device.
required: true
rsa_nbits:
description:
- Number of bits used by the RSA algorithm for the certificate generation.
default: "2048"
'''
EXAMPLES = '''
# Generates a new self-signed certificate using ssh
- name: generate self signed certificate
panos_cert_gen_ssh:
ip_address: "192.168.1.1"
password: "paloalto"
cert_cn: "1.1.1.1"
cert_friendly_name: "test123"
signed_by: "root-ca"
'''
RETURN = '''
# Default return values
'''
ANSIBLE_METADATA = {'metadata_version': '1.1',
'status': ['deprecated'],
'supported_by': 'community'}
from ansible.module_utils._text import to_native
from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils.compat.paramiko import paramiko
import time
_PROMPTBUFF = 4096
def wait_with_timeout(module, shell, prompt, timeout=60):
now = time.time()
result = ""
while True:
if shell.recv_ready():
result += shell.recv(_PROMPTBUFF)
endresult = result.strip()
if len(endresult) != 0 and endresult[-1] == prompt:
break
if time.time() - now > timeout:
module.fail_json(msg="Timeout waiting for prompt")
return result
def generate_cert(module, ip_address, key_filename, password,
cert_cn, cert_friendly_name, signed_by, rsa_nbits):
stdout = ""
client = paramiko.SSHClient()
# add policy to accept all host keys, I haven't found
# a way to retrieve the instance SSH key fingerprint from AWS
client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
if not key_filename:
client.connect(ip_address, username="admin", password=password)
else:
client.connect(ip_address, username="admin", key_filename=key_filename)
shell = client.invoke_shell()
# wait for the shell to start
buff = wait_with_timeout(module, shell, ">")
stdout += buff
# generate self-signed certificate
if isinstance(cert_cn, list):
cert_cn = cert_cn[0]
cmd = 'request certificate generate signed-by {0} certificate-name {1} name {2} algorithm RSA rsa-nbits {3}\n'.format(
signed_by, cert_friendly_name, cert_cn, rsa_nbits)
shell.send(cmd)
# wait for the shell to complete
buff = wait_with_timeout(module, shell, ">")
stdout += buff
# exit
shell.send('exit\n')
if 'Success' not in buff:
module.fail_json(msg="Error generating self signed certificate: " + stdout)
client.close()
return stdout
def main():
argument_spec = dict(
ip_address=dict(required=True),
key_filename=dict(),
password=dict(no_log=True),
cert_cn=dict(required=True),
cert_friendly_name=dict(required=True),
rsa_nbits=dict(default='2048'),
signed_by=dict(required=True)
)
module = AnsibleModule(argument_spec=argument_spec, supports_check_mode=False,
required_one_of=[['key_filename', 'password']])
if paramiko is None:
module.fail_json(msg='paramiko is required for this module')
ip_address = module.params["ip_address"]
key_filename = module.params["key_filename"]
password = module.params["password"]
cert_cn = module.params["cert_cn"]
cert_friendly_name = module.params["cert_friendly_name"]
signed_by = module.params["signed_by"]
rsa_nbits = module.params["rsa_nbits"]
try:
stdout = generate_cert(module,
ip_address,
key_filename,
password,
cert_cn,
cert_friendly_name,
signed_by,
rsa_nbits)
except Exception as exc:
module.fail_json(msg=to_native(exc))
module.exit_json(changed=True, msg="okey dokey")
if __name__ == '__main__':
main()

View file

@ -0,0 +1,147 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
#
# Ansible module to manage PaloAltoNetworks Firewall
# (c) 2016, techbizdev <techbizdev@paloaltonetworks.com>
#
# 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 <http://www.gnu.org/licenses/>.
DOCUMENTATION = '''
---
module: panos_check
short_description: check if PAN-OS device is ready for configuration
description:
- Check if PAN-OS device is ready for being configured (no pending jobs).
- The check could be done once or multiple times until the device is ready.
author: "Luigi Mori (@jtschichold), Ivan Bojer (@ivanbojer)"
requirements:
- pan-python
deprecated:
alternative: Use U(https://galaxy.ansible.com/PaloAltoNetworks/paloaltonetworks) instead.
removed_in: "2.12"
why: Consolidating code base.
options:
timeout:
description:
- timeout of API calls
required: false
default: 0
interval:
description:
- time waited between checks
required: false
default: 0
extends_documentation_fragment:
- community.general.panos
'''
EXAMPLES = '''
# single check on 192.168.1.1 with credentials admin/admin
- name: check if ready
panos_check:
ip_address: "192.168.1.1"
password: "admin"
# check for 10 times, every 30 seconds, if device 192.168.1.1
# is ready, using credentials admin/admin
- name: wait for reboot
panos_check:
ip_address: "192.168.1.1"
password: "admin"
register: result
until: result is not failed
retries: 10
delay: 30
'''
RETURN = '''
# Default return values
'''
ANSIBLE_METADATA = {'metadata_version': '1.1',
'status': ['deprecated'],
'supported_by': 'community'}
from ansible.module_utils.basic import AnsibleModule
import time
try:
import pan.xapi
HAS_LIB = True
except ImportError:
HAS_LIB = False
def check_jobs(jobs, module):
job_check = False
for j in jobs:
status = j.find('.//status')
if status is None:
return False
if status.text != 'FIN':
return False
job_check = True
return job_check
def main():
argument_spec = dict(
ip_address=dict(required=True),
password=dict(required=True, no_log=True),
username=dict(default='admin'),
timeout=dict(default=0, type='int'),
interval=dict(default=0, type='int')
)
module = AnsibleModule(argument_spec=argument_spec, supports_check_mode=False)
if not HAS_LIB:
module.fail_json(msg='pan-python is required for this module')
ip_address = module.params["ip_address"]
password = module.params["password"]
username = module.params['username']
timeout = module.params['timeout']
interval = module.params['interval']
xapi = pan.xapi.PanXapi(
hostname=ip_address,
api_username=username,
api_password=password,
timeout=60
)
checkpnt = time.time() + timeout
while True:
try:
xapi.op(cmd="show jobs all", cmd_xml=True)
except Exception:
pass
else:
jobs = xapi.element_root.findall('.//job')
if check_jobs(jobs, module):
module.exit_json(changed=True, msg="okey dokey")
if time.time() > checkpnt:
break
time.sleep(interval)
module.fail_json(msg="Timeout")
if __name__ == '__main__':
main()

View file

@ -0,0 +1,235 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
#
# Ansible module to manage PaloAltoNetworks Firewall
# (c) 2019, Tomi Raittinen <tomi.raittinen@gmail.com>
# (c) 2016, techbizdev <techbizdev@paloaltonetworks.com>
#
# 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 <http://www.gnu.org/licenses/>.
DOCUMENTATION = '''
---
module: panos_commit
short_description: commit firewall's candidate configuration
description:
- PanOS module that will commit firewall's candidate configuration on
- the device. The new configuration will become active immediately.
author:
- Luigi Mori (@jtschichold)
- Ivan Bojer (@ivanbojer)
- Tomi Raittinen (@traittinen)
requirements:
- pan-python
deprecated:
alternative: Use U(https://galaxy.ansible.com/PaloAltoNetworks/paloaltonetworks) instead.
removed_in: "2.12"
why: Consolidating code base.
options:
ip_address:
description:
- IP address (or hostname) of PAN-OS device.
required: true
password:
description:
- Password for authentication. If the value is not specified in the
task, the value of environment variable C(ANSIBLE_NET_PASSWORD)
will be used instead.
required: true
username:
description:
- Username for authentication. If the value is not specified in the
task, the value of environment variable C(ANSIBLE_NET_USERNAME)
will be used instead if defined. C(admin) will be used if nothing
above is defined.
default: admin
interval:
description:
- interval for checking commit job
default: 0.5
timeout:
description:
- timeout for commit job
sync:
description:
- if commit should be synchronous
type: bool
default: 'yes'
description:
description:
- Commit description/comment
type: str
commit_changes_by:
description:
- Commit changes made by specified admin
type: list
commit_vsys:
description:
- Commit changes for specified VSYS
type: list
'''
EXAMPLES = '''
# Commit candidate config on 192.168.1.1 in sync mode
- panos_commit:
ip_address: "192.168.1.1"
username: "admin"
password: "admin"
'''
RETURN = '''
panos_commit:
description: Information about commit job.
returned: always
type: complex
version_added: 2.8
contains:
job_id:
description: Palo Alto job ID for the commit operation. Only returned if commit job is launched on device.
returned: always
type: str
sample: "139"
status_code:
description: Palo Alto API status code. Null if commit is successful.
returned: always
type: str
sample: 19
status_detail:
description: Palo Alto API detailed status message.
returned: always
type: str
sample: Configuration committed successfully
status_text:
description: Palo Alto API status text.
returned: always
type: str
sample: success
'''
ANSIBLE_METADATA = {'metadata_version': '1.1',
'status': ['deprecated'],
'supported_by': 'community'}
from ansible.module_utils.basic import AnsibleModule, env_fallback
import xml.etree.ElementTree as etree
try:
import pan.xapi
HAS_LIB = True
except ImportError:
HAS_LIB = False
def main():
argument_spec = dict(
ip_address=dict(required=True, type='str'),
password=dict(fallback=(env_fallback, ['ANSIBLE_NET_PASSWORD']), no_log=True),
username=dict(fallback=(env_fallback, ['ANSIBLE_NET_USERNAME']), default="admin"),
interval=dict(default=0.5),
timeout=dict(),
sync=dict(type='bool', default=True),
description=dict(type='str'),
commit_changes_by=dict(type='list'),
commit_vsys=dict(type='list')
)
module = AnsibleModule(argument_spec=argument_spec, supports_check_mode=False)
if not HAS_LIB:
module.fail_json(msg='pan-python is required for this module')
ip_address = module.params["ip_address"]
if not ip_address:
module.fail_json(msg="ip_address should be specified")
password = module.params["password"]
if not password:
module.fail_json(msg="password is required")
username = module.params['username']
if not username:
module.fail_json(msg="username is required")
interval = module.params['interval']
timeout = module.params['timeout']
sync = module.params['sync']
xapi = pan.xapi.PanXapi(
hostname=ip_address,
api_username=username,
api_password=password
)
cmd = "<commit>"
description = module.params["description"]
if description:
cmd += "<description>" + description + "</description>"
commit_changes_by = module.params["commit_changes_by"]
commit_vsys = module.params["commit_vsys"]
if commit_changes_by or commit_vsys:
cmd += "<partial>"
if commit_changes_by:
cmd += "<admin>"
for admin in commit_changes_by:
cmd += "<member>" + admin + "</member>"
cmd += "</admin>"
if commit_vsys:
cmd += "<vsys>"
for vsys in commit_vsys:
cmd += "<member>" + vsys + "</member>"
cmd += "</vsys>"
cmd += "</partial><force></force>"
cmd += "</commit>"
xapi.commit(
cmd=cmd,
sync=sync,
interval=interval,
timeout=timeout
)
try:
result = xapi.xml_root().encode('utf-8')
root = etree.fromstring(result)
job_id = root.find('./result/job/id').text
except AttributeError:
job_id = None
panos_commit_details = dict(
status_text=xapi.status,
status_code=xapi.status_code,
status_detail=xapi.status_detail,
job_id=job_id
)
if "Commit failed" in xapi.status_detail:
module.fail_json(msg=xapi.status_detail, panos_commit=panos_commit_details)
if job_id:
module.exit_json(changed=True, msg="Commit successful.", panos_commit=panos_commit_details)
else:
module.exit_json(changed=False, msg="No changes to commit.", panos_commit=panos_commit_details)
if __name__ == '__main__':
main()

View file

@ -0,0 +1,145 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
#
# Ansible module to manage PaloAltoNetworks Firewall
# (c) 2016, techbizdev <techbizdev@paloaltonetworks.com>
#
# 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 <http://www.gnu.org/licenses/>.
DOCUMENTATION = '''
---
module: panos_dag
short_description: create a dynamic address group
description:
- Create a dynamic address group object in the firewall used for policy rules
author: "Luigi Mori (@jtschichold), Ivan Bojer (@ivanbojer)"
requirements:
- pan-python
deprecated:
alternative: Use U(https://galaxy.ansible.com/PaloAltoNetworks/paloaltonetworks) instead.
removed_in: "2.12"
why: Consolidating code base.
options:
dag_name:
description:
- name of the dynamic address group
required: true
dag_filter:
description:
- dynamic filter user by the dynamic address group
required: true
commit:
description:
- commit if changed
type: bool
default: 'yes'
extends_documentation_fragment:
- community.general.panos
'''
EXAMPLES = '''
- name: dag
panos_dag:
ip_address: "192.168.1.1"
password: "admin"
dag_name: "dag-1"
dag_filter: "'aws-tag.aws:cloudformation:logical-id.ServerInstance' and 'instanceState.running'"
'''
RETURN = '''
# Default return values
'''
ANSIBLE_METADATA = {'metadata_version': '1.1',
'status': ['deprecated'],
'supported_by': 'community'}
from ansible.module_utils.basic import AnsibleModule
try:
import pan.xapi
HAS_LIB = True
except ImportError:
HAS_LIB = False
_ADDRGROUP_XPATH = "/config/devices/entry[@name='localhost.localdomain']" +\
"/vsys/entry[@name='vsys1']/address-group/entry[@name='%s']"
def addressgroup_exists(xapi, group_name):
xapi.get(_ADDRGROUP_XPATH % group_name)
e = xapi.element_root.find('.//entry')
if e is None:
return False
return True
def add_dag(xapi, dag_name, dag_filter):
if addressgroup_exists(xapi, dag_name):
return False
# setup the non encrypted part of the monitor
exml = []
exml.append('<dynamic>')
exml.append('<filter>%s</filter>' % dag_filter)
exml.append('</dynamic>')
exml = ''.join(exml)
xapi.set(xpath=_ADDRGROUP_XPATH % dag_name, element=exml)
return True
def main():
argument_spec = dict(
ip_address=dict(required=True),
password=dict(required=True, no_log=True),
username=dict(default='admin'),
dag_name=dict(required=True),
dag_filter=dict(required=True),
commit=dict(type='bool', default=True)
)
module = AnsibleModule(argument_spec=argument_spec, supports_check_mode=False)
if not HAS_LIB:
module.fail_json(msg='pan-python is required for this module')
ip_address = module.params["ip_address"]
password = module.params["password"]
username = module.params['username']
xapi = pan.xapi.PanXapi(
hostname=ip_address,
api_username=username,
api_password=password
)
dag_name = module.params['dag_name']
dag_filter = module.params['dag_filter']
commit = module.params['commit']
changed = add_dag(xapi, dag_name, dag_filter)
if changed and commit:
xapi.commit(cmd="<commit></commit>", sync=True, interval=1)
module.exit_json(changed=changed, msg="okey dokey")
if __name__ == '__main__':
main()

View file

@ -0,0 +1,232 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
#
# Ansible module to manage PaloAltoNetworks Firewall
# (c) 2016, techbizdev <techbizdev@paloaltonetworks.com>
#
# 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 <http://www.gnu.org/licenses/>.
# limitations under the License.
ANSIBLE_METADATA = {'metadata_version': '1.1',
'status': ['deprecated'],
'supported_by': 'community'}
DOCUMENTATION = '''
---
module: panos_dag_tags
short_description: Create tags for DAG's on PAN-OS devices.
description:
- Create the ip address to tag associations. Tags will in turn be used to create DAG's
author: "Vinay Venkataraghavan (@vinayvenkat)"
requirements:
- pan-python can be obtained from PyPI U(https://pypi.org/project/pan-python/)
- pandevice can be obtained from PyPI U(https://pypi.org/project/pandevice/)
deprecated:
alternative: Use U(https://galaxy.ansible.com/PaloAltoNetworks/paloaltonetworks) instead.
removed_in: "2.12"
why: Consolidating code base.
notes:
- Checkmode is not supported.
- Panorama is not supported.
options:
api_key:
description:
- API key that can be used instead of I(username)/I(password) credentials.
description:
description:
- The purpose / objective of the static Address Group
commit:
description:
- commit if changed
default: true
type: bool
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.
operation:
description:
- The action to be taken. Supported values are I(add)/I(update)/I(find)/I(delete).
tag_names:
description:
- The list of the tags that will be added or removed from the IP address.
ip_to_register:
description:
- IP that will be registered with the given tag names.
extends_documentation_fragment:
- community.general.panos
'''
EXAMPLES = '''
- name: Create the tags to map IP addresses
panos_dag_tags:
ip_address: "{{ ip_address }}"
password: "{{ password }}"
ip_to_register: "{{ ip_to_register }}"
tag_names: "{{ tag_names }}"
description: "Tags to allow certain IP's to access various SaaS Applications"
operation: 'add'
tags: "adddagip"
- name: List the IP address to tag mapping
panos_dag_tags:
ip_address: "{{ ip_address }}"
password: "{{ password }}"
tag_names: "{{ tag_names }}"
description: "List the IP address to tag mapping"
operation: 'list'
tags: "listdagip"
- name: Unregister an IP address from a tag mapping
panos_dag_tags:
ip_address: "{{ ip_address }}"
password: "{{ password }}"
ip_to_register: "{{ ip_to_register }}"
tag_names: "{{ tag_names }}"
description: "Unregister IP address from tag mappings"
operation: 'delete'
tags: "deletedagip"
'''
RETURN = '''
# Default return values
'''
try:
from pandevice import base
from pandevice import firewall
from pandevice import panorama
from pandevice import objects
from pan.xapi import PanXapiError
HAS_LIB = True
except ImportError:
HAS_LIB = False
from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils._text import to_native
def get_devicegroup(device, devicegroup):
dg_list = device.refresh_devices()
for group in dg_list:
if isinstance(group, panorama.DeviceGroup):
if group.name == devicegroup:
return group
return False
def register_ip_to_tag_map(device, ip_addresses, tag):
exc = None
try:
device.userid.register(ip_addresses, tag)
except PanXapiError as exc:
return False, exc
return True, exc
def get_all_address_group_mapping(device):
exc = None
ret = None
try:
ret = device.userid.get_registered_ip()
except PanXapiError as exc:
return False, exc
return ret, exc
def delete_address_from_mapping(device, ip_address, tags):
exc = None
try:
ret = device.userid.unregister(ip_address, tags)
except PanXapiError as exc:
return False, exc
return True, exc
def main():
argument_spec = dict(
ip_address=dict(required=True),
password=dict(required=True, no_log=True),
username=dict(default='admin'),
api_key=dict(no_log=True),
devicegroup=dict(default=None),
description=dict(default=None),
ip_to_register=dict(type='str', required=False),
tag_names=dict(type='list', required=True),
commit=dict(type='bool', default=True),
operation=dict(type='str', required=True)
)
module = AnsibleModule(argument_spec=argument_spec, supports_check_mode=False)
if not HAS_LIB:
module.fail_json(msg='pan-python is required for this module')
ip_address = module.params["ip_address"]
password = module.params["password"]
username = module.params['username']
api_key = module.params['api_key']
commit = module.params['commit']
devicegroup = module.params['devicegroup']
operation = module.params['operation']
# Create the device with the appropriate pandevice type
device = base.PanDevice.create_from_device(ip_address, username, password, api_key=api_key)
# If Panorama, validate the devicegroup
dev_group = None
if devicegroup and isinstance(device, panorama.Panorama):
dev_group = get_devicegroup(device, devicegroup)
if dev_group:
device.add(dev_group)
else:
module.fail_json(msg='\'%s\' device group not found in Panorama. Is the name correct?' % devicegroup)
result = None
if operation == 'add':
result, exc = register_ip_to_tag_map(device,
ip_addresses=module.params.get('ip_to_register', None),
tag=module.params.get('tag_names', None)
)
elif operation == 'list':
result, exc = get_all_address_group_mapping(device)
elif operation == 'delete':
result, exc = delete_address_from_mapping(device,
ip_address=module.params.get('ip_to_register', None),
tags=module.params.get('tag_names', [])
)
else:
module.fail_json(msg="Unsupported option")
if not result:
module.fail_json(msg=exc.message)
if commit:
try:
device.commit(sync=True)
except PanXapiError as exc:
module.fail_json(msg=to_native(exc))
module.exit_json(changed=True, msg=result)
if __name__ == "__main__":
main()

View file

@ -0,0 +1,195 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
#
# Ansible module to manage PaloAltoNetworks Firewall
# (c) 2016, techbizdev <techbizdev@paloaltonetworks.com>
#
# 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 <http://www.gnu.org/licenses/>.
DOCUMENTATION = '''
---
module: panos_import
short_description: import file on PAN-OS devices
description:
- Import file on PAN-OS device
notes:
- API reference documentation can be read from the C(/api/) directory of your appliance
- Certificate validation is enabled by default as of Ansible 2.6. This may break existing playbooks but should be disabled with caution.
author: "Luigi Mori (@jtschichold), Ivan Bojer (@ivanbojer)"
requirements:
- pan-python
- requests
- requests_toolbelt
deprecated:
alternative: Use U(https://galaxy.ansible.com/PaloAltoNetworks/paloaltonetworks) instead.
removed_in: "2.12"
why: Consolidating code base.
options:
category:
description:
- Category of file uploaded. The default is software.
- See API > Import section of the API reference for category options.
default: software
file:
description:
- Location of the file to import into device.
url:
description:
- URL of the file that will be imported to device.
validate_certs:
description:
- If C(no), SSL certificates will not be validated. Disabling certificate validation is not recommended.
default: yes
type: bool
extends_documentation_fragment:
- community.general.panos
'''
EXAMPLES = '''
# import software image PanOS_vm-6.1.1 on 192.168.1.1
- name: import software image into PAN-OS
panos_import:
ip_address: 192.168.1.1
username: admin
password: admin
file: /tmp/PanOS_vm-6.1.1
category: software
'''
RETURN = '''
# Default return values
'''
ANSIBLE_METADATA = {'metadata_version': '1.1',
'status': ['deprecated'],
'supported_by': 'community'}
from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils._text import to_native
import os.path
import xml.etree
import tempfile
import shutil
import os
try:
import pan.xapi
import requests
import requests_toolbelt
HAS_LIB = True
except ImportError:
HAS_LIB = False
def import_file(xapi, module, ip_address, file_, category):
xapi.keygen()
params = {
'type': 'import',
'category': category,
'key': xapi.api_key
}
filename = os.path.basename(file_)
mef = requests_toolbelt.MultipartEncoder(
fields={
'file': (filename, open(file_, 'rb'), 'application/octet-stream')
}
)
r = requests.post(
'https://' + ip_address + '/api/',
verify=module.params['validate_certs'],
params=params,
headers={'Content-Type': mef.content_type},
data=mef
)
# if something goes wrong just raise an exception
r.raise_for_status()
resp = xml.etree.ElementTree.fromstring(r.content)
if resp.attrib['status'] == 'error':
module.fail_json(msg=r.content)
return True, filename
def download_file(url):
r = requests.get(url, stream=True)
fo = tempfile.NamedTemporaryFile(prefix='ai', delete=False)
shutil.copyfileobj(r.raw, fo)
fo.close()
return fo.name
def delete_file(path):
os.remove(path)
def main():
argument_spec = dict(
ip_address=dict(required=True),
password=dict(required=True, no_log=True),
username=dict(default='admin'),
category=dict(default='software'),
file=dict(),
url=dict(),
validate_certs=dict(type='bool', default=True),
)
module = AnsibleModule(argument_spec=argument_spec, supports_check_mode=False, required_one_of=[['file', 'url']])
if not HAS_LIB:
module.fail_json(msg='pan-python, requests, and requests_toolbelt are required for this module')
ip_address = module.params["ip_address"]
password = module.params["password"]
username = module.params['username']
xapi = pan.xapi.PanXapi(
hostname=ip_address,
api_username=username,
api_password=password
)
file_ = module.params['file']
url = module.params['url']
category = module.params['category']
# we can get file from URL or local storage
if url is not None:
file_ = download_file(url)
try:
changed, filename = import_file(xapi, module, ip_address, file_, category)
except Exception as exc:
module.fail_json(msg=to_native(exc))
# cleanup and delete file if local
if url is not None:
delete_file(file_)
module.exit_json(changed=changed, filename=filename, msg="okey dokey")
if __name__ == '__main__':
main()

View file

@ -0,0 +1,179 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
#
# Ansible module to manage PaloAltoNetworks Firewall
# (c) 2016, techbizdev <techbizdev@paloaltonetworks.com>
#
# 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 <http://www.gnu.org/licenses/>.
ANSIBLE_METADATA = {'metadata_version': '1.1',
'status': ['deprecated'],
'supported_by': 'community'}
DOCUMENTATION = '''
---
module: panos_interface
short_description: configure data-port network interface for DHCP
description:
- Configure data-port (DP) network interface for DHCP. By default DP interfaces are static.
author: "Luigi Mori (@jtschichold), Ivan Bojer (@ivanbojer)"
requirements:
- pan-python can be obtained from PyPI U(https://pypi.org/project/pan-python/)
deprecated:
alternative: Use U(https://galaxy.ansible.com/PaloAltoNetworks/paloaltonetworks) instead.
removed_in: "2.12"
why: Consolidating code base.
notes:
- Checkmode is not supported.
options:
if_name:
description:
- Name of the interface to configure.
required: true
zone_name:
description: >
Name of the zone for the interface. If the zone does not exist it is created but if the zone exists and
it is not of the layer3 type the operation will fail.
required: true
create_default_route:
description:
- Whether or not to add default route with router learned via DHCP.
default: "false"
type: bool
commit:
description:
- Commit if changed
default: true
type: bool
extends_documentation_fragment:
- community.general.panos
'''
EXAMPLES = '''
- name: enable DHCP client on ethernet1/1 in zone public
interface:
password: "admin"
ip_address: "192.168.1.1"
if_name: "ethernet1/1"
zone_name: "public"
create_default_route: "yes"
'''
RETURN = '''
# Default return values
'''
from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils._text import to_native
try:
import pan.xapi
from pan.xapi import PanXapiError
HAS_LIB = True
except ImportError:
HAS_LIB = False
_IF_XPATH = "/config/devices/entry[@name='localhost.localdomain']" +\
"/network/interface/ethernet/entry[@name='%s']"
_ZONE_XPATH = "/config/devices/entry[@name='localhost.localdomain']" +\
"/vsys/entry/zone/entry"
_ZONE_XPATH_QUERY = _ZONE_XPATH + "[network/layer3/member/text()='%s']"
_ZONE_XPATH_IF = _ZONE_XPATH + "[@name='%s']/network/layer3/member[text()='%s']"
_VR_XPATH = "/config/devices/entry[@name='localhost.localdomain']" +\
"/network/virtual-router/entry"
def add_dhcp_if(xapi, if_name, zone_name, create_default_route):
if_xml = [
'<entry name="%s">',
'<layer3>',
'<dhcp-client>',
'<create-default-route>%s</create-default-route>',
'</dhcp-client>'
'</layer3>'
'</entry>'
]
cdr = 'yes'
if not create_default_route:
cdr = 'no'
if_xml = (''.join(if_xml)) % (if_name, cdr)
xapi.edit(xpath=_IF_XPATH % if_name, element=if_xml)
xapi.set(xpath=_ZONE_XPATH + "[@name='%s']/network/layer3" % zone_name,
element='<member>%s</member>' % if_name)
xapi.set(xpath=_VR_XPATH + "[@name='default']/interface",
element='<member>%s</member>' % if_name)
return True
def if_exists(xapi, if_name):
xpath = _IF_XPATH % if_name
xapi.get(xpath=xpath)
network = xapi.element_root.find('.//layer3')
return (network is not None)
def main():
argument_spec = dict(
ip_address=dict(required=True),
password=dict(required=True, no_log=True),
username=dict(default='admin'),
if_name=dict(required=True),
zone_name=dict(required=True),
create_default_route=dict(type='bool', default=False),
commit=dict(type='bool', default=True)
)
module = AnsibleModule(argument_spec=argument_spec, supports_check_mode=False)
if not HAS_LIB:
module.fail_json(msg='pan-python is required for this module')
ip_address = module.params["ip_address"]
password = module.params["password"]
username = module.params['username']
xapi = pan.xapi.PanXapi(
hostname=ip_address,
api_username=username,
api_password=password
)
if_name = module.params['if_name']
zone_name = module.params['zone_name']
create_default_route = module.params['create_default_route']
commit = module.params['commit']
ifexists = if_exists(xapi, if_name)
if ifexists:
module.exit_json(changed=False, msg="interface exists, not changed")
try:
changed = add_dhcp_if(xapi, if_name, zone_name, create_default_route)
except PanXapiError as exc:
module.fail_json(msg=to_native(exc))
if changed and commit:
xapi.commit(cmd="<commit></commit>", sync=True, interval=1)
module.exit_json(changed=changed, msg="okey dokey")
if __name__ == '__main__':
main()

View file

@ -0,0 +1,173 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
#
# Ansible module to manage PaloAltoNetworks Firewall
# (c) 2016, techbizdev <techbizdev@paloaltonetworks.com>
#
# 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 <http://www.gnu.org/licenses/>.
DOCUMENTATION = '''
---
module: panos_lic
short_description: apply authcode to a device/instance
description:
- Apply an authcode to a device.
- The authcode should have been previously registered on the Palo Alto Networks support portal.
- The device should have Internet access.
author: "Luigi Mori (@jtschichold), Ivan Bojer (@ivanbojer)"
requirements:
- pan-python
deprecated:
alternative: Use U(https://galaxy.ansible.com/PaloAltoNetworks/paloaltonetworks) instead.
removed_in: "2.12"
why: Consolidating code base.
options:
auth_code:
description:
- authcode to be applied
required: true
force:
description:
- whether to apply authcode even if device is already licensed
required: false
default: "false"
type: bool
extends_documentation_fragment:
- community.general.panos
'''
EXAMPLES = '''
- hosts: localhost
connection: local
tasks:
- name: fetch license
panos_lic:
ip_address: "192.168.1.1"
password: "paloalto"
auth_code: "IBADCODE"
register: result
- name: Display serialnumber (if already registered)
debug:
var: "{{result.serialnumber}}"
'''
RETURN = '''
serialnumber:
description: serialnumber of the device in case that it has been already registered
returned: success
type: str
sample: 007200004214
'''
ANSIBLE_METADATA = {'metadata_version': '1.1',
'status': ['deprecated'],
'supported_by': 'community'}
from ansible.module_utils.basic import AnsibleModule
try:
import pan.xapi
HAS_LIB = True
except ImportError:
HAS_LIB = False
def get_serial(xapi, module):
xapi.op(cmd="show system info", cmd_xml=True)
r = xapi.element_root
serial = r.find('.//serial')
if serial is None:
module.fail_json(msg="No <serial> tag in show system info")
serial = serial.text
return serial
def apply_authcode(xapi, module, auth_code):
try:
xapi.op(cmd='request license fetch auth-code "%s"' % auth_code,
cmd_xml=True)
except pan.xapi.PanXapiError:
if hasattr(xapi, 'xml_document'):
if 'Successfully' in xapi.xml_document:
return
if 'Invalid Auth Code' in xapi.xml_document:
module.fail_json(msg="Invalid Auth Code")
raise
return
def fetch_authcode(xapi, module):
try:
xapi.op(cmd='request license fetch', cmd_xml=True)
except pan.xapi.PanXapiError:
if hasattr(xapi, 'xml_document'):
if 'Successfully' in xapi.xml_document:
return
if 'Invalid Auth Code' in xapi.xml_document:
module.fail_json(msg="Invalid Auth Code")
raise
return
def main():
argument_spec = dict(
ip_address=dict(required=True),
password=dict(required=True, no_log=True),
auth_code=dict(),
username=dict(default='admin'),
force=dict(type='bool', default=False)
)
module = AnsibleModule(argument_spec=argument_spec, supports_check_mode=False)
if not HAS_LIB:
module.fail_json(msg='pan-python is required for this module')
ip_address = module.params["ip_address"]
password = module.params["password"]
auth_code = module.params["auth_code"]
force = module.params['force']
username = module.params['username']
xapi = pan.xapi.PanXapi(
hostname=ip_address,
api_username=username,
api_password=password
)
if not force:
serialnumber = get_serial(xapi, module)
if serialnumber != 'unknown':
return module.exit_json(changed=False, serialnumber=serialnumber)
if auth_code:
apply_authcode(xapi, module, auth_code)
else:
fetch_authcode(xapi, module)
module.exit_json(changed=True, msg="okey dokey")
if __name__ == '__main__':
main()

View file

@ -0,0 +1,125 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
#
# Ansible module to manage PaloAltoNetworks Firewall
# (c) 2016, techbizdev <techbizdev@paloaltonetworks.com>
#
# 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 <http://www.gnu.org/licenses/>.
DOCUMENTATION = '''
---
module: panos_loadcfg
short_description: load configuration on PAN-OS device
description:
- Load configuration on PAN-OS device
author: "Luigi Mori (@jtschichold), Ivan Bojer (@ivanbojer)"
requirements:
- pan-python
deprecated:
alternative: Use U(https://galaxy.ansible.com/PaloAltoNetworks/paloaltonetworks) instead.
removed_in: "2.12"
why: Consolidating code base.
options:
file:
description:
- configuration file to load
commit:
description:
- commit if changed
type: bool
default: 'yes'
extends_documentation_fragment:
- community.general.panos
'''
EXAMPLES = '''
# Import and load config file from URL
- name: import configuration
panos_import:
ip_address: "192.168.1.1"
password: "admin"
url: "{{ConfigURL}}"
category: "configuration"
register: result
- name: load configuration
panos_loadcfg:
ip_address: "192.168.1.1"
password: "admin"
file: "{{result.filename}}"
'''
RETURN = '''
# Default return values
'''
ANSIBLE_METADATA = {'metadata_version': '1.1',
'status': ['deprecated'],
'supported_by': 'community'}
from ansible.module_utils.basic import AnsibleModule
try:
import pan.xapi
HAS_LIB = True
except ImportError:
HAS_LIB = False
def load_cfgfile(xapi, module, ip_address, file_):
# load configuration file
cmd = '<load><config><from>%s</from></config></load>' %\
file_
xapi.op(cmd=cmd)
return True
def main():
argument_spec = dict(
ip_address=dict(required=True),
password=dict(required=True, no_log=True),
username=dict(default='admin'),
file=dict(),
commit=dict(type='bool', default=True)
)
module = AnsibleModule(argument_spec=argument_spec, supports_check_mode=False)
if not HAS_LIB:
module.fail_json(msg='pan-python is required for this module')
ip_address = module.params["ip_address"]
password = module.params["password"]
username = module.params['username']
file_ = module.params['file']
commit = module.params['commit']
xapi = pan.xapi.PanXapi(
hostname=ip_address,
api_username=username,
api_password=password
)
changed = load_cfgfile(xapi, module, ip_address, file_)
if changed and commit:
xapi.commit(cmd="<commit></commit>", sync=True, interval=1)
module.exit_json(changed=changed, msg="okey dokey")
if __name__ == '__main__':
main()

View file

@ -0,0 +1,389 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
#
# Ansible module to manage PaloAltoNetworks Firewall
# (c) 2016, techbizdev <techbizdev@paloaltonetworks.com>
#
# 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 <http://www.gnu.org/licenses/>.
# limitations under the License.
ANSIBLE_METADATA = {'metadata_version': '1.1',
'status': ['deprecated'],
'supported_by': 'community'}
DOCUMENTATION = '''
---
module: panos_match_rule
short_description: Test for match against a security rule on PAN-OS devices or Panorama management console.
description:
- Security policies allow you to enforce rules and take action, and can be as general or specific as needed.
author: "Robert Hagen (@rnh556)"
requirements:
- pan-python can be obtained from PyPI U(https://pypi.org/project/pan-python/)
- pandevice can be obtained from PyPI U(https://pypi.org/project/pandevice/)
deprecated:
alternative: Use U(https://galaxy.ansible.com/PaloAltoNetworks/paloaltonetworks) instead.
removed_in: "2.12"
why: Consolidating code base.
notes:
- Checkmode is not supported.
- Panorama NOT 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_type:
description:
- Type of rule. Valid types are I(security) or I(nat).
required: true
choices:
- security
- nat
source_zone:
description:
- The source zone.
source_ip:
description:
- The source IP address.
required: true
source_port:
description:
- The source port.
source_user:
description:
- The source user or group.
to_interface:
description:
- The inbound interface in a NAT rule.
destination_zone:
description:
- The destination zone.
destination_ip:
description:
- The destination IP address.
destination_port:
description:
- The destination port.
application:
description:
- The application.
protocol:
description:
- The IP protocol number from 1 to 255.
category:
description:
- URL category
vsys_id:
description:
- ID of the VSYS object.
default: "vsys1"
required: true
'''
EXAMPLES = '''
- name: check security rules for Google DNS
panos_match_rule:
ip_address: '{{ ip_address }}'
username: '{{ username }}'
password: '{{ password }}'
rule_type: 'security'
source_ip: '10.0.0.0'
destination_ip: '8.8.8.8'
application: 'dns'
destination_port: '53'
protocol: '17'
register: result
- debug: msg='{{result.stdout_lines}}'
- name: check security rules inbound SSH with user match
panos_match_rule:
ip_address: '{{ ip_address }}'
username: '{{ username }}'
password: '{{ password }}'
rule_type: 'security'
source_ip: '0.0.0.0'
source_user: 'mydomain\\jsmith'
destination_ip: '192.168.100.115'
destination_port: '22'
protocol: '6'
register: result
- debug: msg='{{result.stdout_lines}}'
- name: check NAT rules for source NAT
panos_match_rule:
ip_address: '{{ ip_address }}'
username: '{{ username }}'
password: '{{ password }}'
rule_type: 'nat'
source_zone: 'Prod-DMZ'
source_ip: '10.10.118.50'
to_interface: 'ethernet1/2'
destination_zone: 'Internet'
destination_ip: '0.0.0.0'
protocol: '6'
register: result
- debug: msg='{{result.stdout_lines}}'
- name: check NAT rules for inbound web
panos_match_rule:
ip_address: '{{ ip_address }}'
username: '{{ username }}'
password: '{{ password }}'
rule_type: 'nat'
source_zone: 'Internet'
source_ip: '0.0.0.0'
to_interface: 'ethernet1/1'
destination_zone: 'Prod DMZ'
destination_ip: '192.168.118.50'
destination_port: '80'
protocol: '6'
register: result
- debug: msg='{{result.stdout_lines}}'
- name: check security rules for outbound POP3 in vsys4
panos_match_rule:
ip_address: '{{ ip_address }}'
username: '{{ username }}'
password: '{{ password }}'
vsys_id: 'vsys4'
rule_type: 'security'
source_ip: '10.0.0.0'
destination_ip: '4.3.2.1'
application: 'pop3'
destination_port: '110'
protocol: '6'
register: result
- debug: msg='{{result.stdout_lines}}'
'''
RETURN = '''
# Default return values
'''
from ansible.module_utils.basic import AnsibleModule
try:
from pan.xapi import PanXapiError
from pan.xapi import PanXapiError
from pandevice import base
from pandevice import policies
from pandevice import panorama
import xmltodict
import json
HAS_LIB = True
except ImportError:
HAS_LIB = False
def create_security_test(**kwargs):
security_test = 'test security-policy-match'
# Add the source IP (required)
if kwargs['source_ip']:
security_test += ' source \"%s\"' % kwargs['source_ip']
# Add the source user (optional)
if kwargs['source_user']:
security_test += ' source-user \"%s\"' % kwargs['source_user']
# Add the destination IP (required)
if kwargs['destination_ip']:
security_test += ' destination \"%s\"' % kwargs['destination_ip']
# Add the application (optional)
if kwargs['application']:
security_test += ' application \"%s\"' % kwargs['application']
# Add the destination port (required)
if kwargs['destination_port']:
security_test += ' destination-port \"%s\"' % kwargs['destination_port']
# Add the IP protocol number (required)
if kwargs['protocol']:
security_test += ' protocol \"%s\"' % kwargs['protocol']
# Add the URL category (optional)
if kwargs['category']:
security_test += ' category \"%s\"' % kwargs['category']
# Return the resulting string
return security_test
def create_nat_test(**kwargs):
nat_test = 'test nat-policy-match'
# Add the source zone (optional)
if kwargs['source_zone']:
nat_test += ' from \"%s\"' % kwargs['source_zone']
# Add the source IP (required)
if kwargs['source_ip']:
nat_test += ' source \"%s\"' % kwargs['source_ip']
# Add the source user (optional)
if kwargs['source_port']:
nat_test += ' source-port \"%s\"' % kwargs['source_port']
# Add inbound interface (optional)
if kwargs['to_interface']:
nat_test += ' to-interface \"%s\"' % kwargs['to_interface']
# Add the destination zone (optional)
if kwargs['destination_zone']:
nat_test += ' to \"%s\"' % kwargs['destination_zone']
# Add the destination IP (required)
if kwargs['destination_ip']:
nat_test += ' destination \"%s\"' % kwargs['destination_ip']
# Add the destination port (optional)
if kwargs['destination_port']:
nat_test += ' destination-port \"%s\"' % kwargs['destination_port']
# Add the IP protocol number (required)
if kwargs['protocol']:
nat_test += ' protocol \"%s\"' % kwargs['protocol']
# Return the resulting string
return nat_test
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),
vsys_id=dict(default='vsys1'),
rule_type=dict(required=True, choices=['security', 'nat']),
source_zone=dict(default=None),
source_ip=dict(default=None),
source_user=dict(default=None),
source_port=dict(default=None, type=int),
to_interface=dict(default=None),
destination_zone=dict(default=None),
category=dict(default=None),
application=dict(default=None),
protocol=dict(default=None, type=int),
destination_ip=dict(default=None),
destination_port=dict(default=None, type=int)
)
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 libraries.')
ip_address = module.params["ip_address"]
password = module.params["password"]
username = module.params['username']
api_key = module.params['api_key']
vsys_id = module.params['vsys_id']
rule_type = module.params['rule_type']
source_zone = module.params['source_zone']
source_ip = module.params['source_ip']
source_user = module.params['source_user']
source_port = module.params['source_port']
to_interface = module.params['to_interface']
destination_zone = module.params['destination_zone']
destination_ip = module.params['destination_ip']
destination_port = module.params['destination_port']
category = module.params['category']
application = module.params['application']
protocol = module.params['protocol']
# Create the device with the appropriate pandevice type
device = base.PanDevice.create_from_device(ip_address, username, password, api_key=api_key)
# Fail the module if this is a Panorama instance
if isinstance(device, panorama.Panorama):
module.fail_json(
failed=1,
msg='Panorama is not supported.'
)
# Create and attach security and NAT rulebases. Then populate them.
sec_rule_base = nat_rule_base = policies.Rulebase()
device.add(sec_rule_base)
device.add(nat_rule_base)
policies.SecurityRule.refreshall(sec_rule_base)
policies.NatRule.refreshall(nat_rule_base)
# Which action shall we take on the object?
if rule_type == 'security':
# Search for the object
test_string = create_security_test(
source_ip=source_ip,
source_user=source_user,
destination_ip=destination_ip,
destination_port=destination_port,
application=application,
protocol=protocol,
category=category
)
elif rule_type == 'nat':
test_string = create_nat_test(
source_zone=source_zone,
source_ip=source_ip,
source_port=source_port,
to_interface=to_interface,
destination_zone=destination_zone,
destination_ip=destination_ip,
destination_port=destination_port,
protocol=protocol
)
# Submit the op command with the appropriate test string
try:
response = device.op(cmd=test_string, vsys=vsys_id)
except PanXapiError as exc:
module.fail_json(msg=exc.message)
if response.find('result/rules').__len__() == 1:
rule_name = response.find('result/rules/entry').text.split(';')[0]
elif rule_type == 'nat':
module.exit_json(msg='No matching NAT rule.')
else:
module.fail_json(msg='Rule match failed. Please check playbook syntax.')
if rule_type == 'security':
rule_match = sec_rule_base.find(rule_name, policies.SecurityRule)
elif rule_type == 'nat':
rule_match = nat_rule_base.find(rule_name, policies.NatRule)
# Print out the rule
module.exit_json(
stdout_lines=json.dumps(xmltodict.parse(rule_match.element_str()), indent=2),
msg='Rule matched'
)
if __name__ == '__main__':
main()

View file

@ -0,0 +1,191 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
#
# Ansible module to manage PaloAltoNetworks Firewall
# (c) 2016, techbizdev <techbizdev@paloaltonetworks.com>
#
# 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 <http://www.gnu.org/licenses/>.
DOCUMENTATION = '''
---
module: panos_mgtconfig
short_description: configure management settings of device
description:
- Configure management settings of device
author: "Luigi Mori (@jtschichold), Ivan Bojer (@ivanbojer)"
requirements:
- pan-python
deprecated:
alternative: Use U(https://galaxy.ansible.com/PaloAltoNetworks/paloaltonetworks) instead.
removed_in: "2.12"
why: Consolidating code base.
options:
dns_server_primary:
description:
- address of primary DNS server
dns_server_secondary:
description:
- address of secondary DNS server
panorama_primary:
description:
- address of primary Panorama server
panorama_secondary:
description:
- address of secondary Panorama server
commit:
description:
- commit if changed
type: bool
default: 'yes'
extends_documentation_fragment:
- community.general.panos
'''
EXAMPLES = '''
- name: set dns and panorama
panos_mgtconfig:
ip_address: "192.168.1.1"
password: "admin"
dns_server_primary: "1.1.1.1"
dns_server_secondary: "1.1.1.2"
panorama_primary: "1.1.1.3"
panorama_secondary: "1.1.1.4"
'''
RETURN = '''
# Default return values
'''
ANSIBLE_METADATA = {'metadata_version': '1.1',
'status': ['deprecated'],
'supported_by': 'community'}
from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils._text import to_native
try:
import pan.xapi
from pan.xapi import PanXapiError
HAS_LIB = True
except ImportError:
HAS_LIB = False
_XPATH_DNS_SERVERS = "/config/devices/entry[@name='localhost.localdomain']" +\
"/deviceconfig/system/dns-setting/servers"
_XPATH_PANORAMA_SERVERS = "/config" +\
"/devices/entry[@name='localhost.localdomain']" +\
"/deviceconfig/system"
def set_dns_server(xapi, new_dns_server, primary=True):
if primary:
tag = "primary"
else:
tag = "secondary"
xpath = _XPATH_DNS_SERVERS + "/" + tag
# check the current element value
xapi.get(xpath)
val = xapi.element_root.find(".//" + tag)
if val is not None:
# element exists
val = val.text
if val == new_dns_server:
return False
element = "<%(tag)s>%(value)s</%(tag)s>" %\
dict(tag=tag, value=new_dns_server)
xapi.edit(xpath, element)
return True
def set_panorama_server(xapi, new_panorama_server, primary=True):
if primary:
tag = "panorama-server"
else:
tag = "panorama-server-2"
xpath = _XPATH_PANORAMA_SERVERS + "/" + tag
# check the current element value
xapi.get(xpath)
val = xapi.element_root.find(".//" + tag)
if val is not None:
# element exists
val = val.text
if val == new_panorama_server:
return False
element = "<%(tag)s>%(value)s</%(tag)s>" %\
dict(tag=tag, value=new_panorama_server)
xapi.edit(xpath, element)
return True
def main():
argument_spec = dict(
ip_address=dict(required=True),
password=dict(required=True, no_log=True),
username=dict(default='admin'),
dns_server_primary=dict(),
dns_server_secondary=dict(),
panorama_primary=dict(),
panorama_secondary=dict(),
commit=dict(type='bool', default=True)
)
module = AnsibleModule(argument_spec=argument_spec, supports_check_mode=False)
if not HAS_LIB:
module.fail_json(msg='pan-python is required for this module')
ip_address = module.params["ip_address"]
password = module.params["password"]
username = module.params['username']
dns_server_primary = module.params['dns_server_primary']
dns_server_secondary = module.params['dns_server_secondary']
panorama_primary = module.params['panorama_primary']
panorama_secondary = module.params['panorama_secondary']
commit = module.params['commit']
xapi = pan.xapi.PanXapi(
hostname=ip_address,
api_username=username,
api_password=password
)
changed = False
try:
if dns_server_primary is not None:
changed |= set_dns_server(xapi, dns_server_primary, primary=True)
if dns_server_secondary is not None:
changed |= set_dns_server(xapi, dns_server_secondary, primary=False)
if panorama_primary is not None:
changed |= set_panorama_server(xapi, panorama_primary, primary=True)
if panorama_secondary is not None:
changed |= set_panorama_server(xapi, panorama_secondary, primary=False)
if changed and commit:
xapi.commit(cmd="<commit></commit>", sync=True, interval=1)
except PanXapiError as exc:
module.fail_json(msg=to_native(exc))
module.exit_json(changed=changed, msg="okey dokey")
if __name__ == '__main__':
main()

View file

@ -0,0 +1,471 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
# (c) 2016, techbizdev <techbizdev@paloaltonetworks.com>
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
from __future__ import absolute_import, division, print_function
__metaclass__ = type
DOCUMENTATION = '''
---
module: panos_nat_rule
short_description: create a policy NAT rule
description: >
- Create a policy nat rule. Keep in mind that we can either end up configuring source NAT, destination NAT, or
both. Instead of splitting it into two we will make a fair attempt to determine which one the user wants.
author: "Luigi Mori (@jtschichold), Ivan Bojer (@ivanbojer), Robert Hagen (@rnh556)"
requirements:
- pan-python can be obtained from PyPI U(https://pypi.org/project/pan-python/)
- pandevice can be obtained from PyPI U(https://pypi.org/project/pandevice/)
deprecated:
alternative: Use U(https://galaxy.ansible.com/PaloAltoNetworks/paloaltonetworks) instead.
removed_in: "2.12"
why: Consolidating code base.
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.
operation:
description:
- The action to be taken. Supported values are I(add)/I(update)/I(find)/I(delete).
required: true
choices:
- add
- update
- delete
- find
devicegroup:
description:
- If Panorama, the device group to put this rule in.
rule_name:
description:
- name of the SNAT rule
required: true
description:
description:
- The description
source_zone:
description:
- list of source zones
required: true
destination_zone:
description:
- destination zone
required: true
source_ip:
description:
- list of source addresses
default: ["any"]
destination_ip:
description:
- list of destination addresses
default: ["any"]
service:
description:
- service
default: "any"
snat_type:
description:
- type of source translation
choices:
- static-ip
- dynamic-ip-and-port
- dynamic-ip
snat_address_type:
description:
- type of source translation. Supported values are I(translated-address)/I(translated-address).
default: 'interface-address'
choices:
- interface-address
- translated-address
snat_static_address:
description:
- Source NAT translated address. Used with Static-IP translation.
snat_dynamic_address:
description:
- Source NAT translated address. Used with Dynamic-IP and Dynamic-IP-and-Port.
snat_interface:
description:
- snat interface
snat_interface_address:
description:
- snat interface address
snat_bidirectional:
description:
- bidirectional flag
type: bool
default: 'no'
dnat_address:
description:
- dnat translated address
dnat_port:
description:
- dnat translated port
tag_name:
description:
- Tag for the NAT rule.
to_interface:
description:
- Destination interface.
default: 'any'
commit:
description:
- Commit configuration if changed.
type: bool
default: 'yes'
'''
EXAMPLES = '''
# Create a source and destination nat rule
- name: Create NAT SSH rule for 10.0.1.101
panos_nat_rule:
ip_address: '{{ ip_address }}'
username: '{{ username }}'
password: '{{ password }}'
rule_name: "Web SSH"
source_zone: ["external"]
destination_zone: "external"
source: ["any"]
destination: ["10.0.0.100"]
service: "service-tcp-221"
snat_type: "dynamic-ip-and-port"
snat_interface: "ethernet1/2"
dnat_address: "10.0.1.101"
dnat_port: "22"
'''
RETURN = '''
# Default return values
'''
ANSIBLE_METADATA = {'metadata_version': '1.1',
'status': ['deprecated'],
'supported_by': 'community'}
# import pydevd
# pydevd.settrace('localhost', port=60374, stdoutToServer=True, stderrToServer=True)
from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils._text import to_native
try:
import pan.xapi
from pan.xapi import PanXapiError
import pandevice
from pandevice import base
from pandevice import firewall
from pandevice import panorama
from pandevice import objects
from pandevice import policies
import xmltodict
import json
HAS_LIB = True
except ImportError:
HAS_LIB = False
def get_devicegroup(device, devicegroup):
dg_list = device.refresh_devices()
for group in dg_list:
if isinstance(group, pandevice.panorama.DeviceGroup):
if group.name == devicegroup:
return group
return False
def get_rulebase(device, devicegroup):
# Build the rulebase
if isinstance(device, pandevice.firewall.Firewall):
rulebase = pandevice.policies.Rulebase()
device.add(rulebase)
elif isinstance(device, pandevice.panorama.Panorama):
dg = panorama.DeviceGroup(devicegroup)
device.add(dg)
rulebase = policies.PreRulebase()
dg.add(rulebase)
else:
return False
policies.NatRule.refreshall(rulebase)
return rulebase
def find_rule(rulebase, rule_name):
# Search for the rule name
rule = rulebase.find(rule_name)
if rule:
return rule
else:
return False
def create_nat_rule(**kwargs):
nat_rule = policies.NatRule(
name=kwargs['rule_name'],
description=kwargs['description'],
fromzone=kwargs['source_zone'],
source=kwargs['source_ip'],
tozone=kwargs['destination_zone'],
destination=kwargs['destination_ip'],
service=kwargs['service'],
to_interface=kwargs['to_interface'],
nat_type=kwargs['nat_type']
)
# Source translation: Static IP
if kwargs['snat_type'] in ['static-ip'] and kwargs['snat_static_address']:
nat_rule.source_translation_type = kwargs['snat_type']
nat_rule.source_translation_static_translated_address = kwargs['snat_static_address']
# Bi-directional flag set?
if kwargs['snat_bidirectional']:
nat_rule.source_translation_static_bi_directional = kwargs['snat_bidirectional']
# Source translation: Dynamic IP and port
elif kwargs['snat_type'] in ['dynamic-ip-and-port']:
nat_rule.source_translation_type = kwargs['snat_type']
nat_rule.source_translation_address_type = kwargs['snat_address_type']
# Interface address?
if kwargs['snat_interface']:
nat_rule.source_translation_interface = kwargs['snat_interface']
# Interface IP?
if kwargs['snat_interface_address']:
nat_rule.source_translation_ip_address = kwargs['snat_interface_address']
else:
nat_rule.source_translation_translated_addresses = kwargs['snat_dynamic_address']
# Source translation: Dynamic IP
elif kwargs['snat_type'] in ['dynamic-ip']:
if kwargs['snat_dynamic_address']:
nat_rule.source_translation_type = kwargs['snat_type']
nat_rule.source_translation_translated_addresses = kwargs['snat_dynamic_address']
else:
return False
# Destination translation
if kwargs['dnat_address']:
nat_rule.destination_translated_address = kwargs['dnat_address']
if kwargs['dnat_port']:
nat_rule.destination_translated_port = kwargs['dnat_port']
# Any tags?
if 'tag_name' in kwargs:
nat_rule.tag = kwargs['tag_name']
return nat_rule
def add_rule(rulebase, nat_rule):
if rulebase:
rulebase.add(nat_rule)
nat_rule.create()
return True
else:
return False
def update_rule(rulebase, nat_rule):
if rulebase:
rulebase.add(nat_rule)
nat_rule.apply()
return True
else:
return False
def main():
argument_spec = dict(
ip_address=dict(required=True),
username=dict(default='admin'),
password=dict(required=True, no_log=True),
api_key=dict(no_log=True),
operation=dict(required=True, choices=['add', 'update', 'delete', 'find']),
rule_name=dict(required=True),
description=dict(),
tag_name=dict(),
source_zone=dict(type='list'),
source_ip=dict(type='list', default=['any']),
destination_zone=dict(),
destination_ip=dict(type='list', default=['any']),
service=dict(default='any'),
to_interface=dict(default='any'),
snat_type=dict(choices=['static-ip', 'dynamic-ip-and-port', 'dynamic-ip']),
snat_address_type=dict(choices=['interface-address', 'translated-address'], default='interface-address'),
snat_static_address=dict(),
snat_dynamic_address=dict(type='list'),
snat_interface=dict(),
snat_interface_address=dict(),
snat_bidirectional=dict(type='bool', default=False),
dnat_address=dict(),
dnat_port=dict(),
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 libraries.')
ip_address = module.params["ip_address"]
password = module.params["password"]
username = module.params['username']
api_key = module.params['api_key']
operation = module.params['operation']
rule_name = module.params['rule_name']
description = module.params['description']
tag_name = module.params['tag_name']
source_zone = module.params['source_zone']
source_ip = module.params['source_ip']
destination_zone = module.params['destination_zone']
destination_ip = module.params['destination_ip']
service = module.params['service']
to_interface = module.params['to_interface']
nat_type = 'ipv4'
snat_type = module.params['snat_type']
snat_address_type = module.params['snat_address_type']
snat_static_address = module.params['snat_static_address']
snat_dynamic_address = module.params['snat_dynamic_address']
snat_interface = module.params['snat_interface']
snat_interface_address = module.params['snat_interface_address']
snat_bidirectional = module.params['snat_bidirectional']
dnat_address = module.params['dnat_address']
dnat_port = module.params['dnat_port']
devicegroup = module.params['devicegroup']
commit = module.params['commit']
# Create the device with the appropriate pandevice type
device = base.PanDevice.create_from_device(ip_address, username, password, api_key=api_key)
# If Panorama, validate the devicegroup
dev_group = None
if devicegroup and isinstance(device, panorama.Panorama):
dev_group = get_devicegroup(device, devicegroup)
if dev_group:
device.add(dev_group)
else:
module.fail_json(msg='\'%s\' device group not found in Panorama. Is the name correct?' % devicegroup)
# Get the rulebase
rulebase = get_rulebase(device, dev_group)
# Which action shall we take on the object?
if operation == "find":
# Search for the rule
match = find_rule(rulebase, rule_name)
# If found, format and return the result
if match:
match_dict = xmltodict.parse(match.element_str())
module.exit_json(
stdout_lines=json.dumps(match_dict, indent=2),
msg='Rule matched'
)
else:
module.fail_json(msg='Rule \'%s\' not found. Is the name correct?' % rule_name)
elif operation == "delete":
# Search for the object
match = find_rule(rulebase, rule_name)
# If found, delete it
if match:
try:
match.delete()
if commit:
device.commit(sync=True)
except PanXapiError as exc:
module.fail_json(msg=to_native(exc))
module.exit_json(changed=True, msg='Rule \'%s\' successfully deleted.' % rule_name)
else:
module.fail_json(msg='Rule \'%s\' not found. Is the name correct?' % rule_name)
elif operation == "add":
# Look for required parameters
if source_zone and destination_zone and nat_type:
pass
else:
module.fail_json(msg='Missing parameter. Required: source_zone, destination_zone, nat_type')
# Search for the rule. Fail if found.
match = find_rule(rulebase, rule_name)
if match:
module.fail_json(msg='Rule \'%s\' already exists. Use operation: \'update\' to change it.' % rule_name)
else:
try:
new_rule = create_nat_rule(
rule_name=rule_name,
description=description,
tag_name=tag_name,
source_zone=source_zone,
destination_zone=destination_zone,
source_ip=source_ip,
destination_ip=destination_ip,
service=service,
to_interface=to_interface,
nat_type=nat_type,
snat_type=snat_type,
snat_address_type=snat_address_type,
snat_static_address=snat_static_address,
snat_dynamic_address=snat_dynamic_address,
snat_interface=snat_interface,
snat_interface_address=snat_interface_address,
snat_bidirectional=snat_bidirectional,
dnat_address=dnat_address,
dnat_port=dnat_port
)
changed = add_rule(rulebase, new_rule)
if changed and commit:
device.commit(sync=True)
except PanXapiError as exc:
module.fail_json(msg=to_native(exc))
module.exit_json(changed=changed, msg='Rule \'%s\' successfully added.' % rule_name)
elif operation == 'update':
# Search for the rule. Update if found.
match = find_rule(rulebase, rule_name)
if match:
try:
new_rule = create_nat_rule(
rule_name=rule_name,
description=description,
tag_name=tag_name,
source_zone=source_zone,
destination_zone=destination_zone,
source_ip=source_ip,
destination_ip=destination_ip,
service=service,
to_interface=to_interface,
nat_type=nat_type,
snat_type=snat_type,
snat_address_type=snat_address_type,
snat_static_address=snat_static_address,
snat_dynamic_address=snat_dynamic_address,
snat_interface=snat_interface,
snat_interface_address=snat_interface_address,
snat_bidirectional=snat_bidirectional,
dnat_address=dnat_address,
dnat_port=dnat_port
)
changed = update_rule(rulebase, new_rule)
if changed and commit:
device.commit(sync=True)
except PanXapiError as exc:
module.fail_json(msg=to_native(exc))
module.exit_json(changed=changed, msg='Rule \'%s\' successfully updated.' % rule_name)
else:
module.fail_json(msg='Rule \'%s\' does not exist. Use operation: \'add\' to add it.' % rule_name)
if __name__ == '__main__':
main()

View file

@ -0,0 +1,490 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
#
# Ansible module to manage PaloAltoNetworks Firewall
# (c) 2016, techbizdev <techbizdev@paloaltonetworks.com>
#
# 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 <http://www.gnu.org/licenses/>.
# limitations under the License.
ANSIBLE_METADATA = {'metadata_version': '1.1',
'status': ['deprecated'],
'supported_by': 'community'}
DOCUMENTATION = '''
---
module: panos_object
short_description: create/read/update/delete object in PAN-OS or Panorama
description:
- Policy objects form the match criteria for policy rules and many other functions in PAN-OS. These may include
address object, address groups, service objects, service groups, and tag.
author: "Bob Hagen (@rnh556)"
requirements:
- pan-python can be obtained from PyPI U(https://pypi.org/project/pan-python/)
- pandevice can be obtained from PyPI U(https://pypi.org/project/pandevice/)
deprecated:
alternative: Use U(https://galaxy.ansible.com/PaloAltoNetworks/paloaltonetworks) instead.
removed_in: "2.12"
why: Consolidating code base.
notes:
- Checkmode is not supported.
- Panorama is supported.
options:
ip_address:
description:
- IP address (or hostname) of PAN-OS device or Panorama management console being configured.
required: true
username:
description:
- Username credentials to use for authentication.
default: "admin"
password:
description:
- Password credentials to use for authentication.
required: true
api_key:
description:
- API key that can be used instead of I(username)/I(password) credentials.
operation:
description:
- The operation to be performed. Supported values are I(add)/I(delete)/I(find).
required: true
choices:
- add
- update
- delete
- find
addressobject:
description:
- The name of the address object.
address:
description:
- The IP address of the host or network in CIDR notation.
address_type:
description:
- The type of address object definition. Valid types are I(ip-netmask) and I(ip-range).
default: 'ip-netmask'
choices:
- ip-netmask
- ip-range
- fqdn
addressgroup:
description:
- A static group of address objects or dynamic address group.
static_value:
description:
- A group of address objects to be used in an addressgroup definition.
dynamic_value:
description:
- The filter match criteria to be used in a dynamic addressgroup definition.
serviceobject:
description:
- The name of the service object.
source_port:
description:
- The source port to be used in a service object definition.
destination_port:
description:
- The destination port to be used in a service object definition.
protocol:
description:
- The IP protocol to be used in a service object definition. Valid values are I(tcp) or I(udp).
choices:
- tcp
- udp
servicegroup:
description:
- A group of service objects.
services:
description:
- The group of service objects used in a servicegroup definition.
description:
description:
- The description of the object.
tag_name:
description:
- The name of an object or rule tag.
color:
description: >
- The color of the tag object. Valid values are I(red, green, blue, yellow, copper, orange, purple, gray,
light green, cyan, light gray, blue gray, lime, black, gold, and brown).
choices:
- red
- green
- blue
- yellow
- copper
- orange
- purple
- gray
- light green
- cyan
- light gray
- blue gray
- lime
- black
- gold
- brown
devicegroup:
description: >
- The name of the Panorama device group. The group must exist on Panorama. If device group is not defined it
is assumed that we are contacting a firewall.
'''
EXAMPLES = '''
- name: search for shared address object
panos_object:
ip_address: '{{ ip_address }}'
username: '{{ username }}'
password: '{{ password }}'
operation: 'find'
address: 'DevNet'
- name: create an address group in devicegroup using API key
panos_object:
ip_address: '{{ ip_address }}'
api_key: '{{ api_key }}'
operation: 'add'
addressgroup: 'Prod_DB_Svrs'
static_value: ['prod-db1', 'prod-db2', 'prod-db3']
description: 'Production DMZ database servers'
tag_name: 'DMZ'
devicegroup: 'DMZ Firewalls'
- name: create a global service for TCP 3306
panos_object:
ip_address: '{{ ip_address }}'
api_key: '{{ api_key }}'
operation: 'add'
serviceobject: 'mysql-3306'
destination_port: '3306'
protocol: 'tcp'
description: 'MySQL on tcp/3306'
- name: create a global tag
panos_object:
ip_address: '{{ ip_address }}'
username: '{{ username }}'
password: '{{ password }}'
operation: 'add'
tag_name: 'ProjectX'
color: 'yellow'
description: 'Associated with Project X'
- name: delete an address object from a devicegroup using API key
panos_object:
ip_address: '{{ ip_address }}'
api_key: '{{ api_key }}'
operation: 'delete'
addressobject: 'Win2K test'
'''
RETURN = '''
# Default return values
'''
from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils._text import to_native
try:
import pan.xapi
from pan.xapi import PanXapiError
import pandevice
from pandevice import base
from pandevice import firewall
from pandevice import panorama
from pandevice import objects
import xmltodict
import json
HAS_LIB = True
except ImportError:
HAS_LIB = False
def get_devicegroup(device, devicegroup):
dg_list = device.refresh_devices()
for group in dg_list:
if isinstance(group, pandevice.panorama.DeviceGroup):
if group.name == devicegroup:
return group
return False
def find_object(device, dev_group, obj_name, obj_type):
# Get the firewall objects
obj_type.refreshall(device)
if isinstance(device, pandevice.firewall.Firewall):
addr = device.find(obj_name, obj_type)
return addr
elif isinstance(device, pandevice.panorama.Panorama):
addr = device.find(obj_name, obj_type)
if addr is None:
if dev_group:
device.add(dev_group)
obj_type.refreshall(dev_group)
addr = dev_group.find(obj_name, obj_type)
return addr
else:
return False
def create_object(**kwargs):
if kwargs['addressobject']:
newobject = objects.AddressObject(
name=kwargs['addressobject'],
value=kwargs['address'],
type=kwargs['address_type'],
description=kwargs['description'],
tag=kwargs['tag_name']
)
if newobject.type and newobject.value:
return newobject
else:
return False
elif kwargs['addressgroup']:
newobject = objects.AddressGroup(
name=kwargs['addressgroup'],
static_value=kwargs['static_value'],
dynamic_value=kwargs['dynamic_value'],
description=kwargs['description'],
tag=kwargs['tag_name']
)
if newobject.static_value or newobject.dynamic_value:
return newobject
else:
return False
elif kwargs['serviceobject']:
newobject = objects.ServiceObject(
name=kwargs['serviceobject'],
protocol=kwargs['protocol'],
source_port=kwargs['source_port'],
destination_port=kwargs['destination_port'],
tag=kwargs['tag_name']
)
if newobject.protocol and newobject.destination_port:
return newobject
else:
return False
elif kwargs['servicegroup']:
newobject = objects.ServiceGroup(
name=kwargs['servicegroup'],
value=kwargs['services'],
tag=kwargs['tag_name']
)
if newobject.value:
return newobject
else:
return False
elif kwargs['tag_name']:
newobject = objects.Tag(
name=kwargs['tag_name'],
color=kwargs['color'],
comments=kwargs['description']
)
if newobject.name:
return newobject
else:
return False
else:
return False
def add_object(device, dev_group, new_object):
if dev_group:
dev_group.add(new_object)
else:
device.add(new_object)
new_object.create()
return True
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),
operation=dict(required=True, choices=['add', 'update', 'delete', 'find']),
addressobject=dict(default=None),
addressgroup=dict(default=None),
serviceobject=dict(default=None),
servicegroup=dict(default=None),
address=dict(default=None),
address_type=dict(default='ip-netmask', choices=['ip-netmask', 'ip-range', 'fqdn']),
static_value=dict(type='list', default=None),
dynamic_value=dict(default=None),
protocol=dict(default=None, choices=['tcp', 'udp']),
source_port=dict(default=None),
destination_port=dict(default=None),
services=dict(type='list', default=None),
description=dict(default=None),
tag_name=dict(default=None),
color=dict(default=None, choices=['red', 'green', 'blue', 'yellow', 'copper', 'orange', 'purple',
'gray', 'light green', 'cyan', 'light gray', 'blue gray',
'lime', 'black', 'gold', 'brown']),
devicegroup=dict(default=None)
)
module = AnsibleModule(argument_spec=argument_spec, supports_check_mode=False,
required_one_of=[['api_key', 'password']],
mutually_exclusive=[['addressobject', 'addressgroup',
'serviceobject', 'servicegroup',
'tag_name']]
)
if not HAS_LIB:
module.fail_json(msg='Missing required libraries.')
ip_address = module.params["ip_address"]
password = module.params["password"]
username = module.params['username']
api_key = module.params['api_key']
operation = module.params['operation']
addressobject = module.params['addressobject']
addressgroup = module.params['addressgroup']
serviceobject = module.params['serviceobject']
servicegroup = module.params['servicegroup']
address = module.params['address']
address_type = module.params['address_type']
static_value = module.params['static_value']
dynamic_value = module.params['dynamic_value']
protocol = module.params['protocol']
source_port = module.params['source_port']
destination_port = module.params['destination_port']
services = module.params['services']
description = module.params['description']
tag_name = module.params['tag_name']
color = module.params['color']
devicegroup = module.params['devicegroup']
# Create the device with the appropriate pandevice type
device = base.PanDevice.create_from_device(ip_address, username, password, api_key=api_key)
# If Panorama, validate the devicegroup
dev_group = None
if devicegroup and isinstance(device, panorama.Panorama):
dev_group = get_devicegroup(device, devicegroup)
if dev_group:
device.add(dev_group)
else:
module.fail_json(msg='\'%s\' device group not found in Panorama. Is the name correct?' % devicegroup)
# What type of object are we talking about?
if addressobject:
obj_name = addressobject
obj_type = objects.AddressObject
elif addressgroup:
obj_name = addressgroup
obj_type = objects.AddressGroup
elif serviceobject:
obj_name = serviceobject
obj_type = objects.ServiceObject
elif servicegroup:
obj_name = servicegroup
obj_type = objects.ServiceGroup
elif tag_name:
obj_name = tag_name
obj_type = objects.Tag
else:
module.fail_json(msg='No object type defined!')
# Which operation shall we perform on the object?
if operation == "find":
# Search for the object
match = find_object(device, dev_group, obj_name, obj_type)
# If found, format and return the result
if match:
match_dict = xmltodict.parse(match.element_str())
module.exit_json(
stdout_lines=json.dumps(match_dict, indent=2),
msg='Object matched'
)
else:
module.fail_json(msg='Object \'%s\' not found. Is the name correct?' % obj_name)
elif operation == "delete":
# Search for the object
match = find_object(device, dev_group, obj_name, obj_type)
# If found, delete it
if match:
try:
match.delete()
except PanXapiError as exc:
module.fail_json(msg=to_native(exc))
module.exit_json(changed=True, msg='Object \'%s\' successfully deleted' % obj_name)
else:
module.fail_json(msg='Object \'%s\' not found. Is the name correct?' % obj_name)
elif operation == "add":
# Search for the object. Fail if found.
match = find_object(device, dev_group, obj_name, obj_type)
if match:
module.fail_json(msg='Object \'%s\' already exists. Use operation: \'update\' to change it.' % obj_name)
else:
try:
new_object = create_object(
addressobject=addressobject,
addressgroup=addressgroup,
serviceobject=serviceobject,
servicegroup=servicegroup,
address=address,
address_type=address_type,
static_value=static_value,
dynamic_value=dynamic_value,
protocol=protocol,
source_port=source_port,
destination_port=destination_port,
services=services,
description=description,
tag_name=tag_name,
color=color
)
changed = add_object(device, dev_group, new_object)
except PanXapiError as exc:
module.fail_json(msg=to_native(exc))
module.exit_json(changed=changed, msg='Object \'%s\' successfully added' % obj_name)
elif operation == "update":
# Search for the object. Update if found.
match = find_object(device, dev_group, obj_name, obj_type)
if match:
try:
new_object = create_object(
addressobject=addressobject,
addressgroup=addressgroup,
serviceobject=serviceobject,
servicegroup=servicegroup,
address=address,
address_type=address_type,
static_value=static_value,
dynamic_value=dynamic_value,
protocol=protocol,
source_port=source_port,
destination_port=destination_port,
services=services,
description=description,
tag_name=tag_name,
color=color
)
changed = add_object(device, dev_group, new_object)
except PanXapiError as exc:
module.fail_json(msg=to_native(exc))
module.exit_json(changed=changed, msg='Object \'%s\' successfully updated.' % obj_name)
else:
module.fail_json(msg='Object \'%s\' does not exist. Use operation: \'add\' to add it.' % obj_name)
if __name__ == '__main__':
main()

View file

@ -0,0 +1,158 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
#
# Ansible module to manage PaloAltoNetworks Firewall
# (c) 2016, techbizdev <techbizdev@paloaltonetworks.com>
#
# 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 <http://www.gnu.org/licenses/>.
ANSIBLE_METADATA = {'metadata_version': '1.1',
'status': ['deprecated'],
'supported_by': 'community'}
DOCUMENTATION = '''
---
module: panos_op
short_description: execute arbitrary OP commands on PANW devices (e.g. show interface all)
description: This module will allow user to pass and execute any supported OP command on the PANW device.
author: "Ivan Bojer (@ivanbojer)"
requirements:
- pan-python can be obtained from PyPI U(https://pypi.org/project/pan-python/)
- pandevice can be obtained from PyPI U(https://pypi.org/project/pandevice/)
deprecated:
alternative: Use U(https://galaxy.ansible.com/PaloAltoNetworks/paloaltonetworks) instead.
removed_in: "2.12"
why: Consolidating code base.
notes:
- Checkmode is NOT supported.
- Panorama is NOT supported.
options:
ip_address:
description:
- IP address (or hostname) of PAN-OS device or Panorama management console being configured.
required: true
username:
description:
- Username credentials to use for authentication.
required: false
default: "admin"
password:
description:
- Password credentials to use for authentication.
required: true
api_key:
description:
- API key that can be used instead of I(username)/I(password) credentials.
cmd:
description:
- The OP command to be performed.
required: true
'''
EXAMPLES = '''
- name: show list of all interfaces
panos_op:
ip_address: '{{ ip_address }}'
username: '{{ username }}'
password: '{{ password }}'
cmd: 'show interfaces all'
- name: show system info
panos_op:
ip_address: '{{ ip_address }}'
username: '{{ username }}'
password: '{{ password }}'
cmd: 'show system info'
'''
RETURN = '''
stdout:
description: output of the given OP command as JSON formatted string
returned: success
type: str
sample: "{system: {app-release-date: 2017/05/01 15:09:12}}"
stdout_xml:
description: output of the given OP command as JSON formatted string
returned: success
type: str
sample: "<response status=success><result><system><hostname>fw2</hostname>"
'''
from ansible.module_utils.basic import AnsibleModule
try:
import pan.xapi
from pan.xapi import PanXapiError
import pandevice
from pandevice import base
from pandevice import firewall
from pandevice import panorama
import xmltodict
import json
HAS_LIB = True
except ImportError:
HAS_LIB = False
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),
cmd=dict(required=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 libraries.')
ip_address = module.params["ip_address"]
password = module.params["password"]
username = module.params['username']
api_key = module.params['api_key']
cmd = module.params['cmd']
# Create the device with the appropriate pandevice type
device = base.PanDevice.create_from_device(ip_address, username, password, api_key=api_key)
changed = False
try:
xml_output = device.op(cmd, xml=True)
changed = True
except PanXapiError as exc:
if 'non NULL value' in exc.message:
# rewrap and call again
cmd_array = cmd.split()
cmd_array_len = len(cmd_array)
cmd_array[cmd_array_len - 1] = '\"' + cmd_array[cmd_array_len - 1] + '\"'
cmd2 = ' '.join(cmd_array)
try:
xml_output = device.op(cmd2, xml=True)
changed = True
except PanXapiError as exc:
module.fail_json(msg=exc.message)
obj_dict = xmltodict.parse(xml_output)
json_output = json.dumps(obj_dict)
module.exit_json(changed=changed, msg="Done", stdout=json_output, stdout_xml=xml_output)
if __name__ == '__main__':
main()

View file

@ -0,0 +1,203 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
#
# Ansible module to manage PaloAltoNetworks Firewall
# (c) 2016, techbizdev <techbizdev@paloaltonetworks.com>
#
# 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 <http://www.gnu.org/licenses/>.
DOCUMENTATION = '''
---
module: panos_pg
short_description: create a security profiles group
description:
- Create a security profile group
author: "Luigi Mori (@jtschichold), Ivan Bojer (@ivanbojer)"
requirements:
- pan-python
deprecated:
alternative: Use U(https://galaxy.ansible.com/PaloAltoNetworks/paloaltonetworks) instead.
removed_in: "2.12"
why: Consolidating code base.
options:
pg_name:
description:
- name of the security profile group
required: true
data_filtering:
description:
- name of the data filtering profile
file_blocking:
description:
- name of the file blocking profile
spyware:
description:
- name of the spyware profile
url_filtering:
description:
- name of the url filtering profile
virus:
description:
- name of the anti-virus profile
vulnerability:
description:
- name of the vulnerability profile
wildfire:
description:
- name of the wildfire analysis profile
commit:
description:
- commit if changed
type: bool
default: 'yes'
extends_documentation_fragment:
- community.general.panos
'''
EXAMPLES = '''
- name: setup security profile group
panos_pg:
ip_address: "192.168.1.1"
password: "admin"
username: "admin"
pg_name: "pg-default"
virus: "default"
spyware: "default"
vulnerability: "default"
'''
RETURN = '''
# Default return values
'''
ANSIBLE_METADATA = {'metadata_version': '1.1',
'status': ['deprecated'],
'supported_by': 'community'}
from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils._text import to_native
try:
import pan.xapi
from pan.xapi import PanXapiError
HAS_LIB = True
except ImportError:
HAS_LIB = False
_PG_XPATH = "/config/devices/entry[@name='localhost.localdomain']" +\
"/vsys/entry[@name='vsys1']" +\
"/profile-group/entry[@name='%s']"
def pg_exists(xapi, pg_name):
xapi.get(_PG_XPATH % pg_name)
e = xapi.element_root.find('.//entry')
if e is None:
return False
return True
def add_pg(xapi, pg_name, data_filtering, file_blocking, spyware,
url_filtering, virus, vulnerability, wildfire):
if pg_exists(xapi, pg_name):
return False
exml = []
if data_filtering is not None:
exml.append('<data-filtering><member>%s</member></data-filtering>' %
data_filtering)
if file_blocking is not None:
exml.append('<file-blocking><member>%s</member></file-blocking>' %
file_blocking)
if spyware is not None:
exml.append('<spyware><member>%s</member></spyware>' %
spyware)
if url_filtering is not None:
exml.append('<url-filtering><member>%s</member></url-filtering>' %
url_filtering)
if virus is not None:
exml.append('<virus><member>%s</member></virus>' %
virus)
if vulnerability is not None:
exml.append('<vulnerability><member>%s</member></vulnerability>' %
vulnerability)
if wildfire is not None:
exml.append('<wildfire-analysis><member>%s</member></wildfire-analysis>' %
wildfire)
exml = ''.join(exml)
xapi.set(xpath=_PG_XPATH % pg_name, element=exml)
return True
def main():
argument_spec = dict(
ip_address=dict(required=True),
password=dict(required=True, no_log=True),
username=dict(default='admin'),
pg_name=dict(required=True),
data_filtering=dict(),
file_blocking=dict(),
spyware=dict(),
url_filtering=dict(),
virus=dict(),
vulnerability=dict(),
wildfire=dict(),
commit=dict(type='bool', default=True)
)
module = AnsibleModule(argument_spec=argument_spec, supports_check_mode=False)
if not HAS_LIB:
module.fail_json(msg='pan-python is required for this module')
ip_address = module.params["ip_address"]
password = module.params["password"]
username = module.params['username']
xapi = pan.xapi.PanXapi(
hostname=ip_address,
api_username=username,
api_password=password
)
pg_name = module.params['pg_name']
data_filtering = module.params['data_filtering']
file_blocking = module.params['file_blocking']
spyware = module.params['spyware']
url_filtering = module.params['url_filtering']
virus = module.params['virus']
vulnerability = module.params['vulnerability']
wildfire = module.params['wildfire']
commit = module.params['commit']
try:
changed = add_pg(xapi, pg_name, data_filtering, file_blocking,
spyware, url_filtering, virus, vulnerability, wildfire)
if changed and commit:
xapi.commit(cmd="<commit></commit>", sync=True, interval=1)
except PanXapiError as exc:
module.fail_json(msg=to_native(exc))
module.exit_json(changed=changed, msg="okey dokey")
if __name__ == '__main__':
main()

View file

@ -0,0 +1,495 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
#
# Ansible module to manage PaloAltoNetworks Firewall
# (c) 2016, techbizdev <techbizdev@paloaltonetworks.com>
#
# 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 <http://www.gnu.org/licenses/>.
# limitations under the License.
ANSIBLE_METADATA = {'metadata_version': '1.1',
'status': ['deprecated'],
'supported_by': 'community'}
DOCUMENTATION = '''
---
module: panos_query_rules
short_description: PANOS module that allows search for security rules in PANW NGFW 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: "Bob Hagen (@rnh556)"
requirements:
- pan-python can be obtained from PyPI U(https://pypi.org/project/pan-python/)
- pandevice can be obtained from PyPI U(https://pypi.org/project/pandevice/)
- xmltodict can be obtains from PyPI U(https://pypi.org/project/xmltodict/)
deprecated:
alternative: Use U(https://galaxy.ansible.com/PaloAltoNetworks/paloaltonetworks) instead.
removed_in: "2.12"
why: Consolidating code base.
notes:
- Checkmode is not supported.
- Panorama is supported.
options:
ip_address:
description:
- IP address (or hostname) of PAN-OS firewall or Panorama management console being queried.
required: true
username:
description:
- Username credentials to use for authentication.
default: "admin"
password:
description:
- Password credentials to use for authentication.
required: true
api_key:
description:
- API key that can be used instead of I(username)/I(password) credentials.
application:
description:
- Name of the application or application group to be queried.
source_zone:
description:
- Name of the source security zone to be queried.
source_ip:
description:
- The source IP address to be queried.
source_port:
description:
- The source port to be queried.
destination_zone:
description:
- Name of the destination security zone to be queried.
destination_ip:
description:
- The destination IP address to be queried.
destination_port:
description:
- The destination port to be queried.
protocol:
description:
- The protocol used to be queried. Must be either I(tcp) or I(udp).
choices:
- tcp
- udp
tag_name:
description:
- Name of the rule tag to be queried.
devicegroup:
description:
- The Panorama device group in which to conduct the query.
'''
EXAMPLES = '''
- name: search for rules with tcp/3306
panos_query_rules:
ip_address: '{{ ip_address }}'
username: '{{ username }}'
password: '{{ password }}'
source_zone: 'DevNet'
destination_zone: 'DevVPC'
destination_port: '3306'
protocol: 'tcp'
- name: search devicegroup for inbound rules to dmz host
panos_query_rules:
ip_address: '{{ ip_address }}'
api_key: '{{ api_key }}'
destination_zone: 'DMZ'
destination_ip: '10.100.42.18'
address: 'DeviceGroupA'
- name: search for rules containing a specified rule tag
panos_query_rules:
ip_address: '{{ ip_address }}'
username: '{{ username }}'
password: '{{ password }}'
tag_name: 'ProjectX'
'''
RETURN = '''
# Default return values
'''
from ansible.module_utils.basic import AnsibleModule
try:
import pan.xapi
from pan.xapi import PanXapiError
import pandevice
from pandevice import base
from pandevice import firewall
from pandevice import panorama
from pandevice import objects
from pandevice import policies
import ipaddress
import xmltodict
import json
HAS_LIB = True
except ImportError:
HAS_LIB = False
def get_devicegroup(device, devicegroup):
dg_list = device.refresh_devices()
for group in dg_list:
if isinstance(group, pandevice.panorama.DeviceGroup):
if group.name == devicegroup:
return group
return False
def get_rulebase(device, devicegroup):
# Build the rulebase
if isinstance(device, firewall.Firewall):
rulebase = policies.Rulebase()
device.add(rulebase)
elif isinstance(device, panorama.Panorama):
dg = panorama.DeviceGroup(devicegroup)
device.add(dg)
rulebase = policies.PreRulebase()
dg.add(rulebase)
else:
return False
policies.SecurityRule.refreshall(rulebase)
return rulebase
def get_object(device, dev_group, obj_name):
# Search global address objects
match = device.find(obj_name, objects.AddressObject)
if match:
return match
# Search global address groups
match = device.find(obj_name, objects.AddressGroup)
if match:
return match
# Search Panorama device group
if isinstance(device, pandevice.panorama.Panorama):
# Search device group address objects
match = dev_group.find(obj_name, objects.AddressObject)
if match:
return match
# Search device group address groups
match = dev_group.find(obj_name, objects.AddressGroup)
if match:
return match
return False
def addr_in_obj(addr, obj):
ip = ipaddress.ip_address(addr)
# Process address objects
if isinstance(obj, objects.AddressObject):
if obj.type == 'ip-netmask':
net = ipaddress.ip_network(obj.value)
if ip in net:
return True
if obj.type == 'ip-range':
ip_range = obj.value.split('-')
lower = ipaddress.ip_address(ip_range[0])
upper = ipaddress.ip_address(ip_range[1])
if lower < ip < upper:
return True
return False
def get_services(device, dev_group, svc_list, obj_list):
for svc in svc_list:
# Search global address objects
global_obj_match = device.find(svc, objects.ServiceObject)
if global_obj_match:
obj_list.append(global_obj_match)
# Search global address groups
global_grp_match = device.find(svc, objects.ServiceGroup)
if global_grp_match:
get_services(device, dev_group, global_grp_match.value, obj_list)
# Search Panorama device group
if isinstance(device, pandevice.panorama.Panorama):
# Search device group address objects
dg_obj_match = dev_group.find(svc, objects.ServiceObject)
if dg_obj_match:
obj_list.append(dg_obj_match)
# Search device group address groups
dg_grp_match = dev_group.find(svc, objects.ServiceGroup)
if dg_grp_match:
get_services(device, dev_group, dg_grp_match.value, obj_list)
return obj_list
def port_in_svc(orientation, port, protocol, obj):
# Process address objects
if orientation == 'source':
for x in obj.source_port.split(','):
if '-' in x:
port_range = x.split('-')
lower = int(port_range[0])
upper = int(port_range[1])
if (lower <= int(port) <= upper) and (obj.protocol == protocol):
return True
else:
if port == x and obj.protocol == protocol:
return True
elif orientation == 'destination':
for x in obj.destination_port.split(','):
if '-' in x:
port_range = x.split('-')
lower = int(port_range[0])
upper = int(port_range[1])
if (lower <= int(port) <= upper) and (obj.protocol == protocol):
return True
else:
if port == x and obj.protocol == protocol:
return True
return False
def get_tag(device, dev_group, tag_name):
# Search global address objects
match = device.find(tag_name, objects.Tag)
if match:
return match
# Search Panorama device group
if isinstance(device, panorama.Panorama):
# Search device group address objects
match = dev_group.find(tag_name, objects.Tag)
if match:
return match
return False
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),
application=dict(default=None),
source_zone=dict(default=None),
destination_zone=dict(default=None),
source_ip=dict(default=None),
destination_ip=dict(default=None),
source_port=dict(default=None),
destination_port=dict(default=None),
protocol=dict(default=None, choices=['tcp', 'udp']),
tag_name=dict(default=None),
devicegroup=dict(default=None)
)
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 libraries.')
ip_address = module.params["ip_address"]
password = module.params["password"]
username = module.params['username']
api_key = module.params['api_key']
application = module.params['application']
source_zone = module.params['source_zone']
source_ip = module.params['source_ip']
source_port = module.params['source_port']
destination_zone = module.params['destination_zone']
destination_ip = module.params['destination_ip']
destination_port = module.params['destination_port']
protocol = module.params['protocol']
tag_name = module.params['tag_name']
devicegroup = module.params['devicegroup']
# Create the device with the appropriate pandevice type
device = base.PanDevice.create_from_device(ip_address, username, password, api_key=api_key)
# Grab the global objects
objects.AddressObject.refreshall(device)
objects.AddressGroup.refreshall(device)
objects.ServiceObject.refreshall(device)
objects.ServiceGroup.refreshall(device)
objects.Tag.refreshall(device)
# If Panorama, validate the devicegroup and grab the devicegroup objects
dev_group = None
if devicegroup and isinstance(device, panorama.Panorama):
dev_group = get_devicegroup(device, devicegroup)
if dev_group:
device.add(dev_group)
objects.AddressObject.refreshall(dev_group)
objects.AddressGroup.refreshall(dev_group)
objects.ServiceObject.refreshall(dev_group)
objects.ServiceGroup.refreshall(dev_group)
objects.Tag.refreshall(dev_group)
else:
module.fail_json(
failed=1,
msg='\'%s\' device group not found in Panorama. Is the name correct?' % devicegroup
)
# Build the rulebase and produce list
rulebase = get_rulebase(device, dev_group)
rulelist = rulebase.children
hitbase = policies.Rulebase()
loose_match = True
# Process each rule
for rule in rulelist:
hitlist = []
if source_zone:
source_zone_match = False
if loose_match and 'any' in rule.fromzone:
source_zone_match = True
else:
for object_string in rule.fromzone:
if object_string == source_zone:
source_zone_match = True
hitlist.append(source_zone_match)
if destination_zone:
destination_zone_match = False
if loose_match and 'any' in rule.tozone:
destination_zone_match = True
else:
for object_string in rule.tozone:
if object_string == destination_zone:
destination_zone_match = True
hitlist.append(destination_zone_match)
if source_ip:
source_ip_match = False
if loose_match and 'any' in rule.source:
source_ip_match = True
else:
for object_string in rule.source:
# Get a valid AddressObject or AddressGroup
obj = get_object(device, dev_group, object_string)
# Otherwise the object_string is not an object and should be handled differently
if obj is False:
if '-' in object_string:
obj = ipaddress.ip_address(source_ip)
source_range = object_string.split('-')
source_lower = ipaddress.ip_address(source_range[0])
source_upper = ipaddress.ip_address(source_range[1])
if source_lower <= obj <= source_upper:
source_ip_match = True
else:
if source_ip == object_string:
source_ip_match = True
if isinstance(obj, objects.AddressObject) and addr_in_obj(source_ip, obj):
source_ip_match = True
elif isinstance(obj, objects.AddressGroup) and obj.static_value:
for member_string in obj.static_value:
member = get_object(device, dev_group, member_string)
if addr_in_obj(source_ip, member):
source_ip_match = True
hitlist.append(source_ip_match)
if destination_ip:
destination_ip_match = False
if loose_match and 'any' in rule.destination:
destination_ip_match = True
else:
for object_string in rule.destination:
# Get a valid AddressObject or AddressGroup
obj = get_object(device, dev_group, object_string)
# Otherwise the object_string is not an object and should be handled differently
if obj is False:
if '-' in object_string:
obj = ipaddress.ip_address(destination_ip)
destination_range = object_string.split('-')
destination_lower = ipaddress.ip_address(destination_range[0])
destination_upper = ipaddress.ip_address(destination_range[1])
if destination_lower <= obj <= destination_upper:
destination_ip_match = True
else:
if destination_ip == object_string:
destination_ip_match = True
if isinstance(obj, objects.AddressObject) and addr_in_obj(destination_ip, obj):
destination_ip_match = True
elif isinstance(obj, objects.AddressGroup) and obj.static_value:
for member_string in obj.static_value:
member = get_object(device, dev_group, member_string)
if addr_in_obj(destination_ip, member):
destination_ip_match = True
hitlist.append(destination_ip_match)
if source_port:
source_port_match = False
orientation = 'source'
if loose_match and (rule.service[0] == 'any'):
source_port_match = True
elif rule.service[0] == 'application-default':
source_port_match = False # Fix this once apps are supported
else:
service_list = []
service_list = get_services(device, dev_group, rule.service, service_list)
for obj in service_list:
if port_in_svc(orientation, source_port, protocol, obj):
source_port_match = True
break
hitlist.append(source_port_match)
if destination_port:
destination_port_match = False
orientation = 'destination'
if loose_match and (rule.service[0] == 'any'):
destination_port_match = True
elif rule.service[0] == 'application-default':
destination_port_match = False # Fix this once apps are supported
else:
service_list = []
service_list = get_services(device, dev_group, rule.service, service_list)
for obj in service_list:
if port_in_svc(orientation, destination_port, protocol, obj):
destination_port_match = True
break
hitlist.append(destination_port_match)
if tag_name:
tag_match = False
if rule.tag:
for object_string in rule.tag:
obj = get_tag(device, dev_group, object_string)
if obj and (obj.name == tag_name):
tag_match = True
hitlist.append(tag_match)
# Add to hit rulebase
if False not in hitlist:
hitbase.add(rule)
# Dump the hit rulebase
if hitbase.children:
output_string = xmltodict.parse(hitbase.element_str())
module.exit_json(
stdout_lines=json.dumps(output_string, indent=2),
msg='%s of %s rules matched' % (hitbase.children.__len__(), rulebase.children.__len__())
)
else:
module.fail_json(msg='No matching rules found.')
if __name__ == '__main__':
main()

View file

@ -0,0 +1,110 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
#
# Ansible module to manage PaloAltoNetworks Firewall
# (c) 2016, techbizdev <techbizdev@paloaltonetworks.com>
#
# 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 <http://www.gnu.org/licenses/>.
DOCUMENTATION = '''
---
module: panos_restart
short_description: restart a device
description:
- Restart a device
author: "Luigi Mori (@jtschichold), Ivan Bojer (@ivanbojer)"
requirements:
- pan-python
deprecated:
alternative: Use U(https://galaxy.ansible.com/PaloAltoNetworks/paloaltonetworks) instead.
removed_in: "2.12"
why: Consolidating code base.
extends_documentation_fragment:
- community.general.panos
'''
EXAMPLES = '''
- panos_restart:
ip_address: "192.168.1.1"
username: "admin"
password: "admin"
'''
RETURN = '''
status:
description: success status
returned: success
type: str
sample: "okey dokey"
'''
ANSIBLE_METADATA = {'metadata_version': '1.1',
'status': ['deprecated'],
'supported_by': 'community'}
import sys
import traceback
try:
import pan.xapi
HAS_LIB = True
except ImportError:
HAS_LIB = False
from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils._text import to_native
def main():
argument_spec = dict(
ip_address=dict(),
password=dict(no_log=True),
username=dict(default='admin')
)
module = AnsibleModule(argument_spec=argument_spec, supports_check_mode=False)
if not HAS_LIB:
module.fail_json(msg='pan-python required for this module')
ip_address = module.params["ip_address"]
if not ip_address:
module.fail_json(msg="ip_address should be specified")
password = module.params["password"]
if not password:
module.fail_json(msg="password is required")
username = module.params['username']
xapi = pan.xapi.PanXapi(
hostname=ip_address,
api_username=username,
api_password=password
)
try:
xapi.op(cmd="<request><restart><system></system></restart></request>")
except Exception as e:
if 'succeeded' in to_native(e):
module.exit_json(changed=True, msg=to_native(e))
else:
module.fail_json(msg=to_native(e), exception=traceback.format_exc())
module.exit_json(changed=True, msg="okey dokey")
if __name__ == '__main__':
main()

View file

@ -0,0 +1,268 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
#
# Ansible module to manage PaloAltoNetworks Firewall
# (c) 2016, techbizdev <techbizdev@paloaltonetworks.com>
#
# 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 <http://www.gnu.org/licenses/>.
DOCUMENTATION = '''
---
module: panos_sag
short_description: Create a static address group.
description:
- Create a static address group object in the firewall used for policy rules.
author: "Vinay Venkataraghavan (@vinayvenkat)"
requirements:
- pan-python can be obtained from PyPI U(https://pypi.org/project/pan-python/)
- pandevice can be obtained from PyPI U(https://pypi.org/project/pandevice/)
- xmltodict can be obtained from PyPI U(https://pypi.org/project/xmltodict/)
deprecated:
alternative: Use U(https://galaxy.ansible.com/PaloAltoNetworks/paloaltonetworks) instead.
removed_in: "2.12"
why: Consolidating code base.
options:
api_key:
description:
- API key that can be used instead of I(username)/I(password) credentials.
sag_name:
description:
- name of the dynamic address group
required: true
sag_match_filter:
description:
- Static filter user by the address group
type: list
devicegroup:
description: >
- The name of the Panorama device group. The group must exist on Panorama. If device group is not defined
it is assumed that we are contacting a firewall.
description:
description:
- The purpose / objective of the static Address Group
tags:
description:
- Tags to be associated with the address group
commit:
description:
- commit if changed
type: bool
default: 'yes'
operation:
description:
- The operation to perform Supported values are I(add)/I(list)/I(delete).
required: true
choices:
- add
- list
- delete
extends_documentation_fragment:
- community.general.panos
'''
EXAMPLES = '''
- name: sag
panos_sag:
ip_address: "192.168.1.1"
password: "admin"
sag_name: "sag-1"
static_value: ['test-addresses', ]
description: "A description for the static address group"
tags: ["tags to be associated with the group", ]
'''
RETURN = '''
# Default return values
'''
ANSIBLE_METADATA = {'metadata_version': '1.1',
'status': ['deprecated'],
'supported_by': 'community'}
from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils._text import to_native
try:
from pandevice import base
from pandevice import firewall
from pandevice import panorama
from pandevice import objects
HAS_LIB = True
except ImportError:
HAS_LIB = False
def get_devicegroup(device, devicegroup):
dg_list = device.refresh_devices()
for group in dg_list:
if isinstance(group, panorama.DeviceGroup):
if group.name == devicegroup:
return group
return False
def find_object(device, dev_group, obj_name, obj_type):
# Get the firewall objects
obj_type.refreshall(device)
if isinstance(device, firewall.Firewall):
addr = device.find(obj_name, obj_type)
return addr
elif isinstance(device, panorama.Panorama):
addr = device.find(obj_name, obj_type)
if addr is None:
if dev_group:
device.add(dev_group)
obj_type.refreshall(dev_group)
addr = dev_group.find(obj_name, obj_type)
return addr
else:
return False
def create_address_group_object(**kwargs):
"""
Create an Address object
:param kwargs: key word arguments to instantiate AddressGroup object
@return False or ```objects.AddressObject```
"""
ad_object = objects.AddressGroup(
name=kwargs['address_gp_name'],
static_value=kwargs['sag_match_filter'],
description=kwargs['description'],
tag=kwargs['tag_name']
)
if ad_object.static_value or ad_object.dynamic_value:
return ad_object
else:
return None
def add_address_group(device, dev_group, ag_object):
"""
Create a new dynamic address group object on the
PAN FW.
:param device: Firewall Handle
:param dev_group: Panorama device group
:param ag_object: Address group object
"""
if dev_group:
dev_group.add(ag_object)
else:
device.add(ag_object)
exc = None
try:
ag_object.create()
except Exception as exc:
return False, exc
return True, exc
def delete_address_group(device, dev_group, obj_name):
"""
:param device:
:param dev_group:
:param obj_name:
:return:
"""
static_obj = find_object(device, dev_group, obj_name, objects.AddressGroup)
# If found, delete it
if static_obj:
try:
static_obj.delete()
except Exception as exc:
return False, exc
return True, None
else:
return False, None
def main():
argument_spec = dict(
ip_address=dict(required=True),
password=dict(required=True),
username=dict(default='admin'),
api_key=dict(no_log=True),
sag_match_filter=dict(type='list', required=False),
sag_name=dict(required=True),
commit=dict(type='bool', default=True),
devicegroup=dict(default=None),
description=dict(default=None),
tags=dict(type='list', default=[]),
operation=dict(type='str', required=True, choices=['add', 'list', 'delete'])
)
module = AnsibleModule(argument_spec=argument_spec, supports_check_mode=False,
required_one_of=[['api_key', 'password']])
if not HAS_LIB:
module.fail_json(msg='pan-python is required for this module')
ip_address = module.params["ip_address"]
password = module.params["password"]
username = module.params['username']
api_key = module.params['api_key']
operation = module.params['operation']
ag_object = create_address_group_object(address_gp_name=module.params.get('sag_name', None),
sag_match_filter=module.params.get('sag_match_filter', None),
description=module.params.get('description', None),
tag_name=module.params.get('tags', None)
)
commit = module.params['commit']
devicegroup = module.params['devicegroup']
# Create the device with the appropriate pandevice type
device = base.PanDevice.create_from_device(ip_address, username, password, api_key=api_key)
# If Panorama, validate the devicegroup
dev_group = None
if devicegroup and isinstance(device, panorama.Panorama):
dev_group = get_devicegroup(device, devicegroup)
if dev_group:
device.add(dev_group)
else:
module.fail_json(msg='\'%s\' device group not found in Panorama. Is the name correct?' % devicegroup)
if operation == 'add':
result, exc = add_address_group(device, dev_group, ag_object)
if result and commit:
try:
device.commit(sync=True)
except Exception as exc:
module.fail_json(msg=to_native(exc))
elif operation == 'delete':
obj_name = module.params.get('sag_name', None)
result, exc = delete_address_group(device, dev_group, obj_name)
if not result and exc:
module.fail_json(msg=exc.message)
elif not result:
module.fail_json(msg="Specified object not found.")
else:
module.fail_json(changed=False, msg="Unsupported option.")
module.exit_json(changed=True, msg="Address Group Operation Completed.")
if __name__ == '__main__':
main()

View file

@ -0,0 +1,576 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
# (c) 2016, techbizdev <techbizdev@paloaltonetworks.com>
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
from __future__ import absolute_import, division, print_function
__metaclass__ = type
ANSIBLE_METADATA = {'metadata_version': '1.1',
'status': ['deprecated'],
'supported_by': 'community'}
DOCUMENTATION = '''
---
module: panos_security_rule
short_description: Create security rule policy on PAN-OS devices or Panorama management console.
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), Robert Hagen (@rnh556)"
requirements:
- pan-python can be obtained from PyPI U(https://pypi.org/project/pan-python/)
- pandevice can be obtained from PyPI U(https://pypi.org/project/pandevice/)
- xmltodict can be obtained from PyPI U(https://pypi.org/project/xmltodict/)
deprecated:
alternative: Use U(https://galaxy.ansible.com/PaloAltoNetworks/paloaltonetworks) instead.
removed_in: "2.12"
why: Consolidating code base.
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.
operation:
description:
- The action to be taken. Supported values are I(add)/I(update)/I(find)/I(delete).
default: 'add'
choices:
- add
- update
- delete
- find
category:
description:
- The category.
type: list
default: ['any']
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.
tag_name:
description:
- Administrative tags that can be added to the rule. Note, tags must be already defined.
source_zone:
description:
- List of source zones.
default: "any"
destination_zone:
description:
- List of destination zones.
default: "any"
source_ip:
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_ip:
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.
type: bool
log_end:
description:
- Whether to log at session end.
default: true
type: bool
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.
antivirus:
description:
- Name of the already defined antivirus profile.
vulnerability:
description:
- Name of the already defined vulnerability profile.
spyware:
description:
- Name of the already defined spyware profile.
url_filtering:
description:
- Name of the already defined url_filtering profile.
file_blocking:
description:
- Name of the already defined file_blocking profile.
data_filtering:
description:
- Name of the already defined data_filtering profile.
wildfire_analysis:
description:
- Name of the already defined wildfire_analysis profile.
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.
commit:
description:
- Commit configuration if changed.
type: bool
default: 'yes'
'''
EXAMPLES = '''
- name: add an SSH inbound rule to devicegroup
panos_security_rule:
ip_address: '{{ ip_address }}'
username: '{{ username }}'
password: '{{ password }}'
operation: 'add'
rule_name: 'SSH permit'
description: 'SSH rule test'
tag_name: ['ProjectX']
source_zone: ['public']
destination_zone: ['private']
source_ip: ['any']
source_user: ['any']
destination_ip: ['1.1.1.1']
category: ['any']
application: ['ssh']
service: ['application-default']
hip_profiles: ['any']
action: 'allow'
devicegroup: 'Cloud Edge'
- name: add a rule to allow HTTP multimedia only from CDNs
panos_security_rule:
ip_address: '10.5.172.91'
username: 'admin'
password: 'paloalto'
operation: 'add'
rule_name: 'HTTP Multimedia'
description: 'Allow HTTP multimedia only to host at 1.1.1.1'
source_zone: ['public']
destination_zone: ['private']
source_ip: ['any']
source_user: ['any']
destination_ip: ['1.1.1.1']
category: ['content-delivery-networks']
application: ['http-video', 'http-audio']
service: ['service-http', 'service-https']
hip_profiles: ['any']
action: 'allow'
- name: add a more complex rule that uses security profiles
panos_security_rule:
ip_address: '{{ ip_address }}'
username: '{{ username }}'
password: '{{ password }}'
operation: 'add'
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'
- name: delete a devicegroup security rule
panos_security_rule:
ip_address: '{{ ip_address }}'
api_key: '{{ api_key }}'
operation: 'delete'
rule_name: 'Allow telnet'
devicegroup: 'DC Firewalls'
- name: find a specific security rule
panos_security_rule:
ip_address: '{{ ip_address }}'
password: '{{ password }}'
operation: 'find'
rule_name: 'Allow RDP to DCs'
register: result
- debug: msg='{{result.stdout_lines}}'
'''
RETURN = '''
# Default return values
'''
from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils._text import to_native
try:
import pan.xapi
from pan.xapi import PanXapiError
import pandevice
from pandevice import base
from pandevice import firewall
from pandevice import panorama
from pandevice import objects
from pandevice import policies
import xmltodict
import json
HAS_LIB = True
except ImportError:
HAS_LIB = False
def get_devicegroup(device, devicegroup):
dg_list = device.refresh_devices()
for group in dg_list:
if isinstance(group, pandevice.panorama.DeviceGroup):
if group.name == devicegroup:
return group
return False
def get_rulebase(device, devicegroup):
# Build the rulebase
if isinstance(device, pandevice.firewall.Firewall):
rulebase = pandevice.policies.Rulebase()
device.add(rulebase)
elif isinstance(device, pandevice.panorama.Panorama):
dg = panorama.DeviceGroup(devicegroup)
device.add(dg)
rulebase = policies.PreRulebase()
dg.add(rulebase)
else:
return False
policies.SecurityRule.refreshall(rulebase)
return rulebase
def find_rule(rulebase, rule_name):
# Search for the rule name
rule = rulebase.find(rule_name)
if rule:
return rule
else:
return False
def rule_is_match(propose_rule, current_rule):
match_check = ['name', 'description', 'group_profile', 'antivirus', 'vulnerability',
'spyware', 'url_filtering', 'file_blocking', 'data_filtering',
'wildfire_analysis', 'type', 'action', 'tag', 'log_start', 'log_end']
list_check = ['tozone', 'fromzone', 'source', 'source_user', 'destination', 'category',
'application', 'service', 'hip_profiles']
for check in match_check:
propose_check = getattr(propose_rule, check, None)
current_check = getattr(current_rule, check, None)
if propose_check != current_check:
return False
for check in list_check:
propose_check = getattr(propose_rule, check, [])
current_check = getattr(current_rule, check, [])
if set(propose_check) != set(current_check):
return False
return True
def create_security_rule(**kwargs):
security_rule = policies.SecurityRule(
name=kwargs['rule_name'],
description=kwargs['description'],
fromzone=kwargs['source_zone'],
source=kwargs['source_ip'],
source_user=kwargs['source_user'],
hip_profiles=kwargs['hip_profiles'],
tozone=kwargs['destination_zone'],
destination=kwargs['destination_ip'],
application=kwargs['application'],
service=kwargs['service'],
category=kwargs['category'],
log_start=kwargs['log_start'],
log_end=kwargs['log_end'],
action=kwargs['action'],
type=kwargs['rule_type']
)
if 'tag_name' in kwargs:
security_rule.tag = kwargs['tag_name']
# 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_rule(rulebase, sec_rule):
if rulebase:
rulebase.add(sec_rule)
sec_rule.create()
return True
else:
return False
def update_rule(rulebase, nat_rule):
if rulebase:
rulebase.add(nat_rule)
nat_rule.apply()
return True
else:
return False
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),
operation=dict(default='add', choices=['add', 'update', 'delete', 'find']),
rule_name=dict(required=True),
description=dict(default=''),
tag_name=dict(type='list'),
destination_zone=dict(type='list', default=['any']),
source_zone=dict(type='list', default=['any']),
source_ip=dict(type='list', default=["any"]),
source_user=dict(type='list', default=['any']),
destination_ip=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 libraries.')
ip_address = module.params["ip_address"]
password = module.params["password"]
username = module.params['username']
api_key = module.params['api_key']
operation = module.params['operation']
rule_name = module.params['rule_name']
description = module.params['description']
tag_name = module.params['tag_name']
source_zone = module.params['source_zone']
source_ip = module.params['source_ip']
source_user = module.params['source_user']
hip_profiles = module.params['hip_profiles']
destination_zone = module.params['destination_zone']
destination_ip = module.params['destination_ip']
application = module.params['application']
service = module.params['service']
category = module.params['category']
log_start = module.params['log_start']
log_end = module.params['log_end']
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']
rule_type = module.params['rule_type']
devicegroup = module.params['devicegroup']
commit = module.params['commit']
# Create the device with the appropriate pandevice type
device = base.PanDevice.create_from_device(ip_address, username, password, api_key=api_key)
# If Panorama, validate the devicegroup
dev_group = None
if devicegroup and isinstance(device, panorama.Panorama):
dev_group = get_devicegroup(device, devicegroup)
if dev_group:
device.add(dev_group)
else:
module.fail_json(msg='\'%s\' device group not found in Panorama. Is the name correct?' % devicegroup)
# Get the rulebase
rulebase = get_rulebase(device, dev_group)
# Which action shall we take on the object?
if operation == "find":
# Search for the object
match = find_rule(rulebase, rule_name)
# If found, format and return the result
if match:
match_dict = xmltodict.parse(match.element_str())
module.exit_json(
stdout_lines=json.dumps(match_dict, indent=2),
msg='Rule matched'
)
else:
module.fail_json(msg='Rule \'%s\' not found. Is the name correct?' % rule_name)
elif operation == "delete":
# Search for the object
match = find_rule(rulebase, rule_name)
# If found, delete it
if match:
try:
if commit:
match.delete()
except PanXapiError as exc:
module.fail_json(msg=to_native(exc))
module.exit_json(changed=True, msg='Rule \'%s\' successfully deleted' % rule_name)
else:
module.fail_json(msg='Rule \'%s\' not found. Is the name correct?' % rule_name)
elif operation == "add":
new_rule = create_security_rule(
rule_name=rule_name,
description=description,
tag_name=tag_name,
source_zone=source_zone,
destination_zone=destination_zone,
source_ip=source_ip,
source_user=source_user,
destination_ip=destination_ip,
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
)
# Search for the rule. Fail if found.
match = find_rule(rulebase, rule_name)
if match:
if rule_is_match(match, new_rule):
module.exit_json(changed=False, msg='Rule \'%s\' is already in place' % rule_name)
else:
module.fail_json(msg='Rule \'%s\' already exists. Use operation: \'update\' to change it.' % rule_name)
else:
try:
changed = add_rule(rulebase, new_rule)
if changed and commit:
device.commit(sync=True)
except PanXapiError as exc:
module.fail_json(msg=to_native(exc))
module.exit_json(changed=changed, msg='Rule \'%s\' successfully added' % rule_name)
elif operation == 'update':
# Search for the rule. Update if found.
match = find_rule(rulebase, rule_name)
if match:
try:
new_rule = create_security_rule(
rule_name=rule_name,
description=description,
tag_name=tag_name,
source_zone=source_zone,
destination_zone=destination_zone,
source_ip=source_ip,
source_user=source_user,
destination_ip=destination_ip,
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 = update_rule(rulebase, new_rule)
if changed and commit:
device.commit(sync=True)
except PanXapiError as exc:
module.fail_json(msg=to_native(exc))
module.exit_json(changed=changed, msg='Rule \'%s\' successfully updated' % rule_name)
else:
module.fail_json(msg='Rule \'%s\' does not exist. Use operation: \'add\' to add it.' % rule_name)
if __name__ == '__main__':
main()

View file

@ -0,0 +1,167 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
#
# Ansible module to manage PaloAltoNetworks Firewall
# (c) 2018, Jasper Mackenzie <jasper.mackenzie@gmail.com>
#
# 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 <http://www.gnu.org/licenses/>.
DOCUMENTATION = '''
---
module: panos_set
short_description: Execute arbitrary commands on a PAN-OS device using XPath and element
description:
- Run an arbitrary 'xapi' command taking an XPath (i.e get) or XPath and element (i.e set).
- See https://github.com/kevinsteves/pan-python/blob/master/doc/pan.xapi.rst for details
- Runs a 'set' command by default
- This should support _all_ commands that your PAN-OS device accepts vi it's cli
- cli commands are found as
- Once logged in issue 'debug cli on'
- Enter configuration mode by issuing 'configure'
- Enter your set (or other) command, for example 'set deviceconfig system timezone Australia/Melbourne'
- returns
- >
"<request cmd="set"
obj="/config/devices/entry[@name='localhost.localdomain']/deviceconfig/system"
cookie=XXXX><timezone>Australia/Melbourne</timezone></request>
- The 'xpath' is "/config/devices/entry[@name='localhost.localdomain']/deviceconfig/system"
- The 'element' is "<timezone>Australia/Melbourne</timezone>"
author: "Jasper Mackenzie (@spmp)"
deprecated:
alternative: Use U(https://galaxy.ansible.com/PaloAltoNetworks/paloaltonetworks) instead.
removed_in: "2.12"
why: Consolidating code base.
requirements:
- pan-python
options:
ip_address:
description:
- IP address or host FQDN of the target PAN-OS NVA
required: true
username:
description:
- User name for a user with admin rights on the PAN-OS NVA
default: admin
password:
description:
- Password for the given 'username'
required: true
command:
description:
- Xapi method name which supports 'xpath' or 'xpath' and 'element'
choices:
- set
- edit
- delete
- get
- show
- override
default: set
xpath:
description:
- The 'xpath' for the commands configurable
required: true
element:
description:
- The 'element' for the 'xpath' if required
extends_documentation_fragment:
- community.general.panos
'''
EXAMPLES = '''
- name: Set timezone on PA NVA
panos_set:
ip_address: "192.168.1.1"
username: "my-random-admin"
password: "admin1234"
xpath: "/config/devices/entry/deviceconfig/system"
element: "<timezone>Australia/Melbourne</timezone>"
- name: Commit configuration
panos_commit:
ip_address: "192.168.1.1"
username: "my-random-admin"
password: "admin1234"
'''
RETURN = '''
# Default return values
'''
ANSIBLE_METADATA = {'metadata_version': '1.1',
'status': ['deprecated'],
'supported_by': 'community'}
from ansible.module_utils.basic import AnsibleModule
try:
import pan.xapi
HAS_LIB = True
except ImportError:
HAS_LIB = False
def main():
argument_spec = dict(
ip_address=dict(required=True),
password=dict(required=True, no_log=True),
username=dict(default='admin'),
command=dict(default='set', choices=['set', 'edit', 'delete', 'get', 'show', 'override']),
xpath=dict(required=True),
element=dict(default=None)
)
module = AnsibleModule(argument_spec=argument_spec, supports_check_mode=False)
if not HAS_LIB:
module.fail_json(msg='pan-python is required for this module')
ip_address = module.params["ip_address"]
password = module.params["password"]
username = module.params['username']
xpath = module.params['xpath']
element = module.params['element']
xcommand = module.params['command']
xapi = pan.xapi.PanXapi(
hostname=ip_address,
api_username=username,
api_password=password,
timeout=60
)
if element is None:
# Issue command with no `element`
try:
getattr(xapi, xcommand)(xpath=xpath)
except Exception as e:
raise Exception("Failed to run '%s' with xpath: '%s' with the following error: %s" %
(xcommand, xpath, e))
else:
# Issue command with `element`
try:
getattr(xapi, xcommand)(xpath=xpath, element=element)
except Exception as e:
raise Exception("Failed to run '%s' with xpath: '%s' and element '%s' with the following error: %s" %
(xcommand, xpath, element, e))
module.exit_json(
status="success"
)
if __name__ == '__main__':
main()