1#!/usr/bin/python 2from __future__ import (absolute_import, division, print_function) 3# Copyright 2019 Fortinet, Inc. 4# 5# This program is free software: you can redistribute it and/or modify 6# it under the terms of the GNU General Public License as published by 7# the Free Software Foundation, either version 3 of the License, or 8# (at your option) any later version. 9# 10# This program is distributed in the hope that it will be useful, 11# but WITHOUT ANY WARRANTY; without even the implied warranty of 12# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13# GNU General Public License for more details. 14# 15# You should have received a copy of the GNU General Public License 16# along with this program. If not, see <https://www.gnu.org/licenses/>. 17 18__metaclass__ = type 19 20ANSIBLE_METADATA = {'status': ['preview'], 21 'supported_by': 'community', 22 'metadata_version': '1.1'} 23 24DOCUMENTATION = ''' 25--- 26module: fortios_firewall_interface_policy6 27short_description: Configure IPv6 interface policies in Fortinet's FortiOS and FortiGate. 28description: 29 - This module is able to configure a FortiGate or FortiOS (FOS) device by allowing the 30 user to set and modify firewall feature and interface_policy6 category. 31 Examples include all parameters and values need to be adjusted to datasources before usage. 32 Tested with FOS v6.0.5 33version_added: "2.8" 34author: 35 - Miguel Angel Munoz (@mamunozgonzalez) 36 - Nicolas Thomas (@thomnico) 37notes: 38 - Requires fortiosapi library developed by Fortinet 39 - Run as a local_action in your playbook 40requirements: 41 - fortiosapi>=0.9.8 42options: 43 host: 44 description: 45 - FortiOS or FortiGate IP address. 46 type: str 47 required: false 48 username: 49 description: 50 - FortiOS or FortiGate username. 51 type: str 52 required: false 53 password: 54 description: 55 - FortiOS or FortiGate password. 56 type: str 57 default: "" 58 vdom: 59 description: 60 - Virtual domain, among those defined previously. A vdom is a 61 virtual instance of the FortiGate that can be configured and 62 used as a different unit. 63 type: str 64 default: root 65 https: 66 description: 67 - Indicates if the requests towards FortiGate must use HTTPS protocol. 68 type: bool 69 default: true 70 ssl_verify: 71 description: 72 - Ensures FortiGate certificate must be verified by a proper CA. 73 type: bool 74 default: true 75 version_added: 2.9 76 state: 77 description: 78 - Indicates whether to create or remove the object. 79 This attribute was present already in previous version in a deeper level. 80 It has been moved out to this outer level. 81 type: str 82 required: false 83 choices: 84 - present 85 - absent 86 version_added: 2.9 87 firewall_interface_policy6: 88 description: 89 - Configure IPv6 interface policies. 90 default: null 91 type: dict 92 suboptions: 93 state: 94 description: 95 - B(Deprecated) 96 - Starting with Ansible 2.9 we recommend using the top-level 'state' parameter. 97 - HORIZONTALLINE 98 - Indicates whether to create or remove the object. 99 type: str 100 required: false 101 choices: 102 - present 103 - absent 104 address_type: 105 description: 106 - Policy address type (IPv4 or IPv6). 107 type: str 108 choices: 109 - ipv4 110 - ipv6 111 application_list: 112 description: 113 - Application list name. Source application.list.name. 114 type: str 115 application_list_status: 116 description: 117 - Enable/disable application control. 118 type: str 119 choices: 120 - enable 121 - disable 122 av_profile: 123 description: 124 - Antivirus profile. Source antivirus.profile.name. 125 type: str 126 av_profile_status: 127 description: 128 - Enable/disable antivirus. 129 type: str 130 choices: 131 - enable 132 - disable 133 comments: 134 description: 135 - Comments. 136 type: str 137 dlp_sensor: 138 description: 139 - DLP sensor name. Source dlp.sensor.name. 140 type: str 141 dlp_sensor_status: 142 description: 143 - Enable/disable DLP. 144 type: str 145 choices: 146 - enable 147 - disable 148 dsri: 149 description: 150 - Enable/disable DSRI. 151 type: str 152 choices: 153 - enable 154 - disable 155 dstaddr6: 156 description: 157 - IPv6 address object to limit traffic monitoring to network traffic sent to the specified address or range. 158 type: list 159 suboptions: 160 name: 161 description: 162 - Address name. Source firewall.address6.name firewall.addrgrp6.name. 163 required: true 164 type: str 165 interface: 166 description: 167 - Monitored interface name from available interfaces. Source system.zone.name system.interface.name. 168 type: str 169 ips_sensor: 170 description: 171 - IPS sensor name. Source ips.sensor.name. 172 type: str 173 ips_sensor_status: 174 description: 175 - Enable/disable IPS. 176 type: str 177 choices: 178 - enable 179 - disable 180 label: 181 description: 182 - Label. 183 type: str 184 logtraffic: 185 description: 186 - "Logging type to be used in this policy (Options: all | utm | disable)." 187 type: str 188 choices: 189 - all 190 - utm 191 - disable 192 policyid: 193 description: 194 - Policy ID. 195 required: true 196 type: int 197 scan_botnet_connections: 198 description: 199 - Enable/disable scanning for connections to Botnet servers. 200 type: str 201 choices: 202 - disable 203 - block 204 - monitor 205 service6: 206 description: 207 - Service name. 208 type: list 209 suboptions: 210 name: 211 description: 212 - Address name. Source firewall.service.custom.name firewall.service.group.name. 213 required: true 214 type: str 215 spamfilter_profile: 216 description: 217 - Antispam profile. Source spamfilter.profile.name. 218 type: str 219 spamfilter_profile_status: 220 description: 221 - Enable/disable antispam. 222 type: str 223 choices: 224 - enable 225 - disable 226 srcaddr6: 227 description: 228 - IPv6 address object to limit traffic monitoring to network traffic sent from the specified address or range. 229 type: list 230 suboptions: 231 name: 232 description: 233 - Address name. Source firewall.address6.name firewall.addrgrp6.name. 234 required: true 235 type: str 236 status: 237 description: 238 - Enable/disable this policy. 239 type: str 240 choices: 241 - enable 242 - disable 243 webfilter_profile: 244 description: 245 - Web filter profile. Source webfilter.profile.name. 246 type: str 247 webfilter_profile_status: 248 description: 249 - Enable/disable web filtering. 250 type: str 251 choices: 252 - enable 253 - disable 254''' 255 256EXAMPLES = ''' 257- hosts: localhost 258 vars: 259 host: "192.168.122.40" 260 username: "admin" 261 password: "" 262 vdom: "root" 263 ssl_verify: "False" 264 tasks: 265 - name: Configure IPv6 interface policies. 266 fortios_firewall_interface_policy6: 267 host: "{{ host }}" 268 username: "{{ username }}" 269 password: "{{ password }}" 270 vdom: "{{ vdom }}" 271 https: "False" 272 state: "present" 273 firewall_interface_policy6: 274 address_type: "ipv4" 275 application_list: "<your_own_value> (source application.list.name)" 276 application_list_status: "enable" 277 av_profile: "<your_own_value> (source antivirus.profile.name)" 278 av_profile_status: "enable" 279 comments: "<your_own_value>" 280 dlp_sensor: "<your_own_value> (source dlp.sensor.name)" 281 dlp_sensor_status: "enable" 282 dsri: "enable" 283 dstaddr6: 284 - 285 name: "default_name_13 (source firewall.address6.name firewall.addrgrp6.name)" 286 interface: "<your_own_value> (source system.zone.name system.interface.name)" 287 ips_sensor: "<your_own_value> (source ips.sensor.name)" 288 ips_sensor_status: "enable" 289 label: "<your_own_value>" 290 logtraffic: "all" 291 policyid: "19" 292 scan_botnet_connections: "disable" 293 service6: 294 - 295 name: "default_name_22 (source firewall.service.custom.name firewall.service.group.name)" 296 spamfilter_profile: "<your_own_value> (source spamfilter.profile.name)" 297 spamfilter_profile_status: "enable" 298 srcaddr6: 299 - 300 name: "default_name_26 (source firewall.address6.name firewall.addrgrp6.name)" 301 status: "enable" 302 webfilter_profile: "<your_own_value> (source webfilter.profile.name)" 303 webfilter_profile_status: "enable" 304''' 305 306RETURN = ''' 307build: 308 description: Build number of the fortigate image 309 returned: always 310 type: str 311 sample: '1547' 312http_method: 313 description: Last method used to provision the content into FortiGate 314 returned: always 315 type: str 316 sample: 'PUT' 317http_status: 318 description: Last result given by FortiGate on last operation applied 319 returned: always 320 type: str 321 sample: "200" 322mkey: 323 description: Master key (id) used in the last call to FortiGate 324 returned: success 325 type: str 326 sample: "id" 327name: 328 description: Name of the table used to fulfill the request 329 returned: always 330 type: str 331 sample: "urlfilter" 332path: 333 description: Path of the table used to fulfill the request 334 returned: always 335 type: str 336 sample: "webfilter" 337revision: 338 description: Internal revision number 339 returned: always 340 type: str 341 sample: "17.0.2.10658" 342serial: 343 description: Serial number of the unit 344 returned: always 345 type: str 346 sample: "FGVMEVYYQT3AB5352" 347status: 348 description: Indication of the operation's result 349 returned: always 350 type: str 351 sample: "success" 352vdom: 353 description: Virtual domain used 354 returned: always 355 type: str 356 sample: "root" 357version: 358 description: Version of the FortiGate 359 returned: always 360 type: str 361 sample: "v5.6.3" 362 363''' 364 365from ansible.module_utils.basic import AnsibleModule 366from ansible.module_utils.connection import Connection 367from ansible.module_utils.network.fortios.fortios import FortiOSHandler 368from ansible.module_utils.network.fortimanager.common import FAIL_SOCKET_MSG 369 370 371def login(data, fos): 372 host = data['host'] 373 username = data['username'] 374 password = data['password'] 375 ssl_verify = data['ssl_verify'] 376 377 fos.debug('on') 378 if 'https' in data and not data['https']: 379 fos.https('off') 380 else: 381 fos.https('on') 382 383 fos.login(host, username, password, verify=ssl_verify) 384 385 386def filter_firewall_interface_policy6_data(json): 387 option_list = ['address_type', 'application_list', 'application_list_status', 388 'av_profile', 'av_profile_status', 'comments', 389 'dlp_sensor', 'dlp_sensor_status', 'dsri', 390 'dstaddr6', 'interface', 'ips_sensor', 391 'ips_sensor_status', 'label', 'logtraffic', 392 'policyid', 'scan_botnet_connections', 'service6', 393 'spamfilter_profile', 'spamfilter_profile_status', 'srcaddr6', 394 'status', 'webfilter_profile', 'webfilter_profile_status'] 395 dictionary = {} 396 397 for attribute in option_list: 398 if attribute in json and json[attribute] is not None: 399 dictionary[attribute] = json[attribute] 400 401 return dictionary 402 403 404def underscore_to_hyphen(data): 405 if isinstance(data, list): 406 for elem in data: 407 elem = underscore_to_hyphen(elem) 408 elif isinstance(data, dict): 409 new_data = {} 410 for k, v in data.items(): 411 new_data[k.replace('_', '-')] = underscore_to_hyphen(v) 412 data = new_data 413 414 return data 415 416 417def firewall_interface_policy6(data, fos): 418 vdom = data['vdom'] 419 if 'state' in data and data['state']: 420 state = data['state'] 421 elif 'state' in data['firewall_interface_policy6'] and data['firewall_interface_policy6']: 422 state = data['firewall_interface_policy6']['state'] 423 else: 424 state = True 425 firewall_interface_policy6_data = data['firewall_interface_policy6'] 426 filtered_data = underscore_to_hyphen(filter_firewall_interface_policy6_data(firewall_interface_policy6_data)) 427 428 if state == "present": 429 return fos.set('firewall', 430 'interface-policy6', 431 data=filtered_data, 432 vdom=vdom) 433 434 elif state == "absent": 435 return fos.delete('firewall', 436 'interface-policy6', 437 mkey=filtered_data['policyid'], 438 vdom=vdom) 439 440 441def is_successful_status(status): 442 return status['status'] == "success" or \ 443 status['http_method'] == "DELETE" and status['http_status'] == 404 444 445 446def fortios_firewall(data, fos): 447 448 if data['firewall_interface_policy6']: 449 resp = firewall_interface_policy6(data, fos) 450 451 return not is_successful_status(resp), \ 452 resp['status'] == "success", \ 453 resp 454 455 456def main(): 457 fields = { 458 "host": {"required": False, "type": "str"}, 459 "username": {"required": False, "type": "str"}, 460 "password": {"required": False, "type": "str", "default": "", "no_log": True}, 461 "vdom": {"required": False, "type": "str", "default": "root"}, 462 "https": {"required": False, "type": "bool", "default": True}, 463 "ssl_verify": {"required": False, "type": "bool", "default": True}, 464 "state": {"required": False, "type": "str", 465 "choices": ["present", "absent"]}, 466 "firewall_interface_policy6": { 467 "required": False, "type": "dict", "default": None, 468 "options": { 469 "state": {"required": False, "type": "str", 470 "choices": ["present", "absent"]}, 471 "address_type": {"required": False, "type": "str", 472 "choices": ["ipv4", "ipv6"]}, 473 "application_list": {"required": False, "type": "str"}, 474 "application_list_status": {"required": False, "type": "str", 475 "choices": ["enable", "disable"]}, 476 "av_profile": {"required": False, "type": "str"}, 477 "av_profile_status": {"required": False, "type": "str", 478 "choices": ["enable", "disable"]}, 479 "comments": {"required": False, "type": "str"}, 480 "dlp_sensor": {"required": False, "type": "str"}, 481 "dlp_sensor_status": {"required": False, "type": "str", 482 "choices": ["enable", "disable"]}, 483 "dsri": {"required": False, "type": "str", 484 "choices": ["enable", "disable"]}, 485 "dstaddr6": {"required": False, "type": "list", 486 "options": { 487 "name": {"required": True, "type": "str"} 488 }}, 489 "interface": {"required": False, "type": "str"}, 490 "ips_sensor": {"required": False, "type": "str"}, 491 "ips_sensor_status": {"required": False, "type": "str", 492 "choices": ["enable", "disable"]}, 493 "label": {"required": False, "type": "str"}, 494 "logtraffic": {"required": False, "type": "str", 495 "choices": ["all", "utm", "disable"]}, 496 "policyid": {"required": True, "type": "int"}, 497 "scan_botnet_connections": {"required": False, "type": "str", 498 "choices": ["disable", "block", "monitor"]}, 499 "service6": {"required": False, "type": "list", 500 "options": { 501 "name": {"required": True, "type": "str"} 502 }}, 503 "spamfilter_profile": {"required": False, "type": "str"}, 504 "spamfilter_profile_status": {"required": False, "type": "str", 505 "choices": ["enable", "disable"]}, 506 "srcaddr6": {"required": False, "type": "list", 507 "options": { 508 "name": {"required": True, "type": "str"} 509 }}, 510 "status": {"required": False, "type": "str", 511 "choices": ["enable", "disable"]}, 512 "webfilter_profile": {"required": False, "type": "str"}, 513 "webfilter_profile_status": {"required": False, "type": "str", 514 "choices": ["enable", "disable"]} 515 516 } 517 } 518 } 519 520 module = AnsibleModule(argument_spec=fields, 521 supports_check_mode=False) 522 523 # legacy_mode refers to using fortiosapi instead of HTTPAPI 524 legacy_mode = 'host' in module.params and module.params['host'] is not None and \ 525 'username' in module.params and module.params['username'] is not None and \ 526 'password' in module.params and module.params['password'] is not None 527 528 if not legacy_mode: 529 if module._socket_path: 530 connection = Connection(module._socket_path) 531 fos = FortiOSHandler(connection) 532 533 is_error, has_changed, result = fortios_firewall(module.params, fos) 534 else: 535 module.fail_json(**FAIL_SOCKET_MSG) 536 else: 537 try: 538 from fortiosapi import FortiOSAPI 539 except ImportError: 540 module.fail_json(msg="fortiosapi module is required") 541 542 fos = FortiOSAPI() 543 544 login(module.params, fos) 545 is_error, has_changed, result = fortios_firewall(module.params, fos) 546 fos.logout() 547 548 if not is_error: 549 module.exit_json(changed=has_changed, meta=result) 550 else: 551 module.fail_json(msg="Error in repo", meta=result) 552 553 554if __name__ == '__main__': 555 main() 556