Browse code

panos new module used for security policy creation (#20778)

* - new module used for security policy creation

* - added support for API key authentication
- added support for Panorama

* documentation changes

* documentation update

* minor doc changes based on review comments

* move ANSIBLE_METADATA

* changes to docs per reviewer comments

Ivan Bojer authored on 2017/03/03 01:42:54
Showing 2 changed files
... ...
@@ -235,6 +235,8 @@ Ansible Changes By Release
235 235
 - runit
236 236
 - serverless
237 237
 - set_stats
238
+- panos:
239
+  * panos_security_policy
238 240
 - smartos:
239 241
   * imgadm
240 242
   * vmadm
241 243
new file mode 100644
... ...
@@ -0,0 +1,493 @@
0
+#!/usr/bin/python
1
+# -*- coding: utf-8 -*-
2
+#
3
+# Ansible module to manage PaloAltoNetworks Firewall
4
+# (c) 2016, techbizdev <techbizdev@paloaltonetworks.com>
5
+#
6
+# This file is part of Ansible
7
+#
8
+# Ansible is free software: you can redistribute it and/or modify
9
+# it under the terms of the GNU General Public License as published by
10
+# the Free Software Foundation, either version 3 of the License, or
11
+# (at your option) any later version.
12
+#
13
+# Ansible is distributed in the hope that it will be useful,
14
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
15
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16
+# GNU General Public License for more details.
17
+#
18
+# You should have received a copy of the GNU General Public License
19
+# along with Ansible.  If not, see <http://www.gnu.org/licenses/>.
20
+
21
+ANSIBLE_METADATA = {'status': ['preview'],
22
+                    'supported_by': 'community',
23
+                    'version': '1.0'}
24
+
25
+DOCUMENTATION = '''
26
+---
27
+module: panos_security_policy
28
+short_description: Create security rule policy on PanOS devices.
29
+description: >
30
+    Security policies allow you to enforce rules and take action, and can be as general or specific as needed.
31
+    The policy rules are compared against the incoming traffic in sequence, and because the first rule that matches
32
+    the traffic is applied, the more specific rules must precede the more general ones.
33
+author: "Ivan Bojer (@ivanbojer)"
34
+version_added: "2.3"
35
+requirements:
36
+    - pan-python can be obtained from PyPi U(https://pypi.python.org/pypi/pan-python)
37
+    - pandevice can be obtained from PyPi U(https://pypi.python.org/pypi/pandevice)
38
+notes:
39
+    - Checkmode is not supported.
40
+    - Panorama is supported
41
+options:
42
+    ip_address:
43
+        description:
44
+            - IP address (or hostname) of PAN-OS device being configured.
45
+        required: true
46
+    username:
47
+        description:
48
+            - Username credentials to use for auth unless I(api_key) is set.
49
+        default: "admin"
50
+    password:
51
+        description:
52
+            - Password credentials to use for auth unless I(api_key) is set.
53
+        required: true
54
+    api_key:
55
+        description:
56
+            - API key that can be used instead of I(username)/I(password) credentials.
57
+    rule_name:
58
+        description:
59
+            - Name of the security rule.
60
+        required: true
61
+    rule_type:
62
+        description:
63
+            - Type of security rule (version 6.1 of PanOS and above).
64
+        default: "universal"
65
+    description:
66
+        description:
67
+            - Description for the security rule.
68
+        default: "None"
69
+    tag:
70
+        description:
71
+            - Administrative tags that can be added to the rule. Note, tags must be already defined.
72
+        default: "None"
73
+    from_zone:
74
+        description:
75
+            - List of source zones.
76
+        default: "any"
77
+    to_zone:
78
+        description:
79
+            - List of destination zones.
80
+        default: "any"
81
+    source:
82
+        description:
83
+            - List of source addresses.
84
+        default: "any"
85
+    source_user:
86
+        description:
87
+            - Use users to enforce policy for individual users or a group of users.
88
+        default: "any"
89
+    hip_profiles:
90
+        description: >
91
+            If you are using GlobalProtect with host information profile (HIP) enabled, you can also base the policy
92
+            on information collected by GlobalProtect. For example, the user access level can be determined HIP that
93
+            notifies the firewall about the user's local configuration.
94
+        default: "any"
95
+    destination:
96
+        description:
97
+            - List of destination addresses.
98
+        default: "any"
99
+    application:
100
+        description:
101
+            - List of applications.
102
+        default: "any"
103
+    service:
104
+        description:
105
+            - List of services.
106
+        default: "application-default"
107
+    log_start:
108
+        description:
109
+            - Whether to log at session start.
110
+        default: false
111
+    log_end:
112
+        description:
113
+            - Whether to log at session end.
114
+        default: true
115
+    action:
116
+        description:
117
+            - Action to apply once rules maches.
118
+        default: "allow"
119
+    group_profile:
120
+        description: >
121
+            Security profile group that is already defined in the system. This property supersedes antivirus,
122
+            vulnerability, spyware, url_filtering, file_blocking, data_filtering, and wildfire_analysis properties.
123
+        default: None
124
+    antivirus:
125
+        description:
126
+            - Name of the already defined antivirus profile.
127
+        default: None
128
+    vulnerability:
129
+        description:
130
+            - Name of the already defined vulnerability profile.
131
+        default: None
132
+    spyware:
133
+        description:
134
+            - Name of the already defined spyware profile.
135
+        default: None
136
+    url_filtering:
137
+        description:
138
+            - Name of the already defined url_filtering profile.
139
+        default: None
140
+    file_blocking:
141
+        description:
142
+            - Name of the already defined file_blocking profile.
143
+        default: None
144
+    data_filtering:
145
+        description:
146
+            - Name of the already defined data_filtering profile.
147
+        default: None
148
+    wildfire_analysis:
149
+        description:
150
+            - Name of the already defined wildfire_analysis profile.
151
+        default: None
152
+    devicegroup:
153
+        description: >
154
+            Device groups are used for the Panorama interaction with Firewall(s). The group must exists on Panorama.
155
+            If device group is not define we assume that we are contacting Firewall.
156
+        default: None
157
+    commit:
158
+        description:
159
+            - Commit configuration if changed.
160
+        default: true
161
+'''
162
+
163
+EXAMPLES = '''
164
+- name: permit ssh to 1.1.1.1
165
+  panos_security_policy:
166
+    ip_address: '10.5.172.91'
167
+    username: 'admin'
168
+    password: 'paloalto'
169
+    rule_name: 'SSH permit'
170
+    description: 'SSH rule test'
171
+    from_zone: ['public']
172
+    to_zone: ['private']
173
+    source: ['any']
174
+    source_user: ['any']
175
+    destination: ['1.1.1.1']
176
+    category: ['any']
177
+    application: ['ssh']
178
+    service: ['application-default']
179
+    hip_profiles: ['any']
180
+    action: 'allow'
181
+    commit: false
182
+
183
+- name: Allow HTTP multimedia only from CDNs
184
+  panos_security_policy:
185
+    ip_address: '10.5.172.91'
186
+    username: 'admin'
187
+    password: 'paloalto'
188
+    rule_name: 'HTTP Multimedia'
189
+    description: 'Allow HTTP multimedia only to host at 1.1.1.1'
190
+    from_zone: ['public']
191
+    to_zone: ['private']
192
+    source: ['any']
193
+    source_user: ['any']
194
+    destination: ['1.1.1.1']
195
+    category: ['content-delivery-networks']
196
+    application: ['http-video', 'http-audio']
197
+    service: ['service-http', 'service-https']
198
+    hip_profiles: ['any']
199
+    action: 'allow'
200
+    commit: false
201
+
202
+- name: more complex fictitious rule that uses profiles
203
+  panos_security_policy:
204
+    ip_address: '10.5.172.91'
205
+    username: 'admin'
206
+    password: 'paloalto'
207
+    rule_name: 'Allow HTTP w profile'
208
+    log_start: false
209
+    log_end: true
210
+    action: 'allow'
211
+    antivirus: 'default'
212
+    vulnerability: 'default'
213
+    spyware: 'default'
214
+    url_filtering: 'default'
215
+    wildfire_analysis: 'default'
216
+    commit: false
217
+
218
+- name: deny all
219
+  panos_security_policy:
220
+    ip_address: '10.5.172.91'
221
+    username: 'admin'
222
+    password: 'paloalto'
223
+    rule_name: 'DenyAll'
224
+    log_start: true
225
+    log_end: true
226
+    action: 'deny'
227
+    rule_type: 'interzone'
228
+    commit: false
229
+
230
+# permit ssh to 1.1.1.1 using panorama and pushing the configuration to firewalls
231
+# that are defined in 'DeviceGroupA' device group
232
+- name: permit ssh to 1.1.1.1 through Panorama
233
+  panos_security_policy:
234
+    ip_address: '10.5.172.92'
235
+    password: 'paloalto'
236
+    rule_name: 'SSH permit'
237
+    description: 'SSH rule test'
238
+    from_zone: ['public']
239
+    to_zone: ['private']
240
+    source: ['any']
241
+    source_user: ['any']
242
+    destination: ['1.1.1.1']
243
+    category: ['any']
244
+    application: ['ssh']
245
+    service: ['application-default']
246
+    hip_profiles: ['any']
247
+    action: 'allow'
248
+    devicegroup: 'DeviceGroupA'
249
+'''
250
+
251
+RETURN = '''
252
+# Default return values
253
+'''
254
+
255
+from ansible.module_utils.basic import AnsibleModule
256
+from ansible.module_utils.basic import get_exception
257
+
258
+try:
259
+    import pan.xapi
260
+    from pan.xapi import PanXapiError
261
+    import pandevice
262
+    import pandevice.firewall
263
+    import pandevice.panorama
264
+    import pandevice.objects
265
+    import pandevice.policies
266
+
267
+    HAS_LIB = True
268
+except ImportError:
269
+    HAS_LIB = False
270
+
271
+
272
+def security_rule_exists(device, rule_name):
273
+    if isinstance(device, pandevice.firewall.Firewall):
274
+        rule_base = pandevice.policies.Rulebase.refreshall(device)
275
+    elif isinstance(device, pandevice.panorama.Panorama):
276
+        # look for only pre-rulebase ATM
277
+        rule_base = pandevice.policies.PreRulebase.refreshall(device)
278
+
279
+    if rule_base:
280
+        rule_base = rule_base[0]
281
+        security_rules = rule_base.findall(pandevice.policies.SecurityRule)
282
+
283
+        if security_rules:
284
+            for r in security_rules:
285
+                if r.name == rule_name:
286
+                    return True
287
+
288
+    return False
289
+
290
+
291
+def create_security_rule(**kwargs):
292
+    security_rule = pandevice.policies.SecurityRule(
293
+        name=kwargs['rule_name'],
294
+        description=kwargs['description'],
295
+        tozone=kwargs['to_zone'],
296
+        fromzone=kwargs['from_zone'],
297
+        source=kwargs['source'],
298
+        source_user=kwargs['source_user'],
299
+        destination=kwargs['destination'],
300
+        category=kwargs['category'],
301
+        application=kwargs['application'],
302
+        service=kwargs['service'],
303
+        hip_profiles=kwargs['hip_profiles'],
304
+        log_start=kwargs['log_start'],
305
+        log_end=kwargs['log_end'],
306
+        type=kwargs['rule_type'],
307
+        action=kwargs['action'])
308
+
309
+    if 'tag' in kwargs:
310
+        security_rule.tag = kwargs['tag']
311
+
312
+    # profile settings
313
+    if 'group_profile' in kwargs:
314
+        security_rule.group = kwargs['group_profile']
315
+    else:
316
+        if 'antivirus' in kwargs:
317
+            security_rule.virus = kwargs['antivirus']
318
+        if 'vulnerability' in kwargs:
319
+            security_rule.vulnerability = kwargs['vulnerability']
320
+        if 'spyware' in kwargs:
321
+            security_rule.spyware = kwargs['spyware']
322
+        if 'url_filtering' in kwargs:
323
+            security_rule.url_filtering = kwargs['url_filtering']
324
+        if 'file_blocking' in kwargs:
325
+            security_rule.file_blocking = kwargs['file_blocking']
326
+        if 'data_filtering' in kwargs:
327
+            security_rule.data_filtering = kwargs['data_filtering']
328
+        if 'wildfire_analysis' in kwargs:
329
+            security_rule.wildfire_analysis = kwargs['wildfire_analysis']
330
+
331
+    return security_rule
332
+
333
+
334
+def add_security_rule(device, sec_rule):
335
+    if isinstance(device, pandevice.firewall.Firewall):
336
+        rule_base = pandevice.policies.Rulebase.refreshall(device)
337
+    elif isinstance(device, pandevice.panorama.Panorama):
338
+        # look for only pre-rulebase ATM
339
+        rule_base = pandevice.policies.PreRulebase.refreshall(device)
340
+
341
+    if rule_base:
342
+        rule_base = rule_base[0]
343
+
344
+        rule_base.add(sec_rule)
345
+        sec_rule.create()
346
+
347
+        return True
348
+    else:
349
+        return False
350
+
351
+
352
+def _commit(device, device_group=None):
353
+    """
354
+    :param device: either firewall or panorama
355
+    :param device_group: panorama device group or if none then 'all'
356
+    :return: True if successful
357
+    """
358
+    result = device.commit(sync=True)
359
+
360
+    if isinstance(device, pandevice.panorama.Panorama):
361
+        result = device.commit_all(sync=True, sync_all=True, devicegroup=device_group)
362
+
363
+    return result
364
+
365
+
366
+def main():
367
+    argument_spec = dict(
368
+        ip_address=dict(required=True),
369
+        password=dict(no_log=True),
370
+        username=dict(default='admin'),
371
+        api_key=dict(no_log=True),
372
+        rule_name=dict(required=True),
373
+        description=dict(default=''),
374
+        tag=dict(),
375
+        to_zone=dict(type='list', default=['any']),
376
+        from_zone=dict(type='list', default=['any']),
377
+        source=dict(type='list', default=["any"]),
378
+        source_user=dict(type='list', default=['any']),
379
+        destination=dict(type='list', default=["any"]),
380
+        category=dict(type='list', default=['any']),
381
+        application=dict(type='list', default=['any']),
382
+        service=dict(type='list', default=['application-default']),
383
+        hip_profiles=dict(type='list', default=['any']),
384
+        group_profile=dict(),
385
+        antivirus=dict(),
386
+        vulnerability=dict(),
387
+        spyware=dict(),
388
+        url_filtering=dict(),
389
+        file_blocking=dict(),
390
+        data_filtering=dict(),
391
+        wildfire_analysis=dict(),
392
+        log_start=dict(type='bool', default=False),
393
+        log_end=dict(type='bool', default=True),
394
+        rule_type=dict(default='universal'),
395
+        action=dict(default='allow'),
396
+        devicegroup=dict(),
397
+        commit=dict(type='bool', default=True)
398
+    )
399
+    module = AnsibleModule(argument_spec=argument_spec, supports_check_mode=False,
400
+                           required_one_of=[['api_key', 'password']])
401
+    if not HAS_LIB:
402
+        module.fail_json(msg='Missing required pan-python and pandevice modules.')
403
+
404
+    ip_address = module.params["ip_address"]
405
+    password = module.params["password"]
406
+    username = module.params['username']
407
+    api_key = module.params['api_key']
408
+    rule_name = module.params['rule_name']
409
+    description = module.params['description']
410
+    tag = module.params['tag']
411
+    from_zone = module.params['from_zone']
412
+    to_zone = module.params['to_zone']
413
+    source = module.params['source']
414
+    source_user = module.params['source_user']
415
+    destination = module.params['destination']
416
+    category = module.params['category']
417
+    application = module.params['application']
418
+    service = module.params['service']
419
+    hip_profiles = module.params['hip_profiles']
420
+    log_start = module.params['log_start']
421
+    log_end = module.params['log_end']
422
+    rule_type = module.params['rule_type']
423
+    action = module.params['action']
424
+
425
+    group_profile = module.params['group_profile']
426
+    antivirus = module.params['antivirus']
427
+    vulnerability = module.params['vulnerability']
428
+    spyware = module.params['spyware']
429
+    url_filtering = module.params['url_filtering']
430
+    file_blocking = module.params['file_blocking']
431
+    data_filtering = module.params['data_filtering']
432
+    wildfire_analysis = module.params['wildfire_analysis']
433
+
434
+    devicegroup = module.params['devicegroup']
435
+
436
+    commit = module.params['commit']
437
+
438
+    if devicegroup:
439
+        device = pandevice.panorama.Panorama(ip_address, username, password, api_key=api_key)
440
+        dev_grps = device.refresh_devices()
441
+
442
+        for grp in dev_grps:
443
+            if grp.name == devicegroup:
444
+                break
445
+            module.fail_json(msg=' \'%s\' device group not found in Panorama. Is the name correct?' % devicegroup)
446
+    else:
447
+        device = pandevice.firewall.Firewall(ip_address, username, password, api_key=api_key)
448
+
449
+    if security_rule_exists(device, rule_name):
450
+        module.fail_json(msg='Rule with the same name already exists.')
451
+
452
+    try:
453
+        sec_rule = create_security_rule(
454
+            rule_name=rule_name,
455
+            description=description,
456
+            tag=tag,
457
+            from_zone=from_zone,
458
+            to_zone=to_zone,
459
+            source=source,
460
+            source_user=source_user,
461
+            destination=destination,
462
+            category=category,
463
+            application=application,
464
+            service=service,
465
+            hip_profiles=hip_profiles,
466
+            group_profile=group_profile,
467
+            antivirus=antivirus,
468
+            vulnerability=vulnerability,
469
+            spyware=spyware,
470
+            url_filtering=url_filtering,
471
+            file_blocking=file_blocking,
472
+            data_filtering=data_filtering,
473
+            wildfire_analysis=wildfire_analysis,
474
+            log_start=log_start,
475
+            log_end=log_end,
476
+            rule_type=rule_type,
477
+            action=action
478
+        )
479
+
480
+        changed = add_security_rule(device, sec_rule)
481
+    except PanXapiError:
482
+        exc = get_exception()
483
+        module.fail_json(msg=exc.message)
484
+
485
+    if changed and commit:
486
+        result = _commit(device, devicegroup)
487
+
488
+    module.exit_json(changed=changed, msg="okey dokey")
489
+
490
+
491
+if __name__ == '__main__':
492
+    main()