Files
modoboa-installer/modoboa_installer/database.py
Spitap 6b4302b566 Update from master
commit 5c22600d98
Merge: bc12ca7 bcdbb4a
Author: Antoine Nguyen <tonio@ngyn.org>
Date:   Tue Nov 29 16:54:28 2022 +0100

    Merge pull request #462 from Spitfireap/randomize-api-call-time

    randomize api call time

commit bcdbb4a2ce
Author: Spitap <dev@asdrip.fr>
Date:   Tue Nov 29 14:53:05 2022 +0100

    fix typo

commit bd1ddcef21
Author: Spitap <dev@asdrip.fr>
Date:   Tue Nov 29 13:45:31 2022 +0100

    randomize api call time

commit bc12ca7327
Merge: d364239 bd0ecd0
Author: Antoine Nguyen <tonio@ngyn.org>
Date:   Mon Nov 14 15:49:41 2022 +0100

    Merge pull request #458 from Spitfireap/fix-include_try

    fix typo in dovecot configuration file

commit bd0ecd0949
Author: Spitap <dev@asdrip.fr>
Date:   Thu Nov 10 14:57:43 2022 +0100

    fix typo in dovecot configuration file

commit d364239348
Merge: 61838db 3763300
Author: Antoine Nguyen <tonio@ngyn.org>
Date:   Wed Nov 9 10:51:30 2022 +0100

    Merge pull request #456 from modoboa/feature/improved_backup_restore

    WIP: Improved backup/restore system.

commit 37633008cb
Author: Antoine Nguyen <tonio@ngyn.org>
Date:   Wed Nov 9 10:30:44 2022 +0100

    Fixed restore mode

commit d6f9a5b913
Author: Antoine Nguyen <tonio@ngyn.org>
Date:   Tue Nov 8 17:20:25 2022 +0100

    Few fixes.

commit 8b1d60ee59
Author: Antoine Nguyen <tonio@ngyn.org>
Date:   Tue Nov 8 17:19:23 2022 +0100

    Few fixes

commit 2b5edae5d5
Author: Antoine Nguyen <tonio@ngyn.org>
Date:   Sun Nov 6 10:30:24 2022 +0100

    WIP: Improved backup/restore system.

commit 61838dbe4d
Author: Antoine Nguyen <tonio@ngyn.org>
Date:   Sat Nov 5 09:30:50 2022 +0100

    Check if restore is defined before doing anything else.

    fix #453

commit 962cac3ad9
Merge: 1b192c5 ef2359a
Author: Antoine Nguyen <tonio@ngyn.org>
Date:   Fri Nov 4 09:41:20 2022 +0100

    Merge pull request #450 from Spitfireap/fixed-super-call

    fixed super call in modoboa's script

commit ef2359a2a8
Author: Spitap <dev@asdrip.fr>
Date:   Thu Nov 3 23:10:21 2022 +0100

    fixed super call

commit 1b192c5fd5
Merge: 754d652 b0b0146
Author: Antoine Nguyen <tonio@ngyn.org>
Date:   Thu Nov 3 15:34:48 2022 +0100

    Merge pull request #449 from Spitfireap/fixed-import-typo

    fixed constants import

commit b0b01465d9
Author: Spitap <dev@asdrip.fr>
Date:   Thu Nov 3 15:00:07 2022 +0100

    fixed constants import

commit 754d652fc2
Author: Antoine Nguyen <tonio@ngyn.org>
Date:   Thu Nov 3 12:27:04 2022 +0100

    Few fixes

commit cb5fa75693
Merge: 1afb8e6 e01265a
Author: Antoine Nguyen <tonio@ngyn.org>
Date:   Thu Nov 3 12:20:25 2022 +0100

    Merge pull request #444 from Spitfireap/tighter-config-file-perm

    tighter config file permission

commit 1afb8e61fc
Merge: 15c1779 8dd0b7d
Author: Antoine Nguyen <tonio@ngyn.org>
Date:   Thu Nov 3 12:17:16 2022 +0100

    Merge pull request #424 from Spitfireap/restore

    Backup & restore system

