Changeset - 441a70c073b9
[Not reviewed]
0 9 0
Branko Majic (branko) - 21 months ago 2024-03-07 22:41:28
branko@majic.rs
MAR-232: Switch to using IP addreses in firewall rules for mail_forwarder role:

- Perform lookups on managed machine for the passed-in SMTP relay
host, and use those values when populating the firewall rules.
9 files changed with 77 insertions and 24 deletions:
0 comments (0 inline, 0 general)
docs/about.rst
Show inline comments
 
@@ -8,28 +8,27 @@ The roles are kept as a separate project in hope of making them potentially
 
useful to wider audience, and for reference purposes.
 

	
 
Roles cover different aspects of infrastructure, such as mail servers, web
 
servers, web applications etc. The roles are mainly well-suited for smaller
 
installations.
 

	
 
Roles are written for use with *Debian GNU/Linux*. For more details on
 
supported releases, see :ref:`rolereference`.
 

	
 
At the moment, the roles have been written for and tested against **Ansible
 
2.9.x**.
 

	
 
The roles also utilise the ``dig`` and ``ipv4/ipv6`` lookup plugins
 
which require ``dnspython`` and ``netaddr`` packages (respectively) to
 
be installed. Make sure you have the packages available on controller
 
machine.
 
The roles also utilise the ``ipv4/ipv6`` lookup plugins which require
 
``netaddr`` package to be installed. Make sure you have the packages
 
available on controller machine.
 

	
 

	
 
Why were these roles created?
 
-----------------------------
 

	
 
For a long time I have had a couple of Internet-facing servers where I hosted
 
all the IT infrastructure I needed for my day-to-day life.
 

	
 
This started off with some basic services, like mail and XMPP server, and in
 
time extended to include a web server, code repository etc.
 

	
 
As the number of services I used grew, I found it more difficult to track
docs/index.rst
Show inline comments
 
@@ -8,28 +8,27 @@ The roles are kept as a separate project in hope of making them potentially
 
useful to wider audience, and for reference purposes.
 

	
 
Roles cover different aspects of infrastructure, such as mail servers, web
 
servers, web applications etc. The roles are mainly well-suited for smaller
 
installations.
 

	
 
Roles are written for use with *Debian GNU/Linux*. For more details on
 
supported releases, see :ref:`rolereference`.
 

	
 
At the moment, the roles have been written for and tested against **Ansible
 
2.9.x**.
 

	
 
The roles also utilise the ``dig`` and ``ipv4/ipv6`` lookup plugins
 
which require ``dnspython`` and ``netaddr`` packages (respectively) to
 
be installed. Make sure you have the packages available on controller
 
machine.
 
The roles also utilise the ``ipv4/ipv6`` lookup plugins which require
 
``netaddr`` package to be installed. Make sure you have the packages
 
available on controller machine.
 

	
 

	
 
Contents
 
========
 

	
 
.. toctree::
 
   :maxdepth: 2
 

	
 
   about
 
   usage
 
   rolereference
 
   development
docs/releasenotes.rst
Show inline comments
 
@@ -6,24 +6,26 @@ x.y.z
 
-----
 

	
 
Dropped support for Python 2.7 and Debian 10 Buster. Added support for
 
Debian 12 Bookworm. Some minor improvements and fixes.
 

	
 
**Breaking changes:**
 

	
 
* All roles
 

	
 
  * Dropped support for Debian 10 (Buster).
 
  * Added support for Debian 12 (Bookworm).
 
  * ``netaddr`` Python package is now required for using the roles.
 
  * ``dnspython`` Python package is no longer required for using the
 
    roles.
 

	
 
* ``backup_client`` role
 

	
 
  * Previously the backup would run even if pre-backup scripts would
 
    fail. This is no longer the case, and all pre-backup scripts must
 
    exit with non-zero exit code in order for backup process to
 
    kick-in.
 
  * Old backups are now automatically purged after successful
 
    backup. This could lead to longer runtimes for entire backup
 
    process, as well as higher CPU usage.
 

	
 
