diff --git a/data/Dockerfiles/bootstrap/modules/BootstrapBase.py b/data/Dockerfiles/bootstrap/modules/BootstrapBase.py index f7d6094fd..20304d867 100644 --- a/data/Dockerfiles/bootstrap/modules/BootstrapBase.py +++ b/data/Dockerfiles/bootstrap/modules/BootstrapBase.py @@ -13,6 +13,7 @@ import hashlib import json import psutil import signal +from urllib.parse import quote from pathlib import Path import dns.resolver import mysql.connector @@ -89,17 +90,19 @@ class BootstrapBase: def prepare_template_vars(self, overwrite_path, extra_vars = None): """ - Loads and merges environment variables for Jinja2 templates from multiple sources. + Loads and merges environment variables for Jinja2 templates from multiple sources, and registers custom template filters. - This method combines: + This method combines variables from: 1. System environment variables - 2. Key/value pairs from the MySQL `service_settings` table - 3. An optional dictionary of extra_vars - 4. A JSON file with overrides (if the file exists) + 2. The MySQL `service_settings` table (filtered by service type if defined) + 3. An optional `extra_vars` dictionary + 4. A JSON overwrite file (if it exists at the given path) + + Also registers custom Jinja2 filters. Args: overwrite_path (str or Path): Path to a JSON file containing key-value overrides. - extra_vars (dict, optional): A dictionary of additional variables to include. + extra_vars (dict, optional): Additional variables to merge into the environment. Returns: dict: A dictionary containing all resolved template variables. @@ -108,10 +111,15 @@ class BootstrapBase: Prints errors if database fetch or JSON parsing fails, but does not raise exceptions. """ - # 1. Load env vars + # 1. setup filters + self.env.filters['sha1'] = self.sha1_filter + self.env.filters['urlencode'] = self.urlencode_filter + self.env.filters['escape_quotes'] = self.escape_quotes_filter + + # 2. Load env vars env_vars = dict(os.environ) - # 2. Load from MySQL + # 3. Load from MySQL try: cursor = self.mysql_conn.cursor() @@ -129,11 +137,11 @@ class BootstrapBase: except Exception as e: print(f"Failed to fetch DB service settings: {e}") - # 3. Load extra vars + # 4. Load extra vars if extra_vars: env_vars.update(extra_vars) - # 4. Load overwrites + # 5. Load overwrites overwrite_path = Path(overwrite_path) if overwrite_path.exists(): try: @@ -810,3 +818,9 @@ class BootstrapBase: def sha1_filter(self, value): return hashlib.sha1(value.encode()).hexdigest() + + def urlencode_filter(self, value): + return quote(value, safe='') + + def escape_quotes_filter(self, value): + return value.replace('"', r'\"') diff --git a/data/Dockerfiles/bootstrap/modules/BootstrapDovecot.py b/data/Dockerfiles/bootstrap/modules/BootstrapDovecot.py index 6e3f5aba1..6751b7b5b 100644 --- a/data/Dockerfiles/bootstrap/modules/BootstrapDovecot.py +++ b/data/Dockerfiles/bootstrap/modules/BootstrapDovecot.py @@ -45,10 +45,6 @@ class BootstrapDovecot(BootstrapBase): "ENV_VARS": dict(os.environ) } self.env_vars = self.prepare_template_vars('/service_config/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() diff --git a/data/conf/dovecot/config_templates/dovecot-dict-sql-quota.conf.j2 b/data/conf/dovecot/config_templates/dovecot-dict-sql-quota.conf.j2 index 228c14c81..679ad7ffa 100644 --- a/data/conf/dovecot/config_templates/dovecot-dict-sql-quota.conf.j2 +++ b/data/conf/dovecot/config_templates/dovecot-dict-sql-quota.conf.j2 @@ -1,5 +1,5 @@ {% 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 }}" +connect = "host=/var/run/mysqld/mysqld.sock dbname={{ DBNAME }} user={{ DBUSER }} password={{ DBPASS | escape_quotes }}" map { pattern = priv/quota/storage table = {{ QUOTA_TABLE }} diff --git a/data/conf/dovecot/config_templates/dovecot-dict-sql-sieve_after.conf.j2 b/data/conf/dovecot/config_templates/dovecot-dict-sql-sieve_after.conf.j2 index 602acbe91..d0409dedf 100644 --- a/data/conf/dovecot/config_templates/dovecot-dict-sql-sieve_after.conf.j2 +++ b/data/conf/dovecot/config_templates/dovecot-dict-sql-sieve_after.conf.j2 @@ -1,4 +1,4 @@ -connect = "host=/var/run/mysqld/mysqld.sock dbname={{ DBNAME }} user={{ DBUSER }} password={{ DBPASS }}" +connect = "host=/var/run/mysqld/mysqld.sock dbname={{ DBNAME }} user={{ DBUSER }} password={{ DBPASS | escape_quotes }}" map { pattern = priv/sieve/name/$script_name table = sieve_after diff --git a/data/conf/dovecot/config_templates/dovecot-dict-sql-sieve_before.conf.j2 b/data/conf/dovecot/config_templates/dovecot-dict-sql-sieve_before.conf.j2 index b592df82e..3ba02ea85 100644 --- a/data/conf/dovecot/config_templates/dovecot-dict-sql-sieve_before.conf.j2 +++ b/data/conf/dovecot/config_templates/dovecot-dict-sql-sieve_before.conf.j2 @@ -1,4 +1,4 @@ -connect = "host=/var/run/mysqld/mysqld.sock dbname={{ DBNAME }} user={{ DBUSER }} password={{ DBPASS }}" +connect = "host=/var/run/mysqld/mysqld.sock dbname={{ DBNAME }} user={{ DBUSER }} password={{ DBPASS | escape_quotes }}" map { pattern = priv/sieve/name/$script_name table = sieve_before diff --git a/data/conf/dovecot/config_templates/dovecot-dict-sql-userdb.conf.j2 b/data/conf/dovecot/config_templates/dovecot-dict-sql-userdb.conf.j2 index e7b15e3c8..62adfcf58 100644 --- a/data/conf/dovecot/config_templates/dovecot-dict-sql-userdb.conf.j2 +++ b/data/conf/dovecot/config_templates/dovecot-dict-sql-userdb.conf.j2 @@ -1,4 +1,4 @@ driver = mysql -connect = "host=/var/run/mysqld/mysqld.sock dbname={{ DBNAME }} user={{ DBUSER }} password={{ DBPASS }}" +connect = "host=/var/run/mysqld/mysqld.sock dbname={{ DBNAME }} user={{ DBUSER }} password={{ DBPASS | escape_quotes }}" 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'; diff --git a/data/conf/sogo/config_templates/sogod.plist.j2 b/data/conf/sogo/config_templates/sogod.plist.j2 index 5a483b632..3879a6107 100644 --- a/data/conf/sogo/config_templates/sogod.plist.j2 +++ b/data/conf/sogo/config_templates/sogod.plist.j2 @@ -3,7 +3,7 @@ OCSAclURL - mysql://{{ DBUSER }}:{{ DBPASS }}@{{ DB_HOST }}/{{ DBNAME }}/sogo_acl + mysql://{{ DBUSER }}:{{ DBPASS }}@{{ DB_HOST | urlencode }}/{{ DBNAME }}/sogo_acl SOGoIMAPServer imap://{{ DOVECOT_HOST }}:143/?TLS=YES&tlsVerifyMode=none SOGoSieveServer @@ -15,19 +15,19 @@ SOGoEncryptionKey {{RAND_PASS}} OCSAdminURL - mysql://{{ DBUSER }}:{{ DBPASS }}@{{ DB_HOST }}/{{ DBNAME }}/sogo_admin + mysql://{{ DBUSER }}:{{ DBPASS }}@{{ DB_HOST | urlencode }}/{{ DBNAME }}/sogo_admin OCSCacheFolderURL - mysql://{{ DBUSER }}:{{ DBPASS }}@{{ DB_HOST }}/{{ DBNAME }}/sogo_cache_folder + mysql://{{ DBUSER }}:{{ DBPASS }}@{{ DB_HOST | urlencode }}/{{ DBNAME }}/sogo_cache_folder OCSEMailAlarmsFolderURL - mysql://{{ DBUSER }}:{{ DBPASS }}@{{ DB_HOST }}/{{ DBNAME }}/sogo_alarms_folder + mysql://{{ DBUSER }}:{{ DBPASS }}@{{ DB_HOST | urlencode }}/{{ DBNAME }}/sogo_alarms_folder OCSFolderInfoURL - mysql://{{ DBUSER }}:{{ DBPASS }}@{{ DB_HOST }}/{{ DBNAME }}/sogo_folder_info + mysql://{{ DBUSER }}:{{ DBPASS }}@{{ DB_HOST | urlencode }}/{{ DBNAME }}/sogo_folder_info OCSSessionsFolderURL - mysql://{{ DBUSER }}:{{ DBPASS }}@{{ DB_HOST }}/{{ DBNAME }}/sogo_sessions_folder + mysql://{{ DBUSER }}:{{ DBPASS }}@{{ DB_HOST | urlencode }}/{{ DBNAME }}/sogo_sessions_folder OCSStoreURL - mysql://{{ DBUSER }}:{{ DBPASS }}@{{ DB_HOST }}/{{ DBNAME }}/sogo_store + mysql://{{ DBUSER }}:{{ DBPASS }}@{{ DB_HOST | urlencode }}/{{ DBNAME }}/sogo_store SOGoProfileURL - mysql://{{ DBUSER }}:{{ DBPASS }}@{{ DB_HOST }}/{{ DBNAME }}/sogo_user_profile + mysql://{{ DBUSER }}:{{ DBPASS }}@{{ DB_HOST | urlencode }}/{{ DBNAME }}/sogo_user_profile SOGoTimeZone {{TZ}} domains @@ -69,7 +69,7 @@ prependPasswordScheme YES viewURL - mysql://{{ DBUSER }}:{{ DBPASS }}@{{ DB_HOST }}/{{ DBNAME }}/_sogo_static_view + mysql://{{ DBUSER }}:{{ DBPASS }}@{{ DB_HOST | urlencode }}/{{ DBNAME }}/_sogo_static_view {% if IAM_SETTINGS.authsource == "ldap" and domain.ldap_gal %} diff --git a/docker-compose.yml b/docker-compose.yml index b9bdb6306..29261f557 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -233,7 +233,7 @@ services: - REDIS_SLAVEOF_IP=${REDIS_SLAVEOF_IP:-} - REDIS_SLAVEOF_PORT=${REDIS_SLAVEOF_PORT:-} - REDISPASS=${REDISPASS} - - DB_HOST=${DB_HOST:-%2Fvar%2Frun%2Fmysqld%2Fmysqld.sock} + - DB_HOST=${DB_HOST:-/var/run/mysqld/mysqld.sock} - SOGO_HOST=${SOGO_HOST:-172.22.1.248} - DOVECOT_HOST=${DOVECOT_HOST:-172.22.1.250} - POSTFIX_HOST=${POSTFIX_HOST:-172.22.1.253} @@ -243,8 +243,7 @@ services: volumes: - ./data/hooks/sogo:/hooks:Z - ./data/conf/sogo:/service_config:z - - ./data/conf/sogo/cron.creds:/etc/sogo/cron.creds:z - - ./data/conf/sogo/sieve.creds:/etc/sogo/sieve.creds:z + - ./data/conf/sogo:/etc/sogo:z - ./data/web/inc/init_db.inc.php:/init_db.inc.php:z - ./data/conf/sogo/custom-favicon.ico:/usr/lib/GNUstep/SOGo/WebServerResources/img/sogo.ico:z - ./data/conf/sogo/custom-shortlogo.svg:/usr/lib/GNUstep/SOGo/WebServerResources/img/sogo-compact.svg:z @@ -373,8 +372,7 @@ services: volumes: - ./data/hooks/postfix:/hooks:Z - ./data/conf/postfix:/service_config:z - - ./data/conf/postfix/postscreen_access.cidr:/opt/postfix/conf/postscreen_access.cidr:z - - ./data/conf/postfix/extra.cf:/opt/postfix/conf/extra.cf:z + - ./data/conf/postfix:/opt/postfix/conf:z - ./data/assets/ssl:/etc/ssl/mail/:ro,z - postfix-vol-1:/var/spool/postfix - crypt-vol-1:/var/lib/zeyple