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_system_csf 27short_description: Add this FortiGate to a Security Fabric or set up a new Security Fabric on this FortiGate 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 system feature and csf 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.9" 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 system_csf: 76 description: 77 - Add this FortiGate to a Security Fabric or set up a new Security Fabric on this FortiGate. 78 default: null 79 type: dict 80 suboptions: 81 configuration_sync: 82 description: 83 - Configuration sync mode. 84 type: str 85 choices: 86 - default 87 - local 88 fabric_device: 89 description: 90 - Fabric device configuration. 91 type: list 92 suboptions: 93 device_ip: 94 description: 95 - Device IP. 96 type: str 97 device_type: 98 description: 99 - Device type. 100 type: str 101 choices: 102 - fortimail 103 login: 104 description: 105 - Device login name. 106 type: str 107 name: 108 description: 109 - Device name. 110 required: true 111 type: str 112 password: 113 description: 114 - Device login password. 115 type: str 116 fixed_key: 117 description: 118 - Auto-generated fixed key used when this device is the root. (Will automatically be generated if not set.) 119 type: str 120 group_name: 121 description: 122 - Security Fabric group name. All FortiGates in a Security Fabric must have the same group name. 123 type: str 124 group_password: 125 description: 126 - Security Fabric group password. All FortiGates in a Security Fabric must have the same group password. 127 type: str 128 management_ip: 129 description: 130 - Management IP address of this FortiGate. Used to log into this FortiGate from another FortiGate in the Security Fabric. 131 type: str 132 management_port: 133 description: 134 - Overriding port for management connection (Overrides admin port). 135 type: int 136 status: 137 description: 138 - Enable/disable Security Fabric. 139 type: str 140 choices: 141 - enable 142 - disable 143 trusted_list: 144 description: 145 - Pre-authorized and blocked security fabric nodes. 146 type: list 147 suboptions: 148 action: 149 description: 150 - Security fabric authorization action. 151 type: str 152 choices: 153 - accept 154 - deny 155 downstream_authorization: 156 description: 157 - Trust authorizations by this node's administrator. 158 type: str 159 choices: 160 - enable 161 - disable 162 ha_members: 163 description: 164 - HA members. 165 type: str 166 serial: 167 description: 168 - Serial. 169 required: true 170 type: str 171 upstream_ip: 172 description: 173 - IP address of the FortiGate upstream from this FortiGate in the Security Fabric. 174 type: str 175 upstream_port: 176 description: 177 - The port number to use to communicate with the FortiGate upstream from this FortiGate in the Security Fabric . 178 type: int 179''' 180 181EXAMPLES = ''' 182- hosts: localhost 183 vars: 184 host: "192.168.122.40" 185 username: "admin" 186 password: "" 187 vdom: "root" 188 ssl_verify: "False" 189 tasks: 190 - name: Add this FortiGate to a Security Fabric or set up a new Security Fabric on this FortiGate. 191 fortios_system_csf: 192 host: "{{ host }}" 193 username: "{{ username }}" 194 password: "{{ password }}" 195 vdom: "{{ vdom }}" 196 https: "False" 197 system_csf: 198 configuration_sync: "default" 199 fabric_device: 200 - 201 device_ip: "<your_own_value>" 202 device_type: "fortimail" 203 login: "<your_own_value>" 204 name: "default_name_8" 205 password: "<your_own_value>" 206 fixed_key: "<your_own_value>" 207 group_name: "<your_own_value>" 208 group_password: "<your_own_value>" 209 management_ip: "<your_own_value>" 210 management_port: "14" 211 status: "enable" 212 trusted_list: 213 - 214 action: "accept" 215 downstream_authorization: "enable" 216 ha_members: "<your_own_value>" 217 serial: "<your_own_value>" 218 upstream_ip: "<your_own_value>" 219 upstream_port: "22" 220''' 221 222RETURN = ''' 223build: 224 description: Build number of the fortigate image 225 returned: always 226 type: str 227 sample: '1547' 228http_method: 229 description: Last method used to provision the content into FortiGate 230 returned: always 231 type: str 232 sample: 'PUT' 233http_status: 234 description: Last result given by FortiGate on last operation applied 235 returned: always 236 type: str 237 sample: "200" 238mkey: 239 description: Master key (id) used in the last call to FortiGate 240 returned: success 241 type: str 242 sample: "id" 243name: 244 description: Name of the table used to fulfill the request 245 returned: always 246 type: str 247 sample: "urlfilter" 248path: 249 description: Path of the table used to fulfill the request 250 returned: always 251 type: str 252 sample: "webfilter" 253revision: 254 description: Internal revision number 255 returned: always 256 type: str 257 sample: "17.0.2.10658" 258serial: 259 description: Serial number of the unit 260 returned: always 261 type: str 262 sample: "FGVMEVYYQT3AB5352" 263status: 264 description: Indication of the operation's result 265 returned: always 266 type: str 267 sample: "success" 268vdom: 269 description: Virtual domain used 270 returned: always 271 type: str 272 sample: "root" 273version: 274 description: Version of the FortiGate 275 returned: always 276 type: str 277 sample: "v5.6.3" 278 279''' 280 281from ansible.module_utils.basic import AnsibleModule 282from ansible.module_utils.connection import Connection 283from ansible.module_utils.network.fortios.fortios import FortiOSHandler 284from ansible.module_utils.network.fortimanager.common import FAIL_SOCKET_MSG 285 286 287def login(data, fos): 288 host = data['host'] 289 username = data['username'] 290 password = data['password'] 291 ssl_verify = data['ssl_verify'] 292 293 fos.debug('on') 294 if 'https' in data and not data['https']: 295 fos.https('off') 296 else: 297 fos.https('on') 298 299 fos.login(host, username, password, verify=ssl_verify) 300 301 302def filter_system_csf_data(json): 303 option_list = ['configuration_sync', 'fabric_device', 'fixed_key', 304 'group_name', 'group_password', 'management_ip', 305 'management_port', 'status', 'trusted_list', 306 'upstream_ip', 'upstream_port'] 307 dictionary = {} 308 309 for attribute in option_list: 310 if attribute in json and json[attribute] is not None: 311 dictionary[attribute] = json[attribute] 312 313 return dictionary 314 315 316def underscore_to_hyphen(data): 317 if isinstance(data, list): 318 for elem in data: 319 elem = underscore_to_hyphen(elem) 320 elif isinstance(data, dict): 321 new_data = {} 322 for k, v in data.items(): 323 new_data[k.replace('_', '-')] = underscore_to_hyphen(v) 324 data = new_data 325 326 return data 327 328 329def system_csf(data, fos): 330 vdom = data['vdom'] 331 system_csf_data = data['system_csf'] 332 filtered_data = underscore_to_hyphen(filter_system_csf_data(system_csf_data)) 333 334 return fos.set('system', 335 'csf', 336 data=filtered_data, 337 vdom=vdom) 338 339 340def is_successful_status(status): 341 return status['status'] == "success" or \ 342 status['http_method'] == "DELETE" and status['http_status'] == 404 343 344 345def fortios_system(data, fos): 346 347 if data['system_csf']: 348 resp = system_csf(data, fos) 349 350 return not is_successful_status(resp), \ 351 resp['status'] == "success", \ 352 resp 353 354 355def main(): 356 fields = { 357 "host": {"required": False, "type": "str"}, 358 "username": {"required": False, "type": "str"}, 359 "password": {"required": False, "type": "str", "default": "", "no_log": True}, 360 "vdom": {"required": False, "type": "str", "default": "root"}, 361 "https": {"required": False, "type": "bool", "default": True}, 362 "ssl_verify": {"required": False, "type": "bool", "default": True}, 363 "system_csf": { 364 "required": False, "type": "dict", "default": None, 365 "options": { 366 "configuration_sync": {"required": False, "type": "str", 367 "choices": ["default", "local"]}, 368 "fabric_device": {"required": False, "type": "list", 369 "options": { 370 "device_ip": {"required": False, "type": "str"}, 371 "device_type": {"required": False, "type": "str", 372 "choices": ["fortimail"]}, 373 "login": {"required": False, "type": "str"}, 374 "name": {"required": True, "type": "str"}, 375 "password": {"required": False, "type": "str", "no_log": True} 376 }}, 377 "fixed_key": {"required": False, "type": "str"}, 378 "group_name": {"required": False, "type": "str"}, 379 "group_password": {"required": False, "type": "str", "no_log": True}, 380 "management_ip": {"required": False, "type": "str"}, 381 "management_port": {"required": False, "type": "int"}, 382 "status": {"required": False, "type": "str", 383 "choices": ["enable", "disable"]}, 384 "trusted_list": {"required": False, "type": "list", 385 "options": { 386 "action": {"required": False, "type": "str", 387 "choices": ["accept", "deny"]}, 388 "downstream_authorization": {"required": False, "type": "str", 389 "choices": ["enable", "disable"]}, 390 "ha_members": {"required": False, "type": "str"}, 391 "serial": {"required": True, "type": "str"} 392 }}, 393 "upstream_ip": {"required": False, "type": "str"}, 394 "upstream_port": {"required": False, "type": "int"} 395 396 } 397 } 398 } 399 400 module = AnsibleModule(argument_spec=fields, 401 supports_check_mode=False) 402 403 # legacy_mode refers to using fortiosapi instead of HTTPAPI 404 legacy_mode = 'host' in module.params and module.params['host'] is not None and \ 405 'username' in module.params and module.params['username'] is not None and \ 406 'password' in module.params and module.params['password'] is not None 407 408 if not legacy_mode: 409 if module._socket_path: 410 connection = Connection(module._socket_path) 411 fos = FortiOSHandler(connection) 412 413 is_error, has_changed, result = fortios_system(module.params, fos) 414 else: 415 module.fail_json(**FAIL_SOCKET_MSG) 416 else: 417 try: 418 from fortiosapi import FortiOSAPI 419 except ImportError: 420 module.fail_json(msg="fortiosapi module is required") 421 422 fos = FortiOSAPI() 423 424 login(module.params, fos) 425 is_error, has_changed, result = fortios_system(module.params, fos) 426 fos.logout() 427 428 if not is_error: 429 module.exit_json(changed=has_changed, meta=result) 430 else: 431 module.fail_json(msg="Error in repo", meta=result) 432 433 434if __name__ == '__main__': 435 main() 436