* ``common`` role
 
@@ -34,42 +36,54 @@ Debian 12 Bookworm. Some minor improvements and fixes.
 
    Requirements (input) files for Python 3 are now put under the
 
    ``/etc/pip_check_requirements_upgrades`` directory instead of
 
    ``/etc/pip_check_requirements_upgrades-py3``.
 

	
 
    The ``pip_check_requirements_py3`` /
 
    ``pip_check_requirements_py3_in`` role parameters have been
 
    renamed to ``pip_check_requirements`` /
 
    ``pip_check_requirements_in``.
 

	
 
  * Parameter ``maintenance_allowed_hosts`` has been dropped and
 
    replaced with parameter ``maintenance_allowed_sources``. The new
 
    parameter expects a list of IPv4 and IPv6 addresses (or
 
    subnets). Resolvable names can no longer be specified (and this
 
    particular role no longe relies on presence of the ``dnspython``
 
    package).
 
    subnets). Resolvable names can no longer be specified.
 

	
 
  * NTP server configuration is now based on use of pools instead of
 
    servers. Parameter ``ntp_servers`` has been deprecated and
 
    replaced with parameter ``ntp_pools``.
 

	
 
* ``ldap_server`` role
 

	
 
  * Starting with Debian 12 Bookworm, the role no longer deploys
 
    *rsyslog* and *logrotate* configuration for writing and rotating
 
    the LDAP servers logs under ``/var/log/slapd.log``. Primary
 
    reason is that Debian 12 Bookworm no longer installs *rsyslog* by
 
    default, and it is considered to be deprecated at this point. The
 
    LDAP server logs can be read via ``journalctl -u slapd`` when
 
    necessary.
 

	
 
* ``mail_forwarder`` role
 

	
 
  * Firewall rules for incoming connections from the SMTP relay server
 
    are now based on relay's IPv4 and IPv6 addresses as resolved on
 
    managed machine during deployment time.
 

	
 
    In case the SMTP relay server's IP addresses change, the role
 
    needs to get reapplied against managed machines for those changes
 
    to take place.
 

	
 
    This change in behaviour was introduced to avoid firewall-related
 
    errors due to inability to resolve names via DNS servers during
 
    boot time.
 

	
 
* ``mail_server`` role
 

	
 
  * Parameter ``mail_server_tls_protocols`` has been dropped and
 
    replaced with parameter ``mail_server_minimum_tls_protocol``. Full
 
    list of TLS protocols can no longer be specified, only the minimum
 
    one.
 

	
 
* ``wsgi_website`` role
 

	
 
  * Dropped support for Python 2.7. Only Python 3 is supported now.
 

	
 
    The ``python_version`` role parameter has been dropped. The
docs/rolereference.rst
Show inline comments
 
@@ -1306,26 +1306,33 @@ sending out mails and receiving mails for local users. The SMTP server is
 
provided by Postfix.
 

	
 
SMTP service on server set-up this way is not meant to be exposed to the
 
Internet directly, and should receive delivery failures from the relay server
 
instead.
 

	
 
The role implements the following:
 

	
 
* Installs and configures Postfix.
 
* Purges Exim4 configuration (just in case).
 
* Sets-up aliases for the local recipients.
 
* Installs SWAKS (utility for testing SMTP servers).
 
* Configures firewall to accept SMTP connections from SMTP relay (if one has
 
  been configured). This allows for delivery of bounced e-mails.
 
* Configures firewall to accept SMTP connections from SMTP relay (if
 
  one has been configured). This allows for delivery of bounced
 
  e-mails.
 

	
 
  .. note::
 
     Firewall rules are based on IPv4 and IPv6 addresses resolved via
 
     managed server at time of deployment. If the SMTP relay changes
 
     its IP addresess, this role needs to be reapplied against the
 
     managed machines.
 

	
 
Postfix is configured as follows:
 

	
 
* Local destinations are set-up.
 
* A relay host is set.
 
