Browse Source

concierge-backup rewrite in python3, concierge-secaudit renamed to concierge-permaudit

guillaume 7 years ago
parent
commit
6533be1777
2 changed files with 134 additions and 126 deletions
  1. 134 126
      src/concierge-backup
  2. 0 0
      src/concierge-permaudit

+ 134 - 126
src/concierge-backup

@@ -1,131 +1,139 @@
-#!/bin/sh
-# Requirements : borgbackup
-
-# Read configuration
-. /etc/concierge/backup.cfg
-
-if [ -z "${LOCAL_DIR}" ]; then
-  echo >&2 "LOCAL_DIR is not set.  Aborting.";
-  exit 1;
-fi
-
-if [ -z "${REMOTE_ENABLE}" ]; then
-
-  echo >&2 "REMOTE_ENABLE is not set.  Aborting.";
-  exit 1;
-
-elif [ ${REMOTE_ENABLE} = true ]; then
-
-  if [ -z "${REMOTE_USER}" ]; then
-    echo >&2 "REMOTE_USER is not set.  Aborting.";
-    exit 1;
-  fi
-
-  if [ -z "${REMOTE_HOSTNAME}" ]; then
-    echo >&2 "REMOTE_HOSTNAME is not set.  Aborting.";
-    exit 1;
-  fi
-
-  if [ -z "${REMOTE_DIR}" ]; then
-    echo >&2 "REMOTE_DIR is not set.  Aborting.";
-    exit 1;
-  fi
-
-fi
-
-if [ -z "${ENCRYPTION_ENABLE}" ]; then
-
-  echo >&2 "ENCRYPTION_ENABLE is not set.  Aborting.";
-  exit 1;
-
-elif [ ${ENCRYPTION_ENABLE} = true ]; then
-
-  if [ -z "${ENCRYPTION_PASSPHRASE_PATH}" ]; then
-    echo >&2 "ENCRYPTION_PASSPHRASE_PATH is not set.  Aborting.";
-    exit 1;
-  fi
-
-  ENCRYPTION=--encryption=repokey
-  export BORG_PASSPHRASE=`cat ${ENCRYPTION_PASSPHRASE_PATH}`
-
-else
-
-  ENCRYPTION=--encryption=none
-
-fi
-
-if [ -z "${PGSQL_EXCLUDE_DATNAME}" ]; then
-  PGSQL_EXCLUDE_DATNAME="['template_']"
-fi
-
-HOSTNAME=`hostname`
-LOCAL_DEST=${LOCAL_DIR}/${HOSTNAME}.borg
-
-DATE=`date +%Y-%m-%d`
-BACKUP_NAME="${HOSTNAME}_${DATE}"
-
-# Temporary directory for backup
-BACKUP_TEMP="${LOCAL_DIR}/output-daily"
-
-if ! type "borg" > /dev/null; then
-  echo >&2 "borgbackup is not installed.  Aborting.";
-  exit 1;
-fi
-
-if [ -d "${BACKUP_TEMP}" ]; then
-  echo >&2 "Directory ${BACKUP_TEMP} already exists.  Aborting.";
-  exit 1;
-else
-  mkdir "${BACKUP_TEMP}"
-fi
-
-# Backup ejabberd data if ejabberd_ctl is available
-if type "ejabberdctl" > /dev/null; then
-  EJABBERD_BACKUP=`mktemp -p ~ejabberd/`
-  ejabberdctl backup "$EJABBERD_BACKUP"
-  mv "$EJABBERD_BACKUP" $BACKUP_TEMP/ejabberd.backup
-fi
-
-# Backup PostgreSQL data if pg_dump is available, and user postgres exists
-if type "pg_dump" > /dev/null; then
-  if id -u "postgres" > /dev/null 2>&1; then
-    PG_DBS=`sudo -i -u postgres psql template1 -t -c "SELECT datname FROM pg_database WHERE NOT datname LIKE ANY(ARRAY${PGSQL_EXCLUDE_DATNAME})"`
-    for PG_DB in $PG_DBS
-    do
-      PG_BACKUP=`sudo -u postgres mktemp -t pg_dump_XXXX`
-      sudo -u postgres -i pg_dump -Z3 -Fc "${PG_DB}" > "${PG_BACKUP}"
-      mv "${PG_BACKUP}" "${BACKUP_TEMP}/pgsql_${PG_DB}_Fc.dump"
-    done
-  fi
-fi
-
-if [ ! -d "${LOCAL_DEST}" ]; then
-  # Initialize local borg repository
-  borg init ${ENCRYPTION} "${LOCAL_DEST}"
-fi
-
-# Do local backup
-borg create --exclude-from /etc/concierge/backup.exclude ${LOCAL_DEST}::${BACKUP_NAME} "${BACKUP_TEMP}" ${BACKUP_DIR_INCL}
+#!/usr/bin/python3
+
+import configparser
+import datetime
+import os
+import pwd
+import signal
+import socket
+import shutil
+import subprocess
+import sys
+import tempfile
+
+users = set(map(lambda x : x[0], pwd.getpwall()))
+
+class EjabberdTask:
+  def __init__(self, outputPath):
+    self.outputPath = outputPath
+  def is_supported(self):
+    return shutil.which('ejabberdctl') != None and 'ejabberd' in users
+  def run(self):
+    pwe = pwd.getpwnam('ejabberd')
+    (tempfh, tempfname) = tempfile.mkstemp(dir = pwe.pw_dir)
+    returncode = subprocess.call(['ejabberdctl','backup',tempfname])
+    os.close(tempfh)
+    if returncode == 0:
+      outfname = os.path.join(self.outputPath, 'ejabberd_backup')
+      shutil.move(tempfname, outfname)
+    else:
+      os.remove(tempfname)
+
+class PgDumpTask:
+  def __init__(self, outputPath, excludeDatname):
+    self.outputPath = outputPath
+    self.excludeDatname = excludeDatname
+  def is_supported(self):
+    return shutil.which('pg_dump') != None and 'postgres' in users
+  def run(self):
+    pwe = pwd.getpwnam('postgres')
+    command = 'psql template1 -t -c "SELECT datname FROM pg_database WHERE NOT datname LIKE ANY(ARRAY%s)"' % self.excludeDatname
+    comOut = subprocess.check_output(['su','-c',command,'postgres'], cwd=pwe.pw_dir)
+    dbs = comOut.decode('utf-8').strip("\n ").split("\n ")
+    for dbname in dbs:
+      tempfname = os.path.join(self.outputPath, 'psql_%s_Fc.dump' % dbname)
+      tempfh = open(tempfname, mode='w')
+      command = 'pg_dump -Z3 -Fc "%s"' % (dbname)
+      subprocess.call(['su','-c',command,'postgres'], cwd=pwe.pw_dir, stdout=tempfh)
+
+backupTemp = None
+
+def sighandler(signum, frame):
+  if backupTemp != None:
+    print("Backup was interrupted. Deleting temporary directory")
+    shutil.rmtree(backupTemp)
+    sys.exit(1)
+
+signal.signal(signal.SIGINT, sighandler)
+signal.signal(signal.SIGTERM, sighandler)
+
+conf = configparser.ConfigParser()
+confFile = open('/etc/concierge/backup.cfg', 'r')
+confStr = "[Common]\n" + confFile.read()
+conf.read_string(confStr)
+
+hostname = socket.gethostname()
+backupName = "%s_%s" % (hostname, datetime.date.today().isoformat())
+
+localDir = conf.get('Common', 'LOCAL_DIR')
+localDest = os.path.join(localDir, hostname + ".borg")
+
+remoteEnable = conf.getboolean('Common', 'REMOTE_ENABLE')
+remoteDest = None
+remoteRepo = None
+if remoteEnable:
+  remoteUser = conf.get('Common', 'REMOTE_USER')
+  remoteHost = conf.get('Common', 'REMOTE_HOSTNAME')
+  remoteDir = conf.get('Common', 'REMOTE_DIR')
+  remoteDest = os.path.join(remoteDir, hostname + ".borg")
+  remoteRepo = "%s@%s:%s" % (remoteUser, remoteHost, remoteDest)
+
+encryptionEnable = conf.getboolean('Common', 'ENCRYPTION_ENABLE')
+encryption = "--encryption=none"
+passphrase = None
+if encryptionEnable:
+  encryption = "--encryption=repokey"
+  passphrasePath = conf.get('Common', 'ENCRYPTION_PASSPHRASE_PATH')
+  passphraseFile = open(passphrasePath, 'r')
+  passphrase = passphraseFile.read().strip("\r\n")
+  os.environ['BORG_PASSPHRASE'] = passphrase
+
+pgsqlExcludeDatname = conf.get('Common', 'PGSQL_EXCLUDE_DATNAME', fallback="['template_']").strip('"')
+
+# Temporary directory
+backupTemp = os.path.join(localDir, "output-daily")
+if os.path.exists(backupTemp):
+  print("Directory %s already exists. Aborting. " % backupTemp)
+  sys.exit(1)
+else:
+  os.makedirs(backupTemp)
+
+# Run software-specific backup tasks
+tasks = []
+tasks.append(EjabberdTask(backupTemp))
+tasks.append(PgDumpTask(backupTemp, pgsqlExcludeDatname))
+
+for task in tasks:
+  if task.is_supported():
+    try:
+      task.run()
+    except Exception as e:
+      print("Task %s failed: %s" % (task.__class__.__name__, str(e)))
+
+# Enumerate directories to backup
+backupInclPaths = [backupTemp]
+backupInclPaths += conf.get('Common', 'BACKUP_DIR_INCL', fallback='').strip('"').split(' ')
+# Initialize local borg repository
+if not os.path.exists(localDest):
+  subprocess.call(['borg','init', encryption, localDest])
+
+# Do a local backup
+localArchive = localDest + '::' + backupName
+subprocess.call(['borg','create','--exclude-from','/etc/concierge/backup.exclude',localArchive] + backupInclPaths)
 
 
 # Prune local archives to keep only 7 daily, and 4 weekly, and 3 monthly ones
 # Prune local archives to keep only 7 daily, and 4 weekly, and 3 monthly ones
