diff --git a/README.md b/README.md index 3f85c93..9172a31 100644 --- a/README.md +++ b/README.md @@ -199,7 +199,15 @@ not implemented => development => [testing](https://github.com/ansibleguy/collec | **ACME (Certificates)** | ansibleguy.opnsense.acme_general | [Docs](https://opnsense.ansibleguy.net/modules/acmeclient.html) | unstable | | **ACME (Certificates)** | ansibleguy.opnsense.acme_validation | [Docs](https://opnsense.ansibleguy.net/modules/acmeclient.html) | unstable | | **ACME (Certificates)** | ansibleguy.opnsense.acme_certificate | [Docs](https://opnsense.ansibleguy.net/modules/acmeclient.html) | unstable | - +| **Postfix** | ansibleguy.opnsense.postfix_general | [Docs](https://opnsense.ansibleguy.net/modules/postfix.html) | unstable | +| **Postfix** | ansibleguy.opnsense.postfix_domain | [Docs](https://opnsense.ansibleguy.net/modules/postfix.html) | unstable | +| **Postfix** | ansibleguy.opnsense.postfix_recipient | [Docs](https://opnsense.ansibleguy.net/modules/postfix.html) | unstable | +| **Postfix** | ansibleguy.opnsense.postfix_recipientbcc | [Docs](https://opnsense.ansibleguy.net/modules/postfix.html) | unstable | +| **Postfix** | ansibleguy.opnsense.postfix_sender | [Docs](https://opnsense.ansibleguy.net/modules/postfix.html) | unstable | +| **Postfix** | ansibleguy.opnsense.postfix_senderbcc | [Docs](https://opnsense.ansibleguy.net/modules/postfix.html) | unstable | +| **Postfix** | ansibleguy.opnsense.postfix_sendercanonical | [Docs](https://opnsense.ansibleguy.net/modules/postfix.html) | unstable | +| **Postfix** | ansibleguy.opnsense.postfix_headercheck | [Docs](https://opnsense.ansibleguy.net/modules/postfix.html) | unstable | +| **Postfix** | ansibleguy.opnsense.postfix_address | [Docs](https://opnsense.ansibleguy.net/modules/postfix.html) | unstable | ### Roadmap diff --git a/docs/source/modules/2_list.rst b/docs/source/modules/2_list.rst index 36482b0..cc4084f 100644 --- a/docs/source/modules/2_list.rst +++ b/docs/source/modules/2_list.rst @@ -21,7 +21,7 @@ In most cases the returned type of this module ist a list of dictionaries. :header: "Parameter", "Type", "Required", "Default", "Aliases", "Comment" :widths: 15 10 10 10 10 45 - "target","string","true","\-","tgt, t","What part of the running config should be queried/listed. One of: 'alias', 'rule', 'route', 'cron', 'syslog', 'package', 'unbound_general', 'unbound_acl', 'unbound_host', 'unbound_dot', 'unbound_forward', 'unbound_host_alias', 'ipsec_cert', 'shaper_pipe', 'shaper_queue', 'shaper_rule', 'monit_service', 'monit_test', 'monit_alert', 'wireguard_server', 'wireguard_peer', 'interface_lagg', 'interface_vlan', 'interface_vxlan', 'source_nat', 'frr_bfd', 'frr_bgp_general', 'frr_bgp_neighbor', 'frr_bgp_prefix_list', 'frr_bgp_community_list', 'frr_bgp_as_path', 'frr_bgp_route_map', 'frr_ospf_general', 'frr_ospf_prefix_list', 'frr_ospf_interface', 'frr_ospf_route_map', 'frr_ospf_network', 'frr_ospf3_general', 'frr_ospf3_interface', 'frr_rip', 'bind_general', 'bind_blocklist', 'bind_acl', 'bind_domain', 'bind_record', 'interface_vip', 'webproxy_general', 'webproxy_cache', 'webproxy_parent', 'webproxy_traffic', 'webproxy_forward', 'webproxy_acl', 'webproxy_icap', 'webproxy_auth', 'webproxy_remote_acl', 'webproxy_pac_proxy', 'webproxy_pac_match', 'webproxy_pac_rule', 'unbound_dnsbl'" + "target","string","true","\-","tgt, t","What part of the running config should be queried/listed. One of: 'alias', 'rule', 'route', 'cron', 'syslog', 'package', 'unbound_general', 'unbound_acl', 'unbound_host', 'unbound_dot', 'unbound_forward', 'unbound_host_alias', 'ipsec_cert', 'shaper_pipe', 'shaper_queue', 'shaper_rule', 'monit_service', 'monit_test', 'monit_alert', 'wireguard_server', 'wireguard_peer', 'interface_lagg', 'interface_vlan', 'interface_vxlan', 'source_nat', 'frr_bfd', 'frr_bgp_general', 'frr_bgp_neighbor', 'frr_bgp_prefix_list', 'frr_bgp_community_list', 'frr_bgp_as_path', 'frr_bgp_route_map', 'frr_ospf_general', 'frr_ospf_prefix_list', 'frr_ospf_interface', 'frr_ospf_route_map', 'frr_ospf_network', 'frr_ospf3_general', 'frr_ospf3_interface', 'frr_rip', 'bind_general', 'bind_blocklist', 'bind_acl', 'bind_domain', 'bind_record', 'interface_vip', 'webproxy_general', 'webproxy_cache', 'webproxy_parent', 'webproxy_traffic', 'webproxy_forward', 'webproxy_acl', 'webproxy_icap', 'webproxy_auth', 'webproxy_remote_acl', 'webproxy_pac_proxy', 'webproxy_pac_match', 'webproxy_pac_rule', 'unbound_dnsbl', 'postfix_general', 'postfix_domain', 'postfix_recipient', 'postfix_recipientbcc', 'postfix_sender', 'postfix_senderbcc', 'postfix_sendercanonical', 'postfix_headercheck', 'postfix_address'" .. include:: ../_include/param_basic.rst diff --git a/docs/source/modules/postfix.rst b/docs/source/modules/postfix.rst new file mode 100644 index 0000000..f5ddc28 --- /dev/null +++ b/docs/source/modules/postfix.rst @@ -0,0 +1,379 @@ +.. _modules_postfix: + +.. include:: ../_include/head.rst + +======= +Postfix +======= + +**STATE**: unstable + +**TESTS**: `postfix_general `_ | +`postfix_ `_ | +`postfix_recipient `_ | +`postfix_recipientbcc `_ | +`postfix_sender `_ | +`postfix_senderbcc `_ | +`postfix_sendercanonical `_ | +`postfix_headercheck `_ | +`postfix_address `_ + +**API Docs**: `Plugins - Postfix `_ + +**Service Docs**: `How To: Setting Up A Mail Gateway `_ + + +Contribution +************ + +Thanks to `@jiuka `_ for developing these modules! + +Prerequisites +************* + +You need to install the postfix plugin: + +``` +os-postfix +``` + +You can also install it using the :ref:`ansibleguy.opnsense.package ` module. + +---- + +Definition +********** + +.. include:: ../_include/param_basic.rst + +ansibleguy.opnsense.postfix_general +=================================== + +.. csv-table:: Definition + :header: "Parameter", "Type", "Required", "Default", "Aliases", "Comment" + :widths: 15 10 10 10 10 45 + + "enable","boolean","false","false","\-","Enable Postfix daemon." + "myhostname","string","false","\-","\-","The internet hostname of this mail system. The default is to use the fully-qualified domain name (FQDN). See `postconf(5) myhostname `_" + "mydomain","string","false","\-","\-","The internet domain name of this mail system. The default is to use `myhostname` minus the first component. See `postconf(5) mydomain `_" + "myorigin","string","false","\-","\-","The domain name that locally-posted mail appears to come from. Default to `myhostname`. See `postconf(5) myorigin `_" + "inet_interfaces","list","false","['all']","\-","Specifies a comma-separated list of IP addresses to listen to. See `postconf(5) inet_interfaces `_" + "inet_port","int","false","25","\-","Port to listen on." + "ip_version","string","false","all","\-","The Internet protocols Postfix will attempt to use when making or accepting connections. One of: all, ipv4 or ipv6. See `postconf(5) inet_protocols `_" + "bind_address","string","false","\-","\-","IPv4 address the server should bind to for outgoing connections. See `postconf(5) smtp_bind_address `_" + "bind_address6","string","false","\-","\-","IPv6 address the server should bind to for outgoing connections. See `postconf(5) smtp_bind_address6 `_" + "mynetworks","list","false","['127.0.0.0/8', '[::ffff:127.0.0.0]/104', '[::1]/128']","\-","List of 'trusted' remote SMTP clients. See `postconf(5) mynetworks `_" + "banner","string","false","\-","\-","The text that follows the 220 status code in the SMTP greeting banner. See `postconf(5) smtpd_banner `_" + "message_size_limit","integer","false","51200000","\-","The max size for messages to accept, default is 51200000 Bytes which is 50MB. See `postconf(5) message_size_limit `_" + "masquerade_domains","list","false","[]","\-","List of domains whose subdomain structure will be stripped off in email addresses. See `postconf(5) masquerade_domains `_" + "tls_server_compatibility","string","false","intermediate","\-","TLS version/cipher compatibility of the SMTP service. One of: modern, intermediate or old." + "tls_client_compatibility","string","false","intermediate","\-","TLS version/cipher compatibility of the SMTP client. One of: modern, intermediate or old." + "tlswrappermode","bool","false","false","\-","Request that the Postfix SMTP client connects using the SUBMISSIONS/SMTPS protocol instead of using the STARTTLS command. See `postconf(5) smtp_tls_wrappermode `_" + "certificate","string","false","\-","\-","Choose the certificate to use when other servers want to do TLS with you." + "ca","string","false","\-","\-","Choose the Certificate Authority which signed your certificate." + "smtpclient_security","string","false","may","\-","'none' will disable TLS for sending mail. 'may' will use TLS when offered. 'encrypt' will enforce TLS on all connections. 'dane' will enforce TLS if a TLSA-Record is published." + "relayhost","string","false","\-","smarthost","The next-hop destination(s) for non-local mail. See `postconf(5) relayhost `_" + "smtpauth_enabled","boolean","false","false","\-","Enable authentication against your relayhost." + "smtpauth_user","string","false","\-","\-","The username to use for SMTP authentication against your relayhost." + "smtpauth_password","string","false","\-","\.","The password to use for SMTP authentication against your relayhost." + "enforce_recipient_check","boolean","false","false","\-","Activates recipient restrictions managed by ansibleguy.opnsense.postfix_recipient." + "extensive_helo_restrictions","boolean","false","false","\-","Activate hello restrictions." + "extensive_sender_restrictions","boolean","false","false","\-","Activate sender restrictions." + "reject_unknown_client_hostname","boolean","false","false","\-","Add `reject_unknown_client_hostname `_ to `smtpd_recipient_restrictions `_." + "reject_non_fqdn_helo_hostname","boolean","false","false","\-","Add `reject_non_fqdn_helo_hostname `_ to `smtpd_recipient_restrictions `_." + "reject_invalid_helo_hostname","boolean","false","false","\-","Add `reject_invalid_helo_hostname `_ to `smtpd_recipient_restrictions `_." + "reject_unknown_helo_hostname","boolean","false","false","\-","Add `reject_unknown_helo_hostname `_ to `smtpd_recipient_restrictions `_." + "reject_unauth_pipelining","boolean","false","true","\-","Add `reject_unauth_pipelining `_ to `smtpd_recipient_restrictions `_." + "reject_unknown_sender_domain","boolean","false","true","\-","Reject mails from domains which do not exist. See `postconf(5) reject_unknown_sender_domain `_" + "reject_unknown_recipient_domain","boolean","false","true","\-","Reject mails to domains which do not exist. See `postconf(5) reject_unknown_recipient_domain `_" + "reject_non_fqdn_sender","bool","false","true","\-","Reject senders without a domain or only a hostname. See `postconf(5) reject_non_fqdn_sender `_" + "reject_non_fqdn_recipient","bool","false","true","\-","Rejects recipients without a domain or only a hostname. See `postconf(5) reject_non_fqdn_recipient `_" + "permit_sasl_authenticated","bool","false","true","\-","Allow SASL authenticated senders to relay. Will also enable smtpd_sasl_auth." + "permit_tls_clientcerts","boolean","false","true","\-","Allow mTLS authenticated senders to relay." + "permit_mynetworks","boolean","false","true","\-","Allow client from `mynetworks` to relay." + "reject_unauth_destination","boolean","false","true","\-","Add `reject_unauth_destination `_ to `smtpd_recipient_restrictions `_." + "reject_unverified_recipient","bool","false","false","\-","Use Recipient Address Verification. Please keep in mind that this could put significant load onto the next server." + "delay_warning_time","int","false","0","\-","Time in hours until we send a notification to the sender if mail is delayed. 0 or empty to disable." + "reload","boolean","false","true","\-", .. include:: ../_include/param_reload.rst + + +ansibleguy.opnsense.postfix_domain +================================== + +.. csv-table:: Definition + :header: "Parameter", "Type", "Required", "Default", "Aliases", "Comment" + :widths: 15 10 10 10 10 45 + + "enable","boolean","false","false","\-","Enable or disable domain routing for this entry." + "domainname","string","true","\-","name","Set the domain name to relay for." + "destination","string","false","\-","\-","Set the IP or FQDN to where to send the mails to. Empty means MX will be used. You can also add custom ports via :225 or disable MX lookup via squared brackets." + "reload","boolean","false","true","\-", .. include:: ../_include/param_reload.rst + + +ansibleguy.opnsense.postfix_recipient +===================================== + +.. csv-table:: Definition + :header: "Parameter", "Type", "Required", "Default", "Aliases", "Comment" + :widths: 15 10 10 10 10 45 + + "enable","boolean","false","false","\-","Enable or disable the recipient rule in this entry." + "address","string","true","\-","\-","Recipient address to match." + "action","string","false","\-","\-","Action for this address. One of: OK or REJECT." + "reload","boolean","false","true","\-", .. include:: ../_include/param_reload.rst + + +ansibleguy.opnsense.postfix_recipientbcc +======================================== + +.. csv-table:: Definition + :header: "Parameter", "Type", "Required", "Default", "Aliases", "Comment" + :widths: 15 10 10 10 10 45 + + "enable","boolean","false","false","\-","Enable or disable the BCC recipient rewriting setting." + "address","string","true","\-","from","Pattern to match like user@example.com" + "to","list","false","\-","\-","Recipient address to send the mail as BCC." + "reload","boolean","false","true","\-", .. include:: ../_include/param_reload.rst + + +ansibleguy.opnsense.postfix_sender +================================== + +.. csv-table:: Definition + :header: "Parameter", "Type", "Required", "Default", "Aliases", "Comment" + :widths: 15 10 10 10 10 45 + + "enable","boolean","false","false","\-","Enable or disable the sender address to match for." + "address","string","true","\-","\-","Sender address to match." + "action","string","false","\-","\-","Action for this address. One of: OK or REJECT." + "reload","boolean","false","true","\-", .. include:: ../_include/param_reload.rst + + +ansibleguy.opnsense.postfix_senderbcc +===================================== + +.. csv-table:: Definition + :header: "Parameter", "Type", "Required", "Default", "Aliases", "Comment" + :widths: 15 10 10 10 10 45 + + "enable","boolean","false","false","\-","Enable or disable the BCC sender rewriting setting." + "address","string","true","\-","from","Pattern to match like user@example.com" + "to","list","false","\-","\-","Recipient address to send the mail as BCC." + "reload","boolean","false","true","\-", .. include:: ../_include/param_reload.rst + + +ansibleguy.opnsense.postfix_sendercanonical +=========================================== + +.. csv-table:: Definition + :header: "Parameter", "Type", "Required", "Default", "Aliases", "Comment" + :widths: 15 10 10 10 10 45 + + "enable","boolean","false","false","\-","Enable or disable the sender canonical rewriting setting." + "address","string","true","\-","from","Pattern to match line user@example.com or @example.com" + "to","list","false","\-","\-","How to rewrite the Rewrite From pattern." + "reload","boolean","false","true","\-", .. include:: ../_include/param_reload.rst + + +ansibleguy.opnsense.postfix_headercheck +======================================= + +.. csv-table:: Definition + :header: "Parameter", "Type", "Required", "Default", "Aliases", "Comment" + :widths: 15 10 10 10 10 45 + + "enable","boolean","false","false","\-","Enable or disable the header check." + "expression","string","true","\-","from","Regexp (POSIX regular expression) and an action to process like ``/^\s*User-Agent/ IGNORE``. See the `Postfix manual about header_checks(5) `_" + "filter","string","true","\-","\-","When the header_check should be processed. One of: WHILE_DELIVERING or WHILE_RECEIVING" + "reload","boolean","false","true","\-", .. include:: ../_include/param_reload.rst + + +ansibleguy.opnsense.postfix_address +=================================== + +.. csv-table:: Definition + :header: "Parameter", "Type", "Required", "Default", "Aliases", "Comment" + :widths: 15 10 10 10 10 45 + + "enable","boolean","false","false","\-","Enable or disable the address entry." + "address","string","true","\-","from","Pattern to match line user@example.com or @example.com" + "to","list","false","\-","\-","How to rewrite the Rewrite From pattern." + "reload","boolean","false","true","\-", .. include:: ../_include/param_reload.rst + +---- + +Usage +***** + +ansibleguy.opnsense.postfix_general +=================================== + +Use ``ansibleguy.opnsense.postfix_general`` to setup the postfix daemon. + +ansibleguy.opnsense.postfix_domain +================================== + +Manage accepted domains and the target to forward mails to using the +`relay_domains `_ and +`transport_maps `_. + +ansibleguy.opnsense.postfix_recipient / postfix_sender +====================================================== + +Manage addresses on the +`smtpd_recipient_restrictions `_ and +`check_sender_access `_ maps respectively. + +ansibleguy.opnsense.postfix_recipientbcc / postfix_senderbcc +============================================================ + +Manage entries for the +`recipient_bcc_maps `_ and +`sender_bcc_maps `_ maps respectively. + + +ansibleguy.opnsense.postfix_canonical +===================================== + +Manage entries for the +`sender_canonical_maps `_ map. + + +ansibleguy.opnsense.postfix_headercheck +======================================= + +Manage entries for the +`header_checks `_ (type: WHILE_RECEIVING) and +`smtp_header_checks `_ (type: WHILE_DELIVERING) maps. + + +ansibleguy.opnsense.postfix_address +=================================== + +Manage entries for the +`virtual_alias_maps `_ map. The table format and lookups +are documented in `virtual(5) `_ . + +---- + +Examples +******** + +.. code-block:: yaml + + - hosts: localhost + gather_facts: false + module_defaults: + group/ansibleguy.opnsense.all: + firewall: 'opnsense.template.ansibleguy.net' + api_credential_file: '/home/guy/.secret/opn.key' + + tasks: + - name: Setup Postfix + ansibleguy.opnsense.postfix_general: + enable: true + # myhostname: + # mydomain: + # myorigin: + # inet_interfaces: ['all'] + # inet_port: 25 + # ip_version: all + # bind_address: + # bind_address6 + # mynetworks: ['127.0.0.0/8', '[::ffff:127.0.0.0]/104', '[::1]/128'] + # banner: + # message_size_limit: 51200000 + # masquerade_domains: + # tls_server_compatibility: intermediate + # tls_client_compatibility: intermediate + # tlswrappermode: false + # certificate: + # ca: + # smtpclient_security: may + # relayhost: + # smtpauth_enabled: false + # smtpauth_user: + # smtpauth_password: + # enforce_recipient_check: false + # extensive_helo_restrictions: false + # extensive_sender_restrictions: false + # reject_unknown_client_hostname: false + # reject_non_fqdn_helo_hostname: false + # reject_invalid_helo_hostname: false + # reject_unknown_helo_hostname: false + # reject_unauth_pipelining: true + # reject_unknown_sender_domain: true + # reject_unknown_recipient_domain: true + # reject_non_fqdn_sender: true + # reject_non_fqdn_recipient: true + # permit_sasl_authenticated: true + # permit_tls_clientcerts: true + # permit_mynetworks: true + # reject_unauth_destination: true + # reject_unverified_recipient: false + # delay_warning_time: 0 + # auto_renewal: true + # challenge_port: 43580 + # tls_challenge_port: 43581 + # restart_timeout: 600 + # haproxy_integration: false + # log_level: normal + # show_intro: true + # debug: false + + - name: Add Domain + ansibleguy.opnsense.postfix_domain: + domainname: ansibleguy.net + # destination: mail.ansibleguy.net + # enable: true + + - name: Block Recipient + ansibleguy.opnsense.postfix_recipient: + address: noreply@ansibleguy.net + action: REJECT + # enable: true + + - name: Auto BCC Recipient + ansibleguy.opnsense.postfix_recipient: + address: alice@ansibleguy.net + to: bob@ansibleguy.net + # enable: true + + - name: Block Sender + ansibleguy.opnsense.postfix_recipient: + address: internal-only@ansibleguy.net + action: REJECT + # enable: true + + - name: Auto BCC Sender + ansibleguy.opnsense.postfix_recipient: + address: alice@ansibleguy.net + to: bob@ansibleguy.net + # enable: true + + - name: Sender Canonical Rewriting + ansibleguy.opnsense.postfix_sendercanonical: + address: '@ansibleguy.com' + to: '@ansibleguy.net' + # enable: true + + - name: Strip User-Agent header + ansibleguy.opnsense.header_check + expression: /^\s*User-Agent/ IGNORE + filter: WHILE_DELIVERING + # enable: true + + - name: Address Rewriting + ansibleguy.opnsense.postfix_address + address: root@ansibleguy.net + to: alice@ansibleguy.net + # enable: true + + - name: Listing jobs + ansibleguy.opnsense.list: + target: 'postfix_address' + register: existing_postfix_address + + - name: Printing + ansible.builtin.debug: + var: existing_postfix_address.data diff --git a/meta/runtime.yml b/meta/runtime.yml index 663bde0..6317590 100644 --- a/meta/runtime.yml +++ b/meta/runtime.yml @@ -135,6 +135,16 @@ action_groups: - ansibleguy.opnsense.acme_validation - ansibleguy.opnsense.acme_action - ansibleguy.opnsense.acme_certificate + postfix: + - ansibleguy.opnsense.postfix_general + - ansibleguy.opnsense.postfix_domain + - ansibleguy.opnsense.postfix_recipient + - ansibleguy.opnsense.postfix_recipientbcc + - ansibleguy.opnsense.postfix_sender + - ansibleguy.opnsense.postfix_senderbcc + - ansibleguy.opnsense.postfix_sendercanonical + - ansibleguy.opnsense.postfix_headercheck + - ansibleguy.opnsense.postfix_address all: - metadata: extend_group: @@ -159,6 +169,7 @@ action_groups: - ansibleguy.opnsense.dhcrelay - ansibleguy.opnsense.dhcp - ansibleguy.opnsense.acme + - ansibleguy.opnsense.postfix plugin_routing: modules: diff --git a/plugins/module_utils/main/postfix_address.py b/plugins/module_utils/main/postfix_address.py new file mode 100644 index 0000000..f3d133e --- /dev/null +++ b/plugins/module_utils/main/postfix_address.py @@ -0,0 +1,34 @@ +from ansible.module_utils.basic import AnsibleModule + +from ansible_collections.ansibleguy.opnsense.plugins.module_utils.base.api import \ + Session +from ansible_collections.ansibleguy.opnsense.plugins.module_utils.base.cls import BaseModule + + +class Address(BaseModule): + FIELD_ID = 'address' + CMDS = { + 'add': 'addAddress', + 'del': 'delAddress', + 'set': 'setAddress', + 'search': 'get', + 'toggle': 'toggleAddress', + } + API_KEY_PATH = 'address.addresses.address' + API_MOD = 'postfix' + API_CONT = 'address' + API_CONT_REL = 'service' + API_CMD_REL = 'reconfigure' + FIELDS_CHANGE = ['to'] + FIELDS_ALL = ['enabled', 'address'] + FIELDS_ALL.extend(FIELDS_CHANGE) + FIELDS_TRANSLATE = {'address': 'from'} + FIELDS_TYPING = { + 'bool': ['enabled'], + 'list': ['to'], + } + EXIST_ATTR = 'address' + + def __init__(self, module: AnsibleModule, result: dict, session: Session = None): + BaseModule.__init__(self=self, m=module, r=result, s=session) + self.address = {} diff --git a/plugins/module_utils/main/postfix_domain.py b/plugins/module_utils/main/postfix_domain.py new file mode 100644 index 0000000..69c6e9f --- /dev/null +++ b/plugins/module_utils/main/postfix_domain.py @@ -0,0 +1,32 @@ +from ansible.module_utils.basic import AnsibleModule + +from ansible_collections.ansibleguy.opnsense.plugins.module_utils.base.api import \ + Session +from ansible_collections.ansibleguy.opnsense.plugins.module_utils.base.cls import BaseModule + + +class Domain(BaseModule): + FIELD_ID = 'domainname' + CMDS = { + 'add': 'addDomain', + 'del': 'delDomain', + 'set': 'setDomain', + 'search': 'get', + 'toggle': 'toggleDomain', + } + API_KEY_PATH = 'domain.domains.domain' + API_MOD = 'postfix' + API_CONT = 'domain' + API_CONT_REL = 'service' + API_CMD_REL = 'reconfigure' + FIELDS_CHANGE = ['destination'] + FIELDS_ALL = ['enabled', 'domainname'] + FIELDS_ALL.extend(FIELDS_CHANGE) + FIELDS_TYPING = { + 'bool': ['enabled'], + } + EXIST_ATTR = 'domain' + + def __init__(self, module: AnsibleModule, result: dict, session: Session = None): + BaseModule.__init__(self=self, m=module, r=result, s=session) + self.domain = {} diff --git a/plugins/module_utils/main/postfix_general.py b/plugins/module_utils/main/postfix_general.py new file mode 100644 index 0000000..128e542 --- /dev/null +++ b/plugins/module_utils/main/postfix_general.py @@ -0,0 +1,57 @@ +from ansible.module_utils.basic import AnsibleModule + +from ansible_collections.ansibleguy.opnsense.plugins.module_utils.base.api import \ + Session +from ansible_collections.ansibleguy.opnsense.plugins.module_utils.base.cls import GeneralModule + + +class General(GeneralModule): + CMDS = { + 'set': 'set', + 'search': 'get', + } + API_KEY_PATH = 'general' + API_MOD = 'postfix' + API_CONT = 'general' + API_CONT_REL = 'service' + FIELDS_CHANGE = [ + 'myhostname', 'mydomain', 'myorigin', 'inet_interfaces', 'inet_port', 'ip_version', 'bind_address', + 'bind_address6', 'mynetworks', 'banner', 'message_size_limit', 'masquerade_domains', + 'tls_server_compatibility', 'tls_client_compatibility', 'certificate', 'ca', 'smtpclient_security', + 'relayhost', 'smtpauth_enabled', 'smtpauth_user', 'smtpauth_password', 'enforce_recipient_check', + 'extensive_helo_restrictions', 'extensive_sender_restrictions', 'reject_unknown_client_hostname', + 'reject_non_fqdn_helo_hostname', 'reject_invalid_helo_hostname', 'reject_unknown_helo_hostname', + 'reject_unauth_pipelining', 'reject_unknown_sender_domain', 'reject_unknown_recipient_domain', + 'reject_non_fqdn_sender', 'reject_non_fqdn_recipient', 'permit_sasl_authenticated', 'permit_tls_clientcerts', + 'permit_mynetworks', 'reject_unauth_destination', 'reject_unverified_recipient', 'delay_warning_time', + ] + FIELDS_ALL = ['enabled'] + FIELDS_ALL.extend(FIELDS_CHANGE) + FIELDS_TYPING = { + 'bool': [ + 'enabled', 'tlswrappermode', 'smtpauth_enabled', 'enforce_recipient_check', 'extensive_helo_restrictions', + 'extensive_sender_restrictions', 'reject_unknown_client_hostname', 'reject_non_fqdn_helo_hostname', + 'reject_invalid_helo_hostname', 'reject_unknown_helo_hostname', 'reject_unauth_pipelining', + 'reject_unknown_sender_domain', 'reject_unknown_recipient_domain', 'reject_non_fqdn_sender', + 'reject_non_fqdn_recipient', 'permit_sasl_authenticated', 'permit_tls_clientcerts', 'permit_mynetworks', + 'reject_unauth_destination', 'reject_unverified_recipient', + ], + 'list': ['inet_interfaces', 'mynetworks', 'masquerade_domains'], + 'select': [ + 'ip_version', 'tls_server_compatibility', 'tls_client_compatibility', 'certificate', 'ca', + 'smtpclient_security', + ], + 'int': ['inet_port', 'message_size_limit', 'delay_warning_time'], + } + INT_VALIDATIONS = { + 'inet_port': {'min': 1, 'max': 65535}, + 'delay_warning_time': {'min': 0, 'max': 24}, + } + EXIST_ATTR = 'settings' + SEARCH_ADDITIONAL = { + 'existing_additionalstuff': 'category.sub_category.additional', + } + + def __init__(self, module: AnsibleModule, result: dict, session: Session = None): + GeneralModule.__init__(self=self, m=module, r=result, s=session) + self.settings = {} diff --git a/plugins/module_utils/main/postfix_headercheck.py b/plugins/module_utils/main/postfix_headercheck.py new file mode 100644 index 0000000..70a472d --- /dev/null +++ b/plugins/module_utils/main/postfix_headercheck.py @@ -0,0 +1,35 @@ +from ansible.module_utils.basic import AnsibleModule + +from ansible_collections.ansibleguy.opnsense.plugins.module_utils.base.api import \ + Session +from ansible_collections.ansibleguy.opnsense.plugins.module_utils.base.cls import BaseModule + + +class Headercheck(BaseModule): + FIELD_ID = 'expression' + CMDS = { + 'add': 'addHeadercheck', + 'del': 'delHeadercheck', + 'set': 'setHeadercheck', + 'search': 'get', + 'toggle': 'toggleHeadercheck', + } + API_KEY_PATH = 'headerchecks.headerchecks.headercheck' + API_MOD = 'postfix' + API_CONT = 'headerchecks' + API_CONT_REL = 'service' + API_CMD_REL = 'reconfigure' + FIELDS_CHANGE = [] + FIELDS_ALL = ['enabled', 'expression', 'filter'] + FIELDS_TYPING = { + 'bool': ['enabled'], + 'select': ['filter'], + } + EXIST_ATTR = 'headercheck' + + def __init__(self, module: AnsibleModule, result: dict, session: Session = None): + BaseModule.__init__(self=self, m=module, r=result, s=session) + self.headercheck = {} + + def check(self) -> None: + self.b.find(match_fields=['expression', 'filter']) diff --git a/plugins/module_utils/main/postfix_recipient.py b/plugins/module_utils/main/postfix_recipient.py new file mode 100644 index 0000000..ff72284 --- /dev/null +++ b/plugins/module_utils/main/postfix_recipient.py @@ -0,0 +1,33 @@ +from ansible.module_utils.basic import AnsibleModule + +from ansible_collections.ansibleguy.opnsense.plugins.module_utils.base.api import \ + Session +from ansible_collections.ansibleguy.opnsense.plugins.module_utils.base.cls import BaseModule + + +class Recipient(BaseModule): + FIELD_ID = 'address' + CMDS = { + 'add': 'addRecipient', + 'del': 'delRecipient', + 'set': 'setRecipient', + 'search': 'get', + 'toggle': 'toggleRecipient', + } + API_KEY_PATH = 'recipient.recipients.recipient' + API_MOD = 'postfix' + API_CONT = 'recipient' + API_CONT_REL = 'service' + API_CMD_REL = 'reconfigure' + FIELDS_CHANGE = ['action'] + FIELDS_ALL = ['enabled', 'address'] + FIELDS_ALL.extend(FIELDS_CHANGE) + FIELDS_TYPING = { + 'bool': ['enabled'], + 'select': ['action'], + } + EXIST_ATTR = 'recipient' + + def __init__(self, module: AnsibleModule, result: dict, session: Session = None): + BaseModule.__init__(self=self, m=module, r=result, s=session) + self.recipient = {} diff --git a/plugins/module_utils/main/postfix_recipientbcc.py b/plugins/module_utils/main/postfix_recipientbcc.py new file mode 100644 index 0000000..07474ca --- /dev/null +++ b/plugins/module_utils/main/postfix_recipientbcc.py @@ -0,0 +1,34 @@ +from ansible.module_utils.basic import AnsibleModule + +from ansible_collections.ansibleguy.opnsense.plugins.module_utils.base.api import \ + Session +from ansible_collections.ansibleguy.opnsense.plugins.module_utils.base.cls import BaseModule + + +class RecipientBCC(BaseModule): + FIELD_ID = 'address' + CMDS = { + 'add': 'addRecipientbcc', + 'del': 'delRecipientbcc', + 'set': 'setRecipientbcc', + 'search': 'get', + 'toggle': 'toggleRecipientbcc', + } + API_KEY_PATH = 'recipientbcc.recipientbccs.recipientbcc' + API_MOD = 'postfix' + API_CONT = 'recipientbcc' + API_CONT_REL = 'service' + API_CMD_REL = 'reconfigure' + FIELDS_CHANGE = ['to'] + FIELDS_ALL = ['enabled', 'address'] + FIELDS_ALL.extend(FIELDS_CHANGE) + FIELDS_TRANSLATE = {'address': 'from'} + FIELDS_TYPING = { + 'bool': ['enabled'], + 'list': ['to'], + } + EXIST_ATTR = 'recipient' + + def __init__(self, module: AnsibleModule, result: dict, session: Session = None): + BaseModule.__init__(self=self, m=module, r=result, s=session) + self.recipient = {} diff --git a/plugins/module_utils/main/postfix_sender.py b/plugins/module_utils/main/postfix_sender.py new file mode 100644 index 0000000..0f07621 --- /dev/null +++ b/plugins/module_utils/main/postfix_sender.py @@ -0,0 +1,33 @@ +from ansible.module_utils.basic import AnsibleModule + +from ansible_collections.ansibleguy.opnsense.plugins.module_utils.base.api import \ + Session +from ansible_collections.ansibleguy.opnsense.plugins.module_utils.base.cls import BaseModule + + +class Sender(BaseModule): + FIELD_ID = 'address' + CMDS = { + 'add': 'addSender', + 'del': 'delSender', + 'set': 'setSender', + 'search': 'get', + 'toggle': 'toggleSender', + } + API_KEY_PATH = 'sender.senders.sender' + API_MOD = 'postfix' + API_CONT = 'sender' + API_CONT_REL = 'service' + API_CMD_REL = 'reconfigure' + FIELDS_CHANGE = ['action'] + FIELDS_ALL = ['enabled', 'address'] + FIELDS_ALL.extend(FIELDS_CHANGE) + FIELDS_TYPING = { + 'bool': ['enabled'], + 'select': ['action'], + } + EXIST_ATTR = 'sender' + + def __init__(self, module: AnsibleModule, result: dict, session: Session = None): + BaseModule.__init__(self=self, m=module, r=result, s=session) + self.sender = {} diff --git a/plugins/module_utils/main/postfix_senderbcc.py b/plugins/module_utils/main/postfix_senderbcc.py new file mode 100644 index 0000000..5bb12da --- /dev/null +++ b/plugins/module_utils/main/postfix_senderbcc.py @@ -0,0 +1,34 @@ +from ansible.module_utils.basic import AnsibleModule + +from ansible_collections.ansibleguy.opnsense.plugins.module_utils.base.api import \ + Session +from ansible_collections.ansibleguy.opnsense.plugins.module_utils.base.cls import BaseModule + + +class SenderBCC(BaseModule): + FIELD_ID = 'address' + CMDS = { + 'add': 'addSenderbcc', + 'del': 'delSenderbcc', + 'set': 'setSenderbcc', + 'search': 'get', + 'toggle': 'toggleSenderbcc', + } + API_KEY_PATH = 'senderbcc.senderbccs.senderbcc' + API_MOD = 'postfix' + API_CONT = 'senderbcc' + API_CONT_REL = 'service' + API_CMD_REL = 'reconfigure' + FIELDS_CHANGE = ['to'] + FIELDS_ALL = ['enabled', 'address'] + FIELDS_ALL.extend(FIELDS_CHANGE) + FIELDS_TRANSLATE = {'address': 'from'} + FIELDS_TYPING = { + 'bool': ['enabled'], + 'list': ['to'], + } + EXIST_ATTR = 'sender' + + def __init__(self, module: AnsibleModule, result: dict, session: Session = None): + BaseModule.__init__(self=self, m=module, r=result, s=session) + self.sender = {} diff --git a/plugins/module_utils/main/postfix_sendercanonical.py b/plugins/module_utils/main/postfix_sendercanonical.py new file mode 100644 index 0000000..38db9c0 --- /dev/null +++ b/plugins/module_utils/main/postfix_sendercanonical.py @@ -0,0 +1,34 @@ +from ansible.module_utils.basic import AnsibleModule + +from ansible_collections.ansibleguy.opnsense.plugins.module_utils.base.api import \ + Session +from ansible_collections.ansibleguy.opnsense.plugins.module_utils.base.cls import BaseModule + + +class SenderCanonical(BaseModule): + FIELD_ID = 'address' + CMDS = { + 'add': 'addSendercanonical', + 'del': 'delSendercanonical', + 'set': 'setSendercanonical', + 'search': 'get', + 'toggle': 'toggleSendercanonical', + } + API_KEY_PATH = 'sendercanonical.sendercanonicals.sendercanonical' + API_MOD = 'postfix' + API_CONT = 'sendercanonical' + API_CONT_REL = 'service' + API_CMD_REL = 'reconfigure' + FIELDS_CHANGE = ['to'] + FIELDS_ALL = ['enabled', 'address'] + FIELDS_ALL.extend(FIELDS_CHANGE) + FIELDS_TRANSLATE = {'address': 'from'} + FIELDS_TYPING = { + 'bool': ['enabled'], + 'list': ['to'], + } + EXIST_ATTR = 'sender' + + def __init__(self, module: AnsibleModule, result: dict, session: Session = None): + BaseModule.__init__(self=self, m=module, r=result, s=session) + self.sender = {} diff --git a/plugins/modules/list.py b/plugins/modules/list.py index 03d28c7..f9f1667 100644 --- a/plugins/modules/list.py +++ b/plugins/modules/list.py @@ -36,8 +36,10 @@ 'ipsec_child', 'ipsec_vti', 'ipsec_auth_local', 'ipsec_auth_remote', 'frr_general', 'unbound_general', 'unbound_acl', 'ids_general', 'ids_policy', 'ids_rule', 'ids_ruleset', 'ids_user_rule', 'ids_policy_rule', 'openvpn_instance', 'openvpn_static_key', 'openvpn_client_override', 'dhcrelay_destination', 'dhcrelay_relay', - 'interface_lagg', 'interface_loopback', 'unbound_dnsbl', 'dhcp_reservation', 'dhcp_subnet', 'acme_general', - 'dhcp_general', 'acme_account', 'acme_validation', 'acme_action', 'acme_certificate', + 'interface_lagg', 'interface_loopback', 'unbound_dnsbl', 'dhcp_reservation', 'acme_general', 'acme_account', + 'acme_validation', 'acme_action', 'acme_certificate', 'postfix_general', 'postfix_domain', 'postfix_recipient', + 'postfix_recipientbcc', 'postfix_sender', 'postfix_senderbcc', 'postfix_sendercanonical', 'postfix_headercheck', + 'postfix_address', 'dhcp_subnet', 'dhcp_general', ] @@ -399,6 +401,15 @@ def run_module(): elif target == 'dhcp_reservation': from ansible_collections.ansibleguy.opnsense.plugins.module_utils.main.dhcp_reservation_v4 import \ ReservationV4 as Target_Obj + + elif target == 'dhcp_general': + from ansible_collections.ansibleguy.opnsense.plugins.module_utils.main.dhcp_general import \ + General as Target_Obj + + elif target == 'dhcp_subnet': + from ansible_collections.ansibleguy.opnsense.plugins.module_utils.main.dhcp_subnet_v4 import \ + SubnetV4 as Target_Obj + elif target == 'acme_general': from ansible_collections.ansibleguy.opnsense.plugins.module_utils.main.acme_general import \ General as Target_Obj @@ -419,13 +430,41 @@ def run_module(): from ansible_collections.ansibleguy.opnsense.plugins.module_utils.main.acme_certificate import \ Certificate as Target_Obj - elif target == 'dhcp_general': - from ansible_collections.ansibleguy.opnsense.plugins.module_utils.main.dhcp_general import \ + elif target == 'postfix_general': + from ansible_collections.ansibleguy.opnsense.plugins.module_utils.main.postfix_general import \ General as Target_Obj - elif target == 'dhcp_subnet': - from ansible_collections.ansibleguy.opnsense.plugins.module_utils.main.dhcp_subnet_v4 import \ - SubnetV4 as Target_Obj + elif target == 'postfix_domain': + from ansible_collections.ansibleguy.opnsense.plugins.module_utils.main.postfix_domain import \ + Domain as Target_Obj + + elif target == 'postfix_recipient': + from ansible_collections.ansibleguy.opnsense.plugins.module_utils.main.postfix_recipient import \ + Recipient as Target_Obj + + elif target == 'postfix_recipientbcc': + from ansible_collections.ansibleguy.opnsense.plugins.module_utils.main.postfix_recipientbcc import \ + RecipientBCC as Target_Obj + + elif target == 'postfix_sender': + from ansible_collections.ansibleguy.opnsense.plugins.module_utils.main.postfix_sender import \ + Sender as Target_Obj + + elif target == 'postfix_senderbcc': + from ansible_collections.ansibleguy.opnsense.plugins.module_utils.main.postfix_senderbcc import \ + SenderBCC as Target_Obj + + elif target == 'postfix_sendercanonical': + from ansible_collections.ansibleguy.opnsense.plugins.module_utils.main.postfix_sendercanonical import \ + SenderCanonical as Target_Obj + + elif target == 'postfix_headercheck': + from ansible_collections.ansibleguy.opnsense.plugins.module_utils.main.postfix_headercheck import \ + Headercheck as Target_Obj + + elif target == 'postfix_address': + from ansible_collections.ansibleguy.opnsense.plugins.module_utils.main.postfix_address import \ + Address as Target_Obj except AttributeError: module_dependency_error() diff --git a/plugins/modules/postfix_address.py b/plugins/modules/postfix_address.py new file mode 100644 index 0000000..05a496d --- /dev/null +++ b/plugins/modules/postfix_address.py @@ -0,0 +1,66 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# Copyright: (C) 2025, AnsibleGuy +# GNU General Public License v3.0+ (see https://www.gnu.org/licenses/gpl-3.0.txt) + +from ansible.module_utils.basic import AnsibleModule + +from ansible_collections.ansibleguy.opnsense.plugins.module_utils.base.handler import \ + module_dependency_error, MODULE_EXCEPTIONS + +try: + from ansible_collections.ansibleguy.opnsense.plugins.module_utils.helper.wrapper import module_wrapper + from ansible_collections.ansibleguy.opnsense.plugins.module_utils.defaults.main import \ + OPN_MOD_ARGS, STATE_MOD_ARG, RELOAD_MOD_ARG + from ansible_collections.ansibleguy.opnsense.plugins.module_utils.main.postfix_address import Address + +except MODULE_EXCEPTIONS: + module_dependency_error() + + +# DOCUMENTATION = 'https://opnsense.ansibleguy.net/modules/postfix.html' +# EXAMPLES = 'https://opnsense.ansibleguy.net/modules/postfix.html' + + +def run_module(): + module_args = dict( + address=dict( + type='str', required=True, aliasses=['from'], + description='Set a pattern to match line user@example.com or @example.com', + ), + to=dict( + type='list', required=False, elements='str', + description='Set here how to rewrite the Rewrite From pattern.', + ), + **RELOAD_MOD_ARG, + **STATE_MOD_ARG, + **OPN_MOD_ARGS, + ) + + result = dict( + changed=False, + diff={ + 'before': {}, + 'after': {}, + } + ) + + module = AnsibleModule( + argument_spec=module_args, + supports_check_mode=True, + required_if=[ + ('state', 'present', ('to',)), + ], + ) + + module_wrapper(Address(module=module, result=result)) + module.exit_json(**result) + + +def main(): + run_module() + + +if __name__ == '__main__': + main() diff --git a/plugins/modules/postfix_domain.py b/plugins/modules/postfix_domain.py new file mode 100644 index 0000000..3fb8ef9 --- /dev/null +++ b/plugins/modules/postfix_domain.py @@ -0,0 +1,64 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# Copyright: (C) 2025, AnsibleGuy +# GNU General Public License v3.0+ (see https://www.gnu.org/licenses/gpl-3.0.txt) + +from ansible.module_utils.basic import AnsibleModule + +from ansible_collections.ansibleguy.opnsense.plugins.module_utils.base.handler import \ + module_dependency_error, MODULE_EXCEPTIONS + +try: + from ansible_collections.ansibleguy.opnsense.plugins.module_utils.helper.wrapper import module_wrapper + from ansible_collections.ansibleguy.opnsense.plugins.module_utils.defaults.main import \ + OPN_MOD_ARGS, STATE_MOD_ARG, RELOAD_MOD_ARG + from ansible_collections.ansibleguy.opnsense.plugins.module_utils.main.postfix_domain import Domain + +except MODULE_EXCEPTIONS: + module_dependency_error() + + +# DOCUMENTATION = 'https://opnsense.ansibleguy.net/modules/postfix.html' +# EXAMPLES = 'https://opnsense.ansibleguy.net/modules/postfix.html' + + +def run_module(): + module_args = dict( + domainname=dict( + type='str', required=True, aliases=['name'], + description='Set the unique domain name to relay for.', + ), + destination=dict( + type='str', required=False, + description='Set the IP or FQDN to where to send the mails to. Empty means MX will be used. You can also ' + 'add custom ports via :225 or disable MX lookup via squared brackets.', + ), + **RELOAD_MOD_ARG, + **STATE_MOD_ARG, + **OPN_MOD_ARGS, + ) + + result = dict( + changed=False, + diff={ + 'before': {}, + 'after': {}, + } + ) + + module = AnsibleModule( + argument_spec=module_args, + supports_check_mode=True, + ) + + module_wrapper(Domain(module=module, result=result)) + module.exit_json(**result) + + +def main(): + run_module() + + +if __name__ == '__main__': + main() diff --git a/plugins/modules/postfix_general.py b/plugins/modules/postfix_general.py new file mode 100644 index 0000000..82dbccc --- /dev/null +++ b/plugins/modules/postfix_general.py @@ -0,0 +1,201 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# Copyright: (C) 2025, AnsibleGuy +# GNU General Public License v3.0+ (see https://www.gnu.org/licenses/gpl-3.0.txt) + +from ansible.module_utils.basic import AnsibleModule + +from ansible_collections.ansibleguy.opnsense.plugins.module_utils.base.handler import \ + module_dependency_error, MODULE_EXCEPTIONS + +try: + from ansible_collections.ansibleguy.opnsense.plugins.module_utils.helper.wrapper import module_wrapper + from ansible_collections.ansibleguy.opnsense.plugins.module_utils.defaults.main import \ + OPN_MOD_ARGS, EN_ONLY_MOD_ARG + from ansible_collections.ansibleguy.opnsense.plugins.module_utils.main.postfix_general import General + +except MODULE_EXCEPTIONS: + module_dependency_error() + +# DOCUMENTATION = 'https://opnsense.ansibleguy.net/modules/postfix.html' +# EXAMPLES = 'https://opnsense.ansibleguy.net/modules/postfix.html' + + +def run_module(): + module_args = dict( + myhostname=dict( + type='str', required=False, + description='The \'System Hostname\' parameter specifies the internet hostname of this mail system. ' + 'The default is to use the fully-qualified domain name from gethostname(). It is used as a ' + 'default value for many other configuration parameters.', + ), + mydomain=dict( + type='str', required=False, + description='The \'System Domain\' parameter specifies the local internet domain name. The default is to ' + 'use \'System Hostname\' minus the first component. It is used as a default value for many ' + 'other configuration parameters.', + ), + myorigin=dict( + type='str', required=False, + description='The \'System Origin\' parameter specifies the domain that locally-posted mail appears to ' + 'come from. The default is to append \'System Hostname\', which is fine for small sites.', + ), + inet_interfaces=dict( + type='list', required=False, default=['all'], + description='The \'Listen IPs\' parameter specifies a comma-separated list of IP addresses to listen to. ' + 'Default is to listen on all interfaces.', + ), + inet_port=dict( + type='int', required=False, default=25, + description='Port to listen on. Default is to listen on port 25.', + ), + ip_version=dict( + type='str', required=False, choices=['all', 'ipv4', 'ipv6'], default='all', + description='Choose which IP versions are allowed, defaults to all. One of: all, ipv4 or ipv6.', + ), + bind_address=dict( + type='str', required=False, + description='Specify the IPv4 address the server should bind to for outgoing connections. ' + 'In most cases empty is fine.', + ), + bind_address6=dict( + type='str', required=False, + description='Specify the IPv6 address the server should bind to for outgoing connections. ' + 'In most cases empty is fine.', + ), + mynetworks=dict( + type='list', required=False, elements='str', + default=['127.0.0.0/8', '[::ffff:127.0.0.0]/104', '[::1]/128'], + description='The \'Trusted Networks\' parameter specifies the list of trusted SMTP clients. ' + 'In particular, trusted SMTP clients are allowed to relay mail through Postfix. ' + 'Please use CIDR notation like 192.168.0.0/24 separated by spaces. IPv6 addresses ' + 'have to be in square brackets like [::1]/128.', + ), + banner=dict( + type='str', required=False, + description='The smtpd_banner parameter specifies the text that follows the 220 code in the SMTP ' + 'server\'s greeting banner. Default is "\'System Hostname\' ESMTP Postfix".', + ), + message_size_limit=dict( + type='int', required=False, default=51200000, + description='Set the max size for messages to accept, default is 51200000 Bytes which is 50MB. ' + 'Values must be entered in Bytes.', + ), + masquerade_domains=dict( + type='list', required=False, elements='str', default=[], + description='Masquerade internal domains to the outside. When you set example.com, the domain ' + 'host.internal.example.com will be rewritten to example.com when mail leaves the system.' + ), + tls_server_compatibility=dict( + type='str', required=False, choices=['modern', 'intermediate', 'old'], default='intermediate', + description='TLS version/cipher compatibility of the SMTP service. One of: modern, intermediate or old.' + 'Default to intermediate.', + ), + tls_client_compatibility=dict( + type='str', required=False, choices=['modern', 'intermediate', 'old'], default='intermediate', + description='TLS version/cipher compatibility of the SMTP Client. One of: modern, intermediate or old.' + 'Default to intermediate.', + ), + tlswrappermode=dict( + type='bool', required=False, default=0, + description='If enabled it allows you to use SMTPS.', + ), + certificate=dict( + type='str', required=False, + description='Choose the certificate to use when other servers want to do TLS with you.', + ), + ca=dict( + type='str', required=False, + description='Choose the Certificate Authority which signed your certificate.', + ), + smtpclient_security=dict( + type='str', required=False, choices=['none', 'may', 'encrypt', 'dane'], default='may', + description='\'none\' will disable TLS for sending mail. \'may\' will use TLS when offered. ' + '\'encrypt\' will enforce TLS on all connections. ' + '\'dane\' will enforce TLS if a TLSA-Record is published.', + ), + relayhost=dict( + type='str', required=False, aliases=['smarthost'], + description='Set the IP address or FQDN where all outgoing mails are sent to.', + ), + smtpauth_enabled=dict( + type='bool', required=False, default=False, + description='Check this to enable authentication against your Smarthost.', + ), + smtpauth_user=dict( + type='str', required=False, + description='The username to use for SMTP authentication.', + ), + smtpauth_password=dict( + type='str', required=False, no_log=True, + description='The password to use for SMTP authentication.', + ), + enforce_recipient_check=dict(type='bool', required=False, default=False), + extensive_helo_restrictions=dict(type='bool', required=False, default=False), + extensive_sender_restrictions=dict(type='bool', required=False, default=False), + reject_unknown_client_hostname=dict(type='bool', required=False, default=False), + reject_non_fqdn_helo_hostname=dict(type='bool', required=False, default=False), + reject_invalid_helo_hostname=dict(type='bool', required=False, default=False), + reject_unknown_helo_hostname=dict(type='bool', required=False, default=False), + reject_unauth_pipelining=dict(type='bool', required=False, default=True), + reject_unknown_sender_domain=dict( + type='bool', required=False, default=True, + description='This will reject mails from domains which do not exist.', + ), + reject_unknown_recipient_domain=dict(type='bool', required=False, default=True), + reject_non_fqdn_sender=dict( + type='bool', required=False, default=True, + description='For example senders without a domain or only a hostname.', + ), + reject_non_fqdn_recipient=dict( + type='bool', required=False, default=True, + description='For example recipients without a domain or only a hostname.', + ), + permit_sasl_authenticated=dict( + type='bool', required=False, default=True, + description='Allow SASL authenticated senders to relay. Will also enable smtpd_sasl_auth.', + ), + permit_tls_clientcerts=dict(type='bool', required=False, default=True), + permit_mynetworks=dict(type='bool', required=False, default=True), + reject_unauth_destination=dict(type='bool', required=False, default=True), + reject_unverified_recipient=dict( + type='bool', required=False, default=False, + description='Use Recipient Address Verification. Please keep in mind that this could put significant ' + 'load onto the next server.', + ), + delay_warning_time=dict( + type='int', required=False, default=0, + description='Time until we send a notification to the sender if mail is delayed (in hours). ' + '0 or empty to disable.', + ), + **EN_ONLY_MOD_ARG, + **OPN_MOD_ARGS, + ) + + result = dict( + changed=False, + diff={ + 'before': {}, + 'after': {}, + } + ) + + module = AnsibleModule( + argument_spec=module_args, + supports_check_mode=True, + required_if=[ + ('smtpauth_enabled', True, ('smtpauth_user', 'smtpauth_password')), + ], + ) + + module_wrapper(General(module=module, result=result)) + module.exit_json(**result) + + +def main(): + run_module() + + +if __name__ == '__main__': + main() diff --git a/plugins/modules/postfix_headercheck.py b/plugins/modules/postfix_headercheck.py new file mode 100644 index 0000000..8584afe --- /dev/null +++ b/plugins/modules/postfix_headercheck.py @@ -0,0 +1,64 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# Copyright: (C) 2025, AnsibleGuy +# GNU General Public License v3.0+ (see https://www.gnu.org/licenses/gpl-3.0.txt) + +from ansible.module_utils.basic import AnsibleModule + +from ansible_collections.ansibleguy.opnsense.plugins.module_utils.base.handler import \ + module_dependency_error, MODULE_EXCEPTIONS + +try: + from ansible_collections.ansibleguy.opnsense.plugins.module_utils.helper.wrapper import module_wrapper + from ansible_collections.ansibleguy.opnsense.plugins.module_utils.defaults.main import \ + OPN_MOD_ARGS, STATE_MOD_ARG, RELOAD_MOD_ARG + from ansible_collections.ansibleguy.opnsense.plugins.module_utils.main.postfix_headercheck import Headercheck + +except MODULE_EXCEPTIONS: + module_dependency_error() + + +# DOCUMENTATION = 'https://opnsense.ansibleguy.net/modules/postfix.html' +# EXAMPLES = 'https://opnsense.ansibleguy.net/modules/postfix.html' + + +def run_module(): + module_args = dict( + expression=dict( + type='str', required=True, + description='Set a regexp (POSIX regular expression) and an action to process like ' + '\'/^\\s*User-Agent/ IGNORE\'.', + ), + filter=dict( + type='str', required=True, choices=['WHILE_DELIVERING', 'WHILE_RECEIVING'], + description='Set when the header_check should be processed. One of: WHILE_DELIVERING or WHILE_RECEIVING.', + ), + **RELOAD_MOD_ARG, + **STATE_MOD_ARG, + **OPN_MOD_ARGS, + ) + + result = dict( + changed=False, + diff={ + 'before': {}, + 'after': {}, + } + ) + + module = AnsibleModule( + argument_spec=module_args, + supports_check_mode=True, + ) + + module_wrapper(Headercheck(module=module, result=result)) + module.exit_json(**result) + + +def main(): + run_module() + + +if __name__ == '__main__': + main() diff --git a/plugins/modules/postfix_recipient.py b/plugins/modules/postfix_recipient.py new file mode 100644 index 0000000..5cb4478 --- /dev/null +++ b/plugins/modules/postfix_recipient.py @@ -0,0 +1,66 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# Copyright: (C) 2025, AnsibleGuy +# GNU General Public License v3.0+ (see https://www.gnu.org/licenses/gpl-3.0.txt) + +from ansible.module_utils.basic import AnsibleModule + +from ansible_collections.ansibleguy.opnsense.plugins.module_utils.base.handler import \ + module_dependency_error, MODULE_EXCEPTIONS + +try: + from ansible_collections.ansibleguy.opnsense.plugins.module_utils.helper.wrapper import module_wrapper + from ansible_collections.ansibleguy.opnsense.plugins.module_utils.defaults.main import \ + OPN_MOD_ARGS, STATE_MOD_ARG, RELOAD_MOD_ARG + from ansible_collections.ansibleguy.opnsense.plugins.module_utils.main.postfix_recipient import Recipient + +except MODULE_EXCEPTIONS: + module_dependency_error() + + +# DOCUMENTATION = 'https://opnsense.ansibleguy.net/modules/postfix.html' +# EXAMPLES = 'https://opnsense.ansibleguy.net/modules/postfix.html' + + +def run_module(): + module_args = dict( + address=dict( + type='str', required=True, + description='Set the recipient address to match.', + ), + action=dict( + type='str', required=False, choices=['OK', 'REJECT'], + description='Set the action for this address. One of: OK or REJECT.', + ), + **RELOAD_MOD_ARG, + **STATE_MOD_ARG, + **OPN_MOD_ARGS, + ) + + result = dict( + changed=False, + diff={ + 'before': {}, + 'after': {}, + } + ) + + module = AnsibleModule( + argument_spec=module_args, + supports_check_mode=True, + required_if=[ + ('state', 'present', ('action',)), + ], + ) + + module_wrapper(Recipient(module=module, result=result)) + module.exit_json(**result) + + +def main(): + run_module() + + +if __name__ == '__main__': + main() diff --git a/plugins/modules/postfix_recipientbcc.py b/plugins/modules/postfix_recipientbcc.py new file mode 100644 index 0000000..9d4d5b3 --- /dev/null +++ b/plugins/modules/postfix_recipientbcc.py @@ -0,0 +1,66 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# Copyright: (C) 2025, AnsibleGuy +# GNU General Public License v3.0+ (see https://www.gnu.org/licenses/gpl-3.0.txt) + +from ansible.module_utils.basic import AnsibleModule + +from ansible_collections.ansibleguy.opnsense.plugins.module_utils.base.handler import \ + module_dependency_error, MODULE_EXCEPTIONS + +try: + from ansible_collections.ansibleguy.opnsense.plugins.module_utils.helper.wrapper import module_wrapper + from ansible_collections.ansibleguy.opnsense.plugins.module_utils.defaults.main import \ + OPN_MOD_ARGS, STATE_MOD_ARG, RELOAD_MOD_ARG + from ansible_collections.ansibleguy.opnsense.plugins.module_utils.main.postfix_recipientbcc import RecipientBCC + +except MODULE_EXCEPTIONS: + module_dependency_error() + + +# DOCUMENTATION = 'https://opnsense.ansibleguy.net/modules/postfix.html' +# EXAMPLES = 'https://opnsense.ansibleguy.net/modules/postfix.html' + + +def run_module(): + module_args = dict( + address=dict( + type='str', required=True, aliasses=['from'], + description='Set a pattern to match like user@example.com', + ), + to=dict( + type='list', required=False, elements='str', + description='Set here the recipient address to send the mail as BCC.', + ), + **RELOAD_MOD_ARG, + **STATE_MOD_ARG, + **OPN_MOD_ARGS, + ) + + result = dict( + changed=False, + diff={ + 'before': {}, + 'after': {}, + } + ) + + module = AnsibleModule( + argument_spec=module_args, + supports_check_mode=True, + required_if=[ + ('state', 'present', ('to',)), + ], + ) + + module_wrapper(RecipientBCC(module=module, result=result)) + module.exit_json(**result) + + +def main(): + run_module() + + +if __name__ == '__main__': + main() diff --git a/plugins/modules/postfix_sender.py b/plugins/modules/postfix_sender.py new file mode 100644 index 0000000..88e00aa --- /dev/null +++ b/plugins/modules/postfix_sender.py @@ -0,0 +1,66 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# Copyright: (C) 2025, AnsibleGuy +# GNU General Public License v3.0+ (see https://www.gnu.org/licenses/gpl-3.0.txt) + +from ansible.module_utils.basic import AnsibleModule + +from ansible_collections.ansibleguy.opnsense.plugins.module_utils.base.handler import \ + module_dependency_error, MODULE_EXCEPTIONS + +try: + from ansible_collections.ansibleguy.opnsense.plugins.module_utils.helper.wrapper import module_wrapper + from ansible_collections.ansibleguy.opnsense.plugins.module_utils.defaults.main import \ + OPN_MOD_ARGS, STATE_MOD_ARG, RELOAD_MOD_ARG + from ansible_collections.ansibleguy.opnsense.plugins.module_utils.main.postfix_sender import Sender + +except MODULE_EXCEPTIONS: + module_dependency_error() + + +# DOCUMENTATION = 'https://opnsense.ansibleguy.net/modules/postfix.html' +# EXAMPLES = 'https://opnsense.ansibleguy.net/modules/postfix.html' + + +def run_module(): + module_args = dict( + address=dict( + type='str', required=True, + description='Set the sender address to match.', + ), + action=dict( + type='str', required=False, choices=['OK', 'REJECT'], + description='Set the action for this address. One of: OK or REJECT.', + ), + **RELOAD_MOD_ARG, + **STATE_MOD_ARG, + **OPN_MOD_ARGS, + ) + + result = dict( + changed=False, + diff={ + 'before': {}, + 'after': {}, + } + ) + + module = AnsibleModule( + argument_spec=module_args, + supports_check_mode=True, + required_if=[ + ('state', 'present', ('action',)), + ], + ) + + module_wrapper(Sender(module=module, result=result)) + module.exit_json(**result) + + +def main(): + run_module() + + +if __name__ == '__main__': + main() diff --git a/plugins/modules/postfix_senderbcc.py b/plugins/modules/postfix_senderbcc.py new file mode 100644 index 0000000..44aa01b --- /dev/null +++ b/plugins/modules/postfix_senderbcc.py @@ -0,0 +1,66 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# Copyright: (C) 2025, AnsibleGuy +# GNU General Public License v3.0+ (see https://www.gnu.org/licenses/gpl-3.0.txt) + +from ansible.module_utils.basic import AnsibleModule + +from ansible_collections.ansibleguy.opnsense.plugins.module_utils.base.handler import \ + module_dependency_error, MODULE_EXCEPTIONS + +try: + from ansible_collections.ansibleguy.opnsense.plugins.module_utils.helper.wrapper import module_wrapper + from ansible_collections.ansibleguy.opnsense.plugins.module_utils.defaults.main import \ + OPN_MOD_ARGS, STATE_MOD_ARG, RELOAD_MOD_ARG + from ansible_collections.ansibleguy.opnsense.plugins.module_utils.main.postfix_senderbcc import SenderBCC + +except MODULE_EXCEPTIONS: + module_dependency_error() + + +# DOCUMENTATION = 'https://opnsense.ansibleguy.net/modules/postfix.html' +# EXAMPLES = 'https://opnsense.ansibleguy.net/modules/postfix.html' + + +def run_module(): + module_args = dict( + address=dict( + type='str', required=True, aliasses=['from'], + description='Set a pattern to match like user@example.com', + ), + to=dict( + type='list', required=False, elements='str', + description='Set here the recipient address to send the mail as BCC.', + ), + **RELOAD_MOD_ARG, + **STATE_MOD_ARG, + **OPN_MOD_ARGS, + ) + + result = dict( + changed=False, + diff={ + 'before': {}, + 'after': {}, + } + ) + + module = AnsibleModule( + argument_spec=module_args, + supports_check_mode=True, + required_if=[ + ('state', 'present', ('to',)), + ], + ) + + module_wrapper(SenderBCC(module=module, result=result)) + module.exit_json(**result) + + +def main(): + run_module() + + +if __name__ == '__main__': + main() diff --git a/plugins/modules/postfix_sendercanonical.py b/plugins/modules/postfix_sendercanonical.py new file mode 100644 index 0000000..1706d76 --- /dev/null +++ b/plugins/modules/postfix_sendercanonical.py @@ -0,0 +1,67 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# Copyright: (C) 2025, AnsibleGuy +# GNU General Public License v3.0+ (see https://www.gnu.org/licenses/gpl-3.0.txt) + +from ansible.module_utils.basic import AnsibleModule + +from ansible_collections.ansibleguy.opnsense.plugins.module_utils.base.handler import \ + module_dependency_error, MODULE_EXCEPTIONS + +try: + from ansible_collections.ansibleguy.opnsense.plugins.module_utils.helper.wrapper import module_wrapper + from ansible_collections.ansibleguy.opnsense.plugins.module_utils.defaults.main import \ + OPN_MOD_ARGS, STATE_MOD_ARG, RELOAD_MOD_ARG + from ansible_collections.ansibleguy.opnsense.plugins.module_utils.main.postfix_sendercanonical import \ + SenderCanonical + +except MODULE_EXCEPTIONS: + module_dependency_error() + + +# DOCUMENTATION = 'https://opnsense.ansibleguy.net/modules/postfix.html' +# EXAMPLES = 'https://opnsense.ansibleguy.net/modules/postfix.html' + + +def run_module(): + module_args = dict( + address=dict( + type='str', required=True, aliasses=['from'], + description='Set a pattern to match line user@example.com or @example.com', + ), + to=dict( + type='list', required=False, elements='str', + description='Set here how to rewrite the Rewrite From pattern.', + ), + **RELOAD_MOD_ARG, + **STATE_MOD_ARG, + **OPN_MOD_ARGS, + ) + + result = dict( + changed=False, + diff={ + 'before': {}, + 'after': {}, + } + ) + + module = AnsibleModule( + argument_spec=module_args, + supports_check_mode=True, + required_if=[ + ('state', 'present', ('to',)), + ], + ) + + module_wrapper(SenderCanonical(module=module, result=result)) + module.exit_json(**result) + + +def main(): + run_module() + + +if __name__ == '__main__': + main() diff --git a/scripts/test.sh b/scripts/test.sh index 5742811..222dcad 100755 --- a/scripts/test.sh +++ b/scripts/test.sh @@ -163,6 +163,15 @@ run_test 'acme_account' 1 run_test 'acme_validation' 1 run_test 'acme_action' 1 run_test 'acme_certificate' 0 # check mode => dependency on other acme-entries +run_test 'postfix_general' 1 +run_test 'postfix_domain' 1 +run_test 'postfix_recipient' 1 +run_test 'postfix_recipientbcc' 1 +run_test 'postfix_sender' 1 +run_test 'postfix_senderbcc' 1 +run_test 'postfix_sendercanonical' 1 +run_test 'postfix_headercheck' 1 +run_test 'postfix_address' 1 echo '' echo '##############################' diff --git a/tests/1_cleanup.yml b/tests/1_cleanup.yml index cd91834..ffb8956 100644 --- a/tests/1_cleanup.yml +++ b/tests/1_cleanup.yml @@ -663,6 +663,19 @@ http_host: '127.0.0.1' http_port: 8000 + - name: Cleanup DHCP Settings + ansibleguy.opnsense.dhcp_general: + enabled: false + + - name: Cleanup DHCP Subnets + ansibleguy.opnsense.dhcp_subnet: + subnet: "{{ item }}" + state: 'absent' + loop: + - '192.168.69.0/24' + - '192.168.88.0/24' + - '192.168.89.0/24' + - name: Cleanup ACME Certificates ansibleguy.opnsense.acme_certificate: description: '{{ item }}' @@ -718,15 +731,71 @@ - 'ANSIBLE_TEST_1_10' - 'ANSIBLE_TEST_DUMMY_1_1' - - name: Cleanup DHCP Settings - ansibleguy.opnsense.dhcp_general: - enabled: false + - name: Cleanup Postfix General + ansibleguy.opnsense.postfix_general: + reload: false # speed - - name: Cleanup DHCP Subnets - ansibleguy.opnsense.dhcp_subnet: - subnet: "{{ item }}" + - name: Cleanup Postfix Address + ansibleguy.opnsense.postfix_address: + address: "{{ item }}" state: 'absent' loop: - - '192.168.69.0/24' - - '192.168.88.0/24' - - '192.168.89.0/24' + - 'alice@example.com' + - 'bob@example.com' + + - name: Cleanup Postfix Domain + ansibleguy.opnsense.postfix_domain: + domainname: "{{ item }}" + state: 'absent' + loop: + - 'example.com' + - 'example.net' + + - name: Cleanup Postfix Headercheck + ansibleguy.opnsense.postfix_headercheck: + expression: '/^\s*User-Agent/ IGNORE' + filter: '{{ item }}' + state: 'absent' + loop: + - 'WHILE_RECEIVING' + - 'WHILE_DELIVERING' + + - name: Cleanup Postfix Recipient + ansibleguy.opnsense.postfix_recipient: + address: '{{ item }}' + state: 'absent' + loop: + - 'alice@example.com' + - 'bob@example.com' + + - name: Cleanup Postfix Recipient BCC + ansibleguy.opnsense.postfix_recipientbcc: + address: '{{ item }}' + state: 'absent' + loop: + - 'alice@example.com' + - 'bob@example.com' + + - name: Cleanup Postfix Sender + ansibleguy.opnsense.postfix_sender: + address: '{{ item }}' + state: 'absent' + loop: + - 'alice@example.com' + - 'bob@example.com' + + - name: Cleanup Postfix Sender BCC + ansibleguy.opnsense.postfix_senderbcc: + address: '{{ item }}' + state: 'absent' + loop: + - 'alice@example.com' + - 'bob@example.com' + + - name: Cleanup Postfix Sender Canonical + ansibleguy.opnsense.postfix_sendercanonical: + address: '{{ item }}' + state: 'absent' + loop: + - 'alice@example.com' + - 'bob@example.com' diff --git a/tests/1_dependencies.yml b/tests/1_dependencies.yml index c285361..867b78e 100644 --- a/tests/1_dependencies.yml +++ b/tests/1_dependencies.yml @@ -26,3 +26,4 @@ - 'os-frr' - 'os-bind' - 'os-acme-client' + - 'os-postfix' diff --git a/tests/postfix_address.yml b/tests/postfix_address.yml new file mode 100644 index 0000000..626ce4b --- /dev/null +++ b/tests/postfix_address.yml @@ -0,0 +1,145 @@ +--- + +- name: Testing Postfix Address + hosts: localhost + gather_facts: no + module_defaults: + group/ansibleguy.opnsense.all: + firewall: "{{ lookup('ansible.builtin.env', 'TEST_FIREWALL') }}" + api_credential_file: "{{ lookup('ansible.builtin.env', 'TEST_API_KEY') }}" + ssl_verify: false + + ansibleguy.opnsense.list: + target: 'postfix_address' + + tasks: + - name: Listing + ansibleguy.opnsense.list: + register: opn_pre1 + failed_when: > + 'data' not in opn_pre1 or + opn_pre1.data | length != 0 + + - name: Removing - does not exist + ansibleguy.opnsense.postfix_address: + address: 'ANSIBLE_TEST_99_9' + state: 'absent' + register: opn_pre2 + failed_when: > + opn_pre2.failed or + opn_pre2.changed + + - name: Adding 1 - failing because of missing to value + ansibleguy.opnsense.postfix_address: + address: alice@example.com + register: opn_fail1 + failed_when: not opn_fail1.failed + + - name: Adding 1 + ansibleguy.opnsense.postfix_address: + address: alice@example.com + to: bob@example.com + register: opn1 + failed_when: > + opn1.failed or + not opn1.changed + + - name: Changing 1 + ansibleguy.opnsense.postfix_address: + address: alice@example.com + to: + - bob@example.com + - carol@example.com + register: opn2 + failed_when: > + opn2.failed or + not opn2.changed + + - name: Disabling 1 + ansibleguy.opnsense.postfix_address: + address: alice@example.com + to: + - bob@example.com + - carol@example.com + enabled: false + register: opn3 + failed_when: > + opn3.failed or + not opn3.changed + when: not ansible_check_mode + + - name: Disabling 1 - nothing changed + ansibleguy.opnsense.postfix_address: + address: alice@example.com + to: + - bob@example.com + - carol@example.com + enabled: false + register: opn4 + failed_when: > + opn4.failed or + opn4.changed + when: not ansible_check_mode + + - name: Enabling 1 + ansibleguy.opnsense.postfix_address: + address: alice@example.com + to: + - bob@example.com + - carol@example.com + register: opn5 + failed_when: > + opn5.failed or + not opn5.changed + when: not ansible_check_mode + + - name: Adding 2 + ansibleguy.opnsense.postfix_address: + address: bob@example.com + to: carol@example.com + register: opn6 + failed_when: > + opn6.failed or + not opn6.changed + + - name: Adding 2 - nothing changed + ansibleguy.opnsense.postfix_address: + address: bob@example.com + to: carol@example.com + register: opn7 + failed_when: > + opn7.failed or + opn7.changed + when: not ansible_check_mode + + - name: Removing 2 + ansibleguy.opnsense.postfix_address: + address: bob@example.com + state: absent + register: opn8 + failed_when: > + opn8.failed or + not opn8.changed + when: not ansible_check_mode + + - name: Listing + ansibleguy.opnsense.list: + register: opn9 + failed_when: > + 'data' not in opn9 or + opn9.data | length != 1 + when: not ansible_check_mode + + - name: Cleanup + ansibleguy.opnsense.postfix_address: + address: alice@example.com + state: 'absent' + when: not ansible_check_mode + + - name: Listing + ansibleguy.opnsense.list: + register: opn_clean1 + failed_when: > + 'data' not in opn_clean1 or + opn_clean1.data | length != 0 + when: not ansible_check_mode diff --git a/tests/postfix_domain.yml b/tests/postfix_domain.yml new file mode 100644 index 0000000..d4bada1 --- /dev/null +++ b/tests/postfix_domain.yml @@ -0,0 +1,129 @@ +--- + +- name: Testing Postfix Domain + hosts: localhost + gather_facts: no + module_defaults: + group/ansibleguy.opnsense.all: + firewall: "{{ lookup('ansible.builtin.env', 'TEST_FIREWALL') }}" + api_credential_file: "{{ lookup('ansible.builtin.env', 'TEST_API_KEY') }}" + ssl_verify: false + + ansibleguy.opnsense.list: + target: 'postfix_domain' + + tasks: + - name: Listing + ansibleguy.opnsense.list: + register: opn_pre1 + failed_when: > + 'data' not in opn_pre1 or + opn_pre1.data | length != 0 + + - name: Removing - does not exist + ansibleguy.opnsense.postfix_domain: + domainname: 'ANSIBLE_TEST_99_9' + state: 'absent' + register: opn_pre2 + failed_when: > + opn_pre2.failed or + opn_pre2.changed + + - name: Adding 1 + ansibleguy.opnsense.postfix_domain: + domainname: example.com + destination: 192.168.100.2 + register: opn1 + failed_when: > + opn1.failed or + not opn1.changed + + - name: Changing 1 + ansibleguy.opnsense.postfix_domain: + domainname: example.com + destination: 192.168.100.3 + register: opn2 + failed_when: > + opn2.failed or + not opn2.changed + + - name: Disabling 1 + ansibleguy.opnsense.postfix_domain: + domainname: example.com + destination: 192.168.100.3 + enabled: false + register: opn3 + failed_when: > + opn3.failed or + not opn3.changed + when: not ansible_check_mode + + - name: Disabling 1 - nothing changed + ansibleguy.opnsense.postfix_domain: + domainname: example.com + destination: 192.168.100.3 + enabled: false + register: opn4 + failed_when: > + opn4.failed or + opn4.changed + when: not ansible_check_mode + + - name: Enabling 1 + ansibleguy.opnsense.postfix_domain: + domainname: example.com + destination: 192.168.100.3 + register: opn5 + failed_when: > + opn5.failed or + not opn5.changed + when: not ansible_check_mode + + - name: Adding 2 + ansibleguy.opnsense.postfix_domain: + domainname: example.net + register: opn6 + failed_when: > + opn6.failed or + not opn6.changed + + - name: Adding 2 - nothing changed + ansibleguy.opnsense.postfix_domain: + domainname: example.net + register: opn7 + failed_when: > + opn7.failed or + opn7.changed + when: not ansible_check_mode + + - name: Removing 2 + ansibleguy.opnsense.postfix_domain: + domainname: example.net + state: 'absent' + register: opn8 + failed_when: > + opn8.failed or + not opn8.changed + when: not ansible_check_mode + + - name: Listing + ansibleguy.opnsense.list: + register: opn9 + failed_when: > + 'data' not in opn9 or + opn9.data | length != 1 + when: not ansible_check_mode + + - name: Cleanup + ansibleguy.opnsense.postfix_domain: + domainname: example.com + state: 'absent' + when: not ansible_check_mode + + - name: Listing + ansibleguy.opnsense.list: + register: opn_clean1 + failed_when: > + 'data' not in opn_clean1 or + opn_clean1.data | length != 0 + when: not ansible_check_mode diff --git a/tests/postfix_general.yml b/tests/postfix_general.yml new file mode 100644 index 0000000..e8ad294 --- /dev/null +++ b/tests/postfix_general.yml @@ -0,0 +1,102 @@ +--- + +- name: Testing Postfix Setting + hosts: localhost + gather_facts: no + module_defaults: + group/ansibleguy.opnsense.all: + firewall: "{{ lookup('ansible.builtin.env', 'TEST_FIREWALL') }}" + api_credential_file: "{{ lookup('ansible.builtin.env', 'TEST_API_KEY') }}" + ssl_verify: false + + ansibleguy.opnsense.list: + target: 'postfix_general' + + tasks: + - name: Listing + ansibleguy.opnsense.list: + register: opn_pre1 + failed_when: > + opn_pre1.failed or + 'data' not in opn_pre1 + + - name: Configuring - failing because of invalid inet_port + ansibleguy.opnsense.postfix_general: + inet_port: 999999 + register: opn_fail1 + failed_when: not opn_fail1.failed + + - name: Configuring - failing because of invalid ip_version + ansibleguy.opnsense.postfix_general: + ip_version: ipv5 + register: opn_fail2 + failed_when: not opn_fail2.failed + + - name: Configuring + ansibleguy.opnsense.postfix_general: + myhostname: mx.example.com + mydomain: example.com + register: opn1 + failed_when: > + opn1.failed or + not opn1.changed + + - name: Changing + ansibleguy.opnsense.postfix_general: + myhostname: mx.example.com + mydomain: example.com + inet_port: 26 + ip_version: ipv4 + mynetworks: + - '127.0.0.0/8' + - '[::1]/128' + - '[:ffff:127.0.0.0]/104' + - '192.168.0.0/16' + masquerade_domains: + - example.com + - example.net + tls_server_compatibility: modern + tls_client_compatibility: modern + tlswrappermode: true + smtpclient_security: encrypt + relayhost: relay.example.com + smtpauth_enabled: true + smtpauth_user: postfix + smtpauth_password: SECRET + register: opn2 + failed_when: > + opn2.failed or + not opn2.changed + when: not ansible_check_mode + + - name: Nothing changed + ansibleguy.opnsense.postfix_general: + myhostname: mx.example.com + mydomain: example.com + inet_port: 26 + ip_version: ipv4 + mynetworks: + - '127.0.0.0/8' + - '[::1]/128' + - '[:ffff:127.0.0.0]/104' + - '192.168.0.0/16' + masquerade_domains: + - example.com + - example.net + tls_server_compatibility: modern + tls_client_compatibility: modern + tlswrappermode: true + smtpclient_security: encrypt + relayhost: relay.example.com + smtpauth_enabled: true + smtpauth_user: postfix + smtpauth_password: SECRET + register: opn3 + failed_when: > + opn3.failed or + opn3.changed + when: not ansible_check_mode + + - name: Cleanup + ansibleguy.opnsense.postfix_general: + enabled: false diff --git a/tests/postfix_headercheck.yml b/tests/postfix_headercheck.yml new file mode 100644 index 0000000..cae4d0f --- /dev/null +++ b/tests/postfix_headercheck.yml @@ -0,0 +1,131 @@ +--- + +- name: Testing Postfix Headercheck + hosts: localhost + gather_facts: no + module_defaults: + group/ansibleguy.opnsense.all: + firewall: "{{ lookup('ansible.builtin.env', 'TEST_FIREWALL') }}" + api_credential_file: "{{ lookup('ansible.builtin.env', 'TEST_API_KEY') }}" + ssl_verify: false + + ansibleguy.opnsense.list: + target: 'postfix_headercheck' + + tasks: + - name: Listing + ansibleguy.opnsense.list: + register: opn_pre1 + failed_when: > + 'data' not in opn_pre1 or + opn_pre1.data | length != 0 + + - name: Removing - does not exist + ansibleguy.opnsense.postfix_headercheck: + expression: 'ANSIBLE_TEST_99_9' + filter: WHILE_RECEIVING + state: 'absent' + register: opn_pre2 + failed_when: > + opn_pre2.failed or + opn_pre2.changed + + - name: Adding 1 - failing because of missing filter + ansibleguy.opnsense.postfix_headercheck: + expression: '/^\s*User-Agent/ IGNORE' + register: opn_fail1 + failed_when: not opn_fail1.failed + + - name: Adding 1 + ansibleguy.opnsense.postfix_headercheck: + expression: '/^\s*User-Agent/ IGNORE' + filter: WHILE_RECEIVING + register: opn1 + failed_when: > + opn1.failed or + not opn1.changed + + - name: Disabling 1 + ansibleguy.opnsense.postfix_headercheck: + expression: '/^\s*User-Agent/ IGNORE' + filter: WHILE_RECEIVING + enabled: false + register: opn3 + failed_when: > + opn3.failed or + not opn3.changed + when: not ansible_check_mode + + - name: Disabling 1 - nothing changed + ansibleguy.opnsense.postfix_headercheck: + expression: '/^\s*User-Agent/ IGNORE' + filter: WHILE_RECEIVING + enabled: false + register: opn4 + failed_when: > + opn4.failed or + opn4.changed + when: not ansible_check_mode + + - name: Enabling 1 + ansibleguy.opnsense.postfix_headercheck: + expression: '/^\s*User-Agent/ IGNORE' + filter: WHILE_RECEIVING + register: opn5 + failed_when: > + opn5.failed or + not opn5.changed + when: not ansible_check_mode + + - name: Adding 2 + ansibleguy.opnsense.postfix_headercheck: + expression: '/^\s*User-Agent/ IGNORE' + filter: WHILE_DELIVERING + register: opn6 + failed_when: > + opn6.failed or + not opn6.changed + + - name: Adding 2 - nothing changed + ansibleguy.opnsense.postfix_headercheck: + expression: '/^\s*User-Agent/ IGNORE' + filter: WHILE_DELIVERING + register: opn7 + failed_when: > + opn7.failed or + opn7.changed + when: not ansible_check_mode + + - name: Removing 2 + ansibleguy.opnsense.postfix_headercheck: + expression: '/^\s*User-Agent/ IGNORE' + filter: WHILE_DELIVERING + state: 'absent' + register: opn8 + failed_when: > + opn8.failed or + not opn8.changed + when: not ansible_check_mode + + - name: Listing + ansibleguy.opnsense.list: + register: opn9 + failed_when: > + 'data' not in opn9 or + opn9.data | length != 1 + when: not ansible_check_mode + + - name: Cleanup + ansibleguy.opnsense.postfix_headercheck: + expression: '/^\s*User-Agent/ IGNORE' + filter: WHILE_RECEIVING + state: 'absent' + when: not ansible_check_mode + + - name: Listing + ansibleguy.opnsense.list: + register: opn_clean1 + failed_when: > + 'data' not in opn_clean1 or + opn_clean1.data | length != 0 + when: not ansible_check_mode diff --git a/tests/postfix_recipient.yml b/tests/postfix_recipient.yml new file mode 100644 index 0000000..d879b40 --- /dev/null +++ b/tests/postfix_recipient.yml @@ -0,0 +1,137 @@ +--- + +- name: Testing Postfix Recipient + hosts: localhost + gather_facts: no + module_defaults: + group/ansibleguy.opnsense.all: + firewall: "{{ lookup('ansible.builtin.env', 'TEST_FIREWALL') }}" + api_credential_file: "{{ lookup('ansible.builtin.env', 'TEST_API_KEY') }}" + ssl_verify: false + + ansibleguy.opnsense.list: + target: 'postfix_recipient' + + tasks: + - name: Listing + ansibleguy.opnsense.list: + register: opn_pre1 + failed_when: > + 'data' not in opn_pre1 or + opn_pre1.data | length != 0 + + - name: Removing - does not exist + ansibleguy.opnsense.postfix_recipient: + address: 'ANSIBLE_TEST_99_9' + state: 'absent' + register: opn_pre2 + failed_when: > + opn_pre2.failed or + opn_pre2.changed + + - name: Adding 1 - failing because of missing action + ansibleguy.opnsense.postfix_recipient: + address: alice@example.com + register: opn_fail1 + failed_when: not opn_fail1.failed + + - name: Adding 1 + ansibleguy.opnsense.postfix_recipient: + address: alice@example.com + action: REJECT + register: opn1 + failed_when: > + opn1.failed or + not opn1.changed + + - name: Changing 1 + ansibleguy.opnsense.postfix_recipient: + address: alice@example.com + action: OK + register: opn2 + failed_when: > + opn2.failed or + not opn2.changed + + - name: Disabling 1 + ansibleguy.opnsense.postfix_recipient: + address: alice@example.com + action: OK + enabled: false + register: opn3 + failed_when: > + opn3.failed or + not opn3.changed + when: not ansible_check_mode + + - name: Disabling 1 - nothing changed + ansibleguy.opnsense.postfix_recipient: + address: alice@example.com + action: OK + enabled: false + register: opn4 + failed_when: > + opn4.failed or + opn4.changed + when: not ansible_check_mode + + - name: Enabling 1 + ansibleguy.opnsense.postfix_recipient: + address: alice@example.com + action: OK + register: opn5 + failed_when: > + opn5.failed or + not opn5.changed + when: not ansible_check_mode + + - name: Adding 2 + ansibleguy.opnsense.postfix_recipient: + address: bob@example.com + action: REJECT + register: opn6 + failed_when: > + opn6.failed or + not opn6.changed + + - name: Adding 2 - nothing changed + ansibleguy.opnsense.postfix_recipient: + address: bob@example.com + action: REJECT + register: opn7 + failed_when: > + opn7.failed or + opn7.changed + when: not ansible_check_mode + + - name: Removing 2 + ansibleguy.opnsense.postfix_recipient: + address: bob@example.com + state: 'absent' + register: opn8 + failed_when: > + opn8.failed or + not opn8.changed + when: not ansible_check_mode + + - name: Listing + ansibleguy.opnsense.list: + register: opn9 + failed_when: > + 'data' not in opn9 or + opn9.data | length != 1 + when: not ansible_check_mode + + - name: Cleanup + ansibleguy.opnsense.postfix_recipient: + address: alice@example.com + state: 'absent' + when: not ansible_check_mode + + - name: Listing + ansibleguy.opnsense.list: + register: opn_clean1 + failed_when: > + 'data' not in opn_clean1 or + opn_clean1.data | length != 0 + when: not ansible_check_mode diff --git a/tests/postfix_recipientbcc.yml b/tests/postfix_recipientbcc.yml new file mode 100644 index 0000000..63ff6fc --- /dev/null +++ b/tests/postfix_recipientbcc.yml @@ -0,0 +1,145 @@ +--- + +- name: Testing Postfix Recipient BCC + hosts: localhost + gather_facts: no + module_defaults: + group/ansibleguy.opnsense.all: + firewall: "{{ lookup('ansible.builtin.env', 'TEST_FIREWALL') }}" + api_credential_file: "{{ lookup('ansible.builtin.env', 'TEST_API_KEY') }}" + ssl_verify: false + + ansibleguy.opnsense.list: + target: 'postfix_recipientbcc' + + tasks: + - name: Listing + ansibleguy.opnsense.list: + register: opn_pre1 + failed_when: > + 'data' not in opn_pre1 or + opn_pre1.data | length != 0 + + - name: Removing - does not exist + ansibleguy.opnsense.postfix_recipientbcc: + address: 'ANSIBLE_TEST_99_9' + state: 'absent' + register: opn_pre2 + failed_when: > + opn_pre2.failed or + opn_pre2.changed + + - name: Adding 1 - failing because of missing to value + ansibleguy.opnsense.postfix_recipientbcc: + address: alice@example.com + register: opn_fail1 + failed_when: not opn_fail1.failed + + - name: Adding 1 + ansibleguy.opnsense.postfix_recipientbcc: + address: alice@example.com + to: bob@example.com + register: opn1 + failed_when: > + opn1.failed or + not opn1.changed + + - name: Changing 1 + ansibleguy.opnsense.postfix_recipientbcc: + address: alice@example.com + to: + - bob@example.com + - carol@example.com + register: opn2 + failed_when: > + opn2.failed or + not opn2.changed + + - name: Disabling 1 + ansibleguy.opnsense.postfix_recipientbcc: + address: alice@example.com + to: + - bob@example.com + - carol@example.com + enabled: false + register: opn3 + failed_when: > + opn3.failed or + not opn3.changed + when: not ansible_check_mode + + - name: Disabling 1 - nothing changed + ansibleguy.opnsense.postfix_recipientbcc: + address: alice@example.com + to: + - bob@example.com + - carol@example.com + enabled: false + register: opn4 + failed_when: > + opn4.failed or + opn4.changed + when: not ansible_check_mode + + - name: Enabling 1 + ansibleguy.opnsense.postfix_recipientbcc: + address: alice@example.com + to: + - bob@example.com + - carol@example.com + register: opn5 + failed_when: > + opn5.failed or + not opn5.changed + when: not ansible_check_mode + + - name: Adding 2 + ansibleguy.opnsense.postfix_recipientbcc: + address: bob@example.com + to: carol@example.com + register: opn6 + failed_when: > + opn6.failed or + not opn6.changed + + - name: Adding 2 - nothing changed + ansibleguy.opnsense.postfix_recipientbcc: + address: bob@example.com + to: carol@example.com + register: opn7 + failed_when: > + opn7.failed or + opn7.changed + when: not ansible_check_mode + + - name: Removing 2 + ansibleguy.opnsense.postfix_recipientbcc: + address: bob@example.com + state: absent + register: opn8 + failed_when: > + opn8.failed or + not opn8.changed + when: not ansible_check_mode + + - name: Listing + ansibleguy.opnsense.list: + register: opn9 + failed_when: > + 'data' not in opn9 or + opn9.data | length != 1 + when: not ansible_check_mode + + - name: Cleanup + ansibleguy.opnsense.postfix_recipientbcc: + address: alice@example.com + state: 'absent' + when: not ansible_check_mode + + - name: Listing + ansibleguy.opnsense.list: + register: opn_clean1 + failed_when: > + 'data' not in opn_clean1 or + opn_clean1.data | length != 0 + when: not ansible_check_mode diff --git a/tests/postfix_sender.yml b/tests/postfix_sender.yml new file mode 100644 index 0000000..b012db7 --- /dev/null +++ b/tests/postfix_sender.yml @@ -0,0 +1,137 @@ +--- + +- name: Testing Postfix Sender + hosts: localhost + gather_facts: no + module_defaults: + group/ansibleguy.opnsense.all: + firewall: "{{ lookup('ansible.builtin.env', 'TEST_FIREWALL') }}" + api_credential_file: "{{ lookup('ansible.builtin.env', 'TEST_API_KEY') }}" + ssl_verify: false + + ansibleguy.opnsense.list: + target: 'postfix_sender' + + tasks: + - name: Listing + ansibleguy.opnsense.list: + register: opn_pre1 + failed_when: > + 'data' not in opn_pre1 or + opn_pre1.data | length != 0 + + - name: Removing - does not exist + ansibleguy.opnsense.postfix_sender: + address: 'ANSIBLE_TEST_99_9' + state: 'absent' + register: opn_pre2 + failed_when: > + opn_pre2.failed or + opn_pre2.changed + + - name: Adding 1 - failing because of missing action + ansibleguy.opnsense.postfix_sender: + address: alice@example.com + register: opn_fail1 + failed_when: not opn_fail1.failed + + - name: Adding 1 + ansibleguy.opnsense.postfix_sender: + address: alice@example.com + action: REJECT + register: opn1 + failed_when: > + opn1.failed or + not opn1.changed + + - name: Changing 1 + ansibleguy.opnsense.postfix_sender: + address: alice@example.com + action: OK + register: opn2 + failed_when: > + opn2.failed or + not opn2.changed + + - name: Disabling 1 + ansibleguy.opnsense.postfix_sender: + address: alice@example.com + action: OK + enabled: false + register: opn3 + failed_when: > + opn3.failed or + not opn3.changed + when: not ansible_check_mode + + - name: Disabling 1 - nothing changed + ansibleguy.opnsense.postfix_sender: + address: alice@example.com + action: OK + enabled: false + register: opn4 + failed_when: > + opn4.failed or + opn4.changed + when: not ansible_check_mode + + - name: Enabling 1 + ansibleguy.opnsense.postfix_sender: + address: alice@example.com + action: OK + register: opn5 + failed_when: > + opn5.failed or + not opn5.changed + when: not ansible_check_mode + + - name: Adding 2 + ansibleguy.opnsense.postfix_sender: + address: bob@example.com + action: REJECT + register: opn6 + failed_when: > + opn6.failed or + not opn6.changed + + - name: Adding 2 - nothing changed + ansibleguy.opnsense.postfix_sender: + address: bob@example.com + action: REJECT + register: opn7 + failed_when: > + opn7.failed or + opn7.changed + when: not ansible_check_mode + + - name: Removing 2 + ansibleguy.opnsense.postfix_sender: + address: bob@example.com + state: 'absent' + register: opn8 + failed_when: > + opn8.failed or + not opn8.changed + when: not ansible_check_mode + + - name: Listing + ansibleguy.opnsense.list: + register: opn9 + failed_when: > + 'data' not in opn9 or + opn9.data | length != 1 + when: not ansible_check_mode + + - name: Cleanup + ansibleguy.opnsense.postfix_sender: + address: alice@example.com + state: 'absent' + when: not ansible_check_mode + + - name: Listing + ansibleguy.opnsense.list: + register: opn_clean1 + failed_when: > + 'data' not in opn_clean1 or + opn_clean1.data | length != 0 + when: not ansible_check_mode diff --git a/tests/postfix_senderbcc.yml b/tests/postfix_senderbcc.yml new file mode 100644 index 0000000..c73e960 --- /dev/null +++ b/tests/postfix_senderbcc.yml @@ -0,0 +1,145 @@ +--- + +- name: Testing Postfix Sender BCC + hosts: localhost + gather_facts: no + module_defaults: + group/ansibleguy.opnsense.all: + firewall: "{{ lookup('ansible.builtin.env', 'TEST_FIREWALL') }}" + api_credential_file: "{{ lookup('ansible.builtin.env', 'TEST_API_KEY') }}" + ssl_verify: false + + ansibleguy.opnsense.list: + target: 'postfix_senderbcc' + + tasks: + - name: Listing + ansibleguy.opnsense.list: + register: opn_pre1 + failed_when: > + 'data' not in opn_pre1 or + opn_pre1.data | length != 0 + + - name: Removing - does not exist + ansibleguy.opnsense.postfix_senderbcc: + address: 'ANSIBLE_TEST_99_9' + state: 'absent' + register: opn_pre2 + failed_when: > + opn_pre2.failed or + opn_pre2.changed + + - name: Adding 1 - failing because of missing to value + ansibleguy.opnsense.postfix_senderbcc: + address: alice@example.com + register: opn_fail1 + failed_when: not opn_fail1.failed + + - name: Adding 1 + ansibleguy.opnsense.postfix_senderbcc: + address: alice@example.com + to: bob@example.com + register: opn1 + failed_when: > + opn1.failed or + not opn1.changed + + - name: Changing 1 + ansibleguy.opnsense.postfix_senderbcc: + address: alice@example.com + to: + - bob@example.com + - carol@example.com + register: opn2 + failed_when: > + opn2.failed or + not opn2.changed + + - name: Disabling 1 + ansibleguy.opnsense.postfix_senderbcc: + address: alice@example.com + to: + - bob@example.com + - carol@example.com + enabled: false + register: opn3 + failed_when: > + opn3.failed or + not opn3.changed + when: not ansible_check_mode + + - name: Disabling 1 - nothing changed + ansibleguy.opnsense.postfix_senderbcc: + address: alice@example.com + to: + - bob@example.com + - carol@example.com + enabled: false + register: opn4 + failed_when: > + opn4.failed or + opn4.changed + when: not ansible_check_mode + + - name: Enabling 1 + ansibleguy.opnsense.postfix_senderbcc: + address: alice@example.com + to: + - bob@example.com + - carol@example.com + register: opn5 + failed_when: > + opn5.failed or + not opn5.changed + when: not ansible_check_mode + + - name: Adding 2 + ansibleguy.opnsense.postfix_senderbcc: + address: bob@example.com + to: carol@example.com + register: opn6 + failed_when: > + opn6.failed or + not opn6.changed + + - name: Adding 2 - nothing changed + ansibleguy.opnsense.postfix_senderbcc: + address: bob@example.com + to: carol@example.com + register: opn7 + failed_when: > + opn7.failed or + opn7.changed + when: not ansible_check_mode + + - name: Removing 2 + ansibleguy.opnsense.postfix_senderbcc: + address: bob@example.com + state: absent + register: opn8 + failed_when: > + opn8.failed or + not opn8.changed + when: not ansible_check_mode + + - name: Listing + ansibleguy.opnsense.list: + register: opn9 + failed_when: > + 'data' not in opn9 or + opn9.data | length != 1 + when: not ansible_check_mode + + - name: Cleanup + ansibleguy.opnsense.postfix_senderbcc: + address: alice@example.com + state: 'absent' + when: not ansible_check_mode + + - name: Listing + ansibleguy.opnsense.list: + register: opn_clean1 + failed_when: > + 'data' not in opn_clean1 or + opn_clean1.data | length != 0 + when: not ansible_check_mode diff --git a/tests/postfix_sendercanonical.yml b/tests/postfix_sendercanonical.yml new file mode 100644 index 0000000..f26b28b --- /dev/null +++ b/tests/postfix_sendercanonical.yml @@ -0,0 +1,145 @@ +--- + +- name: Testing Postfix Sender Canonical + hosts: localhost + gather_facts: no + module_defaults: + group/ansibleguy.opnsense.all: + firewall: "{{ lookup('ansible.builtin.env', 'TEST_FIREWALL') }}" + api_credential_file: "{{ lookup('ansible.builtin.env', 'TEST_API_KEY') }}" + ssl_verify: false + + ansibleguy.opnsense.list: + target: 'postfix_sendercanonical' + + tasks: + - name: Listing + ansibleguy.opnsense.list: + register: opn_pre1 + failed_when: > + 'data' not in opn_pre1 or + opn_pre1.data | length != 0 + + - name: Removing - does not exist + ansibleguy.opnsense.postfix_sendercanonical: + address: 'ANSIBLE_TEST_99_9' + state: 'absent' + register: opn_pre2 + failed_when: > + opn_pre2.failed or + opn_pre2.changed + + - name: Adding 1 - failing because of missing to value + ansibleguy.opnsense.postfix_sendercanonical: + address: alice@example.com + register: opn_fail1 + failed_when: not opn_fail1.failed + + - name: Adding 1 + ansibleguy.opnsense.postfix_sendercanonical: + address: alice@example.com + to: bob@example.com + register: opn1 + failed_when: > + opn1.failed or + not opn1.changed + + - name: Changing 1 + ansibleguy.opnsense.postfix_sendercanonical: + address: alice@example.com + to: + - bob@example.com + - carol@example.com + register: opn2 + failed_when: > + opn2.failed or + not opn2.changed + + - name: Disabling 1 + ansibleguy.opnsense.postfix_sendercanonical: + address: alice@example.com + to: + - bob@example.com + - carol@example.com + enabled: false + register: opn3 + failed_when: > + opn3.failed or + not opn3.changed + when: not ansible_check_mode + + - name: Disabling 1 - nothing changed + ansibleguy.opnsense.postfix_sendercanonical: + address: alice@example.com + to: + - bob@example.com + - carol@example.com + enabled: false + register: opn4 + failed_when: > + opn4.failed or + opn4.changed + when: not ansible_check_mode + + - name: Enabling 1 + ansibleguy.opnsense.postfix_sendercanonical: + address: alice@example.com + to: + - bob@example.com + - carol@example.com + register: opn5 + failed_when: > + opn5.failed or + not opn5.changed + when: not ansible_check_mode + + - name: Adding 2 + ansibleguy.opnsense.postfix_sendercanonical: + address: bob@example.com + to: carol@example.com + register: opn6 + failed_when: > + opn6.failed or + not opn6.changed + + - name: Adding 2 - nothing changed + ansibleguy.opnsense.postfix_sendercanonical: + address: bob@example.com + to: carol@example.com + register: opn7 + failed_when: > + opn7.failed or + opn7.changed + when: not ansible_check_mode + + - name: Removing 2 + ansibleguy.opnsense.postfix_sendercanonical: + address: bob@example.com + state: absent + register: opn8 + failed_when: > + opn8.failed or + not opn8.changed + when: not ansible_check_mode + + - name: Listing + ansibleguy.opnsense.list: + register: opn9 + failed_when: > + 'data' not in opn9 or + opn9.data | length != 1 + when: not ansible_check_mode + + - name: Cleanup + ansibleguy.opnsense.postfix_sendercanonical: + address: alice@example.com + state: 'absent' + when: not ansible_check_mode + + - name: Listing + ansibleguy.opnsense.list: + register: opn_clean1 + failed_when: > + 'data' not in opn_clean1 or + opn_clean1.data | length != 0 + when: not ansible_check_mode