diff --git a/roles/backuppc/tasks/main.yml b/roles/backuppc/tasks/main.yml index e4c4f20..bfb5dc3 100644 --- a/roles/backuppc/tasks/main.yml +++ b/roles/backuppc/tasks/main.yml @@ -43,6 +43,11 @@ ssh_key_bits: 4096 tags: bpc -- name: Start and enable the service - service: name=backuppc state=started enabled={{ bpc_enabled }} +- name: Start the service + service: name=backuppc state=started + when: bpc_enabled + tags: bpc + +- name: Handle backuppc service status + service: name=backuppc enabled={{ bpc_enabled }} tags: bpc diff --git a/roles/includes/webapps_set_install_mode.yml b/roles/includes/webapps_set_install_mode.yml index 2727622..578a9d6 100644 --- a/roles/includes/webapps_set_install_mode.yml +++ b/roles/includes/webapps_set_install_mode.yml @@ -4,24 +4,24 @@ - set_fact: current_version='' - name: Check if app is installed - stat: path={{ root_dir }}/meta/ansible_version - register: version_file + stat: path={{ root_dir }}/meta/{{ version_file | default('ansible_version') }} + register: version_file_stat - name: Check installed version - slurp: src={{ root_dir }}/meta/ansible_version + slurp: src={{ root_dir }}/meta/{{ version_file | default('ansible_version') }} register: current_version - when: version_file.stat.exists + when: version_file_stat.stat.exists - set_fact: current_version={{ current_version.content | b64decode | trim }} - when: version_file.stat.exists + when: version_file_stat.stat.exists - name: Set install mode to install set_fact: install_mode='install' - when: not version_file.stat.exists + when: not version_file_stat.stat.exists - name: Set install mode to upgrade set_fact: install_mode='upgrade' when: - - version_file.stat.exists + - version_file_stat.stat.exists - current_version | string != version | string - manage_upgrade | default(True) diff --git a/roles/mailman/defaults/main.yml b/roles/mailman/defaults/main.yml new file mode 100644 index 0000000..a1e530c --- /dev/null +++ b/roles/mailman/defaults/main.yml @@ -0,0 +1,58 @@ +--- + +# Version to install +mailman_version: + core: 3.3.2 + postorius: 1.3.3 + hyperkitty: 1.3.3 +mailman_root_dir: /opt/mailman +mailman_user: mailman +# Should ansible handle upgrades ? If False, only initale inistall +mailman_manage_upgrade: True + +# Can be mysql or postgres +mailman_db_engine: mysql +mailman_db_server: "{{ (mailman_db_engine == 'postgres') | ternary(pg_server,mysql_server) | default('localhost') }}" +mailman_db_port: "{{ (mailman_db_engine == 'postgres') | ternary('5432','3306') }}" +mailman_db_user: mailman +# A random one will be generated if not set +# mailman_db_pass: S3cR3t. +# Two databases are used, one for the core service, another for the web interface +mailman_db_name: + core: mailman + web: mailmanweb + +# Email address of the admin +mailman_site_owner: "{{ system_admin_email | default('admin' + ansible_domain) }}" +mailman_public_url: https://listes.{{ ansible_domain }}/ +# Django secret key. A random one will be generated if not set +#mailman_secret_key: 'p@ssW0rd' + +# Port on which uwsgi will listen +mailman_web_port: 8012 +# IP addresses allowed to access uwsgi port +mailman_web_src_ip: [] + +# Port on which mailiman will listen for LMTP connexions +mailman_lmtp_port: 8024 +# IP/CIDR allowed to access the LMTP service +mailman_lmtp_src_ip: [] + +# Port on which the core service will bind to expose the REST API +# this port will not be exposed, it's limited to localhost and used by the web interface +mailman_rest_port: 8013 +# A password to protect the REST API. The username is mailmanapi +# A random one will be created if not defined here +#mailman_rest_pass: F00/b4r\B4Z + +# Default FROM email +mailman_email_from: mailman-no-reply@{{ ansible_domain }} +mailman_smtp_server: localhost +mailman_smtp_port: 25 +mailman_smtp_tls: False +#mailman_smtp_user: +#mailman_smtp_pass: + +mailman_default_lang: fr + + diff --git a/roles/mailman/handlers/main.yml b/roles/mailman/handlers/main.yml new file mode 100644 index 0000000..fa8653e --- /dev/null +++ b/roles/mailman/handlers/main.yml @@ -0,0 +1,13 @@ +--- + +- name: restart mailman + service: name={{ item }} state=restarted + loop: + - mailman-core + - mailman-web + +- name: restart mailman timers + systemd: name={{ item }}.timer state=restarted + loop: + - mailman-digest + - mailman-notify diff --git a/roles/mailman/meta/main.yml b/roles/mailman/meta/main.yml new file mode 100644 index 0000000..c790a34 --- /dev/null +++ b/roles/mailman/meta/main.yml @@ -0,0 +1,11 @@ +--- + +dependencies: + - role: postgresql_server + when: + - mailman_db_engine == 'postgres' + - mailman_db_server in ['127.0.0.1','localhost'] + - role: mysql_server + when: + - mailman_db_engine == 'mysql' + - mailman_db_server in ['127.0.0.1','localhost'] diff --git a/roles/mailman/tasks/archive_post.yml b/roles/mailman/tasks/archive_post.yml new file mode 100644 index 0000000..29babcd --- /dev/null +++ b/roles/mailman/tasks/archive_post.yml @@ -0,0 +1,10 @@ +--- + +- name: Compress previous version + command: tar cf {{ mailman_archive_dir }}.tar.zst --use-compress-program=zstd ./ + environment: + ZST_CLEVEL: 10 + args: + chdir: "{{ mailman_archive_dir }}" + warn: False + tags: mailman diff --git a/roles/mailman/tasks/archive_pre.yml b/roles/mailman/tasks/archive_pre.yml new file mode 100644 index 0000000..c1940f9 --- /dev/null +++ b/roles/mailman/tasks/archive_pre.yml @@ -0,0 +1,49 @@ +--- + +- name: Create the archive dir + file: + path: "{{ mailman_archive_dir }}" + state: directory + tags: mailman + +- name: Archive previous version + synchronize: + src: "{{ mailman_root_dir }}/{{ item }}" + dest: "{{ mailman_archive_dir }}/" + recursive: True + delete: True + loop: + - venv + - data + delegate_to: "{{ inventory_hostname }}" + tags: mailman + +- name: Dump the database + command: > + /usr/pgsql-13/bin/pg_dump + --clean + --host={{ mailman_db_server | quote }} + --port={{ mailman_db_port | quote }} + --username=sqladmin {{ mailman_db_name | quote }} + --file="{{ mailman_archive_dir }}/{{ mailman_db_name[item] }}.sql" + loop: "{{ mailman_db_name.keys() | list }}" + environment: + - PGPASSWORD: "{{ pg_admin_pass }}" + when: mailman_db_engine == 'postgres' + tags: mailman + +- name: Dump the database + mysql_db: + state: dump + name: "{{ item }}" + target: "{{ mailman_archive_dir }}/{{ mailman_db_name[item] }}.sql.xz" + login_host: "{{ mailman_db_server }}" + login_port: "{{ mailman_db_port }}" + login_user: "{{ mailman_db_user }}" + login_password: "{{ mailman_db_pass }}" + quick: True + single_transaction: True + environment: + XZ_OPT: -T0 + when: mailman_db_engine == 'mysql' + tags: mailman diff --git a/roles/mailman/tasks/cleanup.yml b/roles/mailman/tasks/cleanup.yml new file mode 100644 index 0000000..f28ea04 --- /dev/null +++ b/roles/mailman/tasks/cleanup.yml @@ -0,0 +1,7 @@ +--- + +- name: Remove uneeded files + file: path={{ item }} state=absent + loop: + - "{{ mailman_archive_dir }}" + tags: mailman diff --git a/roles/mailman/tasks/conf.yml b/roles/mailman/tasks/conf.yml new file mode 100644 index 0000000..e8a103f --- /dev/null +++ b/roles/mailman/tasks/conf.yml @@ -0,0 +1,40 @@ +--- + +- name: Deploy configuration + template: src={{ item }}.j2 dest={{ mailman_root_dir }}/etc/{{ item }} group={{ mailman_user }} mode=640 + loop: + - mailman.cfg + - settings.py + - uwsgi.ini + notify: + - restart mailman + tags: mailman + +- block: + - name: Migrate web database + command: "{{ mailman_root_dir }}/venv/bin/mailman-web migrate" + + - name: Collect static assets + shell: echo yes | {{ mailman_root_dir }}/venv/bin/mailman-web collectstatic + + - name: Compress assets + command: "{{ mailman_root_dir }}/venv/bin/mailman-web compress" + when: mailman_install_mode == 'upgrade' or 'install' in [mailman_postorius_install_mode,mailman_hyperkitty_install_mode] + environment: + - MAILMAN_WEB_CONFIG: "{{ mailman_root_dir }}/etc/settings.py" + become_user: "{{ mailman_user }}" + tags: mailman + +- name: Create an initial superuser + django_manage: + command: createsuperuser --noinput --username admin --email {{ mailman_site_owner }} + app_path: "{{ mailman_root_dir }}/venv/bin" + virtualenv: "{{ mailman_root_dir }}/venv" + environment: + DJANGO_SUPERUSER_PASSWORD: "{{ mailman_admin_pass }}" + register: mailman_admin_user + failed_when: + - mailman_admin_user.rc != 0 + - mailman_admin_user.stdout is not search('That username is already taken') + when: mailman_install_mode != 'none' + tags: mailman diff --git a/roles/mailman/tasks/directories.yml b/roles/mailman/tasks/directories.yml new file mode 100644 index 0000000..cdaec0f --- /dev/null +++ b/roles/mailman/tasks/directories.yml @@ -0,0 +1,48 @@ +--- + +- name: Create directories + file: + path: "{{ mailman_root_dir }}/{{ item.dir }}" + state: directory + owner: "{{ item.owner | default(omit) }}" + group: "{{ item.group | default(omit) }}" + mode: "{{ item.mode | default(omit) }}" + loop: + - dir: venv + - dir: archive + mode: 700 + - dir: meta + mode: 700 + - dir: etc + group: "{{ mailman_user }}" + mode: 770 + - dir: data + owner: "{{ mailman_user }}" + group: "{{ mailman_user }}" + mode: 700 + - dir: data/fulltext_index + owner: "{{ mailman_user }}" + group: "{{ mailman_user }}" + mode: 700 + - dir: tmp + owner: "{{ mailman_user }}" + group: "{{ mailman_user }}" + mode: 700 + - dir: log + owner: "{{ mailman_user }}" + group: "{{ mailman_user }}" + mode: 700 + - dir: web/static + group: "{{ mailman_user }}" + mode: 775 + - dir: backup + mode: 700 + tags: mailman + +# mailman-web pretend to support specifying the conf dir +# with the MAILMAN_WEB_CONFIG env var, but it's not, the script +# exits with status code 1 if /etc/mailman3/settings.py doesn't exist +- name: Link config dir + file: src={{ mailman_root_dir }}/etc dest=/etc/mailman3 state=link + tags: mailman + diff --git a/roles/mailman/tasks/facts.yml b/roles/mailman/tasks/facts.yml new file mode 100644 index 0000000..7548726 --- /dev/null +++ b/roles/mailman/tasks/facts.yml @@ -0,0 +1,85 @@ +--- + +- import_tasks: ../includes/webapps_set_install_mode.yml + vars: + - root_dir: "{{ mailman_root_dir }}" + - version: "{{ mailman_version.core }}" + - version_file: 'ansible_core_version' + tags: mailman + +- block: + - set_fact: mailman_core_install_mode={{ (mailman_manage_upgrade and install_mode == 'upgrade') | ternary('none',install_mode) }} + - set_fact: mailman_core_current_version={{ current_version | default('') }} + tags: mailman + +- import_tasks: ../includes/webapps_set_install_mode.yml + vars: + - root_dir: "{{ mailman_root_dir }}" + - version: "{{ mailman_version.postorius }}" + - version_file: 'ansible_postorius_version' + tags: mailman + +- block: + - set_fact: mailman_postorius_install_mode={{ (mailman_manage_upgrade and install_mode == 'upgrade') | ternary('none',install_mode) }} + - set_fact: mailman_postorius_current_version={{ current_version | default('') }} + tags: mailman + +- import_tasks: ../includes/webapps_set_install_mode.yml + vars: + - root_dir: "{{ mailman_root_dir }}" + - version: "{{ mailman_version.hyperkitty }}" + - version_file: 'ansible_hyperkitty_version' + tags: mailman + +- block: + - set_fact: mailman_hyperkitty_install_mode={{ (mailman_manage_upgrade and install_mode == 'upgrade') | ternary('none',install_mode) }} + - set_fact: mailman_hyperkitty_current_version={{ current_version | default('') }} + tags: mailman + +- set_fact: mailman_install_mode='none' + tags: mailman +- set_fact: mailman_install_mode='upgrade' + when: + - "'upgrade' in [mailman_core_install_mode,mailman_postorius_install_mode,mailman_hyperkitty_install_mode]" + - "'install' not in [mailman_core_install_mode,mailman_postorius_install_mode,mailman_hyperkitty_install_mode]" + tags: mailman + +# Create a random pass for the DB if needed +- block: + - import_tasks: ../includes/get_rand_pass.yml + vars: + - pass_file: "{{ mailman_root_dir }}/meta/ansible_dbpass" + - complex: False + - set_fact: mailman_db_pass={{ rand_pass }} + when: mailman_db_pass is not defined + tags: mailman + +# Random secret key +- block: + - import_tasks: ../includes/get_rand_pass.yml + vars: + - pass_file: "{{ mailman_root_dir }}/meta/ansible_secret_key" + - set_fact: mailman_secret_key={{ rand_pass }} + when: mailman_secret_key is not defined + tags: mailman + +# Random API password +- block: + - import_tasks: ../includes/get_rand_pass.yml + vars: + - pass_file: "{{ mailman_root_dir }}/meta/ansible_rest_pass" + - set_fact: mailman_rest_pass={{ rand_pass }} + when: mailman_rest_pass is not defined + tags: mailman + +# Random password for the admin account +- block: + - import_tasks: ../includes/get_rand_pass.yml + vars: + - pass_file: "{{ mailman_root_dir }}/meta/ansible_admin_pass" + - set_fact: mailman_admin_pass={{ rand_pass }} + tags: mailman + +# Shortcut for the archive dir, used to backup current install during upgades +- set_fact: mailman_archive_dir={{ mailman_root_dir }}/archives/core_{{ mailman_core_current_version }}_postorius_{{ mailman_postorius_current_version }}_hyperkitty_{{ mailman_hyperkitty_current_version }} + tags: mailman diff --git a/roles/mailman/tasks/install.yml b/roles/mailman/tasks/install.yml new file mode 100644 index 0000000..594718c --- /dev/null +++ b/roles/mailman/tasks/install.yml @@ -0,0 +1,169 @@ +--- + +- name: Enable python38 module + command: dnf -y module enable python38 + args: + warn: False + changed_when: False + tags: mailman + +- name: Install packages + yum: + name: + - python38-pip + - python38-devel + - git + - gcc + - sassc + tags: mailman + +- name: Wipe the venv on upgrades + file: path={{ mailman_root_dir }}/venv state=absent + when: mailman_install_mode == 'upgrade' + tags: mailman + +- name: Create the venv dir + file: path={{ mailman_root_dir }}/venv state=directory + tags: mailman + +- name: Setup the virtualenv + pip: + name: + - pip + - wheel + - mailman=={{ mailman_version.core }} + - postorius=={{ mailman_version.postorius }} + - HyperKitty=={{ mailman_version.hyperkitty }} + - mailman-web + - uwsgi + - whoosh + virtualenv: "{{ mailman_root_dir }}/venv" + virtualenv_command: /usr/bin/python3.8 -m venv + tags: mailman + +- when: mailman_db_engine == 'postgres' + block: + - name: Install Postgres support + yum: + name: + - postgresql-devel + + - name: Install postgres python support + pip: + name: + - psycopg2-binary + virtualenv: "{{ mailman_root_dir }}/venv" + virtualenv_command: /usr/bin/python3.8 -m venv + + - name: Create the PostgreSQL role + postgresql_user: + db: postgres + name: "{{ mailman_db_user }}" + password: "{{ mailman_db_pass }}" + login_host: "{{ mailman_db_server }}" + login_user: sqladmin + login_password: "{{ pg_admin_pass }}" + tags: mailman + + - name: Create the PostgreSQL databases + postgresql_db: + name: "{{ mailman_db_name[item] }}" + encoding: UTF-8 + template: template0 + owner: "{{ mailman_db_user }}" + login_host: "{{ mailman_db_server }}" + login_user: sqladmin + login_password: "{{ pg_admin_pass }}" + loop: "{{ mailman_db_name.keys() | list }}" + + tags: mailman + +- when: mailman_db_engine == 'mysql' + block: + - name: Install mysql support + yum: + name: + - mariadb-devel + + - name: Install mysql python support + pip: + name: + - mysqlclient + - pymysql + virtualenv: "{{ mailman_root_dir }}/venv" + virtualenv_command: /usr/bin/python3.8 -m venv + + - include_tasks: ../includes/webapps_create_mysql_db.yml + vars: + - db_name: "{{ mailman_db_name[mailman_db] }}" + - db_user: "{{ mailman_db_user }}" + - db_server: "{{ mailman_db_server }}" + - db_pass: "{{ mailman_db_pass }}" + - append_privs: True + loop: "{{ mailman_db_name.keys() | list }}" + loop_control: + loop_var: mailman_db + + tags: mailman + +- name: Create a manage.py link + file: src={{ mailman_root_dir }}/venv/bin/mailman-web dest={{ mailman_root_dir }}/venv/bin/manage.py state=link + tags: mailman + +- name: Deploy systemd units + template: src={{ item }}.j2 dest=/etc/systemd/system/{{ item }} + loop: + - mailman-core.service + - mailman-web.service + notify: restart mailman + register: mailman_units + tags: mailman + +- name: Deploy systemd timers + template: src={{ item }}.j2 dest=/etc/systemd/system/{{ item }} + loop: + - mailman-digest.service + - mailman-digest.timer + - mailman-notify.service + - mailman-notify.timer + notify: restart mailman timers + register: mailman_timers + tags: mailman + +- name: Reload systemd + systemd: daemon_reload=True + when: > + mailman_units.results | selectattr('changed','equalto',True) | list | length > 0 or + mailman_timers.results | selectattr('changed','equalto',True) | list | length > 0 + tags: mailman + +- name: Install con jobs + cron: + cron_file: mailman + special_time: "{{ item.schedule | default(omit) }}" + minute: "{{ item.minute | default(omit) }}" + name: "{{ item.name }}" + user: "{{ mailman_user }}" + job: "{{ mailman_root_dir }}/venv/bin/mailman-web runjobs {{ item.name }}" + loop: + - name: yearly + schedule: yearly + - name: monthly + schedule: monthly + - name: weekly + schedule: weekly + - name: daily + schedule: daily + - name: hourly + schedule: hourly + - name: minutely + - name: quarter_hourly + minute: '2,17,32,47' + tags: mailman + +- name: Install pre/post backup hooks + template: src={{ item }}-backup.sh.j2 dest=/etc/backup/{{ item }}.d/mailman.sh mode=700 + loop: + - pre + - post + tags: mailman diff --git a/roles/mailman/tasks/iptables.yml b/roles/mailman/tasks/iptables.yml new file mode 100644 index 0000000..33bdf77 --- /dev/null +++ b/roles/mailman/tasks/iptables.yml @@ -0,0 +1,15 @@ +--- + +- name: Handle mailman web port in the firewall + iptables_raw: + name: mailman_web_port + state: "{{ (mailman_web_src_ip | length > 0) | ternary('present','absent') }}" + rules: "-A INPUT -m state --state NEW -p tcp --dport {{ mailman_web_port }} -s {{ mailman_web_src_ip | join(',') }} -j ACCEPT" + tags: firewall,mailman + +- name: Handle mailman LMTP port in the firewall + iptables_raw: + name: mailman_lmtp_port + state: "{{ (mailman_lmtp_src_ip | length > 0) | ternary('present','absent') }}" + rules: "-A INPUT -m state --state NEW -p tcp --dport {{ mailman_lmtp_port }} -s {{ mailman_lmtp_src_ip | join(',') }} -j ACCEPT" + tags: firewall,mailman diff --git a/roles/mailman/tasks/main.yml b/roles/mailman/tasks/main.yml new file mode 100644 index 0000000..2151591 --- /dev/null +++ b/roles/mailman/tasks/main.yml @@ -0,0 +1,17 @@ +--- + +- include: user.yml +- include: directories.yml +- include: facts.yml +- include: archive_pre.yml + when: mailman_install_mode == 'upgrade' +- include: install.yml +- include: conf.yml +- include: iptables.yml + when: iptables_manage | default(True) +- include: archive_post.yml + when: mailman_install_mode == 'upgrade' +- include: selinux.yml +- include: services.yml +- include: write_version.yml +- include: cleanup.yml diff --git a/roles/mailman/tasks/selinux.yml b/roles/mailman/tasks/selinux.yml new file mode 100644 index 0000000..ca92d5c --- /dev/null +++ b/roles/mailman/tasks/selinux.yml @@ -0,0 +1,6 @@ +--- + +- name: Restore SELinux contexts + command: restorecon -R {{ mailman_root_dir }} + changed_when: False + tags: mailman diff --git a/roles/mailman/tasks/services.yml b/roles/mailman/tasks/services.yml new file mode 100644 index 0000000..6b43f3e --- /dev/null +++ b/roles/mailman/tasks/services.yml @@ -0,0 +1,15 @@ +--- + +- name: Start and enable services + service: name={{ item }} state=started enabled=True + loop: + - mailman-core + - mailman-web + tags: mailman + +- name: Start and enable timers + systemd: name={{ item }}.timer state=started enabled=True + loop: + - mailman-digest + - mailman-notify + tags: mailman diff --git a/roles/mailman/tasks/user.yml b/roles/mailman/tasks/user.yml new file mode 100644 index 0000000..e904ba9 --- /dev/null +++ b/roles/mailman/tasks/user.yml @@ -0,0 +1,9 @@ +--- + +- name: Create mailman user account + user: name={{ mailman_user }} home={{ mailman_root_dir }} system=True shell=/bin/bash + tags: mailman + +- name: Setup the venv for mailman user + copy: content="source {{ mailman_root_dir }}/venv/bin/activate" dest={{ mailman_root_dir }}/.bashrc owner={{ mailman_user }} group={{ mailman_user }} + tags: mailman diff --git a/roles/mailman/tasks/write_version.yml b/roles/mailman/tasks/write_version.yml new file mode 100644 index 0000000..c6da73d --- /dev/null +++ b/roles/mailman/tasks/write_version.yml @@ -0,0 +1,6 @@ +--- + +- name: Write installed version + copy: content={{ mailman_version[item] }} dest={{ mailman_root_dir }}/meta/ansible_{{ item }}_version + loop: "{{ mailman_version.keys() | list }}" + tags: mailman diff --git a/roles/mailman/templates/mailman-core.service.j2 b/roles/mailman/templates/mailman-core.service.j2 new file mode 100644 index 0000000..6a9645b --- /dev/null +++ b/roles/mailman/templates/mailman-core.service.j2 @@ -0,0 +1,24 @@ +[Unit] +Description=GNU Mailing List Manager +After=syslog.target network.target postgresql.service + +[Service] +Type=forking +PIDFile={{ mailman_root_dir }}/tmp/master.pid +Environment=MAILMAN_CONFIG_FILE={{ mailman_root_dir }}/etc/mailman.cfg +User={{ mailman_user }} +Group={{ mailman_user }} +ExecStart={{ mailman_root_dir }}/venv/bin/mailman start +ExecReload={{ mailman_root_dir }}/venv/bin/mailman restart +ExecStop={{ mailman_root_dir }}/venv/bin/mailman stop +PrivateTmp=yes +ProtectSystem=full +ProtectHome=yes +NoNewPrivileges=yes +MemoryLimit=2048M +Restart=on-failure +StartLimitInterval=0 +RestartSec=30 + +[Install] +WantedBy=multi-user.target diff --git a/roles/mailman/templates/mailman-digest.service.j2 b/roles/mailman/templates/mailman-digest.service.j2 new file mode 100644 index 0000000..0449c5e --- /dev/null +++ b/roles/mailman/templates/mailman-digest.service.j2 @@ -0,0 +1,9 @@ +[Unit] +Description=Mailman digest sender + +[Service] +Type=oneshot +PrivateTmp=yes +User={{ mailman_user }} +Group={{ mailman_user }} +ExecStart={{ mailman_root_dir }}/venv/bin/mailman digest --periodic diff --git a/roles/mailman/templates/mailman-digest.timer.j2 b/roles/mailman/templates/mailman-digest.timer.j2 new file mode 100644 index 0000000..a95cb1d --- /dev/null +++ b/roles/mailman/templates/mailman-digest.timer.j2 @@ -0,0 +1,8 @@ +[Unit] +Description=Mailman digest sender + +[Timer] +OnCalendar=daily + +[Install] +WantedBy=timers.target diff --git a/roles/mailman/templates/mailman-notify.service.j2 b/roles/mailman/templates/mailman-notify.service.j2 new file mode 100644 index 0000000..20d63b0 --- /dev/null +++ b/roles/mailman/templates/mailman-notify.service.j2 @@ -0,0 +1,9 @@ +[Unit] +Description=Mailman notifications + +[Service] +Type=oneshot +PrivateTmp=yes +User={{ mailman_user }} +Group={{ mailman_user }} +ExecStart={{ mailman_root_dir }}/venv/bin/mailman notify diff --git a/roles/mailman/templates/mailman-notify.timer.j2 b/roles/mailman/templates/mailman-notify.timer.j2 new file mode 100644 index 0000000..ae15429 --- /dev/null +++ b/roles/mailman/templates/mailman-notify.timer.j2 @@ -0,0 +1,8 @@ +[Unit] +Description=Mailman notifications + +[Timer] +OnCalendar=*-*-* 10:00:00 + +[Install] +WantedBy=timers.target diff --git a/roles/mailman/templates/mailman-web.service.j2 b/roles/mailman/templates/mailman-web.service.j2 new file mode 100644 index 0000000..8a276fb --- /dev/null +++ b/roles/mailman/templates/mailman-web.service.j2 @@ -0,0 +1,23 @@ +[Unit] +Description=GNU Mailman Web UI +After=syslog.target network.target postgresql.service mailman-core.service + +[Service] +Type=notify +NotifyAccess=all +Environment=PATH={{ mailman_root_dir }}/venv/bin:/bin:/sbin +RuntimeDirectory=uwsgi +User={{ mailman_user }} +Group={{ mailman_user }} +ExecStart={{ mailman_root_dir }}/venv/bin/uwsgi --ini {{ mailman_root_dir }}/etc/uwsgi.ini --die-on-term +PrivateTmp=yes +ProtectSystem=full +ProtectHome=yes +NoNewPrivileges=yes +MemoryLimit=1024M +Restart=on-failure +StartLimitInterval=0 +RestartSec=30 + +[Install] +WantedBy=multi-user.target diff --git a/roles/mailman/templates/mailman.cfg.j2 b/roles/mailman/templates/mailman.cfg.j2 new file mode 100644 index 0000000..4d193b9 --- /dev/null +++ b/roles/mailman/templates/mailman.cfg.j2 @@ -0,0 +1,54 @@ +[paths.ansible] +bin_dir: {{ mailman_root_dir }}/venv/bin +var_dir: {{ mailman_root_dir }}/data +queue_dir: {{ mailman_root_dir }}/data/spool +log_dir: {{ mailman_root_dir }}/log +lock_dir: {{ mailman_root_dir }}/data/locks +etc_dir: {{ mailman_root_dir }}/etc +pid_file: {{ mailman_root_dir }}/tmp/master.pid + +[logging.root] +path = /dev/stdout + +[mailman] +layout: ansible +site_owner: {{ mailman_site_owner }} +default_language: {{ mailman_default_lang }} + +[database] +{% if mailman_db_engine == 'postgres' %} +class: mailman.database.postgresql.PostgreSQLDatabase +url: postgres://{{ mailman_db_user }}:{{ mailman_db_pass | urlencode | regex_replace('/','%2F') }}@{{ mailman_db_server }}:{{ mailman_db_port }}/{{ mailman_db_name.core }} +{% elif mailman_db_engine == 'mysql' %} +class: mailman.database.mysql.MySQLDatabase +url: mysql+pymysql://{{ mailman_db_user }}:{{ mailman_db_pass | urlencode | regex_replace('/','%2F') }}@{{ mailman_db_server }}:{{ mailman_db_port }}/{{ mailman_db_name.core }}?charset=utf8&use_unicode=1 +{% endif %} + +[archiver.prototype] +enable: yes + +[shell] +history_file: $var_dir/history.py + +[mta] +verp_confirmations: yes +verp_personalized_deliveries: yes +verp_delivery_interval: 1 +incoming: mailman.mta.postfix.LMTP +outgoing: mailman.mta.deliver.deliver +lmtp_host: 127.0.0.1 +lmtp_port: {{ mailman_lmtp_port }} +smtp_host: {{ mailman_smtp_server }} +smtp_port: {{ mailman_smtp_port }} +{% if mailman_smtp_user is defined and mailman_smtp_pass is defined %} +smtp_user: {{ mailman_smtp_user }} +smtp_pass: {{ mailman_smtp_pass }} +{% endif %} +smtp_secure_mode: {{ mailman_smtp_tls | ternary('starttls','smtp') }} + +[webservice] +hostname: localhost +port: {{ mailman_rest_port }} +use_https: no +admin_user: mailmanapi +admin_pass: {{ mailman_rest_pass }} diff --git a/roles/mailman/templates/post-backup.sh.j2 b/roles/mailman/templates/post-backup.sh.j2 new file mode 100644 index 0000000..40a5631 --- /dev/null +++ b/roles/mailman/templates/post-backup.sh.j2 @@ -0,0 +1,3 @@ +#!/bin/bash -e + +rm -f {{ mailman_root_dir }}/backup/*.sql.zst diff --git a/roles/mailman/templates/pre-backup.sh.j2 b/roles/mailman/templates/pre-backup.sh.j2 new file mode 100644 index 0000000..d2a97f6 --- /dev/null +++ b/roles/mailman/templates/pre-backup.sh.j2 @@ -0,0 +1,30 @@ +#!/bin/bash -e + +{% for db in mailman_db_name.keys() | list %} +{% if mailman_db_engine == 'postgres' %} +{% if mailman_db_server not in ['localhost', '127.0.0.1'] %} +PGPASSWORD={{ mailman_db_pass | quote }} \ + /usr/pgsql-13/bin/pg_dump \ + --clear \ + --username={{ mailman_db_user | quote }} \ + --host={{ mailman_db_server | quote }} \ + --port={{ mailman_db_port }} \ + {{ mailman_db_name[db] }} | \ + zstd -c > {{ mailman_root_dir }}/backup/{{ mailman_db_name[db] }}.sql.zst +{% else %} +su - postgres -c "/usr/pgsql-13/bin/pg_dump --clear {{ mailman_db_name[db] }}" | \ +{% endif %} + zstd -c > {{ mailman_root_dir }}/backup/{{ mailman_db_name[db] }}.sql.zst +{% else %} +/usr/bin/mysqldump \ +{% if mailman_db_server not in ['localhost', '127.0.0.1'] %} + --user={{ mailman_db_user | quote }} \ + --password={{ mailman_db_pass | quote }} \ + --host={{ mailman_db_server | quote }} \ + --port={{ mailman_db_port }} \ +{% endif %} + --quick --single-transaction \ + --add-drop-table {{ mailman_db_name[db] }} | \ + zstd -c > {{ mailman_root_dir }}/backup/{{ mailman_db_name[db] }}.sql.zst +{% endif %} +{% endfor %} diff --git a/roles/mailman/templates/settings.py.j2 b/roles/mailman/templates/settings.py.j2 new file mode 100644 index 0000000..21fd6ea --- /dev/null +++ b/roles/mailman/templates/settings.py.j2 @@ -0,0 +1,77 @@ +# Mailman Web configuration file. + +from mailman_web.settings.base import * +from mailman_web.settings.mailman import * + +#: Default list of admins who receive the emails from error logging. +ADMINS = ( + ('Mailman Suite Admin', '{{ mailman_site_owner }}'), +) + +DATABASES = { + 'default': { + 'ENGINE': 'django.db.backends.{{ (mailman_db_engine == 'postgres') | ternary('postgresql_psycopg2','mysql') }}', + 'NAME': '{{ mailman_db_name.web }}', + 'USER': '{{ mailman_db_user }}', + 'PASSWORD': '{{ mailman_db_pass }}', + 'HOST': '{{ mailman_db_server }}', + 'PORT': '{{ mailman_db_port }}', + } +} + +# 'collectstatic' command will copy all the static files here. +# Alias this location from your webserver to `/static` +STATIC_ROOT = '{{ mailman_root_dir }}/web/static' + +# Make sure that this directory is created or Django will fail on start. +#LOGGING['handlers']['file']['filename'] = '{{ mailman_root_dir }}/log/mailmanweb.log' +LOGGING = { + 'version': 1, + 'disable_existing_loggers': False, + 'handlers': { + 'console': { + 'class': 'logging.StreamHandler', + }, + }, + 'root': { + 'handlers': ['console'], + 'level': 'WARNING', + } +} + +#: See https://docs.djangoproject.com/en/dev/ref/settings/#allowed-hosts +ALLOWED_HOSTS = [ + 'localhost', # Archiving API from Mailman + '{{ mailman_public_url | urlsplit("hostname") }}' +] + +#: Current Django Site being served. This is used to customize the web host +#: being used to serve the current website. For more details about Django +#: site, see: https://docs.djangoproject.com/en/dev/ref/contrib/sites/ +SITE_ID = 1 + +# Set this to a new secret value. +SECRET_KEY = '{{ mailman_secret_key }}' + +EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend' +EMAIL_HOST = '{{ mailman_smtp_server }}' +EMAIL_PORT = {{ mailman_smtp_port }} +{% if mailman_smtp_user is defined and mailman_smtp_pass is defined %} +EMAIL_HOST_USER='{{ mailman_smtp_user }}' +EMAIL_HOST_PASS='{{ mailman_smtp_pass }}' +{% endif %} +{% if mailman_smtp_tls %} +EMAIL_USE_TLS=True +{% endif %} +DEFAULT_FROM_EMAIL = '{{ mailman_email_from }}' + +HAYSTACK_CONNECTIONS = { + 'default': { + 'ENGINE': 'haystack.backends.whoosh_backend.WhooshEngine', + 'PATH': '{{ mailman_root_dir }}/data/fulltext_index', + }, +} + +MAILMAN_REST_API_URL = 'http://localhost:{{ mailman_rest_port }}' +MAILMAN_REST_API_USER = 'mailmanapi' +MAILMAN_REST_API_PASS = '{{ mailman_rest_pass }}' diff --git a/roles/mailman/templates/uwsgi.ini.j2 b/roles/mailman/templates/uwsgi.ini.j2 new file mode 100644 index 0000000..c3b1fee --- /dev/null +++ b/roles/mailman/templates/uwsgi.ini.j2 @@ -0,0 +1,28 @@ +[uwsgi] +http-socket = 0.0.0.0:{{ mailman_web_port }} +virtualenv = {{ mailman_root_dir }}/venv/ + +module=mailman_web.wsgi:application +pythonpath = /etc/mailman3/ +env = DJANGO_SETTINGS_MODULE=settings + +# Setup default number of processes and threads per process. +master = true +process = 2 +threads = 2 + +# Setup the django_q related worker processes. +attach-daemon = mailman-web qcluster + +# Setup the request log. +req-logger = file:{{ mailman_root_dir }}/log/uwsgi.log + +# Log qcluster commands seperately. +logger = qcluster syslog:mailman-web +log-route = qcluster uwsgi-daemons + +# Last log and it logs the rest of the stuff. +logger = syslog:mailman-web + +# Static assets +check-static = {{ mailman_root_dir }}/web diff --git a/roles/postfix/tasks/main.yml b/roles/postfix/tasks/main.yml index f44aa11..8a1f8be 100644 --- a/roles/postfix/tasks/main.yml +++ b/roles/postfix/tasks/main.yml @@ -6,6 +6,7 @@ - postfix - cyrus-sasl-plain when: ansible_os_family == 'RedHat' + tags: postfix - name: Install postfix apt: @@ -13,22 +14,32 @@ - postfix - libsasl2-modules when: ansible_os_family == 'Debian' + tags: postfix + +- name: Checck if mailman is installed + stat: path={{ mailman_root_dir | default('/opt/mailman') }} + register: postfix_mailman + tags: postfix - name: Deploy configuration template: src=main.cf.j2 dest=/etc/postfix/main.cf mode=644 owner=root group=root notify: restart postfix + tags: postfix - name: Deploy Relay authentication map template: src=relay_auth.j2 dest=/etc/postfix/relay_auth mode=600 owner=root group=root register: relay_auth_file + tags: postfix - name: Check if relay_auth has been hashed stat: path=/etc/postfix/relay_auth.db register: relay_auth_hashed + tags: postfix - name: Rehash postfix relay auth command: postmap /etc/postfix/relay_auth when: relay_auth_file.changed or not relay_auth_hashed.stat.exists + tags: postfix - name: Handle postfix port iptables_raw: @@ -36,8 +47,10 @@ state: "{{ (postfix_src_ip is defined and postfix_src_ip | length > 0 and postfix_networking) | ternary('present','absent') }}" rules: "-A INPUT -m state --state NEW -p tcp -m multiport --dports {{ postfix_ports | default(['25']) | join(',') }} -s {{ postfix_src_ip | join(',') }} -j ACCEPT" when: iptables_manage | default(True) + tags: postfix - name: start and enable the service - service: name=postfix state=started enabled=yes + service: name=postfix state=started enabled=True + tags: postfix ... diff --git a/roles/postfix/templates/main.cf.j2 b/roles/postfix/templates/main.cf.j2 index 1b8f419..f57fcf9 100644 --- a/roles/postfix/templates/main.cf.j2 +++ b/roles/postfix/templates/main.cf.j2 @@ -42,3 +42,11 @@ alias_database = hash:/etc/aliases debug_peer_level = 2 +{% if postfix_mailman.stat.exists %} +recipient_delimiter = + +unknown_local_recipient_reject_code = 550 +owner_request_special = no +transport_maps = hash:{{ mailman_root_dir | default('/opt/mailman') }}/data/data/postfix_lmtp +local_recipient_maps = hash:{{ mailman_root_dir | default('/opt/mailman') }}/data/data/postfix_lmtp +relay_domains = hash:{{ mailman_root_dir | default('/opt/mailman') }}/data/data/postfix_domains +{% endif %} diff --git a/roles/samba/defaults/main.yml b/roles/samba/defaults/main.yml index e8b25a5..9a1f560 100644 --- a/roles/samba/defaults/main.yml +++ b/roles/samba/defaults/main.yml @@ -19,9 +19,9 @@ samba_log_level: > 1 auth_audit:3@/var/log/samba/auth.log auth_json_audit:4@/var/log/samba/json/auth.log - dsdb_json_audit:4@/var/log/samba/json/dsdb.log - dsdb_password_json_audit:4@/var/log/samba/json/dsdb_password.log - dsdb_transaction_json_audit:4@/var/log/samba/json/dsdb_transaction.log + dsdb_json_audit:5@/var/log/samba/json/dsdb.log + dsdb_password_json_audit:5@/var/log/samba/json/dsdb_password.log + dsdb_transaction_json_audit:5@/var/log/samba/json/dsdb_transaction.log dns:3@/var/log/samba/dns.log kerberos:2@/var/log/samba/kerberos.log ldb:2@/var/log/samba/ldb.log