--- - name: mailcow hosts: all tasks: - name: Clone mailcow git repo become: yes git: repo: 'https://github.com/mailcow/mailcow-dockerized.git' version: "master" umask: '022' update: false dest: "/home/docker/mailcow-dockerized" - name: disable Postfix Port ansible.builtin.replace: path: /etc/postfix/master.cf regexp: '^smtp *inet' replace: '#smtp inet' backup: yes notify: - Restart postfix - name: Generate mailcow.conf file shell: ./generate_config.sh environment: MAILCOW_HOSTNAME: "mail.{{inventory_hostname}}" MAILCOW_TZ: "Europe/Berlin" args: executable: /bin/bash chdir: "/home/docker/mailcow-dockerized" creates: /home/docker/mailcow-dockerized/mailcow.conf notify: Restart mailcow - name: disable mailcow letsencrypt (done by traefik) ansible.builtin.replace: path: /home/docker/mailcow-dockerized/mailcow.conf regexp: '^SKIP_LETS_ENCRYPT=n' replace: 'SKIP_LETS_ENCRYPT=y' backup: yes notify: - Restart mailcow - name: change http port 80->9080 (needed by traefik) ansible.builtin.replace: path: /home/docker/mailcow-dockerized/mailcow.conf regexp: '^HTTP_PORT=80' replace: 'HTTP_PORT=9080' backup: yes notify: - Restart mailcow - name: change http bind to localhost (needed by traefik) ansible.builtin.replace: path: /home/docker/mailcow-dockerized/mailcow.conf regexp: '^HTTP_BIND=.*' replace: 'HTTP_BIND=192.168.41.1' backup: yes notify: - Restart mailcow - name: change http port 443->9443 (needed by traefik) ansible.builtin.replace: path: /home/docker/mailcow-dockerized/mailcow.conf regexp: '^HTTPS_PORT=443' replace: 'HTTPS_PORT=9443' backup: yes notify: - Restart mailcow - name: change httpd bind to localhost (needed by traefik) ansible.builtin.replace: path: /home/docker/mailcow-dockerized/mailcow.conf regexp: '^HTTPS_BIND=.*' replace: 'HTTPS_BIND=192.168.41.1' backup: yes notify: - Restart mailcow - name: Start/initialize mailcow ansible.builtin.shell: docker-compose up -d args: chdir: /home/docker/mailcow-dockerized creates: /home/docker/var-lib-docker/volumes/mailcowdockerized_vmail-vol-1/_data/sieve/global_sieve_after.sieve - wait_for: path: /home/docker/var-lib-docker/volumes/mailcowdockerized_vmail-vol-1/_data/sieve/global_sieve_after.sieve - name: /home/docker/mailcow-dockerized/adminpw.sh blockinfile: path: /home/docker/mailcow-dockerized/adminpw.sh create: yes mode: 0750 owner: root group: docker marker: "# {mark} ANSIBLE MANAGED BLOCK" block: | source /home/docker/mailcow-dockerized/mailcow.conf if [[ -z ${DBUSER} ]] || [[ -z ${DBPASS} ]] || [[ -z ${DBNAME} ]] then echo "Cannot find mailcow.conf, make sure this script is run from within the mailcow folder." exit 1 fi # Change password random=$(pwgen -s 32 1) password=$(docker exec -it $(docker ps -qf name=dovecot-mailcow) doveadm pw -s SSHA256 -p ${random} | tr -d '\r') docker exec -it $(docker ps -qf name=mysql-mailcow) mysql -u${DBUSER} -p${DBPASS} ${DBNAME} -e "DELETE FROM admin WHERE username='admin';" docker exec -it $(docker ps -qf name=mysql-mailcow) mysql -u${DBUSER} -p${DBPASS} ${DBNAME} -e "DELETE FROM domain_admins WHERE username='admin';" docker exec -it $(docker ps -qf name=mysql-mailcow) mysql -u${DBUSER} -p${DBPASS} ${DBNAME} -e "INSERT INTO admin (username, password, superadmin, active) VALUES ('admin', '${password}', 1, 1);" docker exec -it $(docker ps -qf name=mysql-mailcow) mysql -u${DBUSER} -p${DBPASS} ${DBNAME} -e "DELETE FROM tfa WHERE username='admin';" echo "${random}" >/home/docker/mailcow-dockerized/.adminpw chmod 400 /home/docker/mailcow-dockerized/.adminpw backup: yes validate: /bin/bash -n %s - name: /home/docker/mailcow-dockerized/adminpw.sh shebang lineinfile: path: /home/docker/mailcow-dockerized/adminpw.sh insertbefore: BOF line: "#!/bin/bash" - name: Generate mailcow admin password ansible.builtin.shell: /home/docker/mailcow-dockerized/adminpw.sh args: chdir: /home/docker/mailcow-dockerized creates: /home/docker/mailcow-dockerized/.adminpw - name: /home/docker/traefik/providers/mailcow.yml Mailcow<->Traefik provider blockinfile: path: /home/docker/traefik/providers/mailcow.yml create: yes mode: 0444 owner: root group: docker marker: "# {mark} ANSIBLE MANAGED BLOCK" block: | http: routers: mailcow: rule: "Host(`mail.{{inventory_hostname}}`)" service: mailcow entryPoints: - "https" tls: certresolver: - "letsencrypt" middlewares: secHeaders@file services: mailcow: loadBalancer: servers: - url: "http://192.168.41.1:9080" - name: /usr/local/sbin/autoupdate.d/mailcow.update blockinfile: path: /usr/local/sbin/autoupdate.d/mailcow.update mode: "0400" owner: root group: root create: yes marker: "# {mark} ANSIBLE MANAGED BLOCK" block: | # mailcow-dockerized if [ -f /home/docker/mailcow-dockerized/update.sh ] then g_echo_ok "Prüfe MailCow Update" cd /home/docker/mailcow-dockerized if ./update.sh -c then g_echo_warn "Installiere MailCow Update $(./update.sh -c)" ./update.sh --no-update-compose -f 2>&1 | sed -e "s/'/'\\\\''/g; 1s/^/'/; \$s/\$/'/" | tee $g_tmp/mailcow-update || g_echo_error "MailCow Update fehlgeschlagen $(cat $g_tmp/mailcow-update)" fi fi # take letsencrypt-certs from traefik . /home/docker/mailcow-dockerized/mailcow.conf cat /home/docker/traefik/letsencrypt/acme.json | jq -r ".letsencrypt.Certificates[] | select(.domain.main==\"$MAILCOW_HOSTNAME\") | .key" | base64 -d >/home/docker/mailcow-dockerized/data/assets/ssl/key.pem cat /home/docker/traefik/letsencrypt/acme.json | jq -r ".letsencrypt.Certificates[] | select(.domain.main==\"$MAILCOW_HOSTNAME\") | .certificate" | base64 -d >/home/docker/mailcow-dockerized/data/assets/ssl/cert.pem docker restart $(docker ps -qaf name=postfix-mailcow) docker restart $(docker ps -qaf name=dovecot-mailcow) backup: yes validate: /bin/bash -n %s - name: /usr/local/sbin/backup.d/mailcow.backup blockinfile: path: /usr/local/sbin/backup.d/mailcow.backup mode: "0400" owner: root group: root create: yes marker: "# {mark} ANSIBLE MANAGED BLOCK" block: | cd /home/docker/mailcow-dockerized mkdir -p ${BACKUPDIR}/mailcow-backup_script BACKUP_LOCATION=${BACKUPDIR}/mailcow-backup_script /home/docker/mailcow-dockerized/helper-scripts/backup_and_restore.sh backup all --delete-days 3 || g_echo_error "MailCow-Backup (mysql crypt redis) war nicht erfolgreich" backup: yes validate: /bin/bash -n %s - name: /home/docker/autoconfig.{{inventory_hostname}}/docker-compose.yml Container Configuration blockinfile: path: /home/docker/autoconfig.{{inventory_hostname}}/docker-compose.yml create: yes mode: 0440 owner: root group: docker marker: "# {mark} ANSIBLE MANAGED BLOCK" block: | version: '3.6' services: autoconfig.{{inventory_hostname}}: image: nginx:latest restart: unless-stopped volumes: - ./htdocs:/usr/share/nginx/html:ro - /etc/localtime:/etc/localtime:ro networks: - traefik labels: - traefik.enable=true # HTTPS - traefik.http.routers.autoconfig-{{ ansible_facts['hostname'] }}.rule=Host(`autoconfig.{{ ansible_facts['nodename'] }}`) || Host(`autodiscover.{{ ansible_facts['nodename'] }}`) - traefik.http.routers.autoconfig-{{ ansible_facts['hostname'] }}.entrypoints=https - traefik.http.routers.autoconfig-{{ ansible_facts['hostname'] }}.tls=true # Proxy to service-port - traefik.http.services.autoconfig-{{ ansible_facts['hostname'] }}.loadbalancer.server.port=80 - traefik.http.routers.autoconfig-{{ ansible_facts['hostname'] }}.service=autoconfig-{{ ansible_facts['hostname'] }} # cert via letsencrypt - traefik.http.routers.autoconfig-{{ ansible_facts['hostname'] }}.tls.certresolver=letsencrypt # Traefik network - traefik.docker.network=traefik # activate secHeaders@file - traefik.http.routers.autoconfig-{{ ansible_facts['hostname'] }}.middlewares=secHeaders@file networks: traefik: external: true backup: yes notify: Restart autoconfig - name: /home/docker/autoconfig.{{inventory_hostname}}/htdocs/index.html blockinfile: path: /home/docker/autoconfig.{{inventory_hostname}}/htdocs/index.html create: yes mode: 0444 owner: root group: root marker: "" block: | OK backup: yes - name: /home/docker/autoconfig.{{inventory_hostname}}/htdocs/mail/config-v1.1.xml blockinfile: path: /home/docker/autoconfig.{{inventory_hostname}}/htdocs/mail/config-v1.1.xml create: yes mode: 0444 owner: root group: root marker: "" block: | {{inventory_hostname}} {{inventory_hostname}} {{ ansible_facts['hostname'] }} mail.{{inventory_hostname}} 993 SSL %EMAILADDRESS% password-cleartext mail.{{inventory_hostname}} 465 SSL %EMAILADDRESS% password-cleartext backup: yes - name: Allow all access to tcp port 25 (smtp) community.general.ufw: rule: allow port: '25' proto: tcp - name: Allow all access to tcp port 465 (submission/tls) community.general.ufw: rule: allow port: '465' proto: tcp - name: Allow all access to tcp port 587 (submission) community.general.ufw: rule: allow port: '587' proto: tcp - name: Allow all access to tcp port 993 (imaps) community.general.ufw: rule: allow port: '993' proto: tcp handlers: - name: Restart mailcow ansible.builtin.shell: docker-compose up -d args: chdir: /home/docker/mailcow-dockerized - name: Restart autoconfig ansible.builtin.shell: docker-compose up -d args: chdir: /home/docker/autoconfig.{{inventory_hostname}} - name: Restart postfix service: name: postfix state: restarted