diff --git a/roles/sys-ctl-alm-compose/tasks/01_core.yml b/roles/sys-ctl-alm-compose/tasks/01_core.yml index 782c01ad..76256f19 100644 --- a/roles/sys-ctl-alm-compose/tasks/01_core.yml +++ b/roles/sys-ctl-alm-compose/tasks/01_core.yml @@ -36,4 +36,6 @@ ansible.builtin.systemd: name: "{{ system_service_id | get_service_name(SOFTWARE_NAME, False) ~ escaped_name.stdout }}.service" state: started - when: MODE_ASSERT | bool + when: + - MODE_ASSERT | bool + - not( IS_CONTAINER | bool ) diff --git a/roles/sys-ctl-alm-email/tasks/01_core.yml b/roles/sys-ctl-alm-email/tasks/01_core.yml index e03a054f..87f29305 100644 --- a/roles/sys-ctl-alm-email/tasks/01_core.yml +++ b/roles/sys-ctl-alm-email/tasks/01_core.yml @@ -2,8 +2,8 @@ - name: Include dependencies include_role: - name: "sys-svc-msmtp" - when: not run_once_sys_svc_msmtp | default(false) + name: "sys-svc-mail" + when: not (run_once_sys_svc_mail | default(false) | bool) - include_role: name: sys-service diff --git a/roles/sys-ctl-hlth-msmtp/tasks/01_core.yml b/roles/sys-ctl-hlth-msmtp/tasks/01_core.yml index df322278..d905519d 100644 --- a/roles/sys-ctl-hlth-msmtp/tasks/01_core.yml +++ b/roles/sys-ctl-hlth-msmtp/tasks/01_core.yml @@ -1,16 +1,8 @@ -- name: Include dependency 'sys-ctl-alm-telegram' - include_role: - name: sys-ctl-alm-telegram - when: run_once_sys_ctl_alm_telegram is not defined - - include_role: name: sys-service vars: system_service_tpl_on_failure: "{{ SYS_SERVICE_ON_FAILURE_COMPOSE }}" system_service_on_calendar: "{{ SYS_SCHEDULE_HEALTH_MSMTP }}" system_service_timer_enabled: true - when: - - not MODE_RESET | bool - - users['no-reply'].mailu_token is defined - include_tasks: utils/once/flag.yml \ No newline at end of file diff --git a/roles/sys-svc-msmtp/README.md b/roles/sys-svc-mail-msmtp/README.md similarity index 100% rename from roles/sys-svc-msmtp/README.md rename to roles/sys-svc-mail-msmtp/README.md diff --git a/roles/sys-svc-mail-msmtp/meta/main.yml b/roles/sys-svc-mail-msmtp/meta/main.yml new file mode 100644 index 00000000..f980b57c --- /dev/null +++ b/roles/sys-svc-mail-msmtp/meta/main.yml @@ -0,0 +1,30 @@ +--- +galaxy_info: + author: "Kevin Veen-Birkenbach" + description: "Installs and configures msmtp as a lightweight SMTP client and sendmail replacement for the Infinito.Nexus ecosystem." + license: "Infinito.Nexus NonCommercial License" + license_url: "https://s.infinito.nexus/license" + company: | + Kevin Veen-Birkenbach + Consulting & Coaching Solutions + https://www.veen.world + min_ansible_version: "2.9" + platforms: + - name: Archlinux + versions: + - rolling + galaxy_tags: + - email + - smtp + - msmtp + - sendmail + - automation + - monitoring + - archlinux + repository: "https://s.infinito.nexus/code" + issue_tracker_url: "https://s.infinito.nexus/issues" + documentation: "https://docs.infinito.nexus" + logo: + class: "" + run_after: [] +dependencies: [] diff --git a/roles/sys-svc-mail-msmtp/tasks/01_core.yml b/roles/sys-svc-mail-msmtp/tasks/01_core.yml new file mode 100644 index 00000000..77e558ab --- /dev/null +++ b/roles/sys-svc-mail-msmtp/tasks/01_core.yml @@ -0,0 +1,24 @@ +- include_tasks: utils/once/flag.yml + +- name: Install msmtp base package + community.general.pacman: + name: + - msmtp + state: present + +- name: Install msmtp-mta when Mailu is used (no local postfix relay) + community.general.pacman: + name: msmtp-mta + state: present + when: "'web-app-mailu' in group_names" + +- name: configure msmtprc.conf.j2 + template: + src: "msmtprc.conf.j2" + dest: "/root/.msmtprc" + mode: 600 + +- include_role: + name: sys-ctl-hlth-msmtp + when: run_once_sys_ctl_hlth_msmtp is not defined + diff --git a/roles/sys-svc-mail-msmtp/tasks/main.yml b/roles/sys-svc-mail-msmtp/tasks/main.yml new file mode 100644 index 00000000..8298b144 --- /dev/null +++ b/roles/sys-svc-mail-msmtp/tasks/main.yml @@ -0,0 +1,3 @@ +- name: "Load MSMTP (once)" + include_tasks: 01_core.yml + when: not (run_once_sys_svc_mail_msmtp | default(false) | bool) \ No newline at end of file diff --git a/roles/sys-svc-mail-msmtp/templates/msmtprc.conf.j2 b/roles/sys-svc-mail-msmtp/templates/msmtprc.conf.j2 new file mode 100644 index 00000000..739b4df2 --- /dev/null +++ b/roles/sys-svc-mail-msmtp/templates/msmtprc.conf.j2 @@ -0,0 +1,40 @@ +# Set default values for all following accounts. +defaults +logfile ~/.msmtp.log + +{% if 'web-app-mailu' in group_names %} +auth on +tls_starttls {{ 'on' if SYSTEM_EMAIL.START_TLS else 'off' }} +{% if SYSTEM_EMAIL.TLS %} +tls on +tls_trust_file /etc/ssl/certs/ca-certificates.crt +{% else %} +tls off +{% endif %} + +{% set no_reply = users.get('no-reply', {}) %} +{% set no_reply_email = no_reply.get('email', SYSTEM_EMAIL.FROM | default('no-reply@' ~ SYSTEM_EMAIL.HOST)) %} +{% set no_reply_token = no_reply.get('mailu_token', '') %} + +account system_email_no_reply +host {{ SYSTEM_EMAIL.HOST }} +port {{ SYSTEM_EMAIL.PORT }} +from {{ no_reply_email }} +user {{ no_reply_email }} +password {{ no_reply_token }} + +account default : system_email_no_reply + +{% else %} +# Localhost relay – no auth +auth off +tls_starttls off +tls off + +account local_relay +host localhost +port 25 +from root@{{ inventory_hostname }} + +account default : local_relay +{% endif %} diff --git a/roles/sys-svc-mail-smtp/README.md b/roles/sys-svc-mail-smtp/README.md new file mode 100644 index 00000000..e9ad67a3 --- /dev/null +++ b/roles/sys-svc-mail-smtp/README.md @@ -0,0 +1,67 @@ +# sys-svc-mail-smtp 📮 + +## Description + +The `sys-svc-mail-smtp` role configures a **local SMTP relay** using [Postfix](https://www.postfix.org/), listening exclusively on `localhost`. +It is designed to be used as a fallback when no central Mailu instance is available, enabling applications and system services to send email via `localhost:25` without additional configuration. + +For general background on SMTP, see [SMTP on Wikipedia](https://en.wikipedia.org/wiki/Simple_Mail_Transfer_Protocol). +For details about Postfix itself, see [Postfix on Wikipedia](https://en.wikipedia.org/wiki/Postfix_(software)). + +## Overview + +This role: + +- Installs Postfix via `pacman` on Arch Linux. +- Configures it as a **loopback-only relay**, so it: + - only listens on `127.0.0.1`, + - does not perform local mailbox delivery, + - and is safe to use as a simple outbound relay for the local host. +- Integrates seamlessly with the `sys-svc-mail` and `sys-svc-mail-msmtp` roles in the Infinito.Nexus stack. + +Typically, `sys-svc-mail` decides whether to: + +- Use Mailu (via `sys-svc-mail-msmtp`), **or** +- Fall back to this role (`sys-svc-mail-smtp`) and send via `localhost`. + +## Purpose + +The main goals of this role are: + +- Provide a **minimal, secure SMTP relay** for hosts that do not run a full mail stack. +- Enable `msmtp` (and any other sendmail-compatible client) to send mail by talking to `localhost:25`. +- Avoid the complexity of a full MTA configuration while still supporting basic outbound notifications. + +This is particularly useful for: + +- Monitoring nodes, +- Utility hosts, +- Development or test environments without Mailu. + +## Features + +- 💾 **Postfix Installation on Arch Linux** + - Uses `community.general.pacman` to install the `postfix` package. + +- 🔒 **Loopback-Only Configuration** + - Configures `inet_interfaces = loopback-only` to restrict the SMTP daemon to `127.0.0.1`. + - Defines `mynetworks = 127.0.0.0/8` for safe local relaying. + +- 🚫 **No Local Mailbox Delivery** + - Sets `local_transport = error: local delivery disabled` to avoid storing mail locally. + - Focus is purely on **relaying** from localhost rather than full MTA behavior. + +- 🧩 **Integration with Infinito.Nexus** + - Meant to be driven by `sys-svc-mail`, which decides when to enable this relay. + - Works hand in hand with `sys-svc-mail-msmtp`, which configures msmtp to talk to `localhost:25` when Mailu is not present. + +## Further Resources + +- SMTP & Mail Transfer: + - SMTP (Wikipedia): +- Postfix: + - Official site: + - Postfix on Wikipedia: +- Related Infinito.Nexus roles: + - `sys-svc-mail`: central mail orchestration + - `sys-svc-mail-msmtp`: msmtp client and sendmail replacement diff --git a/roles/sys-svc-msmtp/meta/main.yml b/roles/sys-svc-mail-smtp/meta/main.yml similarity index 55% rename from roles/sys-svc-msmtp/meta/main.yml rename to roles/sys-svc-mail-smtp/meta/main.yml index f708471d..fa586de7 100644 --- a/roles/sys-svc-msmtp/meta/main.yml +++ b/roles/sys-svc-mail-smtp/meta/main.yml @@ -1,23 +1,30 @@ +--- galaxy_info: author: "Kevin Veen-Birkenbach" - description: "Installs and configures msmtp, a lightweight SMTP client and sendmail replacement." + description: "Configures a local SMTP relay using Postfix, listening only on localhost for secure, lightweight mail delivery." license: "Infinito.Nexus NonCommercial License" license_url: "https://s.infinito.nexus/license" - company: | + company: | Kevin Veen-Birkenbach Consulting & Coaching Solutions https://www.veen.world min_ansible_version: "2.9" platforms: - - name: Archlinux - versions: - - rolling + - name: Archlinux + versions: + - rolling galaxy_tags: - - email - - msmtp - - smtp - - automation - - archlinux + - email + - smtp + - postfix + - relay + - monitoring + - automation + - archlinux repository: "https://s.infinito.nexus/code" issue_tracker_url: "https://s.infinito.nexus/issues" documentation: "https://docs.infinito.nexus" + logo: + class: "" + run_after: [] +dependencies: [] diff --git a/roles/sys-svc-mail-smtp/tasks/01_core.yml b/roles/sys-svc-mail-smtp/tasks/01_core.yml new file mode 100644 index 00000000..502ab06c --- /dev/null +++ b/roles/sys-svc-mail-smtp/tasks/01_core.yml @@ -0,0 +1,26 @@ +- include_tasks: utils/once/flag.yml + +- name: "Ensure msmtp-mta is absent (conflicts with postfix smtp-forwarder)" + community.general.pacman: + name: + - msmtp-mta + state: absent + +- name: "Install local SMTP relay (Postfix)" + community.general.pacman: + name: + - postfix + state: present + +- name: "Configure Postfix as localhost-only relay" + ansible.builtin.template: + src: "postfix-main.cf.j2" + dest: "/etc/postfix/main.cf" + mode: "0644" + +- name: "Ensure postfix is enabled and running" + ansible.builtin.systemd: + name: postfix + enabled: true + state: started + when: not( IS_CONTAINER | bool ) diff --git a/roles/sys-svc-mail-smtp/tasks/main.yml b/roles/sys-svc-mail-smtp/tasks/main.yml new file mode 100644 index 00000000..862e60e5 --- /dev/null +++ b/roles/sys-svc-mail-smtp/tasks/main.yml @@ -0,0 +1,3 @@ +- name: "Load SMTP (once)" + include_tasks: 01_core.yml + when: not (run_once_sys_svc_mail_smtp | default(false) | bool) \ No newline at end of file diff --git a/roles/sys-svc-mail-smtp/templates/postfix-main.cf.j2 b/roles/sys-svc-mail-smtp/templates/postfix-main.cf.j2 new file mode 100644 index 00000000..547d91da --- /dev/null +++ b/roles/sys-svc-mail-smtp/templates/postfix-main.cf.j2 @@ -0,0 +1,8 @@ +# roles/sys-svc-msmtp/templates/postfix-main.cf.j2 +myhostname = {{ inventory_hostname }} +inet_interfaces = loopback-only +mydestination = +relayhost = +mynetworks = 127.0.0.0/8 +local_transport = error: local delivery disabled +default_transport = smtp \ No newline at end of file diff --git a/roles/sys-svc-mail/README.md b/roles/sys-svc-mail/README.md new file mode 100644 index 00000000..f2f1a4c8 --- /dev/null +++ b/roles/sys-svc-mail/README.md @@ -0,0 +1,69 @@ +# sys-svc-mail 📧 + +## Description + +The `sys-svc-mail` role acts as the **central mail orchestration layer** in the Infinito.Nexus stack. +It wires together: + +- [Mailu](https://mailu.io/) as a full-featured mail server (when available), +- [msmtp](https://marlam.de/msmtp/) as a lightweight sendmail-compatible SMTP client, and +- an optional local SMTP relay (Postfix) for hosts **without** Mailu. + +For more background on the underlying protocol, see [Simple Mail Transfer Protocol (SMTP) on Wikipedia](https://en.wikipedia.org/wiki/Simple_Mail_Transfer_Protocol). + +## Overview + +This role provides a **unified mail setup** for your hosts: + +- If the host is part of the `web-app-mailu` group, it: + - checks the reachability of the Mailu endpoint, + - triggers Mailu startup via the Infinito.Nexus helper (`utils/load_app.yml`), + - and prepares the system to send emails through Mailu using the `sys-svc-mail-msmtp` role. + +- If the host is **not** running Mailu, it: + - optionally configures a local SMTP relay via `sys-svc-mail-smtp` (Postfix on `localhost:25`), + - and still configures `msmtp` as a sendmail-compatible client. + +This makes `sys-svc-mail` the canonical entrypoint for “mail capabilities” on a node, abstracting away whether the actual delivery happens via Mailu or a local relay. + +## Purpose + +The main purpose of this role is to: + +- Provide a **consistent mail-sending interface** for all hosts in the Infinito.Nexus ecosystem. +- Automatically choose between: + - **Mailu-backed delivery** (with authentication tokens), or + - a **local SMTP relay on localhost**, + depending on the presence of `web-app-mailu` in the host’s groups. +- Ensure that system services and applications can always send notifications (e.g. health checks, alerts, job results) without each role having to care about the underlying mail plumbing. + +## Features + +- 🔄 **Mailu Integration (when available)** + - Checks Mailu reachability using Ansible’s `uri` module. + - Triggers Mailu startup via `utils/load_app.yml`. + - Ensures handlers are flushed/reset via `utils/load_handlers.yml`. + +- 💡 **Smart Fallback to Localhost** + - If no `web-app-mailu` is present, the role can configure a local Postfix-based SMTP relay via `sys-svc-mail-smtp`. + - Combined with `sys-svc-mail-msmtp`, this enables sending mail via `localhost:25` without additional configuration in other roles. + +- 📨 **msmtp Client Configuration** + - Delegates installation and configuration of msmtp to `sys-svc-mail-msmtp`. + - Supports both authenticated Mailu delivery and unauthenticated localhost-based delivery. + +- 🧩 **Composable Design** + - Uses internal `run_once_*` flags to avoid repeated setup. + - Cleanly integrates with the Infinito.Nexus stack and shared utilities. + +## Further Resources + +- Mail server: + - Mailu: + - SMTP (protocol): +- SMTP client: + - msmtp: + - msmtp on Wikipedia: +- Infinito.Nexus: + - Main repository: + - Documentation: diff --git a/roles/sys-svc-mail/meta/main.yml b/roles/sys-svc-mail/meta/main.yml new file mode 100644 index 00000000..db59f89e --- /dev/null +++ b/roles/sys-svc-mail/meta/main.yml @@ -0,0 +1,30 @@ +--- +galaxy_info: + author: "Kevin Veen-Birkenbach" + description: "Central mail orchestration role for Infinito.Nexus, integrating Mailu, msmtp, and an optional local SMTP relay." + license: "Infinito.Nexus NonCommercial License" + license_url: "https://s.infinito.nexus/license" + company: | + Kevin Veen-Birkenbach + Consulting & Coaching Solutions + https://www.veen.world + min_ansible_version: "2.9" + platforms: + - name: Archlinux + versions: + - rolling + galaxy_tags: + - email + - smtp + - msmtp + - postfix + - automation + - monitoring + - archlinux + repository: "https://s.infinito.nexus/code" + issue_tracker_url: "https://s.infinito.nexus/issues" + documentation: "https://docs.infinito.nexus" + logo: + class: "" + run_after: [] +dependencies: [] diff --git a/roles/sys-svc-mail/tasks/01_core.yml b/roles/sys-svc-mail/tasks/01_core.yml new file mode 100644 index 00000000..f644dce1 --- /dev/null +++ b/roles/sys-svc-mail/tasks/01_core.yml @@ -0,0 +1,42 @@ +- include_tasks: utils/once/flag.yml + +- block: + - name: "Check if Mail Host is reachable" + uri: + url: "{{ WEB_PROTOCOL }}://{{ SYSTEM_EMAIL.HOST }}" + method: HEAD + validate_certs: yes + status_code: 200 + register: mail_host_reachability + failed_when: false + changed_when: false + when: + - run_once_web_app_mailu is not defined + - SYSTEM_EMAIL.HOST == (domains | get_domain('web-app-mailu')) + + - name: "Load Mailu Routines for '{{ role_name }}'" + include_tasks: 02_mailu.yml + when: + - > + ( + mail_host_reachability is defined and + (mail_host_reachability.status | default(0) | int) != 200 + ) + or + ( + users.get('no-reply', {}).get('mailu_token', '') | length == 0 + ) + when: "'web-app-mailu' in group_names" + +- name: Setup emails via localhost + include_role: + name: sys-svc-mail-smtp + when: + - run_once_sys_svc_mail_smtp is not defined + - "'web-app-mailu' not in group_names" + +- name: Setup msmtp client + include_role: + name: sys-svc-mail-msmtp + when: + - run_once_sys_svc_mail_msmtp is not defined diff --git a/roles/sys-svc-msmtp/tasks/02_mailu.yml b/roles/sys-svc-mail/tasks/02_mailu.yml similarity index 100% rename from roles/sys-svc-msmtp/tasks/02_mailu.yml rename to roles/sys-svc-mail/tasks/02_mailu.yml diff --git a/roles/sys-svc-mail/tasks/main.yml b/roles/sys-svc-mail/tasks/main.yml new file mode 100644 index 00000000..8fdff9bf --- /dev/null +++ b/roles/sys-svc-mail/tasks/main.yml @@ -0,0 +1,3 @@ +- name: "Load Mail (once)" + include_tasks: 01_core.yml + when: not (run_once_sys_svc_mail | default(false) | bool) \ No newline at end of file diff --git a/roles/sys-svc-msmtp/tasks/01_core.yml b/roles/sys-svc-msmtp/tasks/01_core.yml deleted file mode 100644 index 29cd25bc..00000000 --- a/roles/sys-svc-msmtp/tasks/01_core.yml +++ /dev/null @@ -1,48 +0,0 @@ -- include_tasks: utils/once/flag.yml - -- name: "Check if Mail Host is reachable" - uri: - url: "{{ WEB_PROTOCOL }}://{{ SYSTEM_EMAIL.HOST }}" - method: HEAD - validate_certs: yes - status_code: 200 - register: mail_host_reachability - failed_when: false - changed_when: false - when: - - run_once_web_app_mailu is not defined - - "'web-app-mailu' in group_names" - - SYSTEM_EMAIL.HOST == (domains | get_domain('web-app-mailu')) - -- name: "Load Mailu Routines for '{{ role_name }}'" - include_tasks: 02_mailu.yml - when: - - "'web-app-mailu' in group_names" - - > - ( - mail_host_reachability is defined and - (mail_host_reachability.status | default(0) | int) != 200 - ) - or - ( - users['no-reply'].mailu_token | default('', true) | length == 0 - ) - -- name: install msmtp msmtp-mta - community.general.pacman: - name: - - msmtp - - msmtp-mta - state: present - -- name: configure msmtprc.conf.j2 - template: - src: "msmtprc.conf.j2" - dest: "/root/.msmtprc" - mode: 600 - -- include_role: - name: sys-ctl-hlth-msmtp - when: run_once_sys_ctl_hlth_msmtp is not defined - -- include_tasks: utils/once/flag.yml diff --git a/roles/sys-svc-msmtp/tasks/main.yml b/roles/sys-svc-msmtp/tasks/main.yml deleted file mode 100644 index ff24898c..00000000 --- a/roles/sys-svc-msmtp/tasks/main.yml +++ /dev/null @@ -1,3 +0,0 @@ -- name: "Load MSMTP Core Once" - include_tasks: 01_core.yml - when: not run_once_sys_svc_msmtp | default(false) \ No newline at end of file diff --git a/roles/sys-svc-msmtp/templates/msmtprc.conf.j2 b/roles/sys-svc-msmtp/templates/msmtprc.conf.j2 deleted file mode 100644 index 7f16ecfb..00000000 --- a/roles/sys-svc-msmtp/templates/msmtprc.conf.j2 +++ /dev/null @@ -1,20 +0,0 @@ -# Set default values for all following accounts. -defaults -auth on -logfile ~/.msmtp.log -tls_starttls {{ 'on' if SYSTEM_EMAIL.START_TLS else 'off' }} -{% if SYSTEM_EMAIL.TLS %} -tls on -tls_trust_file /etc/ssl/certs/ca-certificates.crt -{% else %} -tls off -{% endif %} - -account system_email_no_reply -host {{ SYSTEM_EMAIL.HOST }} -port {{ SYSTEM_EMAIL.PORT }} -from {{ users['no-reply'].email }} -user {{ users['no-reply'].email }} -password {{ users['no-reply'].mailu_token }} - -account default : system_email_no_reply \ No newline at end of file diff --git a/roles/web-app-wordpress/vars/main.yml b/roles/web-app-wordpress/vars/main.yml index 9be34723..8322d644 100644 --- a/roles/web-app-wordpress/vars/main.yml +++ b/roles/web-app-wordpress/vars/main.yml @@ -4,7 +4,7 @@ database_type: "mariadb" # WordPress WORDPRESS_URL: "{{ domains | get_url(application_id, WEB_PROTOCOL) }}" -WORDPRESS_MSMTP_SRC: "{{ [ playbook_dir, 'roles/sys-svc-msmtp/templates/msmtprc.conf.j2' ] | path_join }}" +WORDPRESS_MSMTP_SRC: "{{ [ playbook_dir, 'roles/sys-svc-mail-msmtp/templates/msmtprc.conf.j2' ] | path_join }}" WORDPRESS_MSMTP_ABS: "{{ [ docker_compose.directories.config, 'msmtprc.conf'] | path_join }}" WORDPRESS_MAX_UPLOAD_SIZE: "{{ applications | get_app_conf(application_id, 'max_upload_size') }}" WORDPRESS_CUSTOM_IMAGE: "wordpress_custom"