-borg prune --keep-daily=7 --keep-weekly=4 --keep-monthly=3 ${LOCAL_DEST}
-
-if [ ${REMOTE_ENABLE} = true ]; then
-
-  REMOTE_DEST=${REMOTE_DIR}/${HOSTNAME}.borg
-
-  # Quick check of the remote repository before doing a remote backup
-  if borg check --repository-only ${REMOTE_USER}@${REMOTE_HOSTNAME}:${REMOTE_DEST}; then
-    # Do remote backup
-    borg create --exclude-from /etc/concierge/backup.exclude ${REMOTE_USER}@${REMOTE_HOSTNAME}:${REMOTE_DEST}::${BACKUP_NAME} "${BACKUP_TEMP}" ${BACKUP_DIR_INCL}
+subprocess.call(['borg','prune','--keep-daily=7','--keep-weekly=4','--keep-monthly=3',localDest])
+
+if remoteEnable:
+  returncode = subprocess.call(['borg','check','--repository-only',remoteRepo])
+  if returncode != 0:
+    print("Check failed on remote repository")
+  else:
+    # Do a remote backup
+    remoteArchive = remoteRepo + '::' + backupName
+    subprocess.call(['borg','create','--exclude-from','/etc/concierge/backup.exclude', remoteArchive] + backupInclPaths)
     # Prune remote archives to keep only 7 daily, and 4 weekly, and 3 monthly ones
     # Prune remote archives to keep only 7 daily, and 4 weekly, and 3 monthly ones
-    borg prune --keep-daily=7 --keep-weekly=4 --keep-monthly=3 ${REMOTE_USER}@${REMOTE_HOSTNAME}:${REMOTE_DEST}
-  else
-    echo >&2 "Remote repository is missing of corrupted. Try running: "
-    echo >&2 "borg init ${ENCRYPTION} ${REMOTE_USER}@${REMOTE_HOSTNAME}:${REMOTE_DEST}"
-  fi
-
-fi
+    subprocess.call(['borg','prune','--keep-daily=7','--keep-weekly=4','--keep-monthly=3',remoteRepo])
 
 
 # Delete temporary directory
 # Delete temporary directory
-rm -rf "${BACKUP_TEMP}"
+shutil.rmtree(backupTemp)

src/concierge-secaudit → src/concierge-permaudit