* TLS is enforced for relaying mails, with configurable truststore for server
 
  certificate verification if SMTP relay is used. If SMTP relay is not used
 
  (configured), no certificate verification is done.
 
* Uses 2048-bit Diffie-Hellman parameters for relevant TLS ciphers for
 
  incoming connections.
 

	
 

	
docs/usage.rst
Show inline comments
 
@@ -139,30 +139,29 @@ packages, and to prepare the environment a bit on the Ansible server:
 
3. Set-up the virtual environment (using the ``ansible`` account):
 

	
 
   .. warning::
 
      If you are already logged-in as user ``ansible`` in the server, you will
 
      need to log-out and log-in again in order to be able to use
 
      ``virtualenvwrapper`` commands!
 

	
 
   ::
 

	
 
     mkdir ~/mysite/
 
     mkvirtualenv -p /usr/bin/python3 -a ~/mysite/ mysite
 
     pip install -U pip setuptools
 
     pip install 'ansible~=2.9.0' dnspython netaddr
 
     pip install 'ansible~=2.9.0' netaddr
 

	
 
.. warning::
 
   The ``dnspython`` and ``netaddr`` packages are needed since they
 
   are used internally by some of the roles for the ``dig`` and
 
   ``ipv4/ipv6`` lookup plugins.
 
   The ``netaddr`` package is needed for ``ipv4/ipv6`` lookup plugins
 
   which is used internally by some of the roles.
 

	
 

	
 
Cloning the *Majic Ansible Roles*
 
---------------------------------
 

	
 
With most of the software pieces in place, the only missing thing is the Majic
 
Ansible Roles:
 

	
 
1. Clone the git repository::
 

	
 
     git clone https://code.majic.rs/majic-ansible-roles ~/majic-ansible-roles
 

	
requirements.in
Show inline comments
 
ansible~=2.9.0
 
defusedxml
 
dnspython
 
gimmecert~=0.5.0
 
molecule~=2.22.0
 
netaddr
 
paramiko
 
pip
 
pip-tools
 
python-ldap
 
python-vagrant
 
# @TODO: Required for ansible-lint due to breaking changes in newer version.
 
rich<11.0.0
 
setuptools
 
sh~=1.14.0
requirements.txt
Show inline comments
 
@@ -59,26 +59,24 @@ commonmark==0.9.1
 
    # via rich
 
cookiecutter==2.6.0
 
    # via molecule
 
cryptography==3.2.1
 
    # via
 
    #   ansible
 
    #   gimmecert
 
    #   paramiko
 
defusedxml==0.7.1
 
    # via -r requirements.in
 
distlib==0.3.8
 
    # via virtualenv
 
dnspython==2.6.1
 
    # via -r requirements.in
 
docutils==0.20.1
 
    # via
 
    #   sphinx
 
    #   sphinx-rtd-theme
 
exceptiongroup==1.2.0
 
    # via pytest
 
fasteners==0.19
 
    # via python-gilt
 
filelock==3.13.1
 
    # via virtualenv
 
flake8==7.0.0
 
    # via molecule
 
@@ -117,25 +115,25 @@ packaging==23.2
 
    # via
 
    #   build
 
    #   pytest
 
    #   sphinx
 
paramiko==2.12.0
 
    # via
 
    #   -r requirements.in
 
    #   molecule
 
pathspec==0.12.1
 
    # via yamllint
 
pexpect==4.9.0
 
    # via molecule
 
pip-tools==7.4.0
 
pip-tools==7.4.1
 
    # via -r requirements.in
 
platformdirs==4.2.0
 
    # via virtualenv
 
pluggy==1.4.0
 
    # via pytest
 
pre-commit==1.21.0
 
    # via molecule
 
psutil==5.9.8
 
    # via molecule
 
ptyprocess==0.7.0
 
    # via pexpect
 
pyasn1==0.5.1
roles/mail_forwarder/tasks/main.yml
Show inline comments
 