commit 8dd0b7d497
Author: Spitap <dev@asdrip.fr>
Date:   Thu Nov 3 10:57:03 2022 +0100

    Last camelCase

commit 554611b366
Author: Spitap <dev@asdrip.fr>
Date:   Thu Nov 3 10:54:06 2022 +0100

    review fix

commit 15c17796f2
Merge: ce8e7e6 84d1363
Author: Antoine Nguyen <tonio@ngyn.org>
Date:   Fri Oct 28 09:43:30 2022 +0200

    Merge pull request #446 from Spitfireap/fix-ssl-min-protocol

    fixed ssl_min_protocol setting

commit 84d13633a1
Author: Spitap <dev@asdrip.fr>
Date:   Thu Oct 27 22:37:47 2022 +0200

    fixed ssl_min_protocol setting

commit ce8e7e6027
Merge: 8e8ae5f fe7df27
Author: Antoine Nguyen <tonio@ngyn.org>
Date:   Thu Oct 27 17:56:37 2022 +0200

    Merge pull request #445 from Spitfireap/dovecot-fixes

    Fixes ssl permission error, updated ssl_protocol parameter

commit e01265a4ee
Merge: a5fba03 235ef3b
Author: Spitap <dev@asdrip.fr>
Date:   Thu Oct 27 17:44:37 2022 +0200

    Merge branch 'tighter-config-file-perm' of https://github.com/Spitfireap/modoboa-installer into tighter-config-file-perm

commit a5fba03264
Author: Spitap <dev@asdrip.fr>
Date:   Thu Oct 27 11:13:47 2022 +0200

    tighter config file permission

commit fe7df276fc
Author: Spitap <dev@asdrip.fr>
Date:   Thu Oct 27 17:25:39 2022 +0200

    Check dovecot version greater

commit 8f34f0af6f
Author: Spitap <dev@asdrip.fr>
Date:   Thu Oct 27 17:00:58 2022 +0200

    Fixes ssl permission error, updated ssl_protocol parameter

commit 8e8ae5fb9c
Merge: 67f6cee fefbf54
Author: Antoine Nguyen <tonio@ngyn.org>
Date:   Thu Oct 27 16:49:20 2022 +0200

    Merge pull request #439 from stefaweb/master

    Update config_dict_template.py for default max_servers value

commit 235ef3befb
Author: Spitap <dev@asdrip.fr>
Date:   Thu Oct 27 11:13:47 2022 +0200

    thighter config file permission

commit 67f6cee8ea
Merge: b84abbb 53f7f8e
Author: Antoine Nguyen <tonio@ngyn.org>
Date:   Tue Oct 25 19:32:37 2022 +0200

    Merge pull request #442 from Spitfireap/patch-1

    Set $max_server to 2 to avoid amavis crash

commit 5c9d5c9a03
Author: Spitap <dev@asdrip.fr>
Date:   Tue Oct 25 16:58:57 2022 +0200

    DKIM keys restore, Radicale backup/restore, fixes

commit 4c1f8710b5
Author: Spitap <dev@asdrip.fr>
Date:   Tue Oct 25 16:04:55 2022 +0200

    Added dkim key backup

commit e34eb4b337
Author: Spitap <dev@asdrip.fr>
Date:   Tue Oct 25 13:59:28 2022 +0200

    fix database path

commit 53f7f8ef9d
Author: Spitfireap <45575529+Spitfireap@users.noreply.github.com>
Date:   Wed Oct 19 08:19:40 2022 +0000

    Update config_dict_template.py

commit 35778cd614
Merge: 6726f5b b84abbb
Author: Spitfireap <45575529+Spitfireap@users.noreply.github.com>
Date:   Tue Oct 18 17:17:48 2022 +0200

    Merge branch 'modoboa:master' into restore

commit fefbf549a4
Author: Stephane Leclerc <sleclerc@actionweb.fr>
Date:   Thu Oct 6 13:36:13 2022 +0200

    Update config_dict_template.py for default max_server value

