1#!/usr/local/bin/python3.8 2from __future__ import (absolute_import, division, print_function) 3# Copyright 2019-2020 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_antivirus_settings 27short_description: Configure AntiVirus settings 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 antivirus feature and settings category. 31 Examples include all parameters and values need to be adjusted to datasources before usage. 32 Tested with FOS v6.0.0 33version_added: "2.10" 34author: 35 - Link Zheng (@chillancezen) 36 - Jie Xue (@JieX19) 37 - Hongbin Lu (@fgtdev-hblu) 38 - Frank Shen (@frankshen01) 39 - Miguel Angel Munoz (@mamunozgonzalez) 40 - Nicolas Thomas (@thomnico) 41notes: 42 - Legacy fortiosapi has been deprecated, httpapi is the preferred way to run playbooks 43 44requirements: 45 - ansible>=2.9.0 46options: 47 access_token: 48 description: 49 - Token-based authentication. 50 Generated from GUI of Fortigate. 51 type: str 52 required: false 53 enable_log: 54 description: 55 - Enable/Disable logging for task. 56 type: bool 57 required: false 58 default: false 59 vdom: 60 description: 61 - Virtual domain, among those defined previously. A vdom is a 62 virtual instance of the FortiGate that can be configured and 63 used as a different unit. 64 type: str 65 default: root 66 67 antivirus_settings: 68 description: 69 - Configure AntiVirus settings. 70 default: null 71 type: dict 72 suboptions: 73 default_db: 74 description: 75 - Select the AV database to be used for AV scanning. 76 type: str 77 choices: 78 - normal 79 - extended 80 - extreme 81 grayware: 82 description: 83 - Enable/disable grayware detection when an AntiVirus profile is applied to traffic. 84 type: str 85 choices: 86 - enable 87 - disable 88 machine_learning_detection: 89 description: 90 - Use machine learning based malware detection. 91 type: str 92 choices: 93 - enable 94 - monitor 95 - disable 96 override_timeout: 97 description: 98 - Override the large file scan timeout value in seconds (30 - 3600). Zero is the default value and is used to disable this command. When 99 disabled, the daemon adjusts the large file scan timeout based on the file size. 100 type: int 101 use_extreme_db: 102 description: 103 - Enable/disable the use of Extreme AVDB. 104 type: str 105 choices: 106 - enable 107 - disable 108''' 109 110EXAMPLES = ''' 111- hosts: fortigates 112 collections: 113 - fortinet.fortios 114 connection: httpapi 115 vars: 116 vdom: "root" 117 ansible_httpapi_use_ssl: yes 118 ansible_httpapi_validate_certs: no 119 ansible_httpapi_port: 443 120 tasks: 121 - name: Configure AntiVirus settings. 122 fortios_antivirus_settings: 123 vdom: "{{ vdom }}" 124 antivirus_settings: 125 default_db: "normal" 126 grayware: "enable" 127 machine_learning_detection: "enable" 128 override_timeout: "6" 129 use_extreme_db: "enable" 130 131''' 132 133RETURN = ''' 134build: 135 description: Build number of the fortigate image 136 returned: always 137 type: str 138 sample: '1547' 139http_method: 140 description: Last method used to provision the content into FortiGate 141 returned: always 142 type: str 143 sample: 'PUT' 144http_status: 145 description: Last result given by FortiGate on last operation applied 146 returned: always 147 type: str 148 sample: "200" 149mkey: 150 description: Master key (id) used in the last call to FortiGate 151 returned: success 152 type: str 153 sample: "id" 154name: 155 description: Name of the table used to fulfill the request 156 returned: always 157 type: str 158 sample: "urlfilter" 159path: 160 description: Path of the table used to fulfill the request 161 returned: always 162 type: str 163 sample: "webfilter" 164revision: 165 description: Internal revision number 166 returned: always 167 type: str 168 sample: "17.0.2.10658" 169serial: 170 description: Serial number of the unit 171 returned: always 172 type: str 173 sample: "FGVMEVYYQT3AB5352" 174status: 175 description: Indication of the operation's result 176 returned: always 177 type: str 178 sample: "success" 179vdom: 180 description: Virtual domain used 181 returned: always 182 type: str 183 sample: "root" 184version: 185 description: Version of the FortiGate 186 returned: always 187 type: str 188 sample: "v5.6.3" 189 190''' 191from ansible.module_utils.basic import AnsibleModule 192from ansible.module_utils.connection import Connection 193from ansible_collections.fortinet.fortios.plugins.module_utils.fortios.fortios import FortiOSHandler 194from ansible_collections.fortinet.fortios.plugins.module_utils.fortios.fortios import check_legacy_fortiosapi 195from ansible_collections.fortinet.fortios.plugins.module_utils.fortios.fortios import schema_to_module_spec 196from ansible_collections.fortinet.fortios.plugins.module_utils.fortios.fortios import check_schema_versioning 197from ansible_collections.fortinet.fortios.plugins.module_utils.fortimanager.common import FAIL_SOCKET_MSG 198from ansible_collections.fortinet.fortios.plugins.module_utils.fortios.comparison import is_same_comparison 199from ansible_collections.fortinet.fortios.plugins.module_utils.fortios.comparison import serialize 200 201 202def filter_antivirus_settings_data(json): 203 option_list = ['default_db', 'grayware', 'machine_learning_detection', 204 'override_timeout', 'use_extreme_db'] 205 dictionary = {} 206 207 for attribute in option_list: 208 if attribute in json and json[attribute] is not None: 209 dictionary[attribute] = json[attribute] 210 211 return dictionary 212 213 214def underscore_to_hyphen(data): 215 if isinstance(data, list): 216 for i, elem in enumerate(data): 217 data[i] = underscore_to_hyphen(elem) 218 elif isinstance(data, dict): 219 new_data = {} 220 for k, v in data.items(): 221 new_data[k.replace('_', '-')] = underscore_to_hyphen(v) 222 data = new_data 223 224 return data 225 226 227def antivirus_settings(data, fos): 228 vdom = data['vdom'] 229 antivirus_settings_data = data['antivirus_settings'] 230 filtered_data = underscore_to_hyphen(filter_antivirus_settings_data(antivirus_settings_data)) 231 232 return fos.set('antivirus', 233 'settings', 234 data=filtered_data, 235 vdom=vdom) 236 237 238def is_successful_status(status): 239 return status['status'] == "success" or \ 240 status['http_method'] == "DELETE" and status['http_status'] == 404 241 242 243def fortios_antivirus(data, fos): 244 245 if data['antivirus_settings']: 246 resp = antivirus_settings(data, fos) 247 else: 248 fos._module.fail_json(msg='missing task body: %s' % ('antivirus_settings')) 249 250 return not is_successful_status(resp), \ 251 resp['status'] == "success" and \ 252 (resp['revision_changed'] if 'revision_changed' in resp else True), \ 253 resp 254 255 256versioned_schema = { 257 "type": "dict", 258 "children": { 259 "override_timeout": { 260 "type": "integer", 261 "revisions": { 262 "v6.0.0": True, 263 "v7.0.0": True, 264 "v6.0.5": True, 265 "v6.4.4": True, 266 "v6.4.0": True, 267 "v6.4.1": True, 268 "v6.2.0": True, 269 "v6.2.3": True, 270 "v6.2.5": True, 271 "v6.2.7": True, 272 "v6.0.11": True 273 } 274 }, 275 "machine_learning_detection": { 276 "type": "string", 277 "options": [ 278 { 279 "value": "enable", 280 "revisions": { 281 "v7.0.0": True 282 } 283 }, 284 { 285 "value": "monitor", 286 "revisions": { 287 "v7.0.0": True 288 } 289 }, 290 { 291 "value": "disable", 292 "revisions": { 293 "v7.0.0": True 294 } 295 } 296 ], 297 "revisions": { 298 "v7.0.0": True 299 } 300 }, 301 "use_extreme_db": { 302 "type": "string", 303 "options": [ 304 { 305 "value": "enable", 306 "revisions": { 307 "v6.4.4": True, 308 "v7.0.0": True, 309 "v6.4.0": True, 310 "v6.4.1": True 311 } 312 }, 313 { 314 "value": "disable", 315 "revisions": { 316 "v6.4.4": True, 317 "v7.0.0": True, 318 "v6.4.0": True, 319 "v6.4.1": True 320 } 321 } 322 ], 323 "revisions": { 324 "v6.4.4": True, 325 "v7.0.0": True, 326 "v6.4.0": True, 327 "v6.4.1": True 328 } 329 }, 330 "default_db": { 331 "type": "string", 332 "options": [ 333 { 334 "value": "normal", 335 "revisions": { 336 "v6.0.0": True, 337 "v6.0.5": True, 338 "v6.2.0": True, 339 "v6.2.3": True, 340 "v6.2.5": True, 341 "v6.2.7": True, 342 "v6.0.11": True 343 } 344 }, 345 { 346 "value": "extended", 347 "revisions": { 348 "v6.0.0": True, 349 "v6.0.5": True, 350 "v6.2.0": True, 351 "v6.2.3": True, 352 "v6.2.5": True, 353 "v6.2.7": True, 354 "v6.0.11": True 355 } 356 }, 357 { 358 "value": "extreme", 359 "revisions": { 360 "v6.0.0": True, 361 "v6.0.5": True, 362 "v6.2.0": True, 363 "v6.2.3": True, 364 "v6.2.5": True, 365 "v6.2.7": True, 366 "v6.0.11": True 367 } 368 } 369 ], 370 "revisions": { 371 "v6.0.0": True, 372 "v7.0.0": False, 373 "v6.0.5": True, 374 "v6.4.4": False, 375 "v6.4.0": False, 376 "v6.4.1": False, 377 "v6.2.0": True, 378 "v6.2.3": True, 379 "v6.2.5": True, 380 "v6.2.7": True, 381 "v6.0.11": True 382 } 383 }, 384 "grayware": { 385 "type": "string", 386 "options": [ 387 { 388 "value": "enable", 389 "revisions": { 390 "v6.0.0": True, 391 "v7.0.0": True, 392 "v6.0.5": True, 393 "v6.4.4": True, 394 "v6.4.0": True, 395 "v6.4.1": True, 396 "v6.2.0": True, 397 "v6.2.3": True, 398 "v6.2.5": True, 399 "v6.2.7": True, 400 "v6.0.11": True 401 } 402 }, 403 { 404 "value": "disable", 405 "revisions": { 406 "v6.0.0": True, 407 "v7.0.0": True, 408 "v6.0.5": True, 409 "v6.4.4": True, 410 "v6.4.0": True, 411 "v6.4.1": True, 412 "v6.2.0": True, 413 "v6.2.3": True, 414 "v6.2.5": True, 415 "v6.2.7": True, 416 "v6.0.11": True 417 } 418 } 419 ], 420 "revisions": { 421 "v6.0.0": True, 422 "v7.0.0": True, 423 "v6.0.5": True, 424 "v6.4.4": True, 425 "v6.4.0": True, 426 "v6.4.1": True, 427 "v6.2.0": True, 428 "v6.2.3": True, 429 "v6.2.5": True, 430 "v6.2.7": True, 431 "v6.0.11": True 432 } 433 } 434 }, 435 "revisions": { 436 "v6.0.0": True, 437 "v7.0.0": True, 438 "v6.0.5": True, 439 "v6.4.4": True, 440 "v6.4.0": True, 441 "v6.4.1": True, 442 "v6.2.0": True, 443 "v6.2.3": True, 444 "v6.2.5": True, 445 "v6.2.7": True, 446 "v6.0.11": True 447 } 448} 449 450 451def main(): 452 module_spec = schema_to_module_spec(versioned_schema) 453 mkeyname = None 454 fields = { 455 "access_token": {"required": False, "type": "str", "no_log": True}, 456 "enable_log": {"required": False, "type": bool}, 457 "vdom": {"required": False, "type": "str", "default": "root"}, 458 "antivirus_settings": { 459 "required": False, "type": "dict", "default": None, 460 "options": { 461 } 462 } 463 } 464 for attribute_name in module_spec['options']: 465 fields["antivirus_settings"]['options'][attribute_name] = module_spec['options'][attribute_name] 466 if mkeyname and mkeyname == attribute_name: 467 fields["antivirus_settings"]['options'][attribute_name]['required'] = True 468 469 check_legacy_fortiosapi() 470 module = AnsibleModule(argument_spec=fields, 471 supports_check_mode=False) 472 473 versions_check_result = None 474 if module._socket_path: 475 connection = Connection(module._socket_path) 476 if 'access_token' in module.params: 477 connection.set_option('access_token', module.params['access_token']) 478 479 if 'enable_log' in module.params: 480 connection.set_option('enable_log', module.params['enable_log']) 481 else: 482 connection.set_option('enable_log', False) 483 fos = FortiOSHandler(connection, module, mkeyname) 484 versions_check_result = check_schema_versioning(fos, versioned_schema, "antivirus_settings") 485 486 is_error, has_changed, result = fortios_antivirus(module.params, fos) 487 488 else: 489 module.fail_json(**FAIL_SOCKET_MSG) 490 491 if versions_check_result and versions_check_result['matched'] is False: 492 module.warn("Ansible has detected version mismatch between FortOS system and your playbook, see more details by specifying option -vvv") 493 494 if not is_error: 495 if versions_check_result and versions_check_result['matched'] is False: 496 module.exit_json(changed=has_changed, version_check_warning=versions_check_result, meta=result) 497 else: 498 module.exit_json(changed=has_changed, meta=result) 499 else: 500 if versions_check_result and versions_check_result['matched'] is False: 501 module.fail_json(msg="Error in repo", version_check_warning=versions_check_result, meta=result) 502 else: 503 module.fail_json(msg="Error in repo", meta=result) 504 505 506if __name__ == '__main__': 507 main() 508