@@ -61,24 +61,56 @@
 
    regexp: "^{{ item.key }}"
 
    state: present
 
  with_dict: "{{ local_mail_aliases }}"
 
  notify:
 
    - Rebuild mail aliases
 

	
 
- name: Enable and start postfix service
 
  service:
 
    name: postfix
 
    state: started
 
    enabled: true
 

	
 
- name: Retrieve IPv4 addresses of SMTP relay host
 
  shell: "getent ahostsv4 '{{ smtp_relay_host }}' | awk '{ print $1 }' | sort -u"  # noqa 306
 
  # [306] Shells that use pipes should set the pipefail option
 
  #   The getent ahostsv4 command has non-zero exit code if the
 
  #   supplies name cannot be resolved. However, that is a valid
 
  #   use-case for extracting this information. It effectively means
 
  #   that no IPv4 firewall rules will be deployed for allowing
 
  #   incoming connections from the SMTP relay host.
 
  changed_when: false
 
  register: smtp_relay_host_ipv4
 

	
 
- name: Retrieve IPv6 addresses of SMTP relay host
 
  shell: "getent ahostsv6 '{{ smtp_relay_host }}' | awk '{ print $1 }' | grep -v '^::ffff:' | sort -u"  # noqa 306
 
  # [306] Shells that use pipes should set the pipefail option
 
  #   The getent ahostsv6 command has non-zero exit code if the
 
  #   supplies name cannot be resolved. However, that is a valid
 
  #   use-case for extracting this information. It effectively means
 
  #   that no IPv6 firewall rules will be deployed for allowing
 
  #   incoming connections from the SMTP relay host.
 
  changed_when: false
 
  register: smtp_relay_host_ipv6
 

	
 
- name: Normalise the SMTP relay host IPv4 addresses variable
 
  set_fact:
 
    smtp_relay_host_ipv4: "{{ smtp_relay_host_ipv4.stdout_lines | reject('equalto', '') | list }}"
 
  when: "smtp_relay_host | length != 0"
 

	
 
- name: Normalise the SMTP relay host IPv6 addresses variable
 
  set_fact:
 
    smtp_relay_host_ipv6: "{{ smtp_relay_host_ipv6.stdout_lines | reject('equalto', '') | list }}"
 
  when: "smtp_relay_host | length != 0"
 

	
 
- name: Deploy firewall configuration for mail forwader
 
  template:
 
    src: "ferm_mail.conf.j2"
 
    dest: "/etc/ferm/conf.d/20-mail.conf"
 
    owner: root
 
    group: root
 
    mode: 0640
 
  notify:
 
    - Restart ferm
 

	
 
- name: Install SWAKS
 
  apt:
roles/mail_forwarder/templates/ferm_mail.conf.j2
Show inline comments
 
{% if smtp_relay_host and smtp_from_relay_allowed %}
 
{% if smtp_relay_host_ipv4 %}
 
domain ip {
 
    # Accept incoming connections on port 25 from SMTP relay host.
 
    table filter {
 
        chain INPUT {
 
            # SMTP for server communication.
 
            proto tcp dport 25 {
 
                saddr {{ smtp_relay_host }} ACCEPT;
 
{% for address in smtp_relay_host_ipv4 %}
 
                saddr {{ address }} ACCEPT;
 
{% endfor %}
 
            }
 
        }
 
    }
 
}
 
{% endif %}
 

	
 
{% if lookup('dig', smtp_relay_host + '/AAAA') not in ['NXDOMAIN', ''] %}
 
{% if smtp_relay_host_ipv6 %}
 
domain ip6 {
 
    # Accept incoming connections on port 25 from SMTP relay host.
 
    table filter {
 
        chain INPUT {
 
            # SMTP for server communication.
 
            proto tcp dport 25 {
 
                saddr {{ smtp_relay_host }} ACCEPT;
 
{% for address in smtp_relay_host_ipv6 %}
 
                saddr {{ address }} ACCEPT;
 
{% endfor %}
 
            }
 
        }
 
    }
 
}
 
{% endif %}
 
{% endif %}
0 comments (0 inline, 0 general)