2019-05-10 21:41:24 +08:00
#!/usr/bin/env bash
2018-01-12 15:30:54 +08:00
2018-10-15 17:37:10 +08:00
if [ [ ! -z ${ MAILCOW_BACKUP_LOCATION } ] ] ; then
BACKUP_LOCATION = " ${ MAILCOW_BACKUP_LOCATION } "
fi
2018-01-12 15:30:54 +08:00
if [ [ ! ${ 1 } = ~ ( backup| restore) ] ] ; then
echo "First parameter needs to be 'backup' or 'restore'"
exit 1
fi
[Docker API] Use TLS encryption for communication with "on-the-fly" created key paris (non-exposed)
[Docker API] Create pipe to pass Rspamd UI worker password
[Dovecot] Pull Spamassassin ruleset to be read by Rspamd (MANY THANKS to Peer Heinlein!)
[Dovecot] Garbage collector for deleted maildirs (set keep time via MAILDIR_GC_TIME which defaults to 1440 minutes)
[Web] Flush memcached after mailbox item changes, fixes #1808
[Web] Fix duplicate IDs, fixes #1792
[Compose] Use SQL sockets
[PHP-FPM] Update APCu and Redis libs
[Dovecot] Encrypt maildir with global key pair in crypt-vol-1 (BACKUP!), also fixes #1791
[Web] Fix deletion of spam aliases
[Helper] Add "crypt" to backup script
[Helper] Override file for external SQL socket (not supported!)
[Compose] New images for Rspamd, PHP-FPM, SOGo, Dovecot, Docker API, Watchdog, ACME, Postfix
2018-09-30 04:01:23 +08:00
if [ [ ${ 1 } = = "backup" && ! ${ 2 } = ~ ( crypt| vmail| redis| rspamd| postfix| mysql| all) ] ] ; then
echo "Second parameter needs to be 'vmail', 'crypt', 'redis', 'rspamd', 'postfix', 'mysql' or 'all'"
2018-01-12 15:30:54 +08:00
exit 1
fi
if [ [ -z ${ BACKUP_LOCATION } ] ] ; then
while [ [ -z ${ BACKUP_LOCATION } ] ] ; do
read -ep "Backup location (absolute path, starting with /): " BACKUP_LOCATION
done
fi
if [ [ ! ${ BACKUP_LOCATION } = ~ ^/ ] ] ; then
echo "Backup directory needs to be given as absolute path (starting with /)."
exit 1
fi
if [ [ -f ${ BACKUP_LOCATION } ] ] ; then
echo " ${ BACKUP_LOCATION } is a file! "
exit 1
fi
if [ [ ! -d ${ BACKUP_LOCATION } ] ] ; then
echo " ${ BACKUP_LOCATION } is not a directory "
read -p "Create it now? [y|N] " CREATE_BACKUP_LOCATION
if [ [ ! ${ CREATE_BACKUP_LOCATION ,, } = ~ ^( yes| y) $ ] ] ; then
exit 1
else
2019-02-09 01:11:46 +08:00
mkdir -p ${ BACKUP_LOCATION }
2018-01-12 15:30:54 +08:00
chmod 755 ${ BACKUP_LOCATION }
fi
else
if [ [ ${ 1 } = = "backup" ] ] && [ [ -z $( echo $( stat -Lc %a ${ BACKUP_LOCATION } ) | grep -oE '[0-9][0-9][5-7]' ) ] ] ; then
echo " ${ BACKUP_LOCATION } is not write-able for others, that's required for a backup. "
exit 1
fi
fi
BACKUP_LOCATION = $( echo ${ BACKUP_LOCATION } | sed 's#/$##' )
SCRIPT_DIR = " $( cd " $( dirname " ${ BASH_SOURCE [0] } " ) " && pwd ) "
COMPOSE_FILE = ${ SCRIPT_DIR } /../docker-compose.yml
echo " Using ${ BACKUP_LOCATION } as backup/restore location. "
echo
source ${ SCRIPT_DIR } /../mailcow.conf
2018-04-27 04:57:20 +08:00
CMPS_PRJ = $( echo $COMPOSE_PROJECT_NAME | tr -cd "[A-Za-z-_]" )
2018-01-12 15:30:54 +08:00
function backup( ) {
DATE = $( date +"%Y-%m-%d-%H-%M-%S" )
mkdir -p " ${ BACKUP_LOCATION } /mailcow- ${ DATE } "
chmod 755 " ${ BACKUP_LOCATION } /mailcow- ${ DATE } "
2018-07-29 20:27:51 +08:00
cp " ${ SCRIPT_DIR } /../mailcow.conf " " ${ BACKUP_LOCATION } /mailcow- ${ DATE } "
2018-01-12 15:30:54 +08:00
while ( ( " $# " ) ) ; do
case " $1 " in
vmail| all)
docker run --rm \
-v ${ BACKUP_LOCATION } /mailcow-${ DATE } :/backup \
2019-01-05 07:52:37 +08:00
-v $( docker volume ls -qf name = ${ CMPS_PRJ } _vmail-vol-1) :/vmail:ro \
2019-06-29 04:49:02 +08:00
debian:stretch-slim /bin/tar --warning= 'no-file-ignored' --use-compress-program= "gzip --rsyncable --best" -Pcvpf /backup/backup_vmail.tar.gz /vmail
2018-01-12 15:30:54 +08:00
; ; &
[Docker API] Use TLS encryption for communication with "on-the-fly" created key paris (non-exposed)
[Docker API] Create pipe to pass Rspamd UI worker password
[Dovecot] Pull Spamassassin ruleset to be read by Rspamd (MANY THANKS to Peer Heinlein!)
[Dovecot] Garbage collector for deleted maildirs (set keep time via MAILDIR_GC_TIME which defaults to 1440 minutes)
[Web] Flush memcached after mailbox item changes, fixes #1808
[Web] Fix duplicate IDs, fixes #1792
[Compose] Use SQL sockets
[PHP-FPM] Update APCu and Redis libs
[Dovecot] Encrypt maildir with global key pair in crypt-vol-1 (BACKUP!), also fixes #1791
[Web] Fix deletion of spam aliases
[Helper] Add "crypt" to backup script
[Helper] Override file for external SQL socket (not supported!)
[Compose] New images for Rspamd, PHP-FPM, SOGo, Dovecot, Docker API, Watchdog, ACME, Postfix
2018-09-30 04:01:23 +08:00
crypt| all)
docker run --rm \
-v ${ BACKUP_LOCATION } /mailcow-${ DATE } :/backup \
2019-01-05 07:52:37 +08:00
-v $( docker volume ls -qf name = ${ CMPS_PRJ } _crypt-vol-1) :/crypt:ro \
2019-06-29 04:49:02 +08:00
debian:stretch-slim /bin/tar --warning= 'no-file-ignored' --use-compress-program= "gzip --rsyncable --best" -Pcvpf /backup/backup_crypt.tar.gz /crypt
[Docker API] Use TLS encryption for communication with "on-the-fly" created key paris (non-exposed)
[Docker API] Create pipe to pass Rspamd UI worker password
[Dovecot] Pull Spamassassin ruleset to be read by Rspamd (MANY THANKS to Peer Heinlein!)
[Dovecot] Garbage collector for deleted maildirs (set keep time via MAILDIR_GC_TIME which defaults to 1440 minutes)
[Web] Flush memcached after mailbox item changes, fixes #1808
[Web] Fix duplicate IDs, fixes #1792
[Compose] Use SQL sockets
[PHP-FPM] Update APCu and Redis libs
[Dovecot] Encrypt maildir with global key pair in crypt-vol-1 (BACKUP!), also fixes #1791
[Web] Fix deletion of spam aliases
[Helper] Add "crypt" to backup script
[Helper] Override file for external SQL socket (not supported!)
[Compose] New images for Rspamd, PHP-FPM, SOGo, Dovecot, Docker API, Watchdog, ACME, Postfix
2018-09-30 04:01:23 +08:00
; ; &
2018-01-12 15:30:54 +08:00
redis| all)
2018-01-15 03:53:26 +08:00
docker exec $( docker ps -qf name = redis-mailcow) redis-cli save
2018-01-12 15:30:54 +08:00
docker run --rm \
-v ${ BACKUP_LOCATION } /mailcow-${ DATE } :/backup \
2019-01-05 07:52:37 +08:00
-v $( docker volume ls -qf name = ${ CMPS_PRJ } _redis-vol-1) :/redis:ro \
2019-06-29 04:49:02 +08:00
debian:stretch-slim /bin/tar --warning= 'no-file-ignored' --use-compress-program= "gzip --rsyncable --best" -Pcvpf /backup/backup_redis.tar.gz /redis
2018-01-12 15:30:54 +08:00
; ; &
rspamd| all)
docker run --rm \
-v ${ BACKUP_LOCATION } /mailcow-${ DATE } :/backup \
2019-01-05 07:52:37 +08:00
-v $( docker volume ls -qf name = ${ CMPS_PRJ } _rspamd-vol-1) :/rspamd:ro \
2019-06-29 04:49:02 +08:00
debian:stretch-slim /bin/tar --warning= 'no-file-ignored' --use-compress-program= "gzip --rsyncable --best" -Pcvpf /backup/backup_rspamd.tar.gz /rspamd
2018-01-12 15:30:54 +08:00
; ; &
postfix| all)
2018-01-14 05:09:17 +08:00
docker run --rm \
2018-01-12 15:30:54 +08:00
-v ${ BACKUP_LOCATION } /mailcow-${ DATE } :/backup \
2019-01-05 07:52:37 +08:00
-v $( docker volume ls -qf name = ${ CMPS_PRJ } _postfix-vol-1) :/postfix:ro \
2019-06-29 04:49:02 +08:00
debian:stretch-slim /bin/tar --warning= 'no-file-ignored' --use-compress-program= "gzip --rsyncable --best" -Pcvpf /backup/backup_postfix.tar.gz /postfix
2018-01-12 15:30:54 +08:00
; ; &
mysql| all)
SQLIMAGE = $( grep -iEo '(mysql|mariadb)\:.+' ${ COMPOSE_FILE } )
2018-01-14 05:09:17 +08:00
docker run --rm \
2018-10-23 17:07:27 +08:00
--network $( docker network ls -qf name = ${ CMPS_PRJ } _) \
2019-01-05 07:52:37 +08:00
-v $( docker volume ls -qf name = ${ CMPS_PRJ } _mysql-vol-1) :/var/lib/mysql/:ro \
2018-01-12 15:30:54 +08:00
--entrypoint= \
-v ${ BACKUP_LOCATION } /mailcow-${ DATE } :/backup \
${ SQLIMAGE } /bin/sh -c " mysqldump -hmysql -uroot -p ${ DBROOT } --all-databases | gzip > /backup/backup_mysql.gz "
; ;
esac
shift
done
}
function restore( ) {
docker stop $( docker ps -qf name = watchdog-mailcow)
RESTORE_LOCATION = " ${ 1 } "
shift
while ( ( " $# " ) ) ; do
case " $1 " in
vmail)
docker stop $( docker ps -qf name = dovecot-mailcow)
docker run -it --rm \
-v ${ RESTORE_LOCATION } :/backup \
2018-04-27 04:57:20 +08:00
-v $( docker volume ls -qf name = ${ CMPS_PRJ } _vmail-vol-1) :/vmail \
2018-07-10 04:28:09 +08:00
debian:stretch-slim /bin/tar -Pxvzf /backup/backup_vmail.tar.gz
2018-01-12 15:30:54 +08:00
docker start $( docker ps -aqf name = dovecot-mailcow)
echo
echo "In most cases it is not required to run a full resync, you can run the command printed below at any time after testing wether the restore process broke a mailbox:"
echo
echo " docker exec $( docker ps -qf name = dovecot-mailcow) doveadm force-resync -A '*' "
echo
read -p "Force a resync now? [y|N] " FORCE_RESYNC
if [ [ ${ FORCE_RESYNC ,, } = ~ ^( yes| y) $ ] ] ; then
docker exec $( docker ps -qf name = dovecot-mailcow) doveadm force-resync -A '*'
else
echo "OK, skipped."
fi
; ;
redis)
docker stop $( docker ps -qf name = redis-mailcow)
docker run -it --rm \
-v ${ RESTORE_LOCATION } :/backup \
2018-04-27 04:57:20 +08:00
-v $( docker volume ls -qf name = ${ CMPS_PRJ } _redis-vol-1) :/redis \
2018-07-10 04:28:09 +08:00
debian:stretch-slim /bin/tar -Pxvzf /backup/backup_redis.tar.gz
2018-01-12 15:30:54 +08:00
docker start $( docker ps -aqf name = redis-mailcow)
; ;
[Docker API] Use TLS encryption for communication with "on-the-fly" created key paris (non-exposed)
[Docker API] Create pipe to pass Rspamd UI worker password
[Dovecot] Pull Spamassassin ruleset to be read by Rspamd (MANY THANKS to Peer Heinlein!)
[Dovecot] Garbage collector for deleted maildirs (set keep time via MAILDIR_GC_TIME which defaults to 1440 minutes)
[Web] Flush memcached after mailbox item changes, fixes #1808
[Web] Fix duplicate IDs, fixes #1792
[Compose] Use SQL sockets
[PHP-FPM] Update APCu and Redis libs
[Dovecot] Encrypt maildir with global key pair in crypt-vol-1 (BACKUP!), also fixes #1791
[Web] Fix deletion of spam aliases
[Helper] Add "crypt" to backup script
[Helper] Override file for external SQL socket (not supported!)
[Compose] New images for Rspamd, PHP-FPM, SOGo, Dovecot, Docker API, Watchdog, ACME, Postfix
2018-09-30 04:01:23 +08:00
crypt)
docker stop $( docker ps -qf name = dovecot-mailcow)
docker run -it --rm \
-v ${ RESTORE_LOCATION } :/backup \
-v $( docker volume ls -qf name = ${ CMPS_PRJ } _crypt-vol-1) :/crypt \
debian:stretch-slim /bin/tar -Pxvzf /backup/backup_crypt.tar.gz
docker start $( docker ps -aqf name = dovecot-mailcow)
; ;
2018-01-12 15:30:54 +08:00
rspamd)
docker stop $( docker ps -qf name = rspamd-mailcow)
docker run -it --rm \
-v ${ RESTORE_LOCATION } :/backup \
2018-04-27 04:57:20 +08:00
-v $( docker volume ls -qf name = ${ CMPS_PRJ } _rspamd-vol-1) :/rspamd \
2018-07-10 04:28:09 +08:00
debian:stretch-slim /bin/tar -Pxvzf /backup/backup_rspamd.tar.gz
2018-01-12 15:30:54 +08:00
docker start $( docker ps -aqf name = rspamd-mailcow)
; ;
postfix)
docker stop $( docker ps -qf name = postfix-mailcow)
docker run -it --rm \
-v ${ RESTORE_LOCATION } :/backup \
2018-04-27 04:57:20 +08:00
-v $( docker volume ls -qf name = ${ CMPS_PRJ } _postfix-vol-1) :/postfix \
2018-07-10 04:28:09 +08:00
debian:stretch-slim /bin/tar -Pxvzf /backup/backup_postfix.tar.gz
2018-01-12 15:30:54 +08:00
docker start $( docker ps -aqf name = postfix-mailcow)
; ;
mysql)
SQLIMAGE = $( grep -iEo '(mysql|mariadb)\:.+' ${ COMPOSE_FILE } )
docker stop $( docker ps -qf name = mysql-mailcow)
docker run \
-it --rm \
2018-04-27 04:57:20 +08:00
-v $( docker volume ls -qf name = ${ CMPS_PRJ } _mysql-vol-1) :/var/lib/mysql/ \
2018-01-12 15:30:54 +08:00
--entrypoint= \
-u mysql \
-v ${ RESTORE_LOCATION } :/backup \
${ SQLIMAGE } /bin/sh -c " mysqld --skip-grant-tables & \
until mysqladmin ping; do sleep 3; done && \
echo Restoring... && \
gunzip < backup/backup_mysql.gz | mysql -uroot && \
mysql -uroot -e SHUTDOWN; "
docker start $( docker ps -aqf name = mysql-mailcow)
; ;
esac
shift
done
docker start $( docker ps -aqf name = watchdog-mailcow)
}
if [ [ ${ 1 } = = "backup" ] ] ; then
backup ${ @,, }
elif [ [ ${ 1 } = = "restore" ] ] ; then
i = 1
declare -A FOLDER_SELECTION
if [ [ $( find ${ BACKUP_LOCATION } /mailcow-* -maxdepth 1 -type d 2> /dev/null| wc -l) -lt 1 ] ] ; then
echo "Selected backup location has no subfolders"
exit 1
fi
for folder in $( ls -d ${ BACKUP_LOCATION } /mailcow-*/) ; do
echo " [ ${ i } ] - ${ folder } "
FOLDER_SELECTION[ ${ i } ] = " ${ folder } "
( ( i++) )
done
echo
input_sel = 0
while [ [ ${ input_sel } -lt 1 || ${ input_sel } -gt ${ i } ] ] ; do
read -p "Select a restore point: " input_sel
done
i = 1
echo
declare -A FILE_SELECTION
RESTORE_POINT = " ${ FOLDER_SELECTION [ ${ input_sel } ] } "
[Docker API] Use TLS encryption for communication with "on-the-fly" created key paris (non-exposed)
[Docker API] Create pipe to pass Rspamd UI worker password
[Dovecot] Pull Spamassassin ruleset to be read by Rspamd (MANY THANKS to Peer Heinlein!)
[Dovecot] Garbage collector for deleted maildirs (set keep time via MAILDIR_GC_TIME which defaults to 1440 minutes)
[Web] Flush memcached after mailbox item changes, fixes #1808
[Web] Fix duplicate IDs, fixes #1792
[Compose] Use SQL sockets
[PHP-FPM] Update APCu and Redis libs
[Dovecot] Encrypt maildir with global key pair in crypt-vol-1 (BACKUP!), also fixes #1791
[Web] Fix deletion of spam aliases
[Helper] Add "crypt" to backup script
[Helper] Override file for external SQL socket (not supported!)
[Compose] New images for Rspamd, PHP-FPM, SOGo, Dovecot, Docker API, Watchdog, ACME, Postfix
2018-09-30 04:01:23 +08:00
if [ [ -z $( find " ${ FOLDER_SELECTION [ ${ input_sel } ] } " -maxdepth 1 -type f -regex ".*\(redis\|rspamd\|mysql\|crypt\|vmail\|postfix\).*" ) ] ] ; then
2018-01-12 15:30:54 +08:00
echo "No datasets found"
exit 1
fi
for file in $( ls -f " ${ FOLDER_SELECTION [ ${ input_sel } ] } " ) ; do
if [ [ ${ file } = ~ vmail ] ] ; then
echo " [ ${ i } ] - Mail directory (/var/vmail) "
FILE_SELECTION[ ${ i } ] = "vmail"
( ( i++) )
[Docker API] Use TLS encryption for communication with "on-the-fly" created key paris (non-exposed)
[Docker API] Create pipe to pass Rspamd UI worker password
[Dovecot] Pull Spamassassin ruleset to be read by Rspamd (MANY THANKS to Peer Heinlein!)
[Dovecot] Garbage collector for deleted maildirs (set keep time via MAILDIR_GC_TIME which defaults to 1440 minutes)
[Web] Flush memcached after mailbox item changes, fixes #1808
[Web] Fix duplicate IDs, fixes #1792
[Compose] Use SQL sockets
[PHP-FPM] Update APCu and Redis libs
[Dovecot] Encrypt maildir with global key pair in crypt-vol-1 (BACKUP!), also fixes #1791
[Web] Fix deletion of spam aliases
[Helper] Add "crypt" to backup script
[Helper] Override file for external SQL socket (not supported!)
[Compose] New images for Rspamd, PHP-FPM, SOGo, Dovecot, Docker API, Watchdog, ACME, Postfix
2018-09-30 04:01:23 +08:00
elif [ [ ${ file } = ~ crypt ] ] ; then
echo " [ ${ i } ] - Crypt data "
FILE_SELECTION[ ${ i } ] = "crypt"
( ( i++) )
2018-01-12 15:30:54 +08:00
elif [ [ ${ file } = ~ redis ] ] ; then
echo " [ ${ i } ] - Redis DB "
FILE_SELECTION[ ${ i } ] = "redis"
( ( i++) )
elif [ [ ${ file } = ~ rspamd ] ] ; then
echo " [ ${ i } ] - Rspamd data "
FILE_SELECTION[ ${ i } ] = "rspamd"
( ( i++) )
elif [ [ ${ file } = ~ postfix ] ] ; then
echo " [ ${ i } ] - Postfix data "
FILE_SELECTION[ ${ i } ] = "postfix"
( ( i++) )
elif [ [ ${ file } = ~ mysql ] ] ; then
echo " [ ${ i } ] - SQL DB "
FILE_SELECTION[ ${ i } ] = "mysql"
( ( i++) )
fi
done
echo
input_sel = 0
while [ [ ${ input_sel } -lt 1 || ${ input_sel } -gt ${ i } ] ] ; do
read -p "Select a dataset to restore: " input_sel
done
echo " Restoring ${ FILE_SELECTION [ ${ input_sel } ] } from ${ RESTORE_POINT } ... "
restore " ${ RESTORE_POINT } " ${ FILE_SELECTION [ ${ input_sel } ] }
fi