diff --git a/README.md b/README.md index 28dea3cf..6cfb0063 100644 --- a/README.md +++ b/README.md @@ -30,6 +30,7 @@ Name | Description | [linode.cloud.domain_record](./docs/modules/domain_record.md)|Manage Linode Domain Records.| [linode.cloud.firewall](./docs/modules/firewall.md)|Manage Linode Firewalls.| [linode.cloud.firewall_device](./docs/modules/firewall_device.md)|Manage Linode Firewall Devices.| +[linode.cloud.firewall_settings](./docs/modules/firewall_settings.md)|Configure the firewall settings for the account.| [linode.cloud.image](./docs/modules/image.md)|Manage a Linode Image.| [linode.cloud.instance](./docs/modules/instance.md)|Manage Linode Instances, Configs, and Disks.| [linode.cloud.ip](./docs/modules/ip.md)|Allocates a new IPv4 Address on your Account. The Linode must be configured to support additional addresses - please Open a support ticket requesting additional addresses before attempting allocation.| @@ -68,6 +69,7 @@ Name | Description | [linode.cloud.domain_info](./docs/modules/domain_info.md)|Get info about a Linode Domain.| [linode.cloud.domain_record_info](./docs/modules/domain_record_info.md)|Get info about a Linode Domain Records.| [linode.cloud.firewall_info](./docs/modules/firewall_info.md)|Get info about a Linode Firewall.| +[linode.cloud.firewall_settings_info](./docs/modules/firewall_settings_info.md)|Get info about a Linode Firewall Settings.| [linode.cloud.image_info](./docs/modules/image_info.md)|Get info about a Linode Image.| [linode.cloud.instance_info](./docs/modules/instance_info.md)|Get info about a Linode Instance.| [linode.cloud.ip_info](./docs/modules/ip_info.md)|Get info about a Linode IP.| diff --git a/docs/modules/firewall_settings.md b/docs/modules/firewall_settings.md new file mode 100644 index 00000000..f1955fb7 --- /dev/null +++ b/docs/modules/firewall_settings.md @@ -0,0 +1,61 @@ +# firewall_settings + +Configure the firewall settings for the account. + +- [Minimum Required Fields](#minimum-required-fields) +- [Examples](#examples) +- [Parameters](#parameters) +- [Return Values](#return-values) + +## Minimum Required Fields +| Field | Type | Required | Description | +|-------------|-------|--------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `api_token` | `str` | **Required** | The Linode account personal access token. It is necessary to run the module.
It can be exposed by the environment variable `LINODE_API_TOKEN` instead.
See details in [Usage](https://github.com/linode/ansible_linode?tab=readme-ov-file#usage). | + +## Examples + +```yaml +- name: Update the default firewall settings + linode.cloud.firewall_settings: + default_firewall_ids: + linode: 123456 + nodebalancer: 123456 + public_interface: 123456 + vpc_interface: 123456 +``` + + +## Parameters + +| Field | Type | Required | Description | +|-----------|------|----------|------------------------------------------------------------------------------| +| [`default_firewall_ids` (sub-options)](#default_firewall_ids) |
`dict`
|
Optional
| The default firewall ID for a `linode`, `nodebalancer`, `public_interface`, or `vpc_interface`. Default firewalls can't be deleted or disabled. **(Updatable)** | +| `state` |
`str`
|
Optional
| The desired state of the target. **(Choices: `present`)** | + +### default_firewall_ids + +| Field | Type | Required | Description | +|-----------|------|----------|------------------------------------------------------------------------------| +| `linode` |
`int`
|
Optional
| The Linode's default firewall. | +| `nodebalancer` |
`int`
|
Optional
| The NodeBalancer's default firewall. | +| `public_interface` |
`int`
|
Optional
| The public interface's default firewall. | +| `vpc_interface` |
`int`
|
Optional
| The VPC interface's default firewall. | + +## Return Values + +- `default_firewall_ids` - The default firewall ID for a `linode`, `nodebalancer`, `public_interface`, or `vpc_interface`. Default firewalls can't be deleted or disabled. + + - Sample Response: + ```json + { + "default_firewall_ids": { + "linode": 123456, + "nodebalancer": 123456, + "public_interface": 123456, + "vpc_interface": 123456 + } + } + ``` + - See the [Linode API response documentation](https://techdocs.akamai.com/linode-api/reference/put-firewall-settings) for a list of returned fields + + diff --git a/docs/modules/firewall_settings_info.md b/docs/modules/firewall_settings_info.md new file mode 100644 index 00000000..2ecd6505 --- /dev/null +++ b/docs/modules/firewall_settings_info.md @@ -0,0 +1,40 @@ +# firewall_settings_info + +Get info about a Linode Firewall Settings. + +- [Minimum Required Fields](#minimum-required-fields) +- [Examples](#examples) +- [Parameters](#parameters) +- [Return Values](#return-values) + +## Minimum Required Fields +| Field | Type | Required | Description | +|-------------|-------|--------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `api_token` | `str` | **Required** | The Linode account personal access token. It is necessary to run the module.
It can be exposed by the environment variable `LINODE_API_TOKEN` instead.
See details in [Usage](https://github.com/linode/ansible_linode?tab=readme-ov-file#usage). | + +## Examples + +```yaml +- name: Update the default firewall settings + linode.cloud.firewall_settings_info: {} +``` + + +## Return Values + +- `firewall_settings` - The returned Firewall Settings. + + - Sample Response: + ```json + { + "default_firewall_ids": { + "linode": 123456, + "nodebalancer": 123456, + "public_interface": 123456, + "vpc_interface": 123456 + } + } + ``` + - See the [Linode API response documentation](https://techdocs.akamai.com/linode-api/reference/get-firewall-settings) for a list of returned fields + + diff --git a/plugins/module_utils/doc_fragments/firewall_settings.py b/plugins/module_utils/doc_fragments/firewall_settings.py new file mode 100644 index 00000000..2f414276 --- /dev/null +++ b/plugins/module_utils/doc_fragments/firewall_settings.py @@ -0,0 +1,20 @@ +"""Documentation fragments for the firewall_settings module""" + +specdoc_examples = [''' +- name: Update the default firewall settings + linode.cloud.firewall_settings: + default_firewall_ids: + linode: 123456 + nodebalancer: 123456 + public_interface: 123456 + vpc_interface: 123456''' +] + +result_firewall_settings_samples = ['''{ + "default_firewall_ids": { + "linode": 123456, + "nodebalancer": 123456, + "public_interface": 123456, + "vpc_interface": 123456 + } +}'''] diff --git a/plugins/module_utils/doc_fragments/firewall_settings_info.py b/plugins/module_utils/doc_fragments/firewall_settings_info.py new file mode 100644 index 00000000..0a842ec4 --- /dev/null +++ b/plugins/module_utils/doc_fragments/firewall_settings_info.py @@ -0,0 +1,6 @@ +"""Documentation fragments for the firewall_settings_info module""" + +specdoc_examples = [''' +- name: Update the default firewall settings + linode.cloud.firewall_settings_info: {}''' +] diff --git a/plugins/modules/firewall_settings.py b/plugins/modules/firewall_settings.py new file mode 100644 index 00000000..88df9955 --- /dev/null +++ b/plugins/modules/firewall_settings.py @@ -0,0 +1,159 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +"""This module contains all of the functionality for Linode Firewalls Settings.""" + +from __future__ import absolute_import, division, print_function + +from typing import Any, Optional + +import ansible_collections.linode.cloud.plugins.module_utils.doc_fragments.firewall_settings as docs +from ansible_collections.linode.cloud.plugins.module_utils.linode_common import ( + LinodeModuleBase, +) +from ansible_collections.linode.cloud.plugins.module_utils.linode_docs import ( + global_authors, + global_requirements, +) +from ansible_collections.linode.cloud.plugins.module_utils.linode_helper import ( + filter_null_values, + handle_updates, +) +from ansible_specdoc.objects import ( + FieldType, + SpecDocMeta, + SpecField, + SpecReturnValue, +) + +try: + from linode_api4 import FirewallSettings +except ImportError: + # handled in module_utils.linode_common + pass + +default_firewall_ids_spec: dict = { + "linode": SpecField( + type=FieldType.integer, + description=["The Linode's default firewall."], + ), + "nodebalancer": SpecField( + type=FieldType.integer, + description=["The NodeBalancer's default firewall."], + ), + "public_interface": SpecField( + type=FieldType.integer, + description=["The public interface's default firewall."], + ), + "vpc_interface": SpecField( + type=FieldType.integer, + description=["The VPC interface's default firewall."], + ), +} + +DEFAULT_FIREWALL_SETTING_DESCRIPTION = ( + "The default firewall ID for a `linode`, `nodebalancer`, " + "`public_interface`, or `vpc_interface`. Default firewalls " + "can't be deleted or disabled." +) + +firewall_settings_spec: dict = { + "default_firewall_ids": SpecField( + type=FieldType.dict, + suboptions=default_firewall_ids_spec, + editable=True, + description=[DEFAULT_FIREWALL_SETTING_DESCRIPTION], + ), + "state": SpecField( + type=FieldType.string, + description=["The desired state of the target."], + choices=["present"], + # Firewall settings cannot be removed. + # Thus marking `state` field as optional here. + required=False, + ), +} + +SPECDOC_META = SpecDocMeta( + description=["Configure the firewall settings for the account."], + requirements=global_requirements, + author=global_authors, + options=firewall_settings_spec, + examples=docs.specdoc_examples, + return_values={ + "default_firewall_ids": SpecReturnValue( + description=DEFAULT_FIREWALL_SETTING_DESCRIPTION, + docs_url="https://techdocs.akamai.com/linode-api/reference/put-firewall-settings", + type=FieldType.dict, + sample=docs.result_firewall_settings_samples, + ), + }, +) + + +DOCUMENTATION = r""" +""" +EXAMPLES = r""" +""" +RETURN = r""" +""" + + +class LinodeFirewall(LinodeModuleBase): + """Module for creating and destroying Linode Firewalls""" + + def __init__(self) -> None: + self.module_arg_spec = SPECDOC_META.ansible_spec + + self.results: dict = { + "changed": False, + "actions": [], + "firewall_settings": None, + } + + self._firewall_settings: Optional[FirewallSettings] = None + + self._state = "present" + + super().__init__(module_arg_spec=self.module_arg_spec) + + def _get_firewall_settings(self) -> Optional[FirewallSettings]: + try: + return self.client.networking.firewall_settings() + except Exception as exception: + return self.fail( + msg=f"failed to get firewall settings: {exception}" + ) + + def _update_firewall_settings(self) -> None: + """Update the firewall settings""" + + self._firewall_settings = self._get_firewall_settings() + + handle_updates( + self._firewall_settings, + filter_null_values(self.module.params), + set(["default_firewall_ids"]), + self.register_action, + ) + + self._firewall_settings._api_get() + self.results["firewall_settings"] = self._firewall_settings._raw_json + + def exec_module(self, **kwargs: Any) -> Optional[dict]: + """Entrypoint for Firewall Settings module""" + + self._state = self.module.params.get("state", "present") + self._update_firewall_settings() + + return self.results + + +def main() -> None: + """Constructs and calls the Linode Firewall Settings module""" + + LinodeFirewall() + + +if __name__ == "__main__": + main() diff --git a/plugins/modules/firewall_settings_info.py b/plugins/modules/firewall_settings_info.py new file mode 100644 index 00000000..510eb6e4 --- /dev/null +++ b/plugins/modules/firewall_settings_info.py @@ -0,0 +1,47 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +"""This module contains all of the functionality for Linode Firewalls Settings.""" + +from __future__ import absolute_import, division, print_function + +from ansible_collections.linode.cloud.plugins.module_utils.doc_fragments import ( + firewall_settings as docs_parent, +) +from ansible_collections.linode.cloud.plugins.module_utils.doc_fragments import ( + firewall_settings_info as docs, +) +from ansible_collections.linode.cloud.plugins.module_utils.linode_common_info import ( + InfoModule, + InfoModuleResult, +) +from ansible_specdoc.objects import ( + FieldType, +) +from linode_api4 import FirewallSettings + +module = InfoModule( + examples=docs.specdoc_examples, + primary_result=InfoModuleResult( + display_name="Firewall Settings", + field_name="firewall_settings", + field_type=FieldType.dict, + docs_url="https://techdocs.akamai.com/linode-api/reference/get-firewall-settings", + samples=docs_parent.result_firewall_settings_samples, + get=lambda client, _: client.load(FirewallSettings, None)._raw_json, + ), + attributes=[], +) + +SPECDOC_META = module.spec + + +DOCUMENTATION = r""" +""" +EXAMPLES = r""" +""" +RETURN = r""" +""" + +if __name__ == "__main__": + module.run() diff --git a/tests/integration/targets/firewall_settings/tasks/main.yaml b/tests/integration/targets/firewall_settings/tasks/main.yaml new file mode 100644 index 00000000..5ebc0464 --- /dev/null +++ b/tests/integration/targets/firewall_settings/tasks/main.yaml @@ -0,0 +1,61 @@ +# this test case only works if you have configured default firewall to your account + +- name: firewall_device + block: + - set_fact: + r: "{{ 1000000000 | random }}" + + - name: Get the default firewall settings + linode.cloud.firewall_settings_info: {} + register: default_firewall_settings + + - name: Create a Linode Firewall + linode.cloud.firewall: + api_version: v4beta + label: 'ansible-test-{{ r }}' + rules: + inbound: [] + inbound_policy: DROP + outbound: [] + outbound_policy: DROP + state: present + register: fw + + - name: Assert firewall created + assert: + that: + - fw.changed + + - name: Update firewall settings + linode.cloud.firewall_settings: + api_version: v4beta + default_firewall_ids: + linode: '{{ fw.firewall.id }}' + register: updated_firewall_settings + + - name: Assert firewall settings updated + assert: + that: + - updated_firewall_settings.changed + - updated_firewall_settings.firewall_settings.default_firewall_ids.linode == fw.firewall.id + + always: + - ignore_errors: true + block: + - name: Restore default firewall settings + linode.cloud.firewall_settings: + api_version: v4beta + default_firewall_ids: + linode: '{{ default_firewall_settings.firewall_settings.default_firewall_ids.linode }}' + + - name: Delete Firewall + linode.cloud.firewall: + label: '{{ fw.firewall.label }}' + state: absent + + environment: + LINODE_UA_PREFIX: '{{ ua_prefix }}' + LINODE_API_TOKEN: '{{ api_token }}' + LINODE_API_URL: '{{ api_url }}' + LINODE_API_VERSION: '{{ api_version }}' + LINODE_CA: '{{ ca_file or "" }}'