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_dnstranslation
27short_description: Configure DNS translation 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 dnstranslation 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_dnstranslation:
88        description:
89            - Configure DNS translation.
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            dst:
105                description:
106                    - IPv4 address or subnet on the external network to substitute for the resolved address in DNS query replies. Can be single IP address or
107                       subnet on the external network, but number of addresses must equal number of mapped IP addresses in src.
108                type: str
109            id:
110                description:
111                    - ID.
112                required: true
113                type: int
114            netmask:
115                description:
116                    - If src and dst are subnets rather than single IP addresses, enter the netmask for both src and dst.
117                type: str
118            src:
119                description:
120                    - IPv4 address or subnet on the internal network to compare with the resolved address in DNS query replies. If the resolved address
121                       matches, the resolved address is substituted with dst.
122                type: str
123'''
124
125EXAMPLES = '''
126- hosts: localhost
127  vars:
128   host: "192.168.122.40"
129   username: "admin"
130   password: ""
131   vdom: "root"
132   ssl_verify: "False"
133  tasks:
134  - name: Configure DNS translation.
135    fortios_firewall_dnstranslation:
136      host:  "{{ host }}"
137      username: "{{ username }}"
138      password: "{{ password }}"
139      vdom:  "{{ vdom }}"
140      https: "False"
141      state: "present"
142      firewall_dnstranslation:
143        dst: "<your_own_value>"
144        id:  "4"
145        netmask: "<your_own_value>"
146        src: "<your_own_value>"
147'''
148
149RETURN = '''
150build:
151  description: Build number of the fortigate image
152  returned: always
153  type: str
154  sample: '1547'
155http_method:
156  description: Last method used to provision the content into FortiGate
157  returned: always
158  type: str
159  sample: 'PUT'
160http_status:
161  description: Last result given by FortiGate on last operation applied
162  returned: always
163  type: str
164  sample: "200"
165mkey:
166  description: Master key (id) used in the last call to FortiGate
167  returned: success
168  type: str
169  sample: "id"
170name:
171  description: Name of the table used to fulfill the request
172  returned: always
173  type: str
174  sample: "urlfilter"
175path:
176  description: Path of the table used to fulfill the request
177  returned: always
178  type: str
179  sample: "webfilter"
180revision:
181  description: Internal revision number
182  returned: always
183  type: str
184  sample: "17.0.2.10658"
185serial:
186  description: Serial number of the unit
187  returned: always
188  type: str
189  sample: "FGVMEVYYQT3AB5352"
190status:
191  description: Indication of the operation's result
192  returned: always
193  type: str
194  sample: "success"
195vdom:
196  description: Virtual domain used
197  returned: always
198  type: str
199  sample: "root"
200version:
201  description: Version of the FortiGate
202  returned: always
203  type: str
204  sample: "v5.6.3"
205
206'''
207
208from ansible.module_utils.basic import AnsibleModule
209from ansible.module_utils.connection import Connection
210from ansible.module_utils.network.fortios.fortios import FortiOSHandler
211from ansible.module_utils.network.fortimanager.common import FAIL_SOCKET_MSG
212
213
214def login(data, fos):
215    host = data['host']
216    username = data['username']
217    password = data['password']
218    ssl_verify = data['ssl_verify']
219
220    fos.debug('on')
221    if 'https' in data and not data['https']:
222        fos.https('off')
223    else:
224        fos.https('on')
225
226    fos.login(host, username, password, verify=ssl_verify)
227
228
229def filter_firewall_dnstranslation_data(json):
230    option_list = ['dst', 'id', 'netmask',
231                   'src']
232    dictionary = {}
233
234    for attribute in option_list:
235        if attribute in json and json[attribute] is not None:
236            dictionary[attribute] = json[attribute]
237
238    return dictionary
239
240
241def underscore_to_hyphen(data):
242    if isinstance(data, list):
243        for elem in data:
244            elem = underscore_to_hyphen(elem)
245    elif isinstance(data, dict):
246        new_data = {}
247        for k, v in data.items():
248            new_data[k.replace('_', '-')] = underscore_to_hyphen(v)
249        data = new_data
250
251    return data
252
253
254def firewall_dnstranslation(data, fos):
255    vdom = data['vdom']
256    if 'state' in data and data['state']:
257        state = data['state']
258    elif 'state' in data['firewall_dnstranslation'] and data['firewall_dnstranslation']:
259        state = data['firewall_dnstranslation']['state']
260    else:
261        state = True
262    firewall_dnstranslation_data = data['firewall_dnstranslation']
263    filtered_data = underscore_to_hyphen(filter_firewall_dnstranslation_data(firewall_dnstranslation_data))
264
265    if state == "present":
266        return fos.set('firewall',
267                       'dnstranslation',
268                       data=filtered_data,
269                       vdom=vdom)
270
271    elif state == "absent":
272        return fos.delete('firewall',
273                          'dnstranslation',
274                          mkey=filtered_data['id'],
275                          vdom=vdom)
276
277
278def is_successful_status(status):
279    return status['status'] == "success" or \
280        status['http_method'] == "DELETE" and status['http_status'] == 404
281
282
283def fortios_firewall(data, fos):
284
285    if data['firewall_dnstranslation']:
286        resp = firewall_dnstranslation(data, fos)
287
288    return not is_successful_status(resp), \
289        resp['status'] == "success", \
290        resp
291
292
293def main():
294    fields = {
295        "host": {"required": False, "type": "str"},
296        "username": {"required": False, "type": "str"},
297        "password": {"required": False, "type": "str", "default": "", "no_log": True},
298        "vdom": {"required": False, "type": "str", "default": "root"},
299        "https": {"required": False, "type": "bool", "default": True},
300        "ssl_verify": {"required": False, "type": "bool", "default": True},
301        "state": {"required": False, "type": "str",
302                  "choices": ["present", "absent"]},
303        "firewall_dnstranslation": {
304            "required": False, "type": "dict", "default": None,
305            "options": {
306                "state": {"required": False, "type": "str",
307                          "choices": ["present", "absent"]},
308                "dst": {"required": False, "type": "str"},
309                "id": {"required": True, "type": "int"},
310                "netmask": {"required": False, "type": "str"},
311                "src": {"required": False, "type": "str"}
312
313            }
314        }
315    }
316
317    module = AnsibleModule(argument_spec=fields,
318                           supports_check_mode=False)
319
320    # legacy_mode refers to using fortiosapi instead of HTTPAPI
321    legacy_mode = 'host' in module.params and module.params['host'] is not None and \
322                  'username' in module.params and module.params['username'] is not None and \
323                  'password' in module.params and module.params['password'] is not None
324
325    if not legacy_mode:
326        if module._socket_path:
327            connection = Connection(module._socket_path)
328            fos = FortiOSHandler(connection)
329
330            is_error, has_changed, result = fortios_firewall(module.params, fos)
331        else:
332            module.fail_json(**FAIL_SOCKET_MSG)
333    else:
334        try:
335            from fortiosapi import FortiOSAPI
336        except ImportError:
337            module.fail_json(msg="fortiosapi module is required")
338
339        fos = FortiOSAPI()
340
341        login(module.params, fos)
342        is_error, has_changed, result = fortios_firewall(module.params, fos)
343        fos.logout()
344
345    if not is_error:
346        module.exit_json(changed=has_changed, meta=result)
347    else:
348        module.fail_json(msg="Error in repo", meta=result)
349
350
351if __name__ == '__main__':
352    main()
353