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_vip64 27short_description: Configure IPv6 to IPv4 virtual IPs 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 vip64 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_vip64: 88 description: 89 - Configure IPv6 to IPv4 virtual IPs. 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 arp_reply: 105 description: 106 - Enable ARP reply. 107 type: str 108 choices: 109 - disable 110 - enable 111 color: 112 description: 113 - Color of icon on the GUI. 114 type: int 115 comment: 116 description: 117 - Comment. 118 type: str 119 extip: 120 description: 121 - Start-external-IP [-end-external-IP]. 122 type: str 123 extport: 124 description: 125 - External service port. 126 type: str 127 id: 128 description: 129 - Custom defined id. 130 type: int 131 ldb_method: 132 description: 133 - Load balance method. 134 type: str 135 choices: 136 - static 137 - round-robin 138 - weighted 139 - least-session 140 - least-rtt 141 - first-alive 142 mappedip: 143 description: 144 - Start-mapped-IP [-end-mapped-IP]. 145 type: str 146 mappedport: 147 description: 148 - Mapped service port. 149 type: str 150 monitor: 151 description: 152 - Health monitors. 153 type: list 154 suboptions: 155 name: 156 description: 157 - Health monitor name. Source firewall.ldb-monitor.name. 158 required: true 159 type: str 160 name: 161 description: 162 - VIP64 name. 163 required: true 164 type: str 165 portforward: 166 description: 167 - Enable port forwarding. 168 type: str 169 choices: 170 - disable 171 - enable 172 protocol: 173 description: 174 - Mapped port protocol. 175 type: str 176 choices: 177 - tcp 178 - udp 179 realservers: 180 description: 181 - Real servers. 182 type: list 183 suboptions: 184 client_ip: 185 description: 186 - Restrict server to a client IP in this range. 187 type: str 188 healthcheck: 189 description: 190 - Per server health check. 191 type: str 192 choices: 193 - disable 194 - enable 195 - vip 196 holddown_interval: 197 description: 198 - Hold down interval. 199 type: int 200 id: 201 description: 202 - Real server ID. 203 required: true 204 type: int 205 ip: 206 description: 207 - Mapped server IP. 208 type: str 209 max_connections: 210 description: 211 - Maximum number of connections allowed to server. 212 type: int 213 monitor: 214 description: 215 - Health monitors. Source firewall.ldb-monitor.name. 216 type: str 217 port: 218 description: 219 - Mapped server port. 220 type: int 221 status: 222 description: 223 - Server administrative status. 224 type: str 225 choices: 226 - active 227 - standby 228 - disable 229 weight: 230 description: 231 - weight 232 type: int 233 server_type: 234 description: 235 - Server type. 236 type: str 237 choices: 238 - http 239 - tcp 240 - udp 241 - ip 242 src_filter: 243 description: 244 - "Source IP6 filter (x:x:x:x:x:x:x:x/x)." 245 type: list 246 suboptions: 247 range: 248 description: 249 - Src-filter range. 250 required: true 251 type: str 252 type: 253 description: 254 - "VIP type: static NAT or server load balance." 255 type: str 256 choices: 257 - static-nat 258 - server-load-balance 259 uuid: 260 description: 261 - Universally Unique Identifier (UUID; automatically assigned but can be manually reset). 262 type: str 263''' 264 265EXAMPLES = ''' 266- hosts: localhost 267 vars: 268 host: "192.168.122.40" 269 username: "admin" 270 password: "" 271 vdom: "root" 272 ssl_verify: "False" 273 tasks: 274 - name: Configure IPv6 to IPv4 virtual IPs. 275 fortios_firewall_vip64: 276 host: "{{ host }}" 277 username: "{{ username }}" 278 password: "{{ password }}" 279 vdom: "{{ vdom }}" 280 https: "False" 281 state: "present" 282 firewall_vip64: 283 arp_reply: "disable" 284 color: "4" 285 comment: "Comment." 286 extip: "<your_own_value>" 287 extport: "<your_own_value>" 288 id: "8" 289 ldb_method: "static" 290 mappedip: "<your_own_value>" 291 mappedport: "<your_own_value>" 292 monitor: 293 - 294 name: "default_name_13 (source firewall.ldb-monitor.name)" 295 name: "default_name_14" 296 portforward: "disable" 297 protocol: "tcp" 298 realservers: 299 - 300 client_ip: "<your_own_value>" 301 healthcheck: "disable" 302 holddown_interval: "20" 303 id: "21" 304 ip: "<your_own_value>" 305 max_connections: "23" 306 monitor: "<your_own_value> (source firewall.ldb-monitor.name)" 307 port: "25" 308 status: "active" 309 weight: "27" 310 server_type: "http" 311 src_filter: 312 - 313 range: "<your_own_value>" 314 type: "static-nat" 315 uuid: "<your_own_value>" 316''' 317 318RETURN = ''' 319build: 320 description: Build number of the fortigate image 321 returned: always 322 type: str 323 sample: '1547' 324http_method: 325 description: Last method used to provision the content into FortiGate 326 returned: always 327 type: str 328 sample: 'PUT' 329http_status: 330 description: Last result given by FortiGate on last operation applied 331 returned: always 332 type: str 333 sample: "200" 334mkey: 335 description: Master key (id) used in the last call to FortiGate 336 returned: success 337 type: str 338 sample: "id" 339name: 340 description: Name of the table used to fulfill the request 341 returned: always 342 type: str 343 sample: "urlfilter" 344path: 345 description: Path of the table used to fulfill the request 346 returned: always 347 type: str 348 sample: "webfilter" 349revision: 350 description: Internal revision number 351 returned: always 352 type: str 353 sample: "17.0.2.10658" 354serial: 355 description: Serial number of the unit 356 returned: always 357 type: str 358 sample: "FGVMEVYYQT3AB5352" 359status: 360 description: Indication of the operation's result 361 returned: always 362 type: str 363 sample: "success" 364vdom: 365 description: Virtual domain used 366 returned: always 367 type: str 368 sample: "root" 369version: 370 description: Version of the FortiGate 371 returned: always 372 type: str 373 sample: "v5.6.3" 374 375''' 376 377from ansible.module_utils.basic import AnsibleModule 378from ansible.module_utils.connection import Connection 379from ansible.module_utils.network.fortios.fortios import FortiOSHandler 380from ansible.module_utils.network.fortimanager.common import FAIL_SOCKET_MSG 381 382 383def login(data, fos): 384 host = data['host'] 385 username = data['username'] 386 password = data['password'] 387 ssl_verify = data['ssl_verify'] 388 389 fos.debug('on') 390 if 'https' in data and not data['https']: 391 fos.https('off') 392 else: 393 fos.https('on') 394 395 fos.login(host, username, password, verify=ssl_verify) 396 397 398def filter_firewall_vip64_data(json): 399 option_list = ['arp_reply', 'color', 'comment', 400 'extip', 'extport', 'id', 401 'ldb_method', 'mappedip', 'mappedport', 402 'monitor', 'name', 'portforward', 403 'protocol', 'realservers', 'server_type', 404 'src_filter', 'type', 'uuid'] 405 dictionary = {} 406 407 for attribute in option_list: 408 if attribute in json and json[attribute] is not None: 409 dictionary[attribute] = json[attribute] 410 411 return dictionary 412 413 414def underscore_to_hyphen(data): 415 if isinstance(data, list): 416 for elem in data: 417 elem = underscore_to_hyphen(elem) 418 elif isinstance(data, dict): 419 new_data = {} 420 for k, v in data.items(): 421 new_data[k.replace('_', '-')] = underscore_to_hyphen(v) 422 data = new_data 423 424 return data 425 426 427def firewall_vip64(data, fos): 428 vdom = data['vdom'] 429 if 'state' in data and data['state']: 430 state = data['state'] 431 elif 'state' in data['firewall_vip64'] and data['firewall_vip64']: 432 state = data['firewall_vip64']['state'] 433 else: 434 state = True 435 firewall_vip64_data = data['firewall_vip64'] 436 filtered_data = underscore_to_hyphen(filter_firewall_vip64_data(firewall_vip64_data)) 437 438 if state == "present": 439 return fos.set('firewall', 440 'vip64', 441 data=filtered_data, 442 vdom=vdom) 443 444 elif state == "absent": 445 return fos.delete('firewall', 446 'vip64', 447 mkey=filtered_data['name'], 448 vdom=vdom) 449 450 451def is_successful_status(status): 452 return status['status'] == "success" or \ 453 status['http_method'] == "DELETE" and status['http_status'] == 404 454 455 456def fortios_firewall(data, fos): 457 458 if data['firewall_vip64']: 459 resp = firewall_vip64(data, fos) 460 461 return not is_successful_status(resp), \ 462 resp['status'] == "success", \ 463 resp 464 465 466def main(): 467 fields = { 468 "host": {"required": False, "type": "str"}, 469 "username": {"required": False, "type": "str"}, 470 "password": {"required": False, "type": "str", "default": "", "no_log": True}, 471 "vdom": {"required": False, "type": "str", "default": "root"}, 472 "https": {"required": False, "type": "bool", "default": True}, 473 "ssl_verify": {"required": False, "type": "bool", "default": True}, 474 "state": {"required": False, "type": "str", 475 "choices": ["present", "absent"]}, 476 "firewall_vip64": { 477 "required": False, "type": "dict", "default": None, 478 "options": { 479 "state": {"required": False, "type": "str", 480 "choices": ["present", "absent"]}, 481 "arp_reply": {"required": False, "type": "str", 482 "choices": ["disable", "enable"]}, 483 "color": {"required": False, "type": "int"}, 484 "comment": {"required": False, "type": "str"}, 485 "extip": {"required": False, "type": "str"}, 486 "extport": {"required": False, "type": "str"}, 487 "id": {"required": False, "type": "int"}, 488 "ldb_method": {"required": False, "type": "str", 489 "choices": ["static", "round-robin", "weighted", 490 "least-session", "least-rtt", "first-alive"]}, 491 "mappedip": {"required": False, "type": "str"}, 492 "mappedport": {"required": False, "type": "str"}, 493 "monitor": {"required": False, "type": "list", 494 "options": { 495 "name": {"required": True, "type": "str"} 496 }}, 497 "name": {"required": True, "type": "str"}, 498 "portforward": {"required": False, "type": "str", 499 "choices": ["disable", "enable"]}, 500 "protocol": {"required": False, "type": "str", 501 "choices": ["tcp", "udp"]}, 502 "realservers": {"required": False, "type": "list", 503 "options": { 504 "client_ip": {"required": False, "type": "str"}, 505 "healthcheck": {"required": False, "type": "str", 506 "choices": ["disable", "enable", "vip"]}, 507 "holddown_interval": {"required": False, "type": "int"}, 508 "id": {"required": True, "type": "int"}, 509 "ip": {"required": False, "type": "str"}, 510 "max_connections": {"required": False, "type": "int"}, 511 "monitor": {"required": False, "type": "str"}, 512 "port": {"required": False, "type": "int"}, 513 "status": {"required": False, "type": "str", 514 "choices": ["active", "standby", "disable"]}, 515 "weight": {"required": False, "type": "int"} 516 }}, 517 "server_type": {"required": False, "type": "str", 518 "choices": ["http", "tcp", "udp", 519 "ip"]}, 520 "src_filter": {"required": False, "type": "list", 521 "options": { 522 "range": {"required": True, "type": "str"} 523 }}, 524 "type": {"required": False, "type": "str", 525 "choices": ["static-nat", "server-load-balance"]}, 526 "uuid": {"required": False, "type": "str"} 527 528 } 529 } 530 } 531 532 module = AnsibleModule(argument_spec=fields, 533 supports_check_mode=False) 534 535 # legacy_mode refers to using fortiosapi instead of HTTPAPI 536 legacy_mode = 'host' in module.params and module.params['host'] is not None and \ 537 'username' in module.params and module.params['username'] is not None and \ 538 'password' in module.params and module.params['password'] is not None 539 540 if not legacy_mode: 541 if module._socket_path: 542 connection = Connection(module._socket_path) 543 fos = FortiOSHandler(connection) 544 545 is_error, has_changed, result = fortios_firewall(module.params, fos) 546 else: 547 module.fail_json(**FAIL_SOCKET_MSG) 548 else: 549 try: 550 from fortiosapi import FortiOSAPI 551 except ImportError: 552 module.fail_json(msg="fortiosapi module is required") 553 554 fos = FortiOSAPI() 555 556 login(module.params, fos) 557 is_error, has_changed, result = fortios_firewall(module.params, fos) 558 fos.logout() 559 560 if not is_error: 561 module.exit_json(changed=has_changed, meta=result) 562 else: 563 module.fail_json(msg="Error in repo", meta=result) 564 565 566if __name__ == '__main__': 567 main() 568