mirror of
https://github.com/mailcow/mailcow-dockerized.git
synced 2025-12-19 12:51:29 +00:00
[Dovecot] use python bootstrapper to start DOVECOT container
This commit is contained in:
@@ -10,13 +10,15 @@ def main():
|
|||||||
from modules.BootstrapNginx import Bootstrap
|
from modules.BootstrapNginx import Bootstrap
|
||||||
elif container_name == "postfix-mailcow":
|
elif container_name == "postfix-mailcow":
|
||||||
from modules.BootstrapPostfix import Bootstrap
|
from modules.BootstrapPostfix import Bootstrap
|
||||||
|
elif container_name == "dovecot-mailcow":
|
||||||
|
from modules.BootstrapDovecot import Bootstrap
|
||||||
else:
|
else:
|
||||||
print(f"No bootstrap handler for container: {container_name}", file=sys.stderr)
|
print(f"No bootstrap handler for container: {container_name}", file=sys.stderr)
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
b = Bootstrap(
|
b = Bootstrap(
|
||||||
container=container_name,
|
container=container_name,
|
||||||
db_config = {
|
db_config={
|
||||||
"host": "localhost",
|
"host": "localhost",
|
||||||
"user": os.getenv("DBUSER"),
|
"user": os.getenv("DBUSER"),
|
||||||
"password": os.getenv("DBPASS"),
|
"password": os.getenv("DBPASS"),
|
||||||
@@ -25,7 +27,13 @@ def main():
|
|||||||
'connection_timeout': 2
|
'connection_timeout': 2
|
||||||
},
|
},
|
||||||
db_table="service_settings",
|
db_table="service_settings",
|
||||||
db_settings=['sogo']
|
db_settings=['sogo'],
|
||||||
|
redis_config={
|
||||||
|
"host": os.getenv("REDIS_SLAVEOF_IP") or "redis-mailcow",
|
||||||
|
"port": int(os.getenv("REDIS_SLAVEOF_PORT") or 6379),
|
||||||
|
"password": os.getenv("REDISPASS"),
|
||||||
|
"db": 0
|
||||||
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
b.bootstrap()
|
b.bootstrap()
|
||||||
|
|||||||
@@ -9,21 +9,25 @@ import time
|
|||||||
import socket
|
import socket
|
||||||
import signal
|
import signal
|
||||||
import re
|
import re
|
||||||
|
import redis
|
||||||
|
import hashlib
|
||||||
import json
|
import json
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
import mysql.connector
|
import mysql.connector
|
||||||
from jinja2 import Environment, FileSystemLoader
|
from jinja2 import Environment, FileSystemLoader
|
||||||
|
|
||||||
class BootstrapBase:
|
class BootstrapBase:
|
||||||
def __init__(self, container, db_config, db_table, db_settings):
|
def __init__(self, container, db_config, db_table, db_settings, redis_config):
|
||||||
self.container = container
|
self.container = container
|
||||||
self.db_config = db_config
|
self.db_config = db_config
|
||||||
self.db_table = db_table
|
self.db_table = db_table
|
||||||
self.db_settings = db_settings
|
self.db_settings = db_settings
|
||||||
|
self.redis_config = redis_config
|
||||||
|
|
||||||
self.env = None
|
self.env = None
|
||||||
self.env_vars = None
|
self.env_vars = None
|
||||||
self.mysql_conn = None
|
self.mysql_conn = None
|
||||||
|
self.redis_conn = None
|
||||||
|
|
||||||
def render_config(self, template_name, output_path):
|
def render_config(self, template_name, output_path):
|
||||||
"""
|
"""
|
||||||
@@ -184,16 +188,21 @@ class BootstrapBase:
|
|||||||
|
|
||||||
Args:
|
Args:
|
||||||
path (str or Path): Path to the file or directory.
|
path (str or Path): Path to the file or directory.
|
||||||
user (str): Username for new owner.
|
user (str or int): Username or UID for new owner.
|
||||||
group (str, optional): Group name; defaults to user's group if not provided.
|
group (str or int, optional): Group name or GID; defaults to user's group if not provided.
|
||||||
recursive (bool): If True and path is a directory, ownership is applied recursively.
|
recursive (bool): If True and path is a directory, ownership is applied recursively.
|
||||||
|
|
||||||
Raises:
|
Raises:
|
||||||
FileNotFoundError: If the path does not exist.
|
FileNotFoundError: If the path does not exist.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
uid = pwd.getpwnam(user).pw_uid
|
# Resolve UID
|
||||||
gid = grp.getgrnam(group or user).gr_gid
|
uid = int(user) if str(user).isdigit() else pwd.getpwnam(user).pw_uid
|
||||||
|
# Resolve GID
|
||||||
|
if group is not None:
|
||||||
|
gid = int(group) if str(group).isdigit() else grp.getgrnam(group).gr_gid
|
||||||
|
else:
|
||||||
|
gid = uid if isinstance(user, int) or str(user).isdigit() else grp.getgrnam(user).gr_gid
|
||||||
|
|
||||||
p = Path(path)
|
p = Path(path)
|
||||||
if not p.exists():
|
if not p.exists():
|
||||||
@@ -231,6 +240,67 @@ class BootstrapBase:
|
|||||||
|
|
||||||
shutil.move(str(src_path), str(dst_path))
|
shutil.move(str(src_path), str(dst_path))
|
||||||
|
|
||||||
|
def copy_file(self, src, dst, overwrite=True):
|
||||||
|
"""
|
||||||
|
Copies a file from src to dst using shutil.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
src (str or Path): Source file path.
|
||||||
|
dst (str or Path): Destination file path.
|
||||||
|
overwrite (bool): Whether to overwrite the destination if it exists.
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
FileNotFoundError: If the source file doesn't exist.
|
||||||
|
FileExistsError: If the destination exists and overwrite is False.
|
||||||
|
IOError: If the copy operation fails.
|
||||||
|
"""
|
||||||
|
|
||||||
|
src_path = Path(src)
|
||||||
|
dst_path = Path(dst)
|
||||||
|
|
||||||
|
if not src_path.is_file():
|
||||||
|
raise FileNotFoundError(f"Source file not found: {src_path}")
|
||||||
|
|
||||||
|
if dst_path.exists() and not overwrite:
|
||||||
|
raise FileExistsError(f"Destination exists: {dst_path}")
|
||||||
|
|
||||||
|
dst_path.parent.mkdir(parents=True, exist_ok=True)
|
||||||
|
|
||||||
|
shutil.copy2(src_path, dst_path)
|
||||||
|
|
||||||
|
def remove(self, path, recursive=False, wipe_contents=False):
|
||||||
|
"""
|
||||||
|
Removes a file or directory.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
path (str or Path): The file or directory path to remove.
|
||||||
|
recursive (bool): If True, directories will be removed recursively.
|
||||||
|
wipe_contents (bool): If True and path is a directory, only its contents are removed, not the dir itself.
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
FileNotFoundError: If the path does not exist.
|
||||||
|
ValueError: If a directory is passed without recursive or wipe_contents.
|
||||||
|
"""
|
||||||
|
|
||||||
|
path = Path(path)
|
||||||
|
|
||||||
|
if not path.exists():
|
||||||
|
raise FileNotFoundError(f"Cannot remove: {path} does not exist")
|
||||||
|
|
||||||
|
if wipe_contents and path.is_dir():
|
||||||
|
for child in path.iterdir():
|
||||||
|
if child.is_dir():
|
||||||
|
shutil.rmtree(child)
|
||||||
|
else:
|
||||||
|
child.unlink()
|
||||||
|
elif path.is_file():
|
||||||
|
path.unlink()
|
||||||
|
elif path.is_dir():
|
||||||
|
if recursive:
|
||||||
|
shutil.rmtree(path)
|
||||||
|
else:
|
||||||
|
raise ValueError(f"{path} is a directory. Use recursive=True or wipe_contents=True to remove it.")
|
||||||
|
|
||||||
def create_dir(self, path):
|
def create_dir(self, path):
|
||||||
"""
|
"""
|
||||||
Creates a directory if it does not exist.
|
Creates a directory if it does not exist.
|
||||||
@@ -376,6 +446,49 @@ class BootstrapBase:
|
|||||||
if self.mysql_conn and self.mysql_conn.is_connected():
|
if self.mysql_conn and self.mysql_conn.is_connected():
|
||||||
self.mysql_conn.close()
|
self.mysql_conn.close()
|
||||||
|
|
||||||
|
def connect_redis(self, retries=10, delay=2):
|
||||||
|
"""
|
||||||
|
Establishes a Redis connection and stores it in `self.redis_conn`.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
retries (int): Number of ping retries before giving up.
|
||||||
|
delay (int): Seconds between retries.
|
||||||
|
"""
|
||||||
|
|
||||||
|
client = redis.Redis(
|
||||||
|
host=self.redis_config['host'],
|
||||||
|
port=self.redis_config['port'],
|
||||||
|
password=self.redis_config['password'],
|
||||||
|
db=self.redis_config['db'],
|
||||||
|
decode_responses=True
|
||||||
|
)
|
||||||
|
|
||||||
|
for _ in range(retries):
|
||||||
|
try:
|
||||||
|
if client.ping():
|
||||||
|
self.redis_conn = client
|
||||||
|
return
|
||||||
|
except redis.RedisError as e:
|
||||||
|
print(f"Waiting for Redis... ({e})")
|
||||||
|
time.sleep(delay)
|
||||||
|
|
||||||
|
raise ConnectionError("Redis is not available after multiple attempts.")
|
||||||
|
|
||||||
|
def close_redis(self):
|
||||||
|
"""
|
||||||
|
Closes the Redis connection if it's open.
|
||||||
|
|
||||||
|
Safe to call even if Redis was never connected or already closed.
|
||||||
|
"""
|
||||||
|
|
||||||
|
if self.redis_conn:
|
||||||
|
try:
|
||||||
|
self.redis_conn.close()
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Error while closing Redis connection: {e}")
|
||||||
|
finally:
|
||||||
|
self.redis_conn = None
|
||||||
|
|
||||||
def wait_for_schema_update(self, init_file_path="init_db.inc.php", check_interval=5):
|
def wait_for_schema_update(self, init_file_path="init_db.inc.php", check_interval=5):
|
||||||
"""
|
"""
|
||||||
Waits until the current database schema version matches the expected version
|
Waits until the current database schema version matches the expected version
|
||||||
@@ -559,4 +672,7 @@ class BootstrapBase:
|
|||||||
print(e.stderr.strip())
|
print(e.stderr.strip())
|
||||||
if check:
|
if check:
|
||||||
raise
|
raise
|
||||||
return e
|
return e
|
||||||
|
|
||||||
|
def sha1_filter(self, value):
|
||||||
|
return hashlib.sha1(value.encode()).hexdigest()
|
||||||
|
|||||||
307
data/Dockerfiles/bootstrap/modules/BootstrapDovecot.py
Normal file
307
data/Dockerfiles/bootstrap/modules/BootstrapDovecot.py
Normal file
@@ -0,0 +1,307 @@
|
|||||||
|
from jinja2 import Environment, FileSystemLoader
|
||||||
|
from modules.BootstrapBase import BootstrapBase
|
||||||
|
from pathlib import Path
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import time
|
||||||
|
import pwd
|
||||||
|
import hashlib
|
||||||
|
|
||||||
|
class Bootstrap(BootstrapBase):
|
||||||
|
def bootstrap(self):
|
||||||
|
# Connect to MySQL
|
||||||
|
self.connect_mysql()
|
||||||
|
self.wait_for_schema_update()
|
||||||
|
|
||||||
|
# Connect to Redis
|
||||||
|
self.connect_redis()
|
||||||
|
self.redis_conn.set("DOVECOT_REPL_HEALTH", 1)
|
||||||
|
|
||||||
|
# Wait for DNS
|
||||||
|
self.wait_for_dns("mailcow.email")
|
||||||
|
|
||||||
|
# Create missing directories
|
||||||
|
self.create_dir("/etc/dovecot/sql/")
|
||||||
|
self.create_dir("/etc/dovecot/auth/")
|
||||||
|
self.create_dir("/var/vmail/_garbage")
|
||||||
|
self.create_dir("/var/vmail/sieve")
|
||||||
|
self.create_dir("/etc/sogo")
|
||||||
|
self.create_dir("/var/volatile")
|
||||||
|
|
||||||
|
# Setup Jinja2 Environment and load vars
|
||||||
|
self.env = Environment(
|
||||||
|
loader=FileSystemLoader('./etc/dovecot/config_templates'),
|
||||||
|
keep_trailing_newline=True,
|
||||||
|
lstrip_blocks=True,
|
||||||
|
trim_blocks=True
|
||||||
|
)
|
||||||
|
extra_vars = {
|
||||||
|
"VALID_CERT_DIRS": self.get_valid_cert_dirs(),
|
||||||
|
"RAND_USER": self.rand_pass(),
|
||||||
|
"RAND_PASS": self.rand_pass(),
|
||||||
|
"RAND_PASS2": self.rand_pass(),
|
||||||
|
"ENV_VARS": dict(os.environ)
|
||||||
|
}
|
||||||
|
self.env_vars = self.prepare_template_vars('/overwrites.json', extra_vars)
|
||||||
|
# Escape DBPASS
|
||||||
|
self.env_vars['DBPASS'] = self.env_vars['DBPASS'].replace('"', r'\"')
|
||||||
|
# Set custom filters
|
||||||
|
self.env.filters['sha1'] = self.sha1_filter
|
||||||
|
|
||||||
|
print("Set Timezone")
|
||||||
|
self.set_timezone()
|
||||||
|
|
||||||
|
print("Render config")
|
||||||
|
self.render_config("dovecot-dict-sql-quota.conf.j2", "/etc/dovecot/sql/dovecot-dict-sql-quota.conf")
|
||||||
|
self.render_config("dovecot-dict-sql-userdb.conf.j2", "/etc/dovecot/sql/dovecot-dict-sql-userdb.conf")
|
||||||
|
self.render_config("dovecot-dict-sql-sieve_before.conf.j2", "/etc/dovecot/sql/dovecot-dict-sql-sieve_before.conf")
|
||||||
|
self.render_config("dovecot-dict-sql-sieve_after.conf.j2", "/etc/dovecot/sql/dovecot-dict-sql-sieve_after.conf")
|
||||||
|
self.render_config("mail_plugins.j2", "/etc/dovecot/mail_plugins")
|
||||||
|
self.render_config("mail_plugins_imap.j2", "/etc/dovecot/mail_plugins_imap")
|
||||||
|
self.render_config("mail_plugins_lmtp.j2", "/etc/dovecot/mail_plugins_lmtp")
|
||||||
|
self.render_config("global_sieve_after.sieve.j2", "/var/vmail/sieve/global_sieve_after.sieve")
|
||||||
|
self.render_config("global_sieve_before.sieve.j2", "/var/vmail/sieve/global_sieve_before.sieve")
|
||||||
|
self.render_config("dovecot-master.passwd.j2", "/etc/dovecot/dovecot-master.passwd")
|
||||||
|
self.render_config("dovecot-master.userdb.j2", "/etc/dovecot/dovecot-master.userdb")
|
||||||
|
self.render_config("sieve.creds.j2", "/etc/sogo/sieve.creds")
|
||||||
|
self.render_config("sogo-sso.pass.j2", "/etc/phpfpm/sogo-sso.pass")
|
||||||
|
self.render_config("cron.creds.j2", "/etc/sogo/cron.creds")
|
||||||
|
self.render_config("source_env.sh.j2", "/source_env.sh")
|
||||||
|
self.render_config("maildir_gc.sh.j2", "/usr/local/bin/maildir_gc.sh")
|
||||||
|
self.render_config("dovecot.conf.j2", "/etc/dovecot/dovecot.conf")
|
||||||
|
|
||||||
|
files = [
|
||||||
|
"/etc/dovecot/mail_plugins",
|
||||||
|
"/etc/dovecot/mail_plugins_imap",
|
||||||
|
"/etc/dovecot/mail_plugins_lmtp",
|
||||||
|
"/templates/quarantine.tpl"
|
||||||
|
]
|
||||||
|
for file in files:
|
||||||
|
self.set_permissions(file, 0o644)
|
||||||
|
|
||||||
|
try:
|
||||||
|
# Migrate old sieve_after file
|
||||||
|
self.move_file("/etc/dovecot/sieve_after", "/var/vmail/sieve/global_sieve_after.sieve")
|
||||||
|
except Exception as e:
|
||||||
|
pass
|
||||||
|
try:
|
||||||
|
# Cleanup random user maildirs
|
||||||
|
self.remove("/var/vmail/mailcow.local", wipe_contents=True)
|
||||||
|
except Exception as e:
|
||||||
|
pass
|
||||||
|
try:
|
||||||
|
# Cleanup PIDs
|
||||||
|
self.remove("/tmp/quarantine_notify.pid")
|
||||||
|
except Exception as e:
|
||||||
|
pass
|
||||||
|
try:
|
||||||
|
self.remove("/var/run/dovecot/master.pid")
|
||||||
|
except Exception as e:
|
||||||
|
pass
|
||||||
|
|
||||||
|
# Check permissions of vmail/index/garbage directories.
|
||||||
|
# Do not do this every start-up, it may take a very long time. So we use a stat check here.
|
||||||
|
files = [
|
||||||
|
"/var/vmail",
|
||||||
|
"/var/vmail/_garbage",
|
||||||
|
"/var/vmail_index"
|
||||||
|
]
|
||||||
|
for file in files:
|
||||||
|
path = Path(file)
|
||||||
|
try:
|
||||||
|
stat_info = path.stat()
|
||||||
|
current_user = pwd.getpwuid(stat_info.st_uid).pw_name
|
||||||
|
|
||||||
|
if current_user != "vmail":
|
||||||
|
print(f"Ownership of {path} is {current_user}, fixing to vmail:vmail...")
|
||||||
|
self.set_owner(path, user="vmail", group="vmail", recursive=True)
|
||||||
|
else:
|
||||||
|
print(f"Ownership of {path} is already correct (vmail)")
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Error checking ownership of {path}: {e}")
|
||||||
|
|
||||||
|
# Compile sieve scripts
|
||||||
|
files = [
|
||||||
|
"/var/vmail/sieve/global_sieve_before.sieve",
|
||||||
|
"/var/vmail/sieve/global_sieve_after.sieve",
|
||||||
|
"/usr/lib/dovecot/sieve/report-spam.sieve",
|
||||||
|
"/usr/lib/dovecot/sieve/report-ham.sieve",
|
||||||
|
]
|
||||||
|
for file in files:
|
||||||
|
self.run_command(["sievec", file], check=False)
|
||||||
|
|
||||||
|
# Fix permissions
|
||||||
|
for path in Path("/etc/dovecot/sql").glob("*.conf"):
|
||||||
|
self.set_owner(path, "root", "root")
|
||||||
|
self.set_permissions(path, 0o640)
|
||||||
|
|
||||||
|
files = [
|
||||||
|
"/etc/dovecot/auth/passwd-verify.lua",
|
||||||
|
*Path("/etc/dovecot/sql").glob("dovecot-dict-sql-sieve*"),
|
||||||
|
*Path("/etc/dovecot/sql").glob("dovecot-dict-sql-quota*")
|
||||||
|
]
|
||||||
|
for file in files:
|
||||||
|
self.set_owner(file, "root", "dovecot")
|
||||||
|
|
||||||
|
self.set_permissions("/etc/dovecot/auth/passwd-verify.lua", 0o640)
|
||||||
|
|
||||||
|
for file in ["/var/vmail/sieve", "/var/volatile", "/var/vmail_index"]:
|
||||||
|
self.set_owner(file, "vmail", "vmail", recursive=True)
|
||||||
|
|
||||||
|
self.run_command(["adduser", "vmail", "tty"])
|
||||||
|
self.run_command(["chmod", "g+rw", "/dev/console"])
|
||||||
|
self.set_owner("/dev/console", "root", "tty")
|
||||||
|
files = [
|
||||||
|
"/usr/lib/dovecot/sieve/rspamd-pipe-ham",
|
||||||
|
"/usr/lib/dovecot/sieve/rspamd-pipe-spam",
|
||||||
|
"/usr/local/bin/imapsync_runner.pl",
|
||||||
|
"/usr/local/bin/imapsync",
|
||||||
|
"/usr/local/bin/trim_logs.sh",
|
||||||
|
"/usr/local/bin/sa-rules.sh",
|
||||||
|
"/usr/local/bin/clean_q_aged.sh",
|
||||||
|
"/usr/local/bin/maildir_gc.sh",
|
||||||
|
"/usr/local/sbin/stop-supervisor.sh",
|
||||||
|
"/usr/local/bin/quota_notify.py",
|
||||||
|
"/usr/local/bin/repl_health.sh",
|
||||||
|
"/usr/local/bin/optimize-fts.sh"
|
||||||
|
]
|
||||||
|
for file in files:
|
||||||
|
self.set_permissions(file, 0o755)
|
||||||
|
|
||||||
|
# Collect SA rules once now
|
||||||
|
self.run_command(["/usr/local/bin/sa-rules.sh"], check=False)
|
||||||
|
|
||||||
|
self.generate_mail_crypt_keys()
|
||||||
|
self.cleanup_imapsync_jobs()
|
||||||
|
self.generate_guid_version()
|
||||||
|
|
||||||
|
def get_valid_cert_dirs(self):
|
||||||
|
"""
|
||||||
|
Returns a mapping of domains to their certificate directory path.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
{
|
||||||
|
"example.com": "/etc/ssl/mail/example.com/",
|
||||||
|
"www.example.com": "/etc/ssl/mail/example.com/"
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
sni_map = {}
|
||||||
|
base_path = Path("/etc/ssl/mail")
|
||||||
|
if not base_path.exists():
|
||||||
|
return sni_map
|
||||||
|
|
||||||
|
for cert_dir in base_path.iterdir():
|
||||||
|
if not cert_dir.is_dir():
|
||||||
|
continue
|
||||||
|
|
||||||
|
domains_file = cert_dir / "domains"
|
||||||
|
cert_file = cert_dir / "cert.pem"
|
||||||
|
key_file = cert_dir / "key.pem"
|
||||||
|
|
||||||
|
if not (domains_file.exists() and cert_file.exists() and key_file.exists()):
|
||||||
|
continue
|
||||||
|
|
||||||
|
with open(domains_file, "r") as f:
|
||||||
|
domains = [line.strip() for line in f if line.strip()]
|
||||||
|
for domain in domains:
|
||||||
|
sni_map[domain] = str(cert_dir)
|
||||||
|
|
||||||
|
return sni_map
|
||||||
|
|
||||||
|
def generate_mail_crypt_keys(self):
|
||||||
|
"""
|
||||||
|
Ensures mail_crypt EC keypair exists. Generates if missing. Adjusts permissions.
|
||||||
|
"""
|
||||||
|
|
||||||
|
key_dir = Path("/mail_crypt")
|
||||||
|
priv_key = key_dir / "ecprivkey.pem"
|
||||||
|
pub_key = key_dir / "ecpubkey.pem"
|
||||||
|
|
||||||
|
# Generate keys if they don't exist or are empty
|
||||||
|
if not priv_key.exists() or priv_key.stat().st_size == 0 or \
|
||||||
|
not pub_key.exists() or pub_key.stat().st_size == 0:
|
||||||
|
self.run_command(
|
||||||
|
"openssl ecparam -name prime256v1 -genkey | openssl pkey -out /mail_crypt/ecprivkey.pem",
|
||||||
|
shell=True
|
||||||
|
)
|
||||||
|
self.run_command(
|
||||||
|
"openssl pkey -in /mail_crypt/ecprivkey.pem -pubout -out /mail_crypt/ecpubkey.pem",
|
||||||
|
shell=True
|
||||||
|
)
|
||||||
|
|
||||||
|
# Set ownership to UID 401 (dovecot)
|
||||||
|
self.set_owner(priv_key, user='401')
|
||||||
|
self.set_owner(pub_key, user='401')
|
||||||
|
|
||||||
|
def cleanup_imapsync_jobs(self):
|
||||||
|
"""
|
||||||
|
Cleans up stale imapsync locks and resets running status in the database.
|
||||||
|
|
||||||
|
Deletes the imapsync_busy.lock file if present and sets `is_running` to 0
|
||||||
|
in the `imapsync` table, if it exists.
|
||||||
|
|
||||||
|
Logs:
|
||||||
|
Any issues with file operations or SQL execution.
|
||||||
|
"""
|
||||||
|
|
||||||
|
lock_file = Path("/tmp/imapsync_busy.lock")
|
||||||
|
if lock_file.exists():
|
||||||
|
try:
|
||||||
|
lock_file.unlink()
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Failed to remove lock file: {e}")
|
||||||
|
|
||||||
|
try:
|
||||||
|
cursor = self.mysql_conn.cursor()
|
||||||
|
cursor.execute("SHOW TABLES LIKE 'imapsync'")
|
||||||
|
result = cursor.fetchone()
|
||||||
|
if result:
|
||||||
|
cursor.execute("UPDATE imapsync SET is_running='0'")
|
||||||
|
self.mysql_conn.commit()
|
||||||
|
cursor.close()
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Error updating imapsync table: {e}")
|
||||||
|
|
||||||
|
def generate_guid_version(self):
|
||||||
|
"""
|
||||||
|
Waits for the `versions` table to be created, then generates a GUID
|
||||||
|
based on the mail hostname and Dovecot's public key and inserts it
|
||||||
|
into the `versions` table.
|
||||||
|
|
||||||
|
If the key or hash is missing or malformed, marks it as INVALID.
|
||||||
|
"""
|
||||||
|
|
||||||
|
try:
|
||||||
|
result = self.run_command(["doveconf", "-P"], check=True)
|
||||||
|
pubkey_path = None
|
||||||
|
for line in result.stdout.splitlines():
|
||||||
|
if "mail_crypt_global_public_key" in line:
|
||||||
|
parts = line.split('<')
|
||||||
|
if len(parts) > 1:
|
||||||
|
pubkey_path = parts[1].strip()
|
||||||
|
break
|
||||||
|
|
||||||
|
if pubkey_path and Path(pubkey_path).exists():
|
||||||
|
with open(pubkey_path, "rb") as key_file:
|
||||||
|
pubkey_data = key_file.read()
|
||||||
|
|
||||||
|
hostname = self.env_vars.get("MAILCOW_HOSTNAME", "mailcow.local").encode("utf-8")
|
||||||
|
concat = hostname + pubkey_data
|
||||||
|
guid = hashlib.sha256(concat).hexdigest()
|
||||||
|
|
||||||
|
if len(guid) == 64:
|
||||||
|
version_value = guid
|
||||||
|
else:
|
||||||
|
version_value = "INVALID"
|
||||||
|
|
||||||
|
cursor = self.mysql_conn.cursor()
|
||||||
|
cursor.execute(
|
||||||
|
"REPLACE INTO versions (application, version) VALUES (%s, %s)",
|
||||||
|
("GUID", version_value)
|
||||||
|
)
|
||||||
|
self.mysql_conn.commit()
|
||||||
|
cursor.close()
|
||||||
|
else:
|
||||||
|
print("Public key not found or unreadable. GUID not generated.")
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Failed to generate or store GUID: {e}")
|
||||||
@@ -87,7 +87,7 @@ RUN addgroup -g 5000 vmail \
|
|||||||
perl-proc-processtable \
|
perl-proc-processtable \
|
||||||
perl-app-cpanminus \
|
perl-app-cpanminus \
|
||||||
procps \
|
procps \
|
||||||
python3 \
|
python3 py3-pip \
|
||||||
py3-mysqlclient \
|
py3-mysqlclient \
|
||||||
py3-html2text \
|
py3-html2text \
|
||||||
py3-jinja2 \
|
py3-jinja2 \
|
||||||
@@ -115,25 +115,34 @@ RUN addgroup -g 5000 vmail \
|
|||||||
&& chmod +x /usr/local/bin/gosu \
|
&& chmod +x /usr/local/bin/gosu \
|
||||||
&& gosu nobody true
|
&& gosu nobody true
|
||||||
|
|
||||||
COPY trim_logs.sh /usr/local/bin/trim_logs.sh
|
RUN pip install --break-system-packages \
|
||||||
COPY clean_q_aged.sh /usr/local/bin/clean_q_aged.sh
|
mysql-connector-python \
|
||||||
COPY syslog-ng.conf /etc/syslog-ng/syslog-ng.conf
|
jinja2 \
|
||||||
COPY syslog-ng-redis_slave.conf /etc/syslog-ng/syslog-ng-redis_slave.conf
|
redis
|
||||||
COPY imapsync /usr/local/bin/imapsync
|
|
||||||
COPY imapsync_runner.pl /usr/local/bin/imapsync_runner.pl
|
|
||||||
COPY report-spam.sieve /usr/lib/dovecot/sieve/report-spam.sieve
|
COPY data/Dockerfiles/bootstrap /bootstrap
|
||||||
COPY report-ham.sieve /usr/lib/dovecot/sieve/report-ham.sieve
|
COPY data/Dockerfiles/dovecot/trim_logs.sh /usr/local/bin/trim_logs.sh
|
||||||
COPY rspamd-pipe-ham /usr/lib/dovecot/sieve/rspamd-pipe-ham
|
COPY data/Dockerfiles/dovecot/clean_q_aged.sh /usr/local/bin/clean_q_aged.sh
|
||||||
COPY rspamd-pipe-spam /usr/lib/dovecot/sieve/rspamd-pipe-spam
|
COPY data/Dockerfiles/dovecot/syslog-ng.conf /etc/syslog-ng/syslog-ng.conf
|
||||||
COPY sa-rules.sh /usr/local/bin/sa-rules.sh
|
COPY data/Dockerfiles/dovecot/syslog-ng-redis_slave.conf /etc/syslog-ng/syslog-ng-redis_slave.conf
|
||||||
COPY maildir_gc.sh /usr/local/bin/maildir_gc.sh
|
COPY data/Dockerfiles/dovecot/imapsync /usr/local/bin/imapsync
|
||||||
COPY docker-entrypoint.sh /
|
COPY data/Dockerfiles/dovecot/imapsync_runner.pl /usr/local/bin/imapsync_runner.pl
|
||||||
COPY supervisord.conf /etc/supervisor/supervisord.conf
|
COPY data/Dockerfiles/dovecot/report-spam.sieve /usr/lib/dovecot/sieve/report-spam.sieve
|
||||||
COPY stop-supervisor.sh /usr/local/sbin/stop-supervisor.sh
|
COPY data/Dockerfiles/dovecot/report-ham.sieve /usr/lib/dovecot/sieve/report-ham.sieve
|
||||||
COPY quarantine_notify.py /usr/local/bin/quarantine_notify.py
|
COPY data/Dockerfiles/dovecot/rspamd-pipe-ham /usr/lib/dovecot/sieve/rspamd-pipe-ham
|
||||||
COPY quota_notify.py /usr/local/bin/quota_notify.py
|
COPY data/Dockerfiles/dovecot/rspamd-pipe-spam /usr/lib/dovecot/sieve/rspamd-pipe-spam
|
||||||
COPY repl_health.sh /usr/local/bin/repl_health.sh
|
COPY data/Dockerfiles/dovecot/sa-rules.sh /usr/local/bin/sa-rules.sh
|
||||||
COPY optimize-fts.sh /usr/local/bin/optimize-fts.sh
|
COPY data/Dockerfiles/dovecot/docker-entrypoint.sh /
|
||||||
|
COPY data/Dockerfiles/dovecot/supervisord.conf /etc/supervisor/supervisord.conf
|
||||||
|
COPY data/Dockerfiles/dovecot/stop-supervisor.sh /usr/local/sbin/stop-supervisor.sh
|
||||||
|
COPY data/Dockerfiles/dovecot/quarantine_notify.py /usr/local/bin/quarantine_notify.py
|
||||||
|
COPY data/Dockerfiles/dovecot/quota_notify.py /usr/local/bin/quota_notify.py
|
||||||
|
COPY data/Dockerfiles/dovecot/repl_health.sh /usr/local/bin/repl_health.sh
|
||||||
|
COPY data/Dockerfiles/dovecot/optimize-fts.sh /usr/local/bin/optimize-fts.sh
|
||||||
|
|
||||||
|
RUN chmod +x /docker-entrypoint.sh \
|
||||||
|
/usr/local/sbin/stop-supervisor.sh
|
||||||
|
|
||||||
|
|
||||||
ENTRYPOINT ["/docker-entrypoint.sh"]
|
|
||||||
CMD ["/usr/bin/supervisord", "-c", "/etc/supervisor/supervisord.conf"]
|
CMD ["/usr/bin/supervisord", "-c", "/etc/supervisor/supervisord.conf"]
|
||||||
|
|||||||
@@ -1,253 +1,15 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
set -e
|
|
||||||
|
|
||||||
# Wait for MySQL to warm-up
|
# Run hooks
|
||||||
while ! mariadb-admin status --ssl=false --socket=/var/run/mysqld/mysqld.sock -u${DBUSER} -p${DBPASS} --silent; do
|
for file in /hooks/*; do
|
||||||
echo "Waiting for database to come up..."
|
if [ -x "${file}" ]; then
|
||||||
sleep 2
|
echo "Running hook ${file}"
|
||||||
done
|
"${file}"
|
||||||
|
|
||||||
until dig +short mailcow.email > /dev/null; do
|
|
||||||
echo "Waiting for DNS..."
|
|
||||||
sleep 1
|
|
||||||
done
|
|
||||||
|
|
||||||
# Do not attempt to write to slave
|
|
||||||
if [[ ! -z ${REDIS_SLAVEOF_IP} ]]; then
|
|
||||||
REDIS_CMDLINE="redis-cli -h ${REDIS_SLAVEOF_IP} -p ${REDIS_SLAVEOF_PORT} -a ${REDISPASS} --no-auth-warning"
|
|
||||||
else
|
|
||||||
REDIS_CMDLINE="redis-cli -h redis -p 6379 -a ${REDISPASS} --no-auth-warning"
|
|
||||||
fi
|
|
||||||
|
|
||||||
until [[ $(${REDIS_CMDLINE} PING) == "PONG" ]]; do
|
|
||||||
echo "Waiting for Redis..."
|
|
||||||
sleep 2
|
|
||||||
done
|
|
||||||
|
|
||||||
${REDIS_CMDLINE} SET DOVECOT_REPL_HEALTH 1 > /dev/null
|
|
||||||
|
|
||||||
# Create missing directories
|
|
||||||
[[ ! -d /etc/dovecot/sql/ ]] && mkdir -p /etc/dovecot/sql/
|
|
||||||
[[ ! -d /etc/dovecot/auth/ ]] && mkdir -p /etc/dovecot/auth/
|
|
||||||
[[ ! -d /etc/dovecot/conf.d/ ]] && mkdir -p /etc/dovecot/conf.d/
|
|
||||||
[[ ! -d /var/vmail/_garbage ]] && mkdir -p /var/vmail/_garbage
|
|
||||||
[[ ! -d /var/vmail/sieve ]] && mkdir -p /var/vmail/sieve
|
|
||||||
[[ ! -d /etc/sogo ]] && mkdir -p /etc/sogo
|
|
||||||
[[ ! -d /var/volatile ]] && mkdir -p /var/volatile
|
|
||||||
|
|
||||||
# Set Dovecot sql config parameters, escape " in db password
|
|
||||||
DBPASS=$(echo ${DBPASS} | sed 's/"/\\"/g')
|
|
||||||
|
|
||||||
# Create quota dict for Dovecot
|
|
||||||
if [[ "${MASTER}" =~ ^([yY][eE][sS]|[yY])+$ ]]; then
|
|
||||||
QUOTA_TABLE=quota2
|
|
||||||
else
|
|
||||||
QUOTA_TABLE=quota2replica
|
|
||||||
fi
|
|
||||||
cat <<EOF > /etc/dovecot/sql/dovecot-dict-sql-quota.conf
|
|
||||||
# Autogenerated by mailcow
|
|
||||||
connect = "host=/var/run/mysqld/mysqld.sock dbname=${DBNAME} user=${DBUSER} password=${DBPASS}"
|
|
||||||
map {
|
|
||||||
pattern = priv/quota/storage
|
|
||||||
table = ${QUOTA_TABLE}
|
|
||||||
username_field = username
|
|
||||||
value_field = bytes
|
|
||||||
}
|
|
||||||
map {
|
|
||||||
pattern = priv/quota/messages
|
|
||||||
table = ${QUOTA_TABLE}
|
|
||||||
username_field = username
|
|
||||||
value_field = messages
|
|
||||||
}
|
|
||||||
EOF
|
|
||||||
|
|
||||||
# Create dict used for sieve pre and postfilters
|
|
||||||
cat <<EOF > /etc/dovecot/sql/dovecot-dict-sql-sieve_before.conf
|
|
||||||
# Autogenerated by mailcow
|
|
||||||
connect = "host=/var/run/mysqld/mysqld.sock dbname=${DBNAME} user=${DBUSER} password=${DBPASS}"
|
|
||||||
map {
|
|
||||||
pattern = priv/sieve/name/\$script_name
|
|
||||||
table = sieve_before
|
|
||||||
username_field = username
|
|
||||||
value_field = id
|
|
||||||
fields {
|
|
||||||
script_name = \$script_name
|
|
||||||
}
|
|
||||||
}
|
|
||||||
map {
|
|
||||||
pattern = priv/sieve/data/\$id
|
|
||||||
table = sieve_before
|
|
||||||
username_field = username
|
|
||||||
value_field = script_data
|
|
||||||
fields {
|
|
||||||
id = \$id
|
|
||||||
}
|
|
||||||
}
|
|
||||||
EOF
|
|
||||||
|
|
||||||
cat <<EOF > /etc/dovecot/sql/dovecot-dict-sql-sieve_after.conf
|
|
||||||
# Autogenerated by mailcow
|
|
||||||
connect = "host=/var/run/mysqld/mysqld.sock dbname=${DBNAME} user=${DBUSER} password=${DBPASS}"
|
|
||||||
map {
|
|
||||||
pattern = priv/sieve/name/\$script_name
|
|
||||||
table = sieve_after
|
|
||||||
username_field = username
|
|
||||||
value_field = id
|
|
||||||
fields {
|
|
||||||
script_name = \$script_name
|
|
||||||
}
|
|
||||||
}
|
|
||||||
map {
|
|
||||||
pattern = priv/sieve/data/\$id
|
|
||||||
table = sieve_after
|
|
||||||
username_field = username
|
|
||||||
value_field = script_data
|
|
||||||
fields {
|
|
||||||
id = \$id
|
|
||||||
}
|
|
||||||
}
|
|
||||||
EOF
|
|
||||||
|
|
||||||
echo -n ${ACL_ANYONE} > /etc/dovecot/acl_anyone
|
|
||||||
|
|
||||||
if [[ "${SKIP_FTS}" =~ ^([yY][eE][sS]|[yY])+$ ]]; then
|
|
||||||
echo -e "\e[33mDetecting SKIP_FTS=y... not enabling Flatcurve (FTS) then...\e[0m"
|
|
||||||
echo -n 'quota acl zlib mail_crypt mail_crypt_acl mail_log notify listescape replication lazy_expunge' > /etc/dovecot/mail_plugins
|
|
||||||
echo -n 'quota imap_quota imap_acl acl zlib imap_zlib imap_sieve mail_crypt mail_crypt_acl notify listescape replication mail_log' > /etc/dovecot/mail_plugins_imap
|
|
||||||
echo -n 'quota sieve acl zlib mail_crypt mail_crypt_acl notify listescape replication' > /etc/dovecot/mail_plugins_lmtp
|
|
||||||
else
|
|
||||||
echo -e "\e[32mDetecting SKIP_FTS=n... enabling Flatcurve (FTS)\e[0m"
|
|
||||||
echo -n 'quota acl zlib mail_crypt mail_crypt_acl mail_log notify fts fts_flatcurve listescape replication lazy_expunge' > /etc/dovecot/mail_plugins
|
|
||||||
echo -n 'quota imap_quota imap_acl acl zlib imap_zlib imap_sieve mail_crypt mail_crypt_acl notify mail_log fts fts_flatcurve listescape replication' > /etc/dovecot/mail_plugins_imap
|
|
||||||
echo -n 'quota sieve acl zlib mail_crypt mail_crypt_acl fts fts_flatcurve notify listescape replication' > /etc/dovecot/mail_plugins_lmtp
|
|
||||||
fi
|
|
||||||
chmod 644 /etc/dovecot/mail_plugins /etc/dovecot/mail_plugins_imap /etc/dovecot/mail_plugins_lmtp /templates/quarantine.tpl
|
|
||||||
|
|
||||||
cat <<EOF > /etc/dovecot/sql/dovecot-dict-sql-userdb.conf
|
|
||||||
# Autogenerated by mailcow
|
|
||||||
driver = mysql
|
|
||||||
connect = "host=/var/run/mysqld/mysqld.sock dbname=${DBNAME} user=${DBUSER} password=${DBPASS}"
|
|
||||||
user_query = SELECT CONCAT(JSON_UNQUOTE(JSON_VALUE(attributes, '$.mailbox_format')), mailbox_path_prefix, '%d/%n/${MAILDIR_SUB}:VOLATILEDIR=/var/volatile/%u:INDEX=/var/vmail_index/%u') AS mail, '%s' AS protocol, 5000 AS uid, 5000 AS gid, concat('*:bytes=', quota) AS quota_rule FROM mailbox WHERE username = '%u' AND (active = '1' OR active = '2')
|
|
||||||
iterate_query = SELECT username FROM mailbox WHERE active = '1' OR active = '2';
|
|
||||||
EOF
|
|
||||||
|
|
||||||
|
|
||||||
# Migrate old sieve_after file
|
|
||||||
[[ -f /etc/dovecot/sieve_after ]] && mv /etc/dovecot/sieve_after /etc/dovecot/global_sieve_after
|
|
||||||
# Create global sieve scripts
|
|
||||||
cat /etc/dovecot/global_sieve_after > /var/vmail/sieve/global_sieve_after.sieve
|
|
||||||
cat /etc/dovecot/global_sieve_before > /var/vmail/sieve/global_sieve_before.sieve
|
|
||||||
|
|
||||||
# Check permissions of vmail/index/garbage directories.
|
|
||||||
# Do not do this every start-up, it may take a very long time. So we use a stat check here.
|
|
||||||
if [[ $(stat -c %U /var/vmail/) != "vmail" ]] ; then chown -R vmail:vmail /var/vmail ; fi
|
|
||||||
if [[ $(stat -c %U /var/vmail/_garbage) != "vmail" ]] ; then chown -R vmail:vmail /var/vmail/_garbage ; fi
|
|
||||||
if [[ $(stat -c %U /var/vmail_index) != "vmail" ]] ; then chown -R vmail:vmail /var/vmail_index ; fi
|
|
||||||
|
|
||||||
# Cleanup random user maildirs
|
|
||||||
rm -rf /var/vmail/mailcow.local/*
|
|
||||||
# Cleanup PIDs
|
|
||||||
[[ -f /tmp/quarantine_notify.pid ]] && rm /tmp/quarantine_notify.pid
|
|
||||||
|
|
||||||
# create sni configuration
|
|
||||||
echo "" > /etc/dovecot/sni.conf
|
|
||||||
for cert_dir in /etc/ssl/mail/*/ ; do
|
|
||||||
if [[ ! -f ${cert_dir}domains ]] || [[ ! -f ${cert_dir}cert.pem ]] || [[ ! -f ${cert_dir}key.pem ]]; then
|
|
||||||
continue
|
|
||||||
fi
|
fi
|
||||||
domains=($(cat ${cert_dir}domains))
|
|
||||||
for domain in ${domains[@]}; do
|
|
||||||
echo 'local_name '${domain}' {' >> /etc/dovecot/sni.conf;
|
|
||||||
echo ' ssl_cert = <'${cert_dir}'cert.pem' >> /etc/dovecot/sni.conf;
|
|
||||||
echo ' ssl_key = <'${cert_dir}'key.pem' >> /etc/dovecot/sni.conf;
|
|
||||||
echo '}' >> /etc/dovecot/sni.conf;
|
|
||||||
done
|
|
||||||
done
|
done
|
||||||
|
|
||||||
# Create random master for SOGo sieve features
|
python3 -u /bootstrap/main.py
|
||||||
RAND_USER=$(cat /dev/urandom | tr -dc 'a-z0-9' | fold -w 16 | head -n 1)
|
BOOTSTRAP_EXIT_CODE=$?
|
||||||
RAND_PASS=$(cat /dev/urandom | tr -dc 'a-z0-9' | fold -w 24 | head -n 1)
|
|
||||||
|
|
||||||
if [[ ! -z ${DOVECOT_MASTER_USER} ]] && [[ ! -z ${DOVECOT_MASTER_PASS} ]]; then
|
|
||||||
RAND_USER=${DOVECOT_MASTER_USER}
|
|
||||||
RAND_PASS=${DOVECOT_MASTER_PASS}
|
|
||||||
fi
|
|
||||||
echo ${RAND_USER}@mailcow.local:{SHA1}$(echo -n ${RAND_PASS} | sha1sum | awk '{print $1}'):::::: > /etc/dovecot/dovecot-master.passwd
|
|
||||||
echo ${RAND_USER}@mailcow.local::5000:5000:::: > /etc/dovecot/dovecot-master.userdb
|
|
||||||
echo ${RAND_USER}@mailcow.local:${RAND_PASS} > /etc/sogo/sieve.creds
|
|
||||||
|
|
||||||
if [[ -z ${MAILDIR_SUB} ]]; then
|
|
||||||
MAILDIR_SUB_SHARED=
|
|
||||||
else
|
|
||||||
MAILDIR_SUB_SHARED=/${MAILDIR_SUB}
|
|
||||||
fi
|
|
||||||
cat <<EOF > /etc/dovecot/shared_namespace.conf
|
|
||||||
# Autogenerated by mailcow
|
|
||||||
namespace {
|
|
||||||
type = shared
|
|
||||||
separator = /
|
|
||||||
prefix = Shared/%%u/
|
|
||||||
location = maildir:%%h${MAILDIR_SUB_SHARED}:INDEX=~${MAILDIR_SUB_SHARED}/Shared/%%u
|
|
||||||
subscriptions = no
|
|
||||||
list = children
|
|
||||||
}
|
|
||||||
EOF
|
|
||||||
|
|
||||||
|
|
||||||
cat <<EOF > /etc/dovecot/sogo_trusted_ip.conf
|
|
||||||
# Autogenerated by mailcow
|
|
||||||
remote ${IPV4_NETWORK}.248 {
|
|
||||||
disable_plaintext_auth = no
|
|
||||||
}
|
|
||||||
EOF
|
|
||||||
|
|
||||||
# Create random master Password for SOGo SSO
|
|
||||||
RAND_PASS=$(cat /dev/urandom | tr -dc 'a-z0-9' | fold -w 32 | head -n 1)
|
|
||||||
echo -n ${RAND_PASS} > /etc/phpfpm/sogo-sso.pass
|
|
||||||
# Creating additional creds file for SOGo notify crons (calendars, etc)
|
|
||||||
echo -n ${RAND_USER}@mailcow.local:${RAND_PASS} > /etc/sogo/cron.creds
|
|
||||||
cat <<EOF > /etc/dovecot/sogo-sso.conf
|
|
||||||
# Autogenerated by mailcow
|
|
||||||
passdb {
|
|
||||||
driver = static
|
|
||||||
args = allow_real_nets=${IPV4_NETWORK}.248/32 password={plain}${RAND_PASS}
|
|
||||||
}
|
|
||||||
EOF
|
|
||||||
|
|
||||||
if [[ "${MASTER}" =~ ^([nN][oO]|[nN])+$ ]]; then
|
|
||||||
# Toggling MASTER will result in a rebuild of containers, so the quota script will be recreated
|
|
||||||
cat <<'EOF' > /usr/local/bin/quota_notify.py
|
|
||||||
#!/usr/bin/python3
|
|
||||||
import sys
|
|
||||||
sys.exit()
|
|
||||||
EOF
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Set mail_replica for HA setups
|
|
||||||
if [[ -n ${MAILCOW_REPLICA_IP} && -n ${DOVEADM_REPLICA_PORT} ]]; then
|
|
||||||
cat <<EOF > /etc/dovecot/mail_replica.conf
|
|
||||||
# Autogenerated by mailcow
|
|
||||||
mail_replica = tcp:${MAILCOW_REPLICA_IP}:${DOVEADM_REPLICA_PORT}
|
|
||||||
EOF
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Setting variables for indexer-worker inside fts.conf automatically according to mailcow.conf settings
|
|
||||||
if [[ "${SKIP_FTS}" =~ ^([nN][oO]|[nN])+$ ]]; then
|
|
||||||
echo -e "\e[94mConfiguring FTS Settings...\e[0m"
|
|
||||||
echo -e "\e[94mSetting FTS Memory Limit (per process) to ${FTS_HEAP} MB\e[0m"
|
|
||||||
sed -i "s/vsz_limit\s*=\s*[0-9]*\s*MB*/vsz_limit=${FTS_HEAP} MB/" /etc/dovecot/conf.d/fts.conf
|
|
||||||
echo -e "\e[94mSetting FTS Process Limit to ${FTS_PROCS}\e[0m"
|
|
||||||
sed -i "s/process_limit\s*=\s*[0-9]*/process_limit=${FTS_PROCS}/" /etc/dovecot/conf.d/fts.conf
|
|
||||||
fi
|
|
||||||
|
|
||||||
# 401 is user dovecot
|
|
||||||
if [[ ! -s /mail_crypt/ecprivkey.pem || ! -s /mail_crypt/ecpubkey.pem ]]; then
|
|
||||||
openssl ecparam -name prime256v1 -genkey | openssl pkey -out /mail_crypt/ecprivkey.pem
|
|
||||||
openssl pkey -in /mail_crypt/ecprivkey.pem -pubout -out /mail_crypt/ecpubkey.pem
|
|
||||||
chown 401 /mail_crypt/ecprivkey.pem /mail_crypt/ecpubkey.pem
|
|
||||||
else
|
|
||||||
chown 401 /mail_crypt/ecprivkey.pem /mail_crypt/ecpubkey.pem
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Fix OpenSSL 3.X TLS1.0, 1.1 support (https://community.mailcow.email/d/4062-hi-all/20)
|
# Fix OpenSSL 3.X TLS1.0, 1.1 support (https://community.mailcow.email/d/4062-hi-all/20)
|
||||||
if grep -qE 'ssl_min_protocol\s*=\s*(TLSv1|TLSv1\.1)\s*$' /etc/dovecot/dovecot.conf /etc/dovecot/extra.conf; then
|
if grep -qE 'ssl_min_protocol\s*=\s*(TLSv1|TLSv1\.1)\s*$' /etc/dovecot/dovecot.conf /etc/dovecot/extra.conf; then
|
||||||
@@ -260,89 +22,10 @@ if grep -qE 'ssl_min_protocol\s*=\s*(TLSv1|TLSv1\.1)\s*$' /etc/dovecot/dovecot.c
|
|||||||
echo "CipherString = DEFAULT@SECLEVEL=0" >> /etc/ssl/openssl.cnf
|
echo "CipherString = DEFAULT@SECLEVEL=0" >> /etc/ssl/openssl.cnf
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Compile sieve scripts
|
if [ $BOOTSTRAP_EXIT_CODE -ne 0 ]; then
|
||||||
sievec /var/vmail/sieve/global_sieve_before.sieve
|
echo "Bootstrap failed with exit code $BOOTSTRAP_EXIT_CODE. Not starting Dovecot."
|
||||||
sievec /var/vmail/sieve/global_sieve_after.sieve
|
exit $BOOTSTRAP_EXIT_CODE
|
||||||
sievec /usr/lib/dovecot/sieve/report-spam.sieve
|
|
||||||
sievec /usr/lib/dovecot/sieve/report-ham.sieve
|
|
||||||
|
|
||||||
# Fix permissions
|
|
||||||
chown root:root /etc/dovecot/sql/*.conf
|
|
||||||
chown root:dovecot /etc/dovecot/sql/dovecot-dict-sql-sieve* /etc/dovecot/sql/dovecot-dict-sql-quota* /etc/dovecot/auth/passwd-verify.lua
|
|
||||||
chmod 640 /etc/dovecot/sql/*.conf /etc/dovecot/auth/passwd-verify.lua
|
|
||||||
chown -R vmail:vmail /var/vmail/sieve
|
|
||||||
chown -R vmail:vmail /var/volatile
|
|
||||||
chown -R vmail:vmail /var/vmail_index
|
|
||||||
adduser vmail tty
|
|
||||||
chmod g+rw /dev/console
|
|
||||||
chown root:tty /dev/console
|
|
||||||
chmod +x /usr/lib/dovecot/sieve/rspamd-pipe-ham \
|
|
||||||
/usr/lib/dovecot/sieve/rspamd-pipe-spam \
|
|
||||||
/usr/local/bin/imapsync_runner.pl \
|
|
||||||
/usr/local/bin/imapsync \
|
|
||||||
/usr/local/bin/trim_logs.sh \
|
|
||||||
/usr/local/bin/sa-rules.sh \
|
|
||||||
/usr/local/bin/clean_q_aged.sh \
|
|
||||||
/usr/local/bin/maildir_gc.sh \
|
|
||||||
/usr/local/sbin/stop-supervisor.sh \
|
|
||||||
/usr/local/bin/quota_notify.py \
|
|
||||||
/usr/local/bin/repl_health.sh \
|
|
||||||
/usr/local/bin/optimize-fts.sh
|
|
||||||
|
|
||||||
# Prepare environment file for cronjobs
|
|
||||||
printenv | sed 's/^\(.*\)$/export \1/g' > /source_env.sh
|
|
||||||
|
|
||||||
# Clean old PID if any
|
|
||||||
[[ -f /var/run/dovecot/master.pid ]] && rm /var/run/dovecot/master.pid
|
|
||||||
|
|
||||||
# Clean stopped imapsync jobs
|
|
||||||
rm -f /tmp/imapsync_busy.lock
|
|
||||||
IMAPSYNC_TABLE=$(mariadb --skip-ssl --socket=/var/run/mysqld/mysqld.sock -u ${DBUSER} -p${DBPASS} ${DBNAME} -e "SHOW TABLES LIKE 'imapsync'" -Bs)
|
|
||||||
[[ ! -z ${IMAPSYNC_TABLE} ]] && mariadb --skip-ssl --socket=/var/run/mysqld/mysqld.sock -u ${DBUSER} -p${DBPASS} ${DBNAME} -e "UPDATE imapsync SET is_running='0'"
|
|
||||||
|
|
||||||
# Envsubst maildir_gc
|
|
||||||
echo "$(envsubst < /usr/local/bin/maildir_gc.sh)" > /usr/local/bin/maildir_gc.sh
|
|
||||||
|
|
||||||
# GUID generation
|
|
||||||
while [[ ${VERSIONS_OK} != 'OK' ]]; do
|
|
||||||
if [[ ! -z $(mariadb --skip-ssl --socket=/var/run/mysqld/mysqld.sock -u ${DBUSER} -p${DBPASS} ${DBNAME} -B -e "SELECT 'OK' FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA = \"${DBNAME}\" AND TABLE_NAME = 'versions'") ]]; then
|
|
||||||
VERSIONS_OK=OK
|
|
||||||
else
|
|
||||||
echo "Waiting for versions table to be created..."
|
|
||||||
sleep 3
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
PUBKEY_MCRYPT=$(doveconf -P 2> /dev/null | grep -i mail_crypt_global_public_key | cut -d '<' -f2)
|
|
||||||
if [ -f ${PUBKEY_MCRYPT} ]; then
|
|
||||||
GUID=$(cat <(echo ${MAILCOW_HOSTNAME}) /mail_crypt/ecpubkey.pem | sha256sum | cut -d ' ' -f1 | tr -cd "[a-fA-F0-9.:/] ")
|
|
||||||
if [ ${#GUID} -eq 64 ]; then
|
|
||||||
mariadb --skip-ssl --socket=/var/run/mysqld/mysqld.sock -u ${DBUSER} -p${DBPASS} ${DBNAME} << EOF
|
|
||||||
REPLACE INTO versions (application, version) VALUES ("GUID", "${GUID}");
|
|
||||||
EOF
|
|
||||||
else
|
|
||||||
mariadb --skip-ssl --socket=/var/run/mysqld/mysqld.sock -u ${DBUSER} -p${DBPASS} ${DBNAME} << EOF
|
|
||||||
REPLACE INTO versions (application, version) VALUES ("GUID", "INVALID");
|
|
||||||
EOF
|
|
||||||
fi
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Collect SA rules once now
|
echo "Bootstrap succeeded. Starting Dovecot..."
|
||||||
/usr/local/bin/sa-rules.sh
|
/usr/sbin/dovecot -F
|
||||||
|
|
||||||
# Run hooks
|
|
||||||
for file in /hooks/*; do
|
|
||||||
if [ -x "${file}" ]; then
|
|
||||||
echo "Running hook ${file}"
|
|
||||||
"${file}"
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
|
|
||||||
# For some strange, unknown and stupid reason, Dovecot may run into a race condition, when this file is not touched before it is read by dovecot/auth
|
|
||||||
# May be related to something inside Docker, I seriously don't know
|
|
||||||
touch /etc/dovecot/auth/passwd-verify.lua
|
|
||||||
|
|
||||||
if [[ ! -z ${REDIS_SLAVEOF_IP} ]]; then
|
|
||||||
cp /etc/syslog-ng/syslog-ng-redis_slave.conf /etc/syslog-ng/syslog-ng.conf
|
|
||||||
fi
|
|
||||||
|
|
||||||
exec "$@"
|
|
||||||
|
|||||||
@@ -1,2 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
[ -d /var/vmail/_garbage/ ] && /usr/bin/find /var/vmail/_garbage/ -mindepth 1 -maxdepth 1 -type d -cmin +${MAILDIR_GC_TIME} -exec rm -r {} \;
|
|
||||||
@@ -14,6 +14,11 @@ import sys
|
|||||||
import html2text
|
import html2text
|
||||||
from subprocess import Popen, PIPE, STDOUT
|
from subprocess import Popen, PIPE, STDOUT
|
||||||
|
|
||||||
|
|
||||||
|
# Don't run if role is not master
|
||||||
|
if os.getenv("MASTER").lower() in ["n", "no"]:
|
||||||
|
sys.exit()
|
||||||
|
|
||||||
if len(sys.argv) > 2:
|
if len(sys.argv) > 2:
|
||||||
percent = int(sys.argv[1])
|
percent = int(sys.argv[1])
|
||||||
username = str(sys.argv[2])
|
username = str(sys.argv[2])
|
||||||
|
|||||||
@@ -11,8 +11,8 @@ stderr_logfile=/dev/stderr
|
|||||||
stderr_logfile_maxbytes=0
|
stderr_logfile_maxbytes=0
|
||||||
autostart=true
|
autostart=true
|
||||||
|
|
||||||
[program:dovecot]
|
[program:bootstrap]
|
||||||
command=/usr/sbin/dovecot -F
|
command=/docker-entrypoint.sh
|
||||||
stdout_logfile=/dev/stdout
|
stdout_logfile=/dev/stdout
|
||||||
stdout_logfile_maxbytes=0
|
stdout_logfile_maxbytes=0
|
||||||
stderr_logfile=/dev/stderr
|
stderr_logfile=/dev/stderr
|
||||||
|
|||||||
@@ -9,7 +9,8 @@ RUN apk add --no-cache nginx \
|
|||||||
py3-pip && \
|
py3-pip && \
|
||||||
pip install --upgrade pip && \
|
pip install --upgrade pip && \
|
||||||
pip install Jinja2 \
|
pip install Jinja2 \
|
||||||
mysql-connector-python
|
mysql-connector-python \
|
||||||
|
redis
|
||||||
|
|
||||||
RUN mkdir -p /etc/nginx/includes
|
RUN mkdir -p /etc/nginx/includes
|
||||||
|
|
||||||
|
|||||||
@@ -42,7 +42,8 @@ RUN groupadd -g 102 postfix \
|
|||||||
|
|
||||||
RUN pip install --break-system-packages \
|
RUN pip install --break-system-packages \
|
||||||
mysql-connector-python \
|
mysql-connector-python \
|
||||||
jinja2
|
jinja2 \
|
||||||
|
redis
|
||||||
|
|
||||||
COPY data/Dockerfiles/bootstrap /bootstrap
|
COPY data/Dockerfiles/bootstrap /bootstrap
|
||||||
COPY data/Dockerfiles/postfix/supervisord.conf /etc/supervisor/supervisord.conf
|
COPY data/Dockerfiles/postfix/supervisord.conf /etc/supervisor/supervisord.conf
|
||||||
@@ -55,6 +56,7 @@ COPY data/Dockerfiles/postfix/stop-supervisor.sh /usr/local/sbin/stop-supervisor
|
|||||||
COPY data/Dockerfiles/postfix/docker-entrypoint.sh /docker-entrypoint.sh
|
COPY data/Dockerfiles/postfix/docker-entrypoint.sh /docker-entrypoint.sh
|
||||||
|
|
||||||
RUN chmod +x /usr/local/bin/rspamd-pipe-ham \
|
RUN chmod +x /usr/local/bin/rspamd-pipe-ham \
|
||||||
|
/docker-entrypoint.sh \
|
||||||
/usr/local/bin/rspamd-pipe-spam \
|
/usr/local/bin/rspamd-pipe-spam \
|
||||||
/usr/local/bin/whitelist_forwardinghosts.sh \
|
/usr/local/bin/whitelist_forwardinghosts.sh \
|
||||||
/usr/local/sbin/stop-supervisor.sh
|
/usr/local/sbin/stop-supervisor.sh
|
||||||
|
|||||||
@@ -45,7 +45,8 @@ RUN echo "Building from repository $SOGO_DEBIAN_REPOSITORY" \
|
|||||||
|
|
||||||
RUN pip install --break-system-packages \
|
RUN pip install --break-system-packages \
|
||||||
mysql-connector-python \
|
mysql-connector-python \
|
||||||
jinja2
|
jinja2 \
|
||||||
|
redis
|
||||||
|
|
||||||
|
|
||||||
COPY data/Dockerfiles/bootstrap /bootstrap
|
COPY data/Dockerfiles/bootstrap /bootstrap
|
||||||
|
|||||||
1
data/conf/dovecot/config_templates/cron.creds.j2
Normal file
1
data/conf/dovecot/config_templates/cron.creds.j2
Normal file
@@ -0,0 +1 @@
|
|||||||
|
{{ RAND_USER }}@mailcow.local:{{ RAND_PASS2 }}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
{% set QUOTA_TABLE = "quota2" if MASTER|lower in ["y", "yes"] else "quota2replica" %}
|
||||||
|
connect = "host=/var/run/mysqld/mysqld.sock dbname={{ DBNAME }} user={{ DBUSER }} password={{ DBPASS }}"
|
||||||
|
map {
|
||||||
|
pattern = priv/quota/storage
|
||||||
|
table = {{ QUOTA_TABLE }}
|
||||||
|
username_field = username
|
||||||
|
value_field = bytes
|
||||||
|
}
|
||||||
|
map {
|
||||||
|
pattern = priv/quota/messages
|
||||||
|
table = {{ QUOTA_TABLE }}
|
||||||
|
username_field = username
|
||||||
|
value_field = messages
|
||||||
|
}
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
connect = "host=/var/run/mysqld/mysqld.sock dbname={{ DBNAME }} user={{ DBUSER }} password={{ DBPASS }}"
|
||||||
|
map {
|
||||||
|
pattern = priv/sieve/name/\$script_name
|
||||||
|
table = sieve_after
|
||||||
|
username_field = username
|
||||||
|
value_field = id
|
||||||
|
fields {
|
||||||
|
script_name = \$script_name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
map {
|
||||||
|
pattern = priv/sieve/data/\$id
|
||||||
|
table = sieve_after
|
||||||
|
username_field = username
|
||||||
|
value_field = script_data
|
||||||
|
fields {
|
||||||
|
id = \$id
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
connect = "host=/var/run/mysqld/mysqld.sock dbname={{ DBNAME }} user={{ DBUSER }} password={{ DBPASS }}"
|
||||||
|
map {
|
||||||
|
pattern = priv/sieve/name/\$script_name
|
||||||
|
table = sieve_before
|
||||||
|
username_field = username
|
||||||
|
value_field = id
|
||||||
|
fields {
|
||||||
|
script_name = \$script_name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
map {
|
||||||
|
pattern = priv/sieve/data/\$id
|
||||||
|
table = sieve_before
|
||||||
|
username_field = username
|
||||||
|
value_field = script_data
|
||||||
|
fields {
|
||||||
|
id = \$id
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
driver = mysql
|
||||||
|
connect = "host=/var/run/mysqld/mysqld.sock dbname={{ DBNAME }} user={{ DBUSER }} password={{ DBPASS }}"
|
||||||
|
user_query = SELECT CONCAT(JSON_UNQUOTE(JSON_VALUE(attributes, '$.mailbox_format')), mailbox_path_prefix, '%d/%n/{{ MAILDIR_SUB }}:VOLATILEDIR=/var/volatile/%u:INDEX=/var/vmail_index/%u') AS mail, '%s' AS protocol, 5000 AS uid, 5000 AS gid, concat('*:bytes=', quota) AS quota_rule FROM mailbox WHERE username = '%u' AND (active = '1' OR active = '2')
|
||||||
|
iterate_query = SELECT username FROM mailbox WHERE active = '1' OR active = '2';
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
{%- set master_user = DOVECOT_MASTER_USER or RAND_USER %}
|
||||||
|
{%- set master_pass = DOVECOT_MASTER_PASS or RAND_PASS %}
|
||||||
|
{{ master_user }}@mailcow.local:{SHA1}{{ master_pass | sha1 }}::::::
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
{{ DOVECOT_MASTER_USER or RAND_USER }}@mailcow.local::5000:5000::::
|
||||||
309
data/conf/dovecot/config_templates/dovecot.conf.j2
Normal file
309
data/conf/dovecot/config_templates/dovecot.conf.j2
Normal file
@@ -0,0 +1,309 @@
|
|||||||
|
auth_mechanisms = plain login
|
||||||
|
#mail_debug = yes
|
||||||
|
#auth_debug = yes
|
||||||
|
#log_debug = category=fts-flatcurve # Activate Logging for Flatcurve FTS Searchings
|
||||||
|
log_path = syslog
|
||||||
|
disable_plaintext_auth = yes
|
||||||
|
|
||||||
|
# Uncomment on NFS share
|
||||||
|
#mmap_disable = yes
|
||||||
|
#mail_fsync = always
|
||||||
|
#mail_nfs_index = yes
|
||||||
|
#mail_nfs_storage = yes
|
||||||
|
|
||||||
|
login_log_format_elements = "user=<%u> method=%m rip=%r lip=%l mpid=%e %c %k"
|
||||||
|
mail_home = /var/vmail/%d/%n
|
||||||
|
mail_location = maildir:~/
|
||||||
|
mail_plugins = </etc/dovecot/mail_plugins
|
||||||
|
mail_attachment_fs = crypt:set_prefix=mail_crypt_global:posix:
|
||||||
|
mail_attachment_dir = /var/attachments
|
||||||
|
mail_attachment_min_size = 128k
|
||||||
|
# Significantly speeds up very large mailboxes, but is only safe to enable if
|
||||||
|
# you do not manually modify the files in the `cur` directories in
|
||||||
|
# mailcowdockerized_vmail-vol-1.
|
||||||
|
# https://docs.mailcow.email/manual-guides/Dovecot/u_e-dovecot-performance/
|
||||||
|
maildir_very_dirty_syncs = yes
|
||||||
|
|
||||||
|
# Dovecot 2.2
|
||||||
|
#ssl_protocols = !SSLv3
|
||||||
|
# Dovecot 2.3
|
||||||
|
ssl_min_protocol = TLSv1.2
|
||||||
|
|
||||||
|
ssl_prefer_server_ciphers = yes
|
||||||
|
ssl_cipher_list = ALL:!ADH:!LOW:!SSLv2:!SSLv3:!EXP:!aNULL:!eNULL:!3DES:!MD5:!PSK:!DSS:!RC4:!SEED:!IDEA:+HIGH:+MEDIUM
|
||||||
|
|
||||||
|
# Default in Dovecot 2.3
|
||||||
|
ssl_options = no_compression no_ticket
|
||||||
|
|
||||||
|
# New in Dovecot 2.3
|
||||||
|
ssl_dh = </etc/ssl/mail/dhparams.pem
|
||||||
|
# Dovecot 2.2
|
||||||
|
#ssl_dh_parameters_length = 2048
|
||||||
|
log_timestamp = "%Y-%m-%d %H:%M:%S "
|
||||||
|
recipient_delimiter = +
|
||||||
|
auth_master_user_separator = *
|
||||||
|
mail_shared_explicit_inbox = yes
|
||||||
|
mail_prefetch_count = 30
|
||||||
|
passdb {
|
||||||
|
driver = lua
|
||||||
|
args = file=/etc/dovecot/auth/passwd-verify.lua blocking=yes cache_key=%s:%u:%w
|
||||||
|
result_success = return-ok
|
||||||
|
result_failure = continue
|
||||||
|
result_internalfail = continue
|
||||||
|
}
|
||||||
|
# try a master passwd
|
||||||
|
passdb {
|
||||||
|
driver = passwd-file
|
||||||
|
args = /etc/dovecot/dovecot-master.passwd
|
||||||
|
master = yes
|
||||||
|
skip = authenticated
|
||||||
|
}
|
||||||
|
# check for regular password - if empty (e.g. force-passwd-reset), previous pass=yes passdbs also fail
|
||||||
|
# a return of the following passdb is mandatory
|
||||||
|
passdb {
|
||||||
|
driver = lua
|
||||||
|
args = file=/etc/dovecot/auth/passwd-verify.lua blocking=yes
|
||||||
|
}
|
||||||
|
# Set doveadm_password=your-secret-password in data/conf/dovecot/extra.conf (create if missing)
|
||||||
|
service doveadm {
|
||||||
|
inet_listener {
|
||||||
|
port = 12345
|
||||||
|
}
|
||||||
|
vsz_limit=2048 MB
|
||||||
|
}
|
||||||
|
|
||||||
|
{% include 'dovecot.folders.conf.j2' %}
|
||||||
|
|
||||||
|
protocols = imap sieve lmtp pop3
|
||||||
|
service dict {
|
||||||
|
unix_listener dict {
|
||||||
|
mode = 0660
|
||||||
|
user = vmail
|
||||||
|
group = vmail
|
||||||
|
}
|
||||||
|
}
|
||||||
|
service log {
|
||||||
|
user = dovenull
|
||||||
|
}
|
||||||
|
service config {
|
||||||
|
unix_listener config {
|
||||||
|
user = root
|
||||||
|
group = vmail
|
||||||
|
mode = 0660
|
||||||
|
}
|
||||||
|
}
|
||||||
|
service auth {
|
||||||
|
inet_listener auth-inet {
|
||||||
|
port = 10001
|
||||||
|
}
|
||||||
|
unix_listener auth-master {
|
||||||
|
mode = 0600
|
||||||
|
user = vmail
|
||||||
|
}
|
||||||
|
unix_listener auth-userdb {
|
||||||
|
mode = 0600
|
||||||
|
user = vmail
|
||||||
|
}
|
||||||
|
vsz_limit = 2G
|
||||||
|
}
|
||||||
|
service managesieve-login {
|
||||||
|
inet_listener sieve {
|
||||||
|
port = 4190
|
||||||
|
}
|
||||||
|
inet_listener sieve_haproxy {
|
||||||
|
port = 14190
|
||||||
|
haproxy = yes
|
||||||
|
}
|
||||||
|
service_count = 1
|
||||||
|
process_min_avail = 2
|
||||||
|
vsz_limit = 1G
|
||||||
|
}
|
||||||
|
service imap-login {
|
||||||
|
service_count = 1
|
||||||
|
process_min_avail = 2
|
||||||
|
process_limit = 10000
|
||||||
|
vsz_limit = 1G
|
||||||
|
user = dovenull
|
||||||
|
inet_listener imap_haproxy {
|
||||||
|
port = 10143
|
||||||
|
haproxy = yes
|
||||||
|
}
|
||||||
|
inet_listener imaps_haproxy {
|
||||||
|
port = 10993
|
||||||
|
ssl = yes
|
||||||
|
haproxy = yes
|
||||||
|
}
|
||||||
|
}
|
||||||
|
service pop3-login {
|
||||||
|
service_count = 1
|
||||||
|
process_min_avail = 1
|
||||||
|
vsz_limit = 1G
|
||||||
|
inet_listener pop3_haproxy {
|
||||||
|
port = 10110
|
||||||
|
haproxy = yes
|
||||||
|
}
|
||||||
|
inet_listener pop3s_haproxy {
|
||||||
|
port = 10995
|
||||||
|
ssl = yes
|
||||||
|
haproxy = yes
|
||||||
|
}
|
||||||
|
}
|
||||||
|
service imap {
|
||||||
|
executable = imap
|
||||||
|
user = vmail
|
||||||
|
vsz_limit = 1G
|
||||||
|
}
|
||||||
|
service managesieve {
|
||||||
|
process_limit = 256
|
||||||
|
}
|
||||||
|
service lmtp {
|
||||||
|
inet_listener lmtp-inet {
|
||||||
|
port = 24
|
||||||
|
}
|
||||||
|
user = vmail
|
||||||
|
}
|
||||||
|
listen = *,[::]
|
||||||
|
ssl_cert = </etc/ssl/mail/cert.pem
|
||||||
|
ssl_key = </etc/ssl/mail/key.pem
|
||||||
|
userdb {
|
||||||
|
driver = passwd-file
|
||||||
|
args = /etc/dovecot/dovecot-master.userdb
|
||||||
|
}
|
||||||
|
userdb {
|
||||||
|
args = /etc/dovecot/sql/dovecot-dict-sql-userdb.conf
|
||||||
|
driver = sql
|
||||||
|
skip = found
|
||||||
|
}
|
||||||
|
protocol imap {
|
||||||
|
mail_plugins = </etc/dovecot/mail_plugins_imap
|
||||||
|
imap_metadata = yes
|
||||||
|
}
|
||||||
|
mail_attribute_dict = file:%h/dovecot-attributes
|
||||||
|
protocol lmtp {
|
||||||
|
mail_plugins = </etc/dovecot/mail_plugins_lmtp
|
||||||
|
auth_socket_path = /var/run/dovecot/auth-master
|
||||||
|
}
|
||||||
|
protocol sieve {
|
||||||
|
managesieve_logout_format = bytes=%i/%o
|
||||||
|
}
|
||||||
|
plugin {
|
||||||
|
# Allow "any" or "authenticated" to be used in ACLs
|
||||||
|
acl_anyone = {{ ACL_ANYONE }}
|
||||||
|
acl_shared_dict = file:/var/vmail/shared-mailboxes.db
|
||||||
|
acl = vfile
|
||||||
|
acl_user = %u
|
||||||
|
quota = dict:Userquota::proxy::sqlquota
|
||||||
|
quota_rule2 = Trash:storage=+100%%
|
||||||
|
sieve = /var/vmail/sieve/%u.sieve
|
||||||
|
sieve_plugins = sieve_imapsieve sieve_extprograms
|
||||||
|
sieve_vacation_send_from_recipient = yes
|
||||||
|
sieve_redirect_envelope_from = recipient
|
||||||
|
# From elsewhere to Spam folder
|
||||||
|
imapsieve_mailbox1_name = Junk
|
||||||
|
imapsieve_mailbox1_causes = COPY
|
||||||
|
imapsieve_mailbox1_before = file:/usr/lib/dovecot/sieve/report-spam.sieve
|
||||||
|
# END
|
||||||
|
# From Spam folder to elsewhere
|
||||||
|
imapsieve_mailbox2_name = *
|
||||||
|
imapsieve_mailbox2_from = Junk
|
||||||
|
imapsieve_mailbox2_causes = COPY
|
||||||
|
imapsieve_mailbox2_before = file:/usr/lib/dovecot/sieve/report-ham.sieve
|
||||||
|
# END
|
||||||
|
master_user = %u
|
||||||
|
quota_warning = storage=95%% quota-warning 95 %u
|
||||||
|
quota_warning2 = storage=80%% quota-warning 80 %u
|
||||||
|
sieve_pipe_bin_dir = /usr/lib/dovecot/sieve
|
||||||
|
sieve_global_extensions = +vnd.dovecot.pipe +vnd.dovecot.execute
|
||||||
|
sieve_extensions = +notify +imapflags +vacation-seconds +editheader
|
||||||
|
sieve_max_script_size = 1M
|
||||||
|
sieve_max_redirects = 100
|
||||||
|
sieve_max_actions = 101
|
||||||
|
sieve_quota_max_scripts = 0
|
||||||
|
sieve_quota_max_storage = 0
|
||||||
|
listescape_char = "\\"
|
||||||
|
sieve_vacation_min_period = 5s
|
||||||
|
sieve_vacation_max_period = 0
|
||||||
|
sieve_vacation_default_period = 60s
|
||||||
|
sieve_before = /var/vmail/sieve/global_sieve_before.sieve
|
||||||
|
sieve_before2 = dict:proxy::sieve_before;name=active;bindir=/var/vmail/sieve_before_bindir
|
||||||
|
sieve_after = dict:proxy::sieve_after;name=active;bindir=/var/vmail/sieve_after_bindir
|
||||||
|
sieve_after2 = /var/vmail/sieve/global_sieve_after.sieve
|
||||||
|
sieve_duplicate_default_period = 1m
|
||||||
|
sieve_duplicate_max_period = 7d
|
||||||
|
|
||||||
|
# -- Global keys
|
||||||
|
mail_crypt_global_private_key = </mail_crypt/ecprivkey.pem
|
||||||
|
mail_crypt_global_public_key = </mail_crypt/ecpubkey.pem
|
||||||
|
mail_crypt_save_version = 2
|
||||||
|
|
||||||
|
# Enable compression while saving, lz4 Dovecot v2.3.17+
|
||||||
|
zlib_save = lz4
|
||||||
|
|
||||||
|
mail_log_events = delete undelete expunge copy mailbox_delete mailbox_rename
|
||||||
|
mail_log_fields = uid box msgid size
|
||||||
|
mail_log_cached_only = yes
|
||||||
|
|
||||||
|
# Try set mail_replica
|
||||||
|
{% include 'mail_replica.conf.j2' %}
|
||||||
|
}
|
||||||
|
service quota-warning {
|
||||||
|
executable = script /usr/local/bin/quota_notify.py
|
||||||
|
# use some unprivileged user for executing the quota warnings
|
||||||
|
user = vmail
|
||||||
|
unix_listener quota-warning {
|
||||||
|
user = vmail
|
||||||
|
}
|
||||||
|
}
|
||||||
|
dict {
|
||||||
|
sqlquota = mysql:/etc/dovecot/sql/dovecot-dict-sql-quota.conf
|
||||||
|
sieve_after = mysql:/etc/dovecot/sql/dovecot-dict-sql-sieve_after.conf
|
||||||
|
sieve_before = mysql:/etc/dovecot/sql/dovecot-dict-sql-sieve_before.conf
|
||||||
|
}
|
||||||
|
remote 127.0.0.1 {
|
||||||
|
disable_plaintext_auth = no
|
||||||
|
}
|
||||||
|
submission_host = postfix:588
|
||||||
|
mail_max_userip_connections = 500
|
||||||
|
service stats {
|
||||||
|
unix_listener stats-writer {
|
||||||
|
mode = 0660
|
||||||
|
user = vmail
|
||||||
|
}
|
||||||
|
}
|
||||||
|
imap_max_line_length = 2 M
|
||||||
|
auth_cache_verify_password_with_worker = yes
|
||||||
|
auth_cache_negative_ttl = 60s
|
||||||
|
auth_cache_ttl = 300s
|
||||||
|
auth_cache_size = 10M
|
||||||
|
auth_verbose_passwords = sha1:6
|
||||||
|
service replicator {
|
||||||
|
process_min_avail = 1
|
||||||
|
}
|
||||||
|
service aggregator {
|
||||||
|
fifo_listener replication-notify-fifo {
|
||||||
|
user = vmail
|
||||||
|
}
|
||||||
|
unix_listener replication-notify {
|
||||||
|
user = vmail
|
||||||
|
}
|
||||||
|
}
|
||||||
|
service replicator {
|
||||||
|
unix_listener replicator-doveadm {
|
||||||
|
mode = 0666
|
||||||
|
}
|
||||||
|
}
|
||||||
|
replication_max_conns = 10
|
||||||
|
doveadm_port = 12345
|
||||||
|
replication_dsync_parameters = -d -l 30 -U -n INBOX
|
||||||
|
|
||||||
|
{% include 'sogo_trusted_ip.conf.j2' %}
|
||||||
|
{% include 'shared_namespace.conf.j2' %}
|
||||||
|
{% include 'fts.conf.j2' %}
|
||||||
|
{% include 'sni.conf.j2' %}
|
||||||
|
|
||||||
|
# <Includes>
|
||||||
|
!include_try /etc/dovecot/extra.conf
|
||||||
|
# </Includes>
|
||||||
|
|
||||||
|
default_client_limit = 10400
|
||||||
|
default_vsz_limit = 1024 M
|
||||||
@@ -1,308 +1,308 @@
|
|||||||
namespace inbox {
|
namespace inbox {
|
||||||
inbox = yes
|
inbox = yes
|
||||||
location =
|
location =
|
||||||
separator = /
|
separator = /
|
||||||
mailbox "Trash" {
|
mailbox "Trash" {
|
||||||
auto = subscribe
|
auto = subscribe
|
||||||
special_use = \Trash
|
special_use = \Trash
|
||||||
}
|
}
|
||||||
mailbox "Deleted Messages" {
|
mailbox "Deleted Messages" {
|
||||||
special_use = \Trash
|
special_use = \Trash
|
||||||
}
|
}
|
||||||
mailbox "Deleted Items" {
|
mailbox "Deleted Items" {
|
||||||
special_use = \Trash
|
special_use = \Trash
|
||||||
}
|
}
|
||||||
mailbox "Rubbish" {
|
mailbox "Rubbish" {
|
||||||
special_use = \Trash
|
special_use = \Trash
|
||||||
}
|
}
|
||||||
mailbox "Gelöschte Objekte" {
|
mailbox "Gelöschte Objekte" {
|
||||||
special_use = \Trash
|
special_use = \Trash
|
||||||
}
|
}
|
||||||
mailbox "Gelöschte Elemente" {
|
mailbox "Gelöschte Elemente" {
|
||||||
special_use = \Trash
|
special_use = \Trash
|
||||||
}
|
}
|
||||||
mailbox "Papierkorb" {
|
mailbox "Papierkorb" {
|
||||||
special_use = \Trash
|
special_use = \Trash
|
||||||
}
|
}
|
||||||
mailbox "Itens Excluidos" {
|
mailbox "Itens Excluidos" {
|
||||||
special_use = \Trash
|
special_use = \Trash
|
||||||
}
|
}
|
||||||
mailbox "Itens Excluídos" {
|
mailbox "Itens Excluídos" {
|
||||||
special_use = \Trash
|
special_use = \Trash
|
||||||
}
|
}
|
||||||
mailbox "Lixeira" {
|
mailbox "Lixeira" {
|
||||||
special_use = \Trash
|
special_use = \Trash
|
||||||
}
|
}
|
||||||
mailbox "Prullenbak" {
|
mailbox "Prullenbak" {
|
||||||
special_use = \Trash
|
special_use = \Trash
|
||||||
}
|
}
|
||||||
mailbox "Odstránené položky" {
|
mailbox "Odstránené položky" {
|
||||||
special_use = \Trash
|
special_use = \Trash
|
||||||
}
|
}
|
||||||
mailbox "Koš" {
|
mailbox "Koš" {
|
||||||
special_use = \Trash
|
special_use = \Trash
|
||||||
}
|
}
|
||||||
mailbox "Verwijderde items" {
|
mailbox "Verwijderde items" {
|
||||||
special_use = \Trash
|
special_use = \Trash
|
||||||
}
|
}
|
||||||
mailbox "Удаленные" {
|
mailbox "Удаленные" {
|
||||||
special_use = \Trash
|
special_use = \Trash
|
||||||
}
|
}
|
||||||
mailbox "Удаленные элементы" {
|
mailbox "Удаленные элементы" {
|
||||||
special_use = \Trash
|
special_use = \Trash
|
||||||
}
|
}
|
||||||
mailbox "Корзина" {
|
mailbox "Корзина" {
|
||||||
special_use = \Trash
|
special_use = \Trash
|
||||||
}
|
}
|
||||||
mailbox "Видалені" {
|
mailbox "Видалені" {
|
||||||
special_use = \Trash
|
special_use = \Trash
|
||||||
}
|
}
|
||||||
mailbox "Видалені елементи" {
|
mailbox "Видалені елементи" {
|
||||||
special_use = \Trash
|
special_use = \Trash
|
||||||
}
|
}
|
||||||
mailbox "Кошик" {
|
mailbox "Кошик" {
|
||||||
special_use = \Trash
|
special_use = \Trash
|
||||||
}
|
}
|
||||||
mailbox "废件箱" {
|
mailbox "废件箱" {
|
||||||
special_use = \Trash
|
special_use = \Trash
|
||||||
}
|
}
|
||||||
mailbox "已删除消息" {
|
mailbox "已删除消息" {
|
||||||
special_use = \Trash
|
special_use = \Trash
|
||||||
}
|
}
|
||||||
mailbox "已删除邮件" {
|
mailbox "已删除邮件" {
|
||||||
special_use = \Trash
|
special_use = \Trash
|
||||||
}
|
}
|
||||||
mailbox "Archive" {
|
mailbox "Archive" {
|
||||||
auto = subscribe
|
auto = subscribe
|
||||||
special_use = \Archive
|
special_use = \Archive
|
||||||
}
|
}
|
||||||
mailbox "Archiv" {
|
mailbox "Archiv" {
|
||||||
special_use = \Archive
|
special_use = \Archive
|
||||||
}
|
}
|
||||||
mailbox "Archives" {
|
mailbox "Archives" {
|
||||||
special_use = \Archive
|
special_use = \Archive
|
||||||
}
|
}
|
||||||
mailbox "Arquivo" {
|
mailbox "Arquivo" {
|
||||||
special_use = \Archive
|
special_use = \Archive
|
||||||
}
|
}
|
||||||
mailbox "Arquivos" {
|
mailbox "Arquivos" {
|
||||||
special_use = \Archive
|
special_use = \Archive
|
||||||
}
|
}
|
||||||
mailbox "Archief" {
|
mailbox "Archief" {
|
||||||
special_use = \Archive
|
special_use = \Archive
|
||||||
}
|
}
|
||||||
mailbox "Archív" {
|
mailbox "Archív" {
|
||||||
special_use = \Archive
|
special_use = \Archive
|
||||||
}
|
}
|
||||||
mailbox "Archivovať" {
|
mailbox "Archivovať" {
|
||||||
special_use = \Archive
|
special_use = \Archive
|
||||||
}
|
}
|
||||||
mailbox "归档" {
|
mailbox "归档" {
|
||||||
special_use = \Archive
|
special_use = \Archive
|
||||||
}
|
}
|
||||||
mailbox "Архив" {
|
mailbox "Архив" {
|
||||||
special_use = \Archive
|
special_use = \Archive
|
||||||
}
|
}
|
||||||
mailbox "Архів" {
|
mailbox "Архів" {
|
||||||
special_use = \Archive
|
special_use = \Archive
|
||||||
}
|
}
|
||||||
mailbox "Sent" {
|
mailbox "Sent" {
|
||||||
auto = subscribe
|
auto = subscribe
|
||||||
special_use = \Sent
|
special_use = \Sent
|
||||||
}
|
}
|
||||||
mailbox "Sent Messages" {
|
mailbox "Sent Messages" {
|
||||||
special_use = \Sent
|
special_use = \Sent
|
||||||
}
|
}
|
||||||
mailbox "Sent Items" {
|
mailbox "Sent Items" {
|
||||||
special_use = \Sent
|
special_use = \Sent
|
||||||
}
|
}
|
||||||
mailbox "已发送" {
|
mailbox "已发送" {
|
||||||
special_use = \Sent
|
special_use = \Sent
|
||||||
}
|
}
|
||||||
mailbox "已发送消息" {
|
mailbox "已发送消息" {
|
||||||
special_use = \Sent
|
special_use = \Sent
|
||||||
}
|
}
|
||||||
mailbox "已发送邮件" {
|
mailbox "已发送邮件" {
|
||||||
special_use = \Sent
|
special_use = \Sent
|
||||||
}
|
}
|
||||||
mailbox "Отправленные" {
|
mailbox "Отправленные" {
|
||||||
special_use = \Sent
|
special_use = \Sent
|
||||||
}
|
}
|
||||||
mailbox "Отправленные элементы" {
|
mailbox "Отправленные элементы" {
|
||||||
special_use = \Sent
|
special_use = \Sent
|
||||||
}
|
}
|
||||||
mailbox "Надіслані" {
|
mailbox "Надіслані" {
|
||||||
special_use = \Sent
|
special_use = \Sent
|
||||||
}
|
}
|
||||||
mailbox "Надіслані елементи" {
|
mailbox "Надіслані елементи" {
|
||||||
special_use = \Sent
|
special_use = \Sent
|
||||||
}
|
}
|
||||||
mailbox "Gesendet" {
|
mailbox "Gesendet" {
|
||||||
special_use = \Sent
|
special_use = \Sent
|
||||||
}
|
}
|
||||||
mailbox "Gesendete Objekte" {
|
mailbox "Gesendete Objekte" {
|
||||||
special_use = \Sent
|
special_use = \Sent
|
||||||
}
|
}
|
||||||
mailbox "Gesendete Elemente" {
|
mailbox "Gesendete Elemente" {
|
||||||
special_use = \Sent
|
special_use = \Sent
|
||||||
}
|
}
|
||||||
mailbox "Itens Enviados" {
|
mailbox "Itens Enviados" {
|
||||||
special_use = \Sent
|
special_use = \Sent
|
||||||
}
|
}
|
||||||
mailbox "Enviados" {
|
mailbox "Enviados" {
|
||||||
special_use = \Sent
|
special_use = \Sent
|
||||||
}
|
}
|
||||||
mailbox "Verzonden items" {
|
mailbox "Verzonden items" {
|
||||||
special_use = \Sent
|
special_use = \Sent
|
||||||
}
|
}
|
||||||
mailbox "Verzonden" {
|
mailbox "Verzonden" {
|
||||||
special_use = \Sent
|
special_use = \Sent
|
||||||
}
|
}
|
||||||
mailbox "Odoslaná pošta" {
|
mailbox "Odoslaná pošta" {
|
||||||
special_use = \Sent
|
special_use = \Sent
|
||||||
}
|
}
|
||||||
mailbox "Odoslané" {
|
mailbox "Odoslané" {
|
||||||
special_use = \Sent
|
special_use = \Sent
|
||||||
}
|
}
|
||||||
mailbox "Drafts" {
|
mailbox "Drafts" {
|
||||||
auto = subscribe
|
auto = subscribe
|
||||||
special_use = \Drafts
|
special_use = \Drafts
|
||||||
}
|
}
|
||||||
mailbox "Entwürfe" {
|
mailbox "Entwürfe" {
|
||||||
special_use = \Drafts
|
special_use = \Drafts
|
||||||
}
|
}
|
||||||
mailbox "Rascunhos" {
|
mailbox "Rascunhos" {
|
||||||
special_use = \Drafts
|
special_use = \Drafts
|
||||||
}
|
}
|
||||||
mailbox "Concepten" {
|
mailbox "Concepten" {
|
||||||
special_use = \Drafts
|
special_use = \Drafts
|
||||||
}
|
}
|
||||||
mailbox "Koncepty" {
|
mailbox "Koncepty" {
|
||||||
special_use = \Drafts
|
special_use = \Drafts
|
||||||
}
|
}
|
||||||
mailbox "草稿" {
|
mailbox "草稿" {
|
||||||
special_use = \Drafts
|
special_use = \Drafts
|
||||||
}
|
}
|
||||||
mailbox "草稿箱" {
|
mailbox "草稿箱" {
|
||||||
special_use = \Drafts
|
special_use = \Drafts
|
||||||
}
|
}
|
||||||
mailbox "Черновики" {
|
mailbox "Черновики" {
|
||||||
special_use = \Drafts
|
special_use = \Drafts
|
||||||
}
|
}
|
||||||
mailbox "Чернетки" {
|
mailbox "Чернетки" {
|
||||||
special_use = \Drafts
|
special_use = \Drafts
|
||||||
}
|
}
|
||||||
mailbox "Junk" {
|
mailbox "Junk" {
|
||||||
auto = subscribe
|
auto = subscribe
|
||||||
special_use = \Junk
|
special_use = \Junk
|
||||||
}
|
}
|
||||||
mailbox "Junk-E-Mail" {
|
mailbox "Junk-E-Mail" {
|
||||||
special_use = \Junk
|
special_use = \Junk
|
||||||
}
|
}
|
||||||
mailbox "Junk E-Mail" {
|
mailbox "Junk E-Mail" {
|
||||||
special_use = \Junk
|
special_use = \Junk
|
||||||
}
|
}
|
||||||
mailbox "Spam" {
|
mailbox "Spam" {
|
||||||
special_use = \Junk
|
special_use = \Junk
|
||||||
}
|
}
|
||||||
mailbox "Lixo Eletrônico" {
|
mailbox "Lixo Eletrônico" {
|
||||||
special_use = \Junk
|
special_use = \Junk
|
||||||
}
|
}
|
||||||
mailbox "Nevyžiadaná pošta" {
|
mailbox "Nevyžiadaná pošta" {
|
||||||
special_use = \Junk
|
special_use = \Junk
|
||||||
}
|
}
|
||||||
mailbox "Infikované položky" {
|
mailbox "Infikované položky" {
|
||||||
special_use = \Junk
|
special_use = \Junk
|
||||||
}
|
}
|
||||||
mailbox "Ongewenste e-mail" {
|
mailbox "Ongewenste e-mail" {
|
||||||
special_use = \Junk
|
special_use = \Junk
|
||||||
}
|
}
|
||||||
mailbox "垃圾" {
|
mailbox "垃圾" {
|
||||||
special_use = \Junk
|
special_use = \Junk
|
||||||
}
|
}
|
||||||
mailbox "垃圾箱" {
|
mailbox "垃圾箱" {
|
||||||
special_use = \Junk
|
special_use = \Junk
|
||||||
}
|
}
|
||||||
mailbox "Нежелательная почта" {
|
mailbox "Нежелательная почта" {
|
||||||
special_use = \Junk
|
special_use = \Junk
|
||||||
}
|
}
|
||||||
mailbox "Спам" {
|
mailbox "Спам" {
|
||||||
special_use = \Junk
|
special_use = \Junk
|
||||||
}
|
}
|
||||||
mailbox "Небажана пошта" {
|
mailbox "Небажана пошта" {
|
||||||
special_use = \Junk
|
special_use = \Junk
|
||||||
}
|
}
|
||||||
mailbox "Koncepty" {
|
mailbox "Koncepty" {
|
||||||
special_use = \Drafts
|
special_use = \Drafts
|
||||||
}
|
}
|
||||||
mailbox "Nevyžádaná pošta" {
|
mailbox "Nevyžádaná pošta" {
|
||||||
special_use = \Junk
|
special_use = \Junk
|
||||||
}
|
}
|
||||||
mailbox "Odstraněná pošta" {
|
mailbox "Odstraněná pošta" {
|
||||||
special_use = \Trash
|
special_use = \Trash
|
||||||
}
|
}
|
||||||
mailbox "Odeslaná pošta" {
|
mailbox "Odeslaná pošta" {
|
||||||
special_use = \Sent
|
special_use = \Sent
|
||||||
}
|
}
|
||||||
mailbox "Skräp" {
|
mailbox "Skräp" {
|
||||||
special_use = \Trash
|
special_use = \Trash
|
||||||
}
|
}
|
||||||
mailbox "Borttagna Meddelanden" {
|
mailbox "Borttagna Meddelanden" {
|
||||||
special_use = \Trash
|
special_use = \Trash
|
||||||
}
|
}
|
||||||
mailbox "Arkiv" {
|
mailbox "Arkiv" {
|
||||||
special_use = \Archive
|
special_use = \Archive
|
||||||
}
|
}
|
||||||
mailbox "Arkeverat" {
|
mailbox "Arkeverat" {
|
||||||
special_use = \Archive
|
special_use = \Archive
|
||||||
}
|
}
|
||||||
mailbox "Skickat" {
|
mailbox "Skickat" {
|
||||||
special_use = \Sent
|
special_use = \Sent
|
||||||
}
|
}
|
||||||
mailbox "Skickade Meddelanden" {
|
mailbox "Skickade Meddelanden" {
|
||||||
special_use = \Sent
|
special_use = \Sent
|
||||||
}
|
}
|
||||||
mailbox "Utkast" {
|
mailbox "Utkast" {
|
||||||
special_use = \Drafts
|
special_use = \Drafts
|
||||||
}
|
}
|
||||||
mailbox "Skraldespand" {
|
mailbox "Skraldespand" {
|
||||||
special_use = \Trash
|
special_use = \Trash
|
||||||
}
|
}
|
||||||
mailbox "Slettet mails" {
|
mailbox "Slettet mails" {
|
||||||
special_use = \Trash
|
special_use = \Trash
|
||||||
}
|
}
|
||||||
mailbox "Arkiv" {
|
mailbox "Arkiv" {
|
||||||
special_use = \Archive
|
special_use = \Archive
|
||||||
}
|
}
|
||||||
mailbox "Arkiveret mails" {
|
mailbox "Arkiveret mails" {
|
||||||
special_use = \Archive
|
special_use = \Archive
|
||||||
}
|
}
|
||||||
mailbox "Sendt" {
|
mailbox "Sendt" {
|
||||||
special_use = \Sent
|
special_use = \Sent
|
||||||
}
|
}
|
||||||
mailbox "Sendte mails" {
|
mailbox "Sendte mails" {
|
||||||
special_use = \Sent
|
special_use = \Sent
|
||||||
}
|
}
|
||||||
mailbox "Udkast" {
|
mailbox "Udkast" {
|
||||||
special_use = \Drafts
|
special_use = \Drafts
|
||||||
}
|
}
|
||||||
mailbox "Kladde" {
|
mailbox "Kladde" {
|
||||||
special_use = \Drafts
|
special_use = \Drafts
|
||||||
}
|
}
|
||||||
mailbox "Πρόχειρα" {
|
mailbox "Πρόχειρα" {
|
||||||
special_use = \Drafts
|
special_use = \Drafts
|
||||||
}
|
}
|
||||||
mailbox "Απεσταλμένα" {
|
mailbox "Απεσταλμένα" {
|
||||||
special_use = \Sent
|
special_use = \Sent
|
||||||
}
|
}
|
||||||
mailbox "Κάδος απορριμάτων" {
|
mailbox "Κάδος απορριμάτων" {
|
||||||
special_use = \Trash
|
special_use = \Trash
|
||||||
}
|
}
|
||||||
mailbox "Ανεπιθύμητα" {
|
mailbox "Ανεπιθύμητα" {
|
||||||
special_use = \Junk
|
special_use = \Junk
|
||||||
}
|
}
|
||||||
mailbox "Αρχειοθετημένα" {
|
mailbox "Αρχειοθετημένα" {
|
||||||
special_use = \Archive
|
special_use = \Archive
|
||||||
}
|
}
|
||||||
prefix =
|
prefix =
|
||||||
}
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
# mailcow FTS Flatcurve Settings, change them as you like.
|
{% if SKIP_FTS|lower in ['n', 'no'] %}
|
||||||
plugin {
|
plugin {
|
||||||
fts_autoindex = yes
|
fts_autoindex = yes
|
||||||
fts_autoindex_exclude = \Junk
|
fts_autoindex_exclude = \Junk
|
||||||
@@ -24,14 +24,11 @@ plugin {
|
|||||||
fts_index_timeout = 300s
|
fts_index_timeout = 300s
|
||||||
}
|
}
|
||||||
|
|
||||||
### THIS PART WILL BE CHANGED BY MODIFYING mailcow.conf AUTOMATICALLY DURING RUNTIME! ###
|
|
||||||
|
|
||||||
service indexer-worker {
|
service indexer-worker {
|
||||||
# Max amount of simultaniously running indexer jobs.
|
# Max amount of simultaniously running indexer jobs.
|
||||||
process_limit=1
|
process_limit = {{ FTS_PROCS }}
|
||||||
|
|
||||||
# Max amount of RAM used by EACH indexer process.
|
# Max amount of RAM used by EACH indexer process.
|
||||||
vsz_limit=128 MB
|
vsz_limit = {{ FTS_HEAP }} MB
|
||||||
}
|
}
|
||||||
|
{% endif %}
|
||||||
### THIS PART WILL BE CHANGED BY MODIFYING mailcow.conf AUTOMATICALLY DURING RUNTIME! ###
|
|
||||||
5
data/conf/dovecot/config_templates/mail_plugins.j2
Normal file
5
data/conf/dovecot/config_templates/mail_plugins.j2
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
{%- if SKIP_FTS|lower in ["y", "yes"] -%}
|
||||||
|
quota acl zlib mail_crypt mail_crypt_acl mail_log notify listescape replication lazy_expunge
|
||||||
|
{%- else -%}
|
||||||
|
quota acl zlib mail_crypt mail_crypt_acl mail_log notify fts fts_flatcurve listescape replication lazy_expunge
|
||||||
|
{%- endif -%}
|
||||||
5
data/conf/dovecot/config_templates/mail_plugins_imap.j2
Normal file
5
data/conf/dovecot/config_templates/mail_plugins_imap.j2
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
{%- if SKIP_FTS|lower in ["y", "yes"] -%}
|
||||||
|
quota imap_quota imap_acl acl zlib imap_zlib imap_sieve mail_crypt mail_crypt_acl notify listescape replication mail_log
|
||||||
|
{%- else -%}
|
||||||
|
quota imap_quota imap_acl acl zlib imap_zlib imap_sieve mail_crypt mail_crypt_acl notify mail_log fts fts_flatcurve listescape replication
|
||||||
|
{%- endif -%}
|
||||||
5
data/conf/dovecot/config_templates/mail_plugins_lmtp.j2
Normal file
5
data/conf/dovecot/config_templates/mail_plugins_lmtp.j2
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
{%- if SKIP_FTS|lower in ["y", "yes"] -%}
|
||||||
|
quota sieve acl zlib mail_crypt mail_crypt_acl notify listescape replication
|
||||||
|
{%- else -%}
|
||||||
|
quota sieve acl zlib mail_crypt mail_crypt_acl fts fts_flatcurve notify listescape replication
|
||||||
|
{%- endif -%}
|
||||||
3
data/conf/dovecot/config_templates/mail_replica.conf.j2
Normal file
3
data/conf/dovecot/config_templates/mail_replica.conf.j2
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
{% if MAILCOW_REPLICA_IP and DOVEADM_REPLICA_PORT %}
|
||||||
|
mail_replica = tcp:{{ MAILCOW_REPLICA_IP }}:{{ DOVEADM_REPLICA_PORT }}
|
||||||
|
{% endif %}
|
||||||
2
data/conf/dovecot/config_templates/maildir_gc.sh.j2
Normal file
2
data/conf/dovecot/config_templates/maildir_gc.sh.j2
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
[ -d /var/vmail/_garbage/ ] && /usr/bin/find /var/vmail/_garbage/ -mindepth 1 -maxdepth 1 -type d -cmin +{{ MAILDIR_GC_TIME }} -exec rm -r {} \;
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
{% set MAILDIR_SUB_SHARED = '' if not MAILDIR_SUB else '/' ~ MAILDIR_SUB %}
|
||||||
|
namespace {
|
||||||
|
type = shared
|
||||||
|
separator = /
|
||||||
|
prefix = Shared/%%u/
|
||||||
|
location = maildir:%%h{{ MAILDIR_SUB_SHARED }}:INDEX=~{{ MAILDIR_SUB_SHARED }}/Shared/%%u
|
||||||
|
subscriptions = no
|
||||||
|
list = children
|
||||||
|
}
|
||||||
1
data/conf/dovecot/config_templates/sieve.creds.j2
Normal file
1
data/conf/dovecot/config_templates/sieve.creds.j2
Normal file
@@ -0,0 +1 @@
|
|||||||
|
{{ DOVECOT_MASTER_USER or RAND_USER }}@mailcow.local:{{ DOVECOT_MASTER_PASS or RAND_PASS }}
|
||||||
6
data/conf/dovecot/config_templates/sni.conf.j2
Normal file
6
data/conf/dovecot/config_templates/sni.conf.j2
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
{% for domain, path in VALID_CERT_DIRS.items() %}
|
||||||
|
local_name "{{ domain }}" {
|
||||||
|
ssl_cert = <{{ path }}/cert.pem
|
||||||
|
ssl_key = <{{ path }}/key.pem
|
||||||
|
}
|
||||||
|
{% endfor %}
|
||||||
1
data/conf/dovecot/config_templates/sogo-sso.pass.j2
Normal file
1
data/conf/dovecot/config_templates/sogo-sso.pass.j2
Normal file
@@ -0,0 +1 @@
|
|||||||
|
{{ RAND_PASS2 }}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
remote {{ IPV4_NETWORK }}.248 {
|
||||||
|
disable_plaintext_auth = no
|
||||||
|
}
|
||||||
3
data/conf/dovecot/config_templates/source_env.sh.j2
Normal file
3
data/conf/dovecot/config_templates/source_env.sh.j2
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
{% for key, value in ENV_VARS.items() %}
|
||||||
|
export {{ key }}="{{ value | replace('"', '\\"') }}"
|
||||||
|
{% endfor %}
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
#hosts = 1.2.3.4
|
|
||||||
#dn = cn=admin,dc=example,dc=local
|
|
||||||
#dnpass = password
|
|
||||||
#ldap_version = 3
|
|
||||||
#base = ou=People,dc=example,dc=local
|
|
||||||
#auth_bind = no
|
|
||||||
#pass_filter = (&(objectClass=posixAccount)(mail=%u))
|
|
||||||
#pass_attrs = mail=user,userPassword=password
|
|
||||||
#default_pass_scheme = SSHA
|
|
||||||
@@ -252,7 +252,7 @@ services:
|
|||||||
- sogo
|
- sogo
|
||||||
|
|
||||||
dovecot-mailcow:
|
dovecot-mailcow:
|
||||||
image: ghcr.io/mailcow/dovecot:2.33
|
image: ghcr.io/mailcow/dovecot:nightly-19052025
|
||||||
depends_on:
|
depends_on:
|
||||||
- mysql-mailcow
|
- mysql-mailcow
|
||||||
- netfilter-mailcow
|
- netfilter-mailcow
|
||||||
@@ -267,6 +267,7 @@ services:
|
|||||||
- ./data/assets/ssl:/etc/ssl/mail/:ro,z
|
- ./data/assets/ssl:/etc/ssl/mail/:ro,z
|
||||||
- ./data/conf/sogo/:/etc/sogo/:z
|
- ./data/conf/sogo/:/etc/sogo/:z
|
||||||
- ./data/conf/phpfpm/sogo-sso/:/etc/phpfpm/:z
|
- ./data/conf/phpfpm/sogo-sso/:/etc/phpfpm/:z
|
||||||
|
- ./data/web/inc/init_db.inc.php:/init_db.inc.php:z
|
||||||
- vmail-vol-1:/var/vmail
|
- vmail-vol-1:/var/vmail
|
||||||
- vmail-index-vol-1:/var/vmail_index
|
- vmail-index-vol-1:/var/vmail_index
|
||||||
- crypt-vol-1:/mail_crypt/
|
- crypt-vol-1:/mail_crypt/
|
||||||
@@ -275,6 +276,7 @@ services:
|
|||||||
- rspamd-vol-1:/var/lib/rspamd
|
- rspamd-vol-1:/var/lib/rspamd
|
||||||
- mysql-socket-vol-1:/var/run/mysqld/
|
- mysql-socket-vol-1:/var/run/mysqld/
|
||||||
environment:
|
environment:
|
||||||
|
- CONTAINER_NAME=dovecot-mailcow
|
||||||
- DOVECOT_MASTER_USER=${DOVECOT_MASTER_USER:-}
|
- DOVECOT_MASTER_USER=${DOVECOT_MASTER_USER:-}
|
||||||
- DOVECOT_MASTER_PASS=${DOVECOT_MASTER_PASS:-}
|
- DOVECOT_MASTER_PASS=${DOVECOT_MASTER_PASS:-}
|
||||||
- MAILCOW_REPLICA_IP=${MAILCOW_REPLICA_IP:-}
|
- MAILCOW_REPLICA_IP=${MAILCOW_REPLICA_IP:-}
|
||||||
|
|||||||
Reference in New Issue
Block a user