commit 6726f5b1a2
Author: Spitap <dev@asdrip.fr>
Date:   Mon Sep 26 13:39:28 2022 +0200

    Improved path generation, path mistake proofing

commit a192cbcbd0
Author: Spitap <dev@asdrip.fr>
Date:   Mon Sep 19 16:40:25 2022 +0200

    Updated doc, default path on conf file

commit 5bed9655ea
Author: Spitap <dev@asdrip.fr>
Date:   Mon Sep 19 15:53:19 2022 +0200

    fixed typo

commit 6b096a7470
Author: Spitap <dev@asdrip.fr>
Date:   Mon Sep 19 15:50:03 2022 +0200

    Simplified db dumps restore

commit e30add03fd
Merge: d75d83f 1f8dd1b
Author: Spitap <dev@asdrip.fr>
Date:   Mon Sep 19 15:39:05 2022 +0200

    Update from master

commit d75d83f202
Author: Spitap <dev@asdrip.fr>
Date:   Mon Sep 19 15:13:44 2022 +0200

    more refactoring

commit f3811b4b39
Author: Spitap <dev@asdrip.fr>
Date:   Mon Sep 19 14:59:43 2022 +0200

    refactoring

commit b0d56b3989
Author: Spitap <dev@asdrip.fr>
Date:   Thu Sep 15 11:32:57 2022 +0200

    PEP formating

commit 53e3e3ec58
Author: Spitap <dev@asdrip.fr>
Date:   Fri Aug 5 15:20:11 2022 +0200

    Better UX, use of os to concatenate path

commit e546d2cb23
Author: Spitap <dev@asdrip.fr>
Date:   Wed Jul 27 16:32:59 2022 +0200

    Better UX

commit 70faa1c5cb
Author: Spitap <dev@asdrip.fr>
Date:   Wed Jul 27 15:58:41 2022 +0200

    Fixed backupdir index

commit 563979a7dd
Author: Spitap <dev@asdrip.fr>
Date:   Wed Jul 27 15:51:22 2022 +0200

    fixed mail backup/restore

commit ee2ccf0647
Author: Spitap <dev@asdrip.fr>
Date:   Wed Jul 27 14:35:48 2022 +0200

    Fixed postfix install, added restore to readme

commit 2077c94b52
Author: Spitap <dev@asdrip.fr>
Date:   Tue Jul 26 17:05:00 2022 +0200

    Fix amavis config file not copied to right location

commit 4a7222bd24
Author: Spitap <dev@asdrip.fr>
Date:   Tue Jul 26 16:53:24 2022 +0200

    Fixed nginx call to uwsgi

commit e7b6104195
Author: Spitap <dev@asdrip.fr>
Date:   Tue Jul 26 16:39:41 2022 +0200

    fixed install within class

commit 4a00590354
Author: Spitap <dev@asdrip.fr>
Date:   Tue Jul 26 16:20:03 2022 +0200

    fixed restore disclamer

commit 15768c429e
Author: Spitap <dev@asdrip.fr>
Date:   Tue Jul 26 12:07:42 2022 +0200

    Restore workflow done

commit 439ffb94c4
Author: Spitap <dev@asdrip.fr>
Date:   Mon Jul 25 18:54:47 2022 +0200

    initial commit

commit 37bc21dfd3
Author: Spitap <dev@asdrip.fr>
Date:   Tue Jul 26 10:36:08 2022 +0200

    Backup postewhite.conf instead of custom whitelist

    Postwhite.conf contains a custom host list

commit 26204143af
Merge: 2097055 d495afd
Author: Spitap <dev@asdrip.fr>
Date:   Mon Jul 25 22:10:26 2022 +0200

    Merge branch 'master' into backup

commit 20970557de
Author: Spitap <dev@asdrip.fr>
Date:   Mon Jul 25 22:05:35 2022 +0200

    Allow to disable mail backup

commit 632c26596e
Author: Spitap <dev@asdrip.fr>
Date:   Mon Jul 25 21:52:15 2022 +0200

    Update backup readme

commit 9e1c18cd6b
Author: Spitap <dev@asdrip.fr>
Date:   Thu Jul 21 19:09:53 2022 +0200

    Fix argument passed as list instead of string

commit db6457c5f5
Author: Spitap <dev@asdrip.fr>
Date:   Thu Jul 21 19:07:18 2022 +0200

    better path handling

commit 579faccfa5
Author: Spitap <dev@asdrip.fr>
Date:   Thu Jul 21 19:00:32 2022 +0200

    added an automatic bash option (no path provided) or a path provided bash (for cron job)

commit 5318fa279b
Author: Spitap <dev@asdrip.fr>
Date:   Thu Jul 21 18:00:50 2022 +0200

    bash option

commit 74de6a9bb1
Author: Spitap <dev@asdrip.fr>
Date:   Thu Jul 21 17:31:56 2022 +0200

    Reset pgpass before trying to backup secondary dbs

commit 54185a7c5a
Author: Spitap <dev@asdrip.fr>
Date:   Thu Jul 21 17:26:40 2022 +0200

    Fix database backup logic issue

commit 1f9d69c37c
Author: Spitap <dev@asdrip.fr>
Date:   Thu Jul 21 17:21:59 2022 +0200

    Fix copy issue

commit 8d02d2a9fb
Author: Spitap <dev@asdrip.fr>
Date:   Thu Jul 21 17:09:23 2022 +0200

    added safe mkdir in utils, use utils.mkdir_safe() in backup

commit 6f604a5fec
Author: Spitap <dev@asdrip.fr>
Date:   Thu Jul 21 16:53:56 2022 +0200

    Fix loop logic

commit 568c4a65a0
Author: Spitap <dev@asdrip.fr>
Date:   Thu Jul 21 16:51:32 2022 +0200

    fix none-type passed to os.path

commit dc84a79528
Author: Spitap <dev@asdrip.fr>
Date:   Thu Jul 21 14:12:35 2022 +0200

    Note : capitalize affects only first letter

commit 304e25fa3c
Author: Spitap <dev@asdrip.fr>
Date:   Thu Jul 21 14:10:57 2022 +0200

    Fix getattr

commit 070efd61c4
Author: Spitap <dev@asdrip.fr>
Date:   Thu Jul 21 14:08:39 2022 +0200

    Fix import

commit 9917d8023e
Author: Spitap <dev@asdrip.fr>
Date:   Thu Jul 21 14:02:41 2022 +0200

    Edited README, fix backup run process

commit 27b9de6755
Author: Spitap <dev@asdrip.fr>
Date:   Thu Jul 21 13:48:44 2022 +0200

    database backup

commit 56ed214fb5
Author: Spitap <dev@asdrip.fr>
Date:   Tue Jul 19 19:06:53 2022 +0200

    Starting work on backup system
2022-12-22 18:47:55 +01:00

287 lines
9.9 KiB
Python

"""Database related tools."""
import os
import pwd
import stat
from . import package
from . import system
from . import utils
class Database(object):
"""Common database backend."""
default_port = None
packages = None
service = None
def __init__(self, config):
"""Install if necessary."""
self.config = config
engine = self.config.get("database", "engine")
self.dbhost = self.config.get("database", "host")
self.dbport = self.config.getint(
"database", "port", fallback=self.default_port)
self.dbuser = config.get(engine, "user")
self.dbpassword = config.get(engine, "password")
if self.config.getboolean("database", "install"):
self.install_package()
def install_package(self):
"""Install database package if required."""
package.backend.install_many(self.packages[package.backend.FORMAT])
system.enable_and_start_service(self.service)
class PostgreSQL(Database):
"""Postgres."""
default_port = 5432
packages = {
"deb": ["postgresql", "postgresql-server-dev-all"],
"rpm": ["postgresql-server", "postgresql-server-devel", "postgresql"]
}
service = "postgresql"
def __init__(self, config):
super().__init__(config)
self._pgpass_done = False
def install_package(self):
"""Install database if required."""
name, version = utils.dist_info()
if "CentOS" in name:
initdb_cmd = "postgresql-setup --initdb"
cfgfile = "/var/lib/pgsql/data/pg_hba.conf"
package.backend.install_many(self.packages[package.backend.FORMAT])
utils.exec_cmd(initdb_cmd)
pattern = "s/^host(.+)ident$/host$1md5/"
utils.exec_cmd("perl -pi -e '{}' {}".format(pattern, cfgfile))
else:
package.backend.install_many(self.packages[package.backend.FORMAT])
system.enable_and_start_service(self.service)
def _exec_query(self, query, dbname=None, dbuser=None, dbpassword=None):
"""Exec a postgresql query."""
cmd = "psql"
if dbname:
cmd += " -d {}".format(dbname)
if dbuser:
self._setup_pgpass(dbname, dbuser, dbpassword)
cmd += " -h {} -p {} -U {} -w".format(
self.dbhost, self.dbport, dbuser)
query = query.replace("'", "'\"'\"'")
cmd = "{} -c '{}' ".format(cmd, query)
utils.exec_cmd(cmd, sudo_user=self.dbuser)
def create_user(self, name, password):
"""Create a user."""
query = "SELECT 1 FROM pg_roles WHERE rolname='{}'".format(name)
code, output = utils.exec_cmd(
"""psql -tAc "{}" | grep -q 1""".format(query),
sudo_user=self.dbuser)
if not code:
return
query = "CREATE USER {} PASSWORD '{}'".format(name, password)
self._exec_query(query)
def create_database(self, name, owner):
"""Create a database."""
code, output = utils.exec_cmd(
"psql -lqt | cut -d \| -f 1 | grep -w {} | wc -l"
.format(name), sudo_user=self.dbuser)
if code:
return
utils.exec_cmd(
"createdb {} -O {}".format(name, owner),
sudo_user=self.dbuser)
def grant_access(self, dbname, user):
"""Grant access to dbname."""
query = "GRANT ALL ON DATABASE {} TO {}".format(dbname, user)
self._exec_query(query)
def grant_right_on_table(self, dbname, table, user, right):
"""Grant specific right to user on table."""
query = "GRANT {} ON {} TO {}".format(
right.upper(), table, user)
self._exec_query(query, dbname=dbname)
def _setup_pgpass(self, dbname, dbuser, dbpasswd):
"""Setup .pgpass file."""
if self._pgpass_done:
return
if self.dbhost not in ["localhost", "127.0.0.1"]:
self._pgpass_done = True
return
pw = pwd.getpwnam(self.dbuser)
target = os.path.join(pw[5], ".pgpass")
with open(target, "w") as fp:
fp.write("127.0.0.1:*:{}:{}:{}\n".format(
dbname, dbname, dbpasswd))
mode = stat.S_IRUSR | stat.S_IWUSR
os.chmod(target, mode)
os.chown(target, pw[2], pw[3])
self._pgpass_done = True
def load_sql_file(self, dbname, dbuser, dbpassword, path):
"""Load SQL file."""
self._setup_pgpass(dbname, dbuser, dbpassword)
cmd = "psql -h {} -p {} -d {} -U {} -w < {}".format(
self.dbhost, self.dbport, dbname, dbuser, path)
utils.exec_cmd(cmd, sudo_user=self.dbuser)
def dump_database(self, dbname, dbuser, dbpassword, path):
"""Dump DB to SQL file."""
# Reset pgpass since we backup multiple db (different secret set)
self._pgpass_done = False
self._setup_pgpass(dbname, dbuser, dbpassword)
cmd = "pg_dump -h {} -d {} -U {} -O -w > {}".format(
self.dbhost, dbname, dbuser, path)
utils.exec_cmd(cmd, sudo_user=self.dbuser)
class MySQL(Database):
"""MySQL backend."""
default_port = 3306
packages = {
"deb": ["mariadb-server"],
"rpm": ["mariadb", "mariadb-devel", "mariadb-server"],
}
service = "mariadb"
def _escape(self, query):
"""Replace special characters."""
return query.replace("'", "'\"'\"'")
def install_package(self):
"""Preseed package installation."""
name, version = utils.dist_info()
name = name.lower()
if name.startswith("debian"):
if version.startswith("8"):
self.packages["deb"].append("libmysqlclient-dev")
elif version.startswith("11"):
self.packages["deb"].append("libmariadb-dev")
else:
self.packages["deb"].append("libmariadbclient-dev")
elif name == "ubuntu":
self.packages["deb"].append("libmysqlclient-dev")
super(MySQL, self).install_package()
queries = []
if name.startswith("debian"):
if version.startswith("8"):
package.backend.preconfigure(
"mariadb-server", "root_password", "password",
self.dbpassword)
package.backend.preconfigure(
"mariadb-server", "root_password_again", "password",
self.dbpassword)
return
if version.startswith("11"):
queries = [
"SET PASSWORD FOR 'root'@'localhost' = PASSWORD('{}')"
.format(self.dbpassword),
"flush privileges"
]
if not queries:
queries = [
"UPDATE user SET plugin='' WHERE user='root'",
"UPDATE user SET password=PASSWORD('{}') WHERE USER='root'"
.format(self.dbpassword),
"flush privileges"
]
for query in queries:
utils.exec_cmd(
"mysql -D mysql -e '{}'".format(self._escape(query)))
def _exec_query(self, query, dbname=None, dbuser=None, dbpassword=None):
"""Exec a mysql query."""
if dbuser is None and dbpassword is None:
dbuser = self.dbuser
dbpassword = self.dbpassword
cmd = "mysql -h {} -P {} -u {}".format(
self.dbhost, self.dbport, dbuser)
if dbpassword:
cmd += " -p{}".format(dbpassword)
if dbname:
cmd += " -D {}".format(dbname)
utils.exec_cmd(cmd + """ -e '{}' """.format(self._escape(query)))
def create_user(self, name, password):
"""Create a user."""
self._exec_query(
"CREATE USER '{}'@'%' IDENTIFIED BY '{}'".format(
name, password))
self._exec_query(
"CREATE USER '{}'@'localhost' IDENTIFIED BY '{}'".format(
name, password))
def create_database(self, name, owner):
"""Create a database."""
self._exec_query(
"CREATE DATABASE IF NOT EXISTS {} "
"DEFAULT CHARACTER SET {} "
"DEFAULT COLLATE {}".format(
name, self.config.get("mysql", "charset"),
self.config.get("mysql", "collation"))
)
self.grant_access(name, owner)
def grant_access(self, dbname, user):
"""Grant access to dbname."""
self._exec_query(
"GRANT ALL PRIVILEGES ON {}.* to '{}'@'%'"
.format(dbname, user))
self._exec_query(
"GRANT ALL PRIVILEGES ON {}.* to '{}'@'localhost'"
.format(dbname, user))
def grant_right_on_table(self, dbname, table, user, right):
"""Grant specific right to user on table."""
query = "GRANT {} ON {}.{} TO '{}'@'%'".format(
right.upper(), dbname, table, user)
self._exec_query(query)
def load_sql_file(self, dbname, dbuser, dbpassword, path):
"""Load SQL file."""
utils.exec_cmd(
"mysql -h {} -P {} -u {} -p{} {} < {}".format(
self.dbhost, self.dbport, dbuser, dbpassword, dbname, path)
)
def dump_database(self, dbname, dbuser, dbpassword, path):
"""Dump DB to SQL file."""
cmd = "mysqldump -h {} -u {} -p{} {} > {}".format(
self.dbhost, dbuser, dbpassword, dbname, path)
utils.exec_cmd(cmd, sudo_user=self.dbuser)
def get_backend(config):
"""Return appropriate backend."""
engine = config.get("database", "engine")
if engine == "postgres":
backend = PostgreSQL
elif engine == "mysql":
backend = MySQL
else:
raise utils.FatalError("database engine not supported")
return backend(config)
def create(config, name, password):
"""Create a database and a user."""
backend = get_backend(config)
backend.create_user(name, password)
backend.create_database(name)
def grant_database_access(config, name, user):
"""Grant access to a database."""
get_backend(config).grant_access(name, user)