Mailcow: Move to Nix and Docker-Compose.
diff --git a/mailcow/mailcow.yaml b/mailcow/mailcow.yaml
new file mode 100644
index 0000000..a403259
--- /dev/null
+++ b/mailcow/mailcow.yaml
@@ -0,0 +1,475 @@
+---
+apiVersion: extensions/v1beta1
+kind: Ingress
+metadata:
+ name: mailcow
+ namespace: mulk
+ labels:
+ name: mailcow
+ k8s-app: mailcow
+ annotations:
+ kubernetes.io/ingress.class: traefik
+ traefik.ingress.kubernetes.io/preserve-host: "true"
+spec:
+ rules:
+ - host: mail.benkard.de
+ http:
+ paths:
+ - path: /
+ backend:
+ serviceName: mailcow
+ servicePort: 80
+ - host: autodiscover.benkard.de
+ http:
+ paths:
+ - path: /
+ backend:
+ serviceName: mailcow
+ servicePort: 80
+
+---
+apiVersion: v1
+kind: Service
+metadata:
+ name: mailcow-pub
+ namespace: mulk
+ labels:
+ name: mailcow-pub
+ k8s-app: mailcow
+spec:
+ selector:
+ name: mailcow
+ type: NodePort
+ externalTrafficPolicy: Local
+ ports:
+ - name: smtp-alt
+ port: 31025
+ targetPort: 25
+ protocol: TCP
+ nodePort: 31025
+---
+apiVersion: v1
+kind: Service
+metadata:
+ name: mailcow
+ namespace: mulk
+ labels:
+ name: mailcow
+ k8s-app: mailcow
+spec:
+ selector:
+ name: mailcow
+ type: ClusterIP
+ ports:
+ - name: http
+ port: 80
+ targetPort: 80
+ protocol: TCP
+
+---
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+ name: mailcow
+ namespace: mulk
+ labels:
+ name: mailcow
+ k8s-app: mailcow
+
+spec:
+ replicas: 1
+
+ strategy:
+ rollingUpdate:
+ maxSurge: 1
+ maxUnavailable: 1
+
+ selector:
+ matchLabels:
+ k8s-app: mailcow
+ name: mailcow
+
+ template:
+ metadata:
+ labels:
+ name: mailcow
+ k8s-app: mailcow
+
+ spec:
+ imagePullSecrets:
+ - name: portus-token
+
+ runtimeClassName: kata
+
+ containers:
+ - name: master
+ image: docker.benkard.de/mulk/mailcow:latest
+
+ securityContext:
+ # In a Kata container, this only gives the container full
+ # access to the guest VM rather than the host. (To ensure
+ # this, it is important to set privileged_without_host_devices
+ # = true in the [plugins.cri.containerd.runtimes.kata] section
+ # of containerd's config.toml.)
+ privileged: true
+
+ env:
+ - name: COMPOSE_HTTP_TIMEOUT
+ value: "600"
+
+ ports:
+ - name: http
+ containerPort: 80
+ - name: smtp
+ hostPort: 25
+ containerPort: 25
+ - name: pop
+ hostPort: 110
+ containerPort: 110
+ - name: imap
+ hostPort: 143
+ containerPort: 143
+ - name: smtps
+ hostPort: 465
+ containerPort: 465
+ - name: submission
+ hostPort: 587
+ containerPort: 587
+ - name: imaps
+ hostPort: 993
+ containerPort: 993
+ - name: pops
+ hostPort: 995
+ containerPort: 995
+ - name: sieve
+ hostPort: 4190
+ containerPort: 4190
+ - name: doveadm
+ hostPort: 19991
+ containerPort: 12345
+
+ volumeMounts:
+ # Configuration data.
+ - name: assets
+ subPath: ssl
+ mountPath: /mailcow-dockerized/data/assets/ssl
+ - name: config
+ mountPath: /mailcow-dockerized/data/conf
+ - name: secrets
+ subPath: mailcow.conf
+ mountPath: /mailcow-dockerized/mailcow.conf
+
+ # State.
+ - name: crypt-data
+ mountPath: /vol/crypt-data
+ - name: postfix-data
+ mountPath: /vol/postfix-data
+ - name: redis-data
+ mountPath: /vol/redis-data
+ - name: rspamd-data
+ mountPath: /vol/rspamd-data
+ - name: solr-data
+ mountPath: /vol/solr-data
+ - name: sogo-web
+ mountPath: /vol/sogo-web
+ - name: sogo-userdata-backup
+ mountPath: /vol/sogo-userdata-backup
+ - name: vmail
+ mountPath: /vol/vmail
+ - name: vmail-index
+ mountPath: /vol/vmail-index
+ - name: web-data
+ mountPath: /vol/web-data
+ #- name: docker-data
+ # subPath: vfs
+ # mountPath: /var/lib/docker/vfs
+ #- name: docker-data
+ # subPath: image
+ # mountPath: /var/lib/docker/image
+ #- name: docker-data
+ # subPath: overlay2
+ # mountPath: /var/lib/docker/overlay2
+ #- name: docker-data
+ # mountPath: /var/lib/docker
+ - name: docker-data
+ mountPath: /vol/docker-data
+
+ volumes:
+ - name: assets
+ persistentVolumeClaim:
+ claimName: mailcow-assets
+ - name: config
+ persistentVolumeClaim:
+ claimName: mailcow-config-v2
+ - name: crypt-data
+ persistentVolumeClaim:
+ claimName: mailcow-crypt
+ - name: postfix-data
+ persistentVolumeClaim:
+ claimName: mailcow-postfix
+ - name: redis-data
+ persistentVolumeClaim:
+ claimName: mailcow-redis
+ - name: rspamd-data
+ persistentVolumeClaim:
+ claimName: mailcow-rspamd
+ - name: solr-data
+ persistentVolumeClaim:
+ claimName: mailcow-solr
+ - name: sogo-web
+ persistentVolumeClaim:
+ claimName: mailcow-sogo-web
+ - name: sogo-userdata-backup
+ persistentVolumeClaim:
+ claimName: mailcow-sogo-userdata-backup
+ - name: vmail
+ persistentVolumeClaim:
+ claimName: mailcow-vmail
+ - name: vmail-index
+ persistentVolumeClaim:
+ claimName: mailcow-vmail-index
+ - name: web-data
+ persistentVolumeClaim:
+ claimName: mailcow-web
+ - name: docker-data
+ persistentVolumeClaim:
+ claimName: mailcow-docker
+ - name: secrets
+ secret:
+ secretName: mailcow-secrets
+
+---
+apiVersion: v1
+kind: PersistentVolumeClaim
+metadata:
+ name: mailcow-web
+ namespace: mulk
+ labels:
+ k8s-app: mailcow
+ annotations:
+ volume.beta.kubernetes.io/storage-provisioner: rancher.io/local-path
+ volume.kubernetes.io/selected-node: ifirn
+spec:
+ storageClassName: local-path
+ accessModes:
+ - ReadWriteOnce
+ resources:
+ requests:
+ storage: 1Gi
+---
+apiVersion: v1
+kind: PersistentVolumeClaim
+metadata:
+ name: mailcow-docker
+ namespace: mulk
+ labels:
+ k8s-app: mailcow
+ annotations:
+ volume.beta.kubernetes.io/storage-provisioner: rancher.io/local-path
+ volume.kubernetes.io/selected-node: ifirn
+spec:
+ storageClassName: local-path
+ accessModes:
+ - ReadWriteOnce
+ resources:
+ requests:
+ storage: 1Gi
+---
+apiVersion: v1
+kind: PersistentVolumeClaim
+metadata:
+ name: mailcow-assets
+ namespace: mulk
+ labels:
+ k8s-app: mailcow
+ annotations:
+ volume.beta.kubernetes.io/storage-provisioner: rancher.io/local-path
+ volume.kubernetes.io/selected-node: ifirn
+spec:
+ storageClassName: local-path
+ accessModes:
+ - ReadWriteOnce
+ resources:
+ requests:
+ storage: 1Gi
+---
+apiVersion: v1
+kind: PersistentVolumeClaim
+metadata:
+ name: mailcow-solr
+ namespace: mulk
+ labels:
+ k8s-app: mailcow
+ annotations:
+ volume.beta.kubernetes.io/storage-provisioner: rancher.io/local-path
+ volume.kubernetes.io/selected-node: ifirn
+spec:
+ storageClassName: local-path
+ accessModes:
+ - ReadWriteOnce
+ resources:
+ requests:
+ storage: 1Gi
+---
+apiVersion: v1
+kind: PersistentVolumeClaim
+metadata:
+ name: mailcow-sogo-web
+ namespace: mulk
+ labels:
+ k8s-app: mailcow
+ annotations:
+ volume.beta.kubernetes.io/storage-provisioner: rancher.io/local-path
+ volume.kubernetes.io/selected-node: ifirn
+spec:
+ storageClassName: local-path
+ accessModes:
+ - ReadWriteOnce
+ resources:
+ requests:
+ storage: 1Gi
+---
+apiVersion: v1
+kind: PersistentVolumeClaim
+metadata:
+ name: mailcow-sogo-userdata-backup
+ namespace: mulk
+ labels:
+ k8s-app: mailcow
+ annotations:
+ volume.beta.kubernetes.io/storage-provisioner: rancher.io/local-path
+ volume.kubernetes.io/selected-node: ifirn
+spec:
+ storageClassName: local-path
+ accessModes:
+ - ReadWriteOnce
+ resources:
+ requests:
+ storage: 1Gi
+---
+apiVersion: v1
+kind: PersistentVolumeClaim
+metadata:
+ name: mailcow-vmail
+ namespace: mulk
+ labels:
+ k8s-app: mailcow
+ annotations:
+ volume.beta.kubernetes.io/storage-provisioner: rancher.io/local-path
+ volume.kubernetes.io/selected-node: ifirn
+spec:
+ storageClassName: local-path
+ accessModes:
+ - ReadWriteOnce
+ resources:
+ requests:
+ storage: 1Gi
+---
+apiVersion: v1
+kind: PersistentVolumeClaim
+metadata:
+ name: mailcow-vmail-index
+ namespace: mulk
+ labels:
+ k8s-app: mailcow
+ annotations:
+ volume.beta.kubernetes.io/storage-provisioner: rancher.io/local-path
+ volume.kubernetes.io/selected-node: ifirn
+spec:
+ storageClassName: local-path
+ accessModes:
+ - ReadWriteOnce
+ resources:
+ requests:
+ storage: 1Gi
+---
+apiVersion: v1
+kind: PersistentVolumeClaim
+metadata:
+ name: mailcow-redis
+ namespace: mulk
+ labels:
+ k8s-app: mailcow
+ annotations:
+ volume.beta.kubernetes.io/storage-provisioner: rancher.io/local-path
+ volume.kubernetes.io/selected-node: ifirn
+spec:
+ storageClassName: local-path
+ accessModes:
+ - ReadWriteOnce
+ resources:
+ requests:
+ storage: 1Gi
+---
+apiVersion: v1
+kind: PersistentVolumeClaim
+metadata:
+ name: mailcow-rspamd
+ namespace: mulk
+ labels:
+ k8s-app: mailcow
+ annotations:
+ volume.beta.kubernetes.io/storage-provisioner: rancher.io/local-path
+ volume.kubernetes.io/selected-node: ifirn
+spec:
+ storageClassName: local-path
+ accessModes:
+ - ReadWriteOnce
+ resources:
+ requests:
+ storage: 1Gi
+---
+apiVersion: v1
+kind: PersistentVolumeClaim
+metadata:
+ name: mailcow-postfix
+ namespace: mulk
+ labels:
+ k8s-app: mailcow
+ annotations:
+ volume.beta.kubernetes.io/storage-provisioner: rancher.io/local-path
+ volume.kubernetes.io/selected-node: ifirn
+spec:
+ storageClassName: local-path
+ accessModes:
+ - ReadWriteOnce
+ resources:
+ requests:
+ storage: 1Gi
+---
+apiVersion: v1
+kind: PersistentVolumeClaim
+metadata:
+ name: mailcow-crypt
+ namespace: mulk
+ labels:
+ k8s-app: mailcow
+ annotations:
+ volume.beta.kubernetes.io/storage-provisioner: rancher.io/local-path
+ volume.kubernetes.io/selected-node: ifirn
+spec:
+ storageClassName: local-path
+ accessModes:
+ - ReadWriteOnce
+ resources:
+ requests:
+ storage: 1Gi
+---
+apiVersion: v1
+kind: PersistentVolumeClaim
+metadata:
+ name: mailcow-config-v2
+ namespace: mulk
+ labels:
+ k8s-app: mailcow
+ annotations:
+ volume.beta.kubernetes.io/storage-provisioner: rancher.io/local-path
+ volume.kubernetes.io/selected-node: ifirn
+spec:
+ storageClassName: local-path
+ accessModes:
+ - ReadWriteOnce
+ resources:
+ requests:
+ storage: 1Gi
+---
diff --git a/mailcow/src/mailcow-dockerized/data/Dockerfiles/dockerapi/dockerapi.py b/mailcow/src/mailcow-dockerized/data/Dockerfiles/dockerapi/dockerapi.py
index 20e9d0e..32824a2 100644
--- a/mailcow/src/mailcow-dockerized/data/Dockerfiles/dockerapi/dockerapi.py
+++ b/mailcow/src/mailcow-dockerized/data/Dockerfiles/dockerapi/dockerapi.py
@@ -227,29 +227,11 @@
# api call: container_post - post_action: exec - cmd: system - task: mysql_upgrade
def container_post__exec__system__mysql_upgrade(self, container_id):
- for container in docker_client.containers.list(filters={"id": container_id}):
- sql_return = container.exec_run(["/bin/bash", "-c", "/usr/bin/mysql_upgrade -uroot -p'" + os.environ['DBROOT'].replace("'", "'\\''") + "'\n"], user='mysql')
- if sql_return.exit_code == 0:
- matched = False
- for line in sql_return.output.decode('utf-8').split("\n"):
- if 'is already upgraded to' in line:
- matched = True
- if matched:
- return jsonify(type='success', msg='mysql_upgrade: already upgraded', text=sql_return.output.decode('utf-8'))
- else:
- container.restart()
- return jsonify(type='warning', msg='mysql_upgrade: upgrade was applied', text=sql_return.output.decode('utf-8'))
- else:
- return jsonify(type='error', msg='mysql_upgrade: error running command', text=sql_return.output.decode('utf-8'))
+ return jsonify(type='success', msg='mysql_upgrade: not touching fake MySQL', text='')
# api call: container_post - post_action: exec - cmd: system - task: mysql_tzinfo_to_sql
def container_post__exec__system__mysql_tzinfo_to_sql(self, container_id):
- for container in docker_client.containers.list(filters={"id": container_id}):
- sql_return = container.exec_run(["/bin/bash", "-c", "/usr/bin/mysql_tzinfo_to_sql /usr/share/zoneinfo | /bin/sed 's/Local time zone must be set--see zic manual page/FCTY/' | /usr/bin/mysql -uroot -p'" + os.environ['DBROOT'].replace("'", "'\\''") + "' mysql \n"], user='mysql')
- if sql_return.exit_code == 0:
- return jsonify(type='info', msg='mysql_tzinfo_to_sql: command completed successfully', text=sql_return.output.decode('utf-8'))
- else:
- return jsonify(type='error', msg='mysql_tzinfo_to_sql: error running command', text=sql_return.output.decode('utf-8'))
+ return jsonify(type='success', msg='mysql_tzinfo_to_sql: not touching fake MySQL', text='')
# api call: container_post - post_action: exec - cmd: reload - task: dovecot
def container_post__exec__reload__dovecot(self, container_id):
@@ -284,7 +266,7 @@
def container_post__exec__sieve__print(self, container_id):
if 'username' in request.json and 'script_name' in request.json:
for container in docker_client.containers.list(filters={"id": container_id}):
- cmd = ["/bin/bash", "-c", "/usr/bin/doveadm sieve get -u '" + request.json['username'].replace("'", "'\\''") + "' '" + request.json['script_name'].replace("'", "'\\''") + "'"]
+ cmd = ["/bin/bash", "-c", "/usr/bin/doveadm sieve get -u '" + request.json['username'].replace("'", "'\\''") + "' '" + request.json['script_name'].replace("'", "'\\''") + "'"]
sieve_return = container.exec_run(cmd)
return exec_run_handler('utf8_text_only', sieve_return)
diff --git a/mailcow/src/mailcow-dockerized/data/conf/postfix/main.cf b/mailcow/src/mailcow-dockerized/data/conf/postfix/main.cf
index e8da794..3e4b2b1 100644
--- a/mailcow/src/mailcow-dockerized/data/conf/postfix/main.cf
+++ b/mailcow/src/mailcow-dockerized/data/conf/postfix/main.cf
@@ -16,7 +16,7 @@
alias_maps = hash:/etc/aliases
alias_database = hash:/etc/aliases
relayhost =
-mynetworks = 127.0.0.0/8 [::ffff:127.0.0.0]/104 [::1]/128 10.0.0.0/8 172.16.0.0/12 192.168.0.0/16 [fe80::]/10 [fc00::]/7
+mynetworks = 127.0.0.0/8 [::ffff:127.0.0.0]/104 [::1]/128 172.22.1.0/24 [fe80::]/10 [fc00::]/7
mailbox_size_limit = 0
recipient_delimiter = +
inet_interfaces = all
diff --git a/mailcow/src/mailcow-dockerized/docker-compose.yml b/mailcow/src/mailcow-dockerized/docker-compose.yml
index 2c9c93a..ff1e5eb 100644
--- a/mailcow/src/mailcow-dockerized/docker-compose.yml
+++ b/mailcow/src/mailcow-dockerized/docker-compose.yml
@@ -552,7 +552,7 @@
driver: bridge
driver_opts:
com.docker.network.bridge.name: br-mailcow
- enable_ipv6: true
+ enable_ipv6: false
ipam:
driver: default
config: