diff --git a/README.md b/README.md index 466cc1a..3cf8b0a 100644 --- a/README.md +++ b/README.md @@ -120,6 +120,54 @@ If no value is provided, `listmaster` will be joined with a @ to the For example: *listmaster@lists.example.com*. +### sympa.conf + +As *sympa_listmaster* described hereabove, several variables described hereafter set a parameter in *sympa.conf* configuration file. For [those parameters avaialable for *sympa.conf*](https://www.sympa.community/gpldoc/man/sympa_config.5.html) which have not (yet) a variable associated in this role, it proposes the composed variable *sympa_config_extra_parameters*, which can contain either: + +* for backwards compatibility: a multiline string, which is simply appended to *sympa.conf*, +* a dict describing sympa parameters, similar to *sympa_robots* elements described hereafter. + +### Sympa robots + +#### *sympa_robots* + +With *sympa_robots* the role defines and populates the `robots.conf` files of a multi-domains Sympa server. + +*sympa_robots* is a dict, where each key is the *domain* on which the robot operates (mail and web), and the value is a dict which defines the values of all the parameters of the robot. It may contain parameters such as `wwsympa_url`, `listmaster`, `title`, or any other [configuration parameter valid in a `robot.conf` file](https://www.sympa.community/gpldoc/man/sympa_config.5.html), with its associated value(s). + +*listmaster* can be defined as an ansible list of emails, or as a string of comma separated email addresses. Compound paramentes can be defined as sub-dicts. For instance: +``` +sympa_robots: + lists.mydomain.tld: + listmaster: ... + + archive: + web_access: public + mail_access: private +``` +will render in the `lists.mydomain.tld/robot.conf` configuration file: +``` +# Robot configuration for lists.mydomain.tld +domain lists.mydomain.tld + +# Robot's listmaster +listmaster ... + +# archive value(s) +archive.web_access public +archive.mail_access private +``` +For backwards compatibility, *sympa_robots* can also be defined as a list of dicts where all items have a parameter *domain* defining the robot: +``` +sympa_robots: + - domain: lists.mydomain.tld + listmaster: ... + + archive: + web_access: public + mail_access: private +``` + ### Installation You can pick from different installation methods through the @@ -321,6 +369,16 @@ Protocol for SOAP URL. Defaults to `https`. Number of FCGI instances (default: 2) +### LDAP + +#### *sympa_ldap_auth_enabled* + +Whether authentication through LDAP is enabled. Defaults to `false`. + +#### *sympa_ldap_auth_secure* + +Whether authentication through LDAP is secure. Defaults to `true`. + ## Dependencies None. diff --git a/defaults/main.yml b/defaults/main.yml index eb59288..01a8237 100644 --- a/defaults/main.yml +++ b/defaults/main.yml @@ -54,8 +54,6 @@ sympa_config_sendmail: /usr/sbin/sendmail sympa_config_sendmail_aliases: /etc/mail/sympa/aliases sympa_config_shared_feature: 'on' -sympa_config_dkim_feature: false - sympa_edit_list_defaults: - role: privileged_owner privilege: write @@ -134,10 +132,14 @@ sympa_soap_fcgi_enabled: false sympa_soap_fcgi_protocol: https sympa_soap_fcgi_instances: 2 +sympa_ldap_auth_enabled: false +sympa_ldap_auth_secure: true + sympa_web_nginx_snippet: | location = / { return 301 /{{ sympa_web_path }}; } + location /{{ sympa_web_path }} { include fastcgi_params; fastcgi_pass unix:{{ sympa_web_fcgi_socket }}; @@ -159,6 +161,22 @@ sympa_soap_nginx_snippet: | fastcgi_pass unix:/run/sympasoap/sympasoap.socket; } +sympa_robots: {} + sympa_install_odbc_driver: false sympa_rsyslog_unix_user: "{{ sympa_unix_user }}" + +sympa_dkim_feature: false +sympa_config_dkim_feature: '{{ sympa_dkim_feature }}' # config requirements if dkim enabled + +# sympa_dkim_add_signature_to: +# - robot +# - list +# sympa_dkim_signature_apply_on: +# - any + +sympa_dkim_private_key_path: /etc/ssl/dkim_private.key + +# sympa_dkim_signer_domain: lists.example.org +# sympa_dkim_selector: sympa diff --git a/tasks/config-make-install.yml b/tasks/config-make-install.yml index 1411a0c..0aa723f 100644 --- a/tasks/config-make-install.yml +++ b/tasks/config-make-install.yml @@ -9,6 +9,7 @@ owner: "{{ sympa_unix_user }}" group: "{{ sympa_unix_group }}" mode: 0755 + recurse: true - name: Ensure that configuration directory exists file: @@ -34,7 +35,7 @@ become_user: "{{ sympa_unix_user }}" vars: ansible_ssh_pipelining: true - when: sympa_installation_method != 'source' or sympa_source_patches | count + when: sympa_installation_method != 'source' or sympa_source_patches | count > 0 - name: Add symlink for pod2man on Arch Linux file: diff --git a/tasks/main.yml b/tasks/main.yml index d7be4c2..7725013 100644 --- a/tasks/main.yml +++ b/tasks/main.yml @@ -134,9 +134,9 @@ - sympa - sympa-conf -- name: Create configuration files for robots on request - import_tasks: robots.yml - when: sympa_robots | default([]) | count > 0 +- name: Create configuration files for robots on request (dict variable) + import_tasks: + file: robots.yml tags: - sympa - sympa-conf @@ -184,7 +184,7 @@ mode: 0640 when: - sympa_topics is defined - - sympa_topics | count + - sympa_topics | count > 0 notify: - restart sympa - restart wwsympa diff --git a/tasks/perl-modules.yml b/tasks/perl-modules.yml index 5c37b95..01639d0 100644 --- a/tasks/perl-modules.yml +++ b/tasks/perl-modules.yml @@ -42,6 +42,14 @@ name: "{{ sympa_openssldev_package }}" when: sympa_config_dkim_feature or sympa_soap_fcgi_enabled +- name: Install SSL development library and zlib development needed for LDAP + package: + name: + - "{{ sympa_openssldev_package }}" + - "{{ sympa_zlibdev_package }}" + when: sympa_ldap_auth_enabled and sympa_ldap_auth_secure + tags: sympaldap + - name: Add Mail::DKIM::Verifier to features if DKIM support is requested set_fact: sympa_cpanm_features: "{{ sympa_cpanm_features + ['Mail::DKIM::Verifier'] }}" @@ -58,6 +66,25 @@ when: sympa_soap_fcgi_enabled tags: sympasoap +- name: Add ldap-secure to features if LDAP (ssl) auth is enabled + set_fact: + sympa_cpanm_features: "{{ sympa_cpanm_features + ['ldap-secure'] }}" + when: sympa_ldap_auth_enabled and sympa_ldap_auth_secure + tags: sympaldap + +- name: Add ldap to features if LDAP (no SSL) auth is enabled + set_fact: + sympa_cpanm_features: "{{ sympa_cpanm_features + ['ldap'] }}" + when: sympa_ldap_auth_enabled and not sympa_ldap_auth_secure + tags: sympaldap + +- name: Print features before install + ansible.builtin.debug: + var: sympa_cpanm_features + verbosity: 1 + - name: Install Perl modules with cpanm command: "{{ sympa_cpanm_program }} --notest --installdeps {{ sympa_source_directory }}{% for feature in sympa_cpanm_features %} --with-feature {{ feature }}{% endfor %}" - tags: sympasoap + tags: + - sympasoap + - sympaldap diff --git a/tasks/robot.yml b/tasks/robot.yml new file mode 100644 index 0000000..45ce610 --- /dev/null +++ b/tasks/robot.yml @@ -0,0 +1,40 @@ +--- +# Copyright (C) 2020-2022 Stefan Hornburg (Racke) +# SPDX-License-Identifier: GPL-2.0-or-later + +- name: Create directory for {{ sympa_robot_domain }} robot + file: + state: directory + path: "{{ sympa_configuration_directory }}/{{ sympa_robot_domain }}" + owner: "{{ sympa_unix_user }}" + group: "{{ sympa_unix_group }}" + mode: 0755 + +- name: Create list data directory for {{ sympa_robot_domain }} robot + file: + state: directory + path: "{{ sympa_list_home_directory }}/{{ sympa_robot_domain }}" + owner: "{{ sympa_unix_user }}" + group: "{{ sympa_unix_group }}" + mode: 0755 + +- debug: + var: "{{ item }}" + verbosity: 2 + loop: + - sympa_robot_domain + - sympa_robot + - sympa_robots + + +- name: Create {{ sympa_robot_domain }} robot configuration file + template: + src: robot.conf.j2 + dest: "{{ sympa_configuration_directory }}/{{ sympa_robot_domain }}/robot.conf" + owner: "{{ sympa_unix_user }}" + group: "{{ sympa_unix_group }}" + mode: 0644 + notify: + - restart sympa + - restart wwsympa + diff --git a/tasks/robots.yml b/tasks/robots.yml index 7e08c31..046aadd 100644 --- a/tasks/robots.yml +++ b/tasks/robots.yml @@ -1,47 +1,41 @@ --- -# Copyright (C) 2020-2022 Stefan Hornburg (Racke) -# SPDX-License-Identifier: GPL-2.0-or-later +# Sympa robots configuration -- name: Create directory for robots - file: - state: directory - path: "{{ sympa_configuration_directory }}/{{ item.domain }}" - owner: "{{ sympa_unix_user }}" - group: "{{ sympa_unix_group }}" - mode: 0755 - when: item.domain != sympa_domain - loop: "{{ sympa_robots }}" +- name: Create configuration files for robots on request (dict variable) + include_tasks: + file: robot.yml + apply: + tags: + - sympa + - sympa-conf + - sympa-robots + loop: "{{ sympa_robots | list }}" + loop_control: + loop_var: sympa_robot_domain + vars: + sympa_robot_domain_query: '"{{ sympa_robot_domain }}"' + sympa_robot: "{{ sympa_robots | community.general.json_query ( sympa_robot_domain_query ) }}" + when: + - sympa_robots is mapping + - sympa_robot_domain != sympa_domain -- name: Create list data directory for robots - file: - state: directory - path: "{{ sympa_list_home_directory }}/{{ item.domain }}" - owner: "{{ sympa_unix_user }}" - group: "{{ sympa_unix_group }}" - mode: 0755 - when: item.domain != sympa_domain - loop: "{{ sympa_robots }}" -- name: Create list data directory for robots - file: - state: directory - path: "{{ sympa_list_home_directory }}/{{ item.domain }}" - owner: "{{ sympa_unix_user }}" - group: "{{ sympa_unix_group }}" - mode: 0755 - when: item.domain != sympa_domain - loop: "{{ sympa_robots }}" +- name: Create configuration files for robots on request (list variable) + include_tasks: + file: robot.yml + apply: + tags: + - sympa + - sympa-conf + - sympa-robots + loop: "{{ sympa_robots | list }}" + loop_control: + loop_var: sympa_robot + vars: + sympa_robot_domain: "{{ sympa_robot.domain }}" + when: + - sympa_robots is not mapping # then it must be a list of dicts + - sympa_robot_domain != sympa_domain -- name: Create configuration file - template: - src: robot.conf.j2 - dest: "{{ sympa_configuration_directory }}/{{ item.domain }}/robot.conf" - owner: "{{ sympa_unix_user }}" - group: "{{ sympa_unix_group }}" - mode: 0644 - when: item.domain != sympa_domain - loop: "{{ sympa_robots }}" - notify: - - restart sympa - - restart wwsympa +... diff --git a/tasks/source.yml b/tasks/source.yml index 5c7b028..61ecc1f 100644 --- a/tasks/source.yml +++ b/tasks/source.yml @@ -43,7 +43,7 @@ - name: Ensure that patch binary is installed package: name: patch - when: sympa_source_patches | count + when: sympa_source_patches | count > 0 - name: Apply patches patch: diff --git a/templates/dbconfig-common.j2 b/templates/dbconfig-common.j2 index f9d9247..5ff29ae 100644 --- a/templates/dbconfig-common.j2 +++ b/templates/dbconfig-common.j2 @@ -1,7 +1,7 @@ dbc_install='true' dbc_upgrade='true' dbc_remove='' -dbc_dbtype='{{ sympa_database.type | lower }}' +dbc_dbtype='{{ "pgsql" if sympa_database.type | lower == "postgresql" else sympa_database.type | lower }}' dbc_dbuser='{{ sympa_database.user }}' dbc_dbpass='{{ sympa_database.password | default("") }}' dbc_dbserver='{{ sympa_database.host | default("") }}' diff --git a/templates/robot.conf.j2 b/templates/robot.conf.j2 index 6df1004..5ccbd57 100644 --- a/templates/robot.conf.j2 +++ b/templates/robot.conf.j2 @@ -1,8 +1,39 @@ -# Robot configuration for {{ item.domain }} -domain {{ item.domain }} +{# Macro to accept multi-value parameters as comma separated string or yaml list #} +{% macro sympa_string_or_list(sympa_param, sympa_param_value) -%} +{{ sympa_param }} {% if sympa_param_value is string %}{{ sympa_param_value }}{% else %}{{ sympa_param_value | join(',') }}{% endif %} +{%- endmacro %} + +# Robot configuration for {{ sympa_robot_domain }} +domain {{ sympa_robot_domain }} + +# Robot's listmaster +{% if sympa_robot.listmaster is defined %} +{{ sympa_string_or_list('listmaster', sympa_robot.listmaster) }} +{% else %} +listmaster listmaster@{{ sympa_robot_domain }} +{% endif %} {% if sympa_web_fcgi_enabled %} + # Web interface -wwsympa_url {{ sympa_web_fcgi_protocol }}://{{ item.web_domain | default(item.domain) }}/{{ sympa_web_path }} -cookie_domain {{ item.web_domain | default(item.domain) }} +wwsympa_url {{ sympa_web_fcgi_protocol }}://{{ sympa_robot.wwsympa_url | default(sympa_robot.web_domain) | default(sympa_robot_domain) }}/{{ sympa_web_path }} +cookie_domain {{ sympa_robot.wwsympa_url | default(sympa_robot.web_domain) | default(sympa_robot_domain) }} {% endif %} + +{% if sympa_robot is mapping %} +{% for robot_param,value in sympa_robot.items() %} + +{% if not robot_param in ['domain', 'listmaster', 'wwsympa_url', 'web_domain'] %} + +{% if value is mapping %} +# {{ robot_param }} value(s) +{% for sub_el,val in value.items() -%} +{{ sympa_string_or_list( robot_param + '.' + sub_el, val ) }} +{% endfor %} +{% else %} +# {{ robot_param }} value +{{ sympa_string_or_list( robot_param, value ) }} +{%- endif %} +{%- endif %} +{%- endfor %} +{%- endif %} diff --git a/templates/sympa.conf.j2 b/templates/sympa.conf.j2 index 7ffbb02..0cd8888 100644 --- a/templates/sympa.conf.j2 +++ b/templates/sympa.conf.j2 @@ -1,11 +1,13 @@ +{# Macro to accept multi-value parameters as comma separated string or yaml list #} +{% macro sympa_string_or_list(sympa_param, sympa_param_value) -%} +{{ sympa_param }} {% if sympa_param_value is string %}{{ sympa_param_value }}{% else %}{{ sympa_param_value | join(',') }}{% endif %} +{%- endmacro %} # Main domain - domain {{ sympa_domain }} # Listmaster - {% if sympa_listmaster is defined %} -listmaster {% if sympa_listmaster is string %}{{ sympa_listmaster }}{% else %}{{ sympa_listmaster | join(',') }}{% endif %} +{{ sympa_string_or_list('listmaster', sympa_listmaster) }} {% else %} listmaster listmaster@{{ sympa_domain }} {% endif %} @@ -14,6 +16,7 @@ listmaster listmaster@{{ sympa_domain }} # Web interface wwsympa_url {{ sympa_web_fcgi_protocol }}://{{ sympa_web_domain | default(sympa_domain) }}/{{ sympa_web_path }} {% if sympa_config_title is defined %} + title {{ sympa_config_title }} {% endif %} {% if sympa_config_favicon_url is defined %} @@ -98,7 +101,7 @@ create_list {{ sympa_config_create_list }} lang {{ sympa_config_lang }} {% endif %} {% if sympa_config_supported_lang is defined %} -supported_lang {% if sympa_config_supported_lang is string %}{{ sympa_config_supported_lang }}{% else %}{{ sympa_config_supported_lang | join(',') }}{% endif %} +{{ sympa_string_or_list('supported_lang', sympa_config_supported_lang ) }} {% endif %} # Email distribution settings @@ -114,28 +117,88 @@ alias_manager {{ sympa_config_alias_manager }} {% if sympa_config_aliases_program is defined %} aliases_program {{ sympa_config_aliases_program }} {% endif %} +{% if sympa_config_aliases_db_type is defined %} +aliases_db_type {{ sympa_config_aliases_db_type }} +{% endif %} {% if sympa_config_aliases_wrapper is defined %} + aliases_wrapper {{ sympa_config_aliases_wrapper }} {% endif %} +{% if sympa_config_max_size is defined %} ## Incoming email -{% if sympa_config_max_size is defined %} max_size {{ sympa_config_max_size }} {% endif %} +{% if sympa_config_merge_feature is defined %} ## Personalization -{% if sympa_config_merge_feature is defined %} merge_feature {{ sympa_config_merge_feature | bool | ternary('on', 'off') }} {% endif %} +{% if sympa_installation_method != 'package' %} # Archiving -{% if sympa_installation_method != 'package' %} mhonarc /usr/local/bin/mhonarc {% endif %} process_archive {{ sympa_config_process_archive | bool | ternary('on', 'off') }} + +## DKIM +# adapted from stuvusIT/sympa repository on Github + +## dkim_feature +dkim_feature {{ sympa_dkim_feature | bool | ternary('on', 'off') }} + +## dkim_add_signature_to +## Which service messages to be signed +## Inserts a DKIM signature to service messages in context of robot, list or both +{% if sympa_dkim_add_signature_to is defined %} +{{ sympa_string_or_list('dkim_add_signature_to', sympa_dkim_add_signature_to ) }} +{% endif %} + +{% if sympa_dkim_signature_apply_on | default([]) | length > 0 %} +## dkim_signature_apply_on +## Type of message that is added a DKIM signature before distribution to +## subscribers. Possible values are "none", "any" or a list of the following +## keywords: "md5_authenticated_messages", "smime_authenticated_messages", +## "dkim_authenticated_messages", "editor_validated_messages". +{{ sympa_string_or_list('dkim_signature_apply_on', sympa_dkim_signature_apply_on ) }} +{% else %} +# dkim_signature_apply_on defaults is: md5_authenticated_messages,smime_authenticated_messages,dkim_authenticated_messages,editor_validated_messages +{% endif %} + +## dkim_parameters.private_key_path +{% if sympa_dkim_private_key_path is defined %} +## Location of the file where DKIM private key is stored +dkim_parameters.private_key_path {{ sympa_dkim_private_key_path }} +{% endif %} +{% if sympa_dkim_signer_domain is defined %} + +## dkim_parameters.signer_domain +## The "d=" tag as defined in rfc 4871, default is virtual host domain name +dkim_parameters.signer_domain {{ sympa_dkim_signer_domain }} +{% endif %} +{% if sympa_dkim_selector is defined %} + +## dkim_parameters.selector +dkim_parameters.selector {{ sympa_dkim_selector }} +{% endif %} {% if sympa_config_extra_parameters is defined %} # Extra parameters -{{ sympa_config_extra_parameters }} +{% if sympa_config_extra_parameters is mapping %} +{% for sympa_param,value in sympa_config_extra_parameters.items() %} + +{% if value is mapping %} +# {{ sympa_param }} value(s) +{% for sub_el,val in value.items() -%} +{{ sympa_string_or_list( sympa_param + '.' + sub_el, val ) }} +{% endfor %} +{% else %} +# {{ sympa_param }} value +{{ sympa_string_or_list( sympa_param, value ) }} +{%- endif %} +{%- endfor %} +{% else %}{# then sympa_config_extra_parameters is a multiline string - for backwards compatibility #} +{{ sympa_config_extra_parameters }} +{% endif %} {% endif %} diff --git a/templates/topics.conf.j2 b/templates/topics.conf.j2 index 6aca137..8ceef78 100644 --- a/templates/topics.conf.j2 +++ b/templates/topics.conf.j2 @@ -1,4 +1,4 @@ -{% for topic in sympa_topics %} +{% for topic in sympa_topics | default([]) %} {{ topic.name }} title {{ topic.title | default(topic.name ) }}