Generate the SOGo module's MariaDB password at install time instead of shipping a constant

Disclaimer: worked out together with an AI assistant (Claude).

What I found

The generated sogo.conf of the sogo1 module contains the database credentials in clear text:

SOGoProfileURL = "mysql://sogo:Nethesis,1234@127.0.0.1:3306/sogo/sogo_user_profile";
OCSFolderInfoURL = "mysql://sogo:Nethesis,1234@127.0.0.1:3306/sogo/sogo_folder_info";
...

The password is a constant. It does not appear in ~/.config/state/environment, and expand-configuration does not pass it in via properties — it is baked into the shipped Jinja2 template, identical on every installation.

Why I think it is worth changing

To be clear about the actual exposure: MariaDB is not published outside the pod. The unit only exposes SOGo itself (--publish 127.0.0.1:${TCP_PORT}:20001). Reaching the database therefore requires either the sogo1 module user or root on the node — and anyone with either already has access to everything the database holds. So this is not a remotely exploitable hole, and I would not call it a vulnerability.

Where it does matter is defence in depth. An attacker who gains code execution inside the sogo-app container through an application-level flaw currently has full database access immediately, with no further step, because the credential is publicly known from the module source. A password generated at install time would cost that attacker an additional step. That is the whole argument — modest, but it is the kind of step that is cheap to add once and free thereafter.

There is also a practical side: the constant makes it awkward to paste diagnostic output. Every cat sogo.conf in a forum thread leaks a password — harmless here precisely because it is the well-known default, but it trains a bad habit.

Why it looks feasible

The mechanism already exists in the same module. sogo-app.service runs:

ExecStartPre=/usr/local/bin/runagent reveal-master-secret

So secret handling is in place — the database password is simply not routed through it. Making it a generated value would mean adding it to the module environment and referencing it from the template like the other parameters (workers_count, ldap_password, etc. are already handled that way).

As it stands, an administrator cannot practically change it: it would have to be altered in the template and inside the MariaDB container at the same time, and any template edit is lost on the next module update unless carried in a sogo.conf.local override — which then also freezes the rest of the template.

Question

Is there a reason the SOGo module keeps this as a constant while other modules generate their secrets? If it is simply historical, would a change be considered — at least for new installations, with existing ones left as they are?

Best regards,
Thorsten

Thanks both for the analysis. From a pure security standpoint, I’d argue the gain here is close to nil.
mariadb-app.service has no --publish for the DB port: MariaDB is only reachable from inside the pod’s network namespace, shared with sogo-app. The only realistic way to reach it is code execution inside the sogo-app container itself — and at that point the attacker already reads the credential straight out of sogo.conf, generated or not, because the application needs it in clear text to function. Generating the password doesn’t remove that read path, it just changes its value.
The one case where it would help: an admin accidentally exposing port 3306 outside the pod (firewall rule, NAT, manual podman mapping change). There a generated password forces at least a brute-force instead of a known default. That’s a real but narrow scenario, and it’s a misconfiguration case, not a flaw in the module itself.
Weighed against that: generating and rotating the secret safely (keeping ALTER USER in MariaDB in sync with the generated value, across module reinstalls and Core Updates, without a window where the app has a stale password) is real ongoing complexity, for a benefit that only covers a network-misconfiguration edge case.
So I wouldn’t call this a bug, more a defense-in-depth trade-off where the cost currently looks higher than the benefit.

Agreed, we are not talking about a security issue but about best practice as the dot on an i.

the fix is generated against ldap, also I released a fix against the dashboard of sogo

When you have administrator access, you can see pretty much everything anyway. If you look at SOGo itself, for example, the configuration might only be around 256 characters, but at the end of the day, it will still be stored in clear text in a configuration file.

I’m not sure there’s really a way around that, WordPress does the same, as do pretty much any LAMP application.

[root@R1-pve ~]# cat /home/sogo1/.config/state/config/sogo.conf 
{

  /* 10 Database configuration (mysql) */
    SOGoProfileURL = "mysql://sogo:Nethesis,1234@127.0.0.1:3306/sogo/sogo_user_profile";
    OCSFolderInfoURL = "mysql://sogo:Nethesis,1234@127.0.0.1:3306/sogo/sogo_folder_info";
    OCSSessionsFolderURL = "mysql://sogo:Nethesis,1234@127.0.0.1:3306/sogo/sogo_sessions_folder"; 
    OCSEMailAlarmsFolderURL = "mysql://sogo:Nethesis,1234@127.0.0.1:3306/sogo/sogo_alarms_folder";
    OCSAdminURL = "mysql://sogo:Nethesis,1234@127.0.0.1:3306/sogo/sogo_admin";

I am opened to a pull request if you have an idea, please feel free to go ahead and share them.