commit 9f5e2946f53c99d6a41b61a7f961a0909af9f3c6 Author: olli Date: Sun Jul 10 10:51:01 2022 +0200 first commit diff --git a/README.md b/README.md new file mode 100644 index 0000000..e69de29 diff --git a/mailcow.yml b/mailcow.yml new file mode 100644 index 0000000..a7d2bf0 --- /dev/null +++ b/mailcow.yml @@ -0,0 +1,325 @@ +--- + +- name: mailcow + hosts: ds9.dedyn.io jarvis.olmusic.de + 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 +