# {{ ansible_managed }} #!/bin/bash [ ! -f /usr/bin/proxmox-backup-client ] && echo "proxmox-backup-client is not installed: exiting." && exit 1 {% if pbs_random_delay | int > 0 %} ## random delay to avoid PBS connection flood ## delay=$((RANDOM % {{ pbs_random_delay }})) echo "Sleeping ${delay}s before starting backup (max delay: {{ pbs_random_delay }}s)..." sleep $delay ## end of random delay ## {% endif %} today=`date +%Y-%m-%d` backup_dir={{ backup_dir }} backup_local_retention={{ backup_local_retention }} ### PBS #### PBS_RATE={{ pbs_rate | default('60000000') }} export PBS_FINGERPRINT="{{ pbs_fingerprint }}" export PBS_PASSWORD="{{ pbs_password }}" export PBS_USER="{{ pbs_user }}" export PBS_SERVER="{{ pbs_server }}" export PBS_DATASTORE="{{ pbs_datastore }}" export PBS_REPOSITORY="${PBS_USER}@${PBS_SERVER}:${PBS_DATASTORE}" export PBS_LOG="{{ pbs_log_level | default('error') }}" ## PBS login ## /usr/bin/proxmox-backup-client login if [ $? -ne 0 ]; then echo "Error on PBS login - exiting." exit 1 fi ## end of PBS login ## ## today symlink ## mkdir -p $backup_dir/$today ln -sfn $backup_dir/$today $backup_dir/today ## end of today symlink ## ## audit ## mkdir -p $backup_dir/$today/audit /usr/bin/tar cfz $backup_dir/$today/audit/crontab.tgz -C /var/spool/cron/ crontabs/ /usr/bin/dpkg -l > $backup_dir/$today/audit/dpkg.txt [ -f /sbin/iptables ] && /sbin/iptables -L -n > $backup_dir/$today/audit/firewall.txt [ -f /sbin/iptables ] && /sbin/iptables -L -n -t nat > $backup_dir/$today/audit/firewall_nat.txt [ -f /sbin/ip6tables ] && /sbin/ip6tables -L -n > $backup_dir/$today/audit/firewall6.txt [ -f /sbin/ip6tables ] && /sbin/ip6tables -L -n -t nat > $backup_dir/$today/audit/firewall6_nat.txt [ -f /usr/bin/pstree ] && /usr/bin/pstree > $backup_dir/$today/audit/pstree.txt /bin/ps faux > $backup_dir/$today/audit/ps.txt /bin/systemctl list-units > $backup_dir/$today/audit/systemctl_unit.txt /usr/bin/getent passwd > $backup_dir/$today/audit/users.txt [ -f /usr/bin/pvs ] && /usr/sbin/pvs > $backup_dir/$today/audit/pvs.txt [ -f /usr/bin/vgs ] && /usr/sbin/vgs > $backup_dir/$today/audit/vgs.txt [ -f /usr/bin/lvs ] && /usr/sbin/lvs > $backup_dir/$today/audit/lvs.txt for dev in /sys/block/*; do dev=$(basename $dev) if test -b /dev/$dev && file -s /dev/$dev | egrep -q 'partition table|boot sector'; then /usr/sbin/sfdisk -d /dev/$dev >> $backup_dir/$today/audit/partition-table_$dev.txt fi done ## end of audit ## {% if backup_mysql_enabled is sameas true %} ## mysql ## backup_mysql_host="{{ backup_mysql_host | default('') }}" backup_mysql_user="{{ backup_mysql_user | default('') }}" backup_mysql_password="{{ backup_mysql_password | default('') }}" backup_mysql_docker_enabled="{{ (backup_mysql_docker_enabled | default(false)) | ternary('true', 'false') }}" backup_mysql_docker_container="{{ backup_mysql_docker_container | default('') }}" mysql_cmd=(/usr/bin/mysql) mysqldump_cmd=(/usr/bin/mysqldump) if [ "$backup_mysql_docker_enabled" = "true" ]; then if [ -z "$backup_mysql_docker_container" ]; then echo "MySQL Docker mode enabled but container name is empty: exiting." exit 1 fi mysql_cmd=(/usr/bin/docker exec -i "$backup_mysql_docker_container" mysql) mysqldump_cmd=(/usr/bin/docker exec -i "$backup_mysql_docker_container" mysqldump) fi mysql_connection_args=() [ -n "$backup_mysql_host" ] && mysql_connection_args+=("--host=$backup_mysql_host") [ -n "$backup_mysql_user" ] && mysql_connection_args+=("--user=$backup_mysql_user") [ -n "$backup_mysql_password" ] && mysql_connection_args+=("--password=$backup_mysql_password") mysql_ignore_tables_args=({% for table in backup_mysql_ignore_tables | default([]) %}"--ignore-table={{ table }}" {% endfor %}) mysql_databases=$("${mysql_cmd[@]}" "${mysql_connection_args[@]}" -e "SHOW DATABASES;" | grep -Ev "(Database|information_schema|performance_schema{% for db in backup_mysql_ignore_databases | default([]) %}|{{ db }}{% endfor %})") mkdir -p $backup_dir/$today/mysql for db in $mysql_databases; do "${mysqldump_cmd[@]}" "${mysql_connection_args[@]}" "${mysql_ignore_tables_args[@]}" -e -q -Q --single-transaction --routines --events --triggers "$db" | gzip -c > $backup_dir/$today/mysql/dump_$db.sql.gz; done ## end of mysql ## {% endif %} {% if backup_mariadb_enabled is sameas true %} ## mariadb ## backup_mariadb_host="{{ backup_mariadb_host | default('') }}" backup_mariadb_user="{{ backup_mariadb_user | default('') }}" backup_mariadb_password="{{ backup_mariadb_password | default('') }}" backup_mariadb_docker_enabled="{{ (backup_mariadb_docker_enabled | default(false)) | ternary('true', 'false') }}" backup_mariadb_docker_container="{{ backup_mariadb_docker_container | default('') }}" mariadb_cmd=(/usr/bin/mariadb) mariadbdump_cmd=(/usr/bin/mariadb-dump) if [ "$backup_mariadb_docker_enabled" = "true" ]; then if [ -z "$backup_mariadb_docker_container" ]; then echo "MariaDB Docker mode enabled but container name is empty: exiting." exit 1 fi mariadb_cmd=(/usr/bin/docker exec -i "$backup_mariadb_docker_container" mariadb) mariadbdump_cmd=(/usr/bin/docker exec -i "$backup_mariadb_docker_container" mariadb-dump) fi mariadb_connection_args=() [ -n "$backup_mariadb_host" ] && mariadb_connection_args+=("--host=$backup_mariadb_host") [ -n "$backup_mariadb_user" ] && mariadb_connection_args+=("--user=$backup_mariadb_user") [ -n "$backup_mariadb_password" ] && mariadb_connection_args+=("--password=$backup_mariadb_password") mariadb_ignore_tables_args=({% for table in backup_mariadb_ignore_tables | default([]) %}"--ignore-table={{ table }}" {% endfor %}) mariadb_databases=$("${mariadb_cmd[@]}" "${mariadb_connection_args[@]}" -e "SHOW DATABASES;" | grep -Ev "(Database|information_schema|performance_schema{% for db in backup_mariadb_ignore_databases | default([]) %}|{{ db }}{% endfor %})") mkdir -p $backup_dir/$today/mariadb for db in $mariadb_databases; do "${mariadbdump_cmd[@]}" "${mariadb_connection_args[@]}" "${mariadb_ignore_tables_args[@]}" -e -q -Q --single-transaction --routines --events --triggers "$db" | gzip -c > $backup_dir/$today/mariadb/dump_$db.sql.gz; done ## end of mariadb ## {% endif %} {% if backup_pgsql_enabled is sameas true %} ## postgresql ## pg_port={{ backup_pg_port | default('5432') }} mkdir -p $backup_dir/$today/postgresql backup_pg_host="{{ backup_pg_host | default('') }}" backup_pg_user="{{ backup_pg_user | default('') }}" backup_pg_password="{{ backup_pg_password | default('') }}" backup_pg_docker_enabled="{{ (backup_pg_docker_enabled | default(false)) | ternary('true', 'false') }}" backup_pg_docker_container="{{ backup_pg_docker_container | default('') }}" if [ "$backup_pg_docker_enabled" = "true" ]; then if [ -z "$backup_pg_docker_container" ]; then echo "PostgreSQL Docker mode enabled but container name is empty: exiting." exit 1 fi pg_connection_args=("-p" "$pg_port") [ -n "$backup_pg_host" ] && pg_connection_args+=("-h" "$backup_pg_host") [ -n "$backup_pg_user" ] && pg_connection_args+=("-U" "$backup_pg_user") if [ -n "$backup_pg_password" ]; then pg_databases=$(/usr/bin/docker exec -e PGPASSWORD="$backup_pg_password" -i "$backup_pg_docker_container" psql "${pg_connection_args[@]}" -At -c "SELECT datname FROM pg_database WHERE datistemplate = false;") else pg_databases=$(/usr/bin/docker exec -i "$backup_pg_docker_container" psql "${pg_connection_args[@]}" -At -c "SELECT datname FROM pg_database WHERE datistemplate = false;") fi for db in $pg_databases; do if [ -n "$backup_pg_password" ]; then /usr/bin/docker exec -e PGPASSWORD="$backup_pg_password" -i "$backup_pg_docker_container" pg_dump "${pg_connection_args[@]}" -Z9 -Fc -b "$db" > "$backup_dir/$today/postgresql/dump_$db.sql" else /usr/bin/docker exec -i "$backup_pg_docker_container" pg_dump "${pg_connection_args[@]}" -Z9 -Fc -b "$db" > "$backup_dir/$today/postgresql/dump_$db.sql" fi done if [ -n "$backup_pg_password" ]; then /usr/bin/docker exec -e PGPASSWORD="$backup_pg_password" -i "$backup_pg_docker_container" pg_dumpall "${pg_connection_args[@]}" --roles-only > "$backup_dir/$today/postgresql/role.sql" else /usr/bin/docker exec -i "$backup_pg_docker_container" pg_dumpall "${pg_connection_args[@]}" --roles-only > "$backup_dir/$today/postgresql/role.sql" fi elif [ -n "$backup_pg_host" ] || [ -n "$backup_pg_user" ] || [ -n "$backup_pg_password" ]; then pg_connection_args=("-p" "$pg_port") [ -n "$backup_pg_host" ] && pg_connection_args+=("-h" "$backup_pg_host") [ -n "$backup_pg_user" ] && pg_connection_args+=("-U" "$backup_pg_user") if [ -n "$backup_pg_password" ]; then export PGPASSWORD="$backup_pg_password" fi pg_databases=$(/usr/bin/psql "${pg_connection_args[@]}" -At -c "SELECT datname FROM pg_database WHERE datistemplate = false;") for db in $pg_databases; do /usr/bin/pg_dump "${pg_connection_args[@]}" -Z9 -Fc -b -f "$backup_dir/$today/postgresql/dump_$db.sql" "$db" done /usr/bin/pg_dumpall "${pg_connection_args[@]}" --roles-only > "$backup_dir/$today/postgresql/role.sql" if [ -n "$backup_pg_password" ]; then unset PGPASSWORD fi else chown -R postgres: $backup_dir/$today/postgresql pg_databases=`sudo su - postgres -c "/usr/bin/psql -p $pg_port -t -c 'SELECT datname FROM pg_database'"|grep -v "template0"` for db in $pg_databases; do sudo su - postgres -c "/usr/bin/pg_dump -p $pg_port -Z9 -Fc -b -f $backup_dir/$today/postgresql/dump_$db.sql $db"; done sudo su - postgres -c "/usr/bin/pg_dumpall --roles-only > $backup_dir/$today/postgresql/role.sql" fi ## end of postresql ## {% endif %} {% if backup_gitea_enabled is sameas true %} ## gitea ## backup_gitea_config="{{ backup_gitea_config | default('/data/gitea/conf/app.ini') }}" backup_gitea_docker_enabled="{{ (backup_gitea_docker_enabled | default(false)) | ternary('true', 'false') }}" backup_gitea_docker_container="{{ backup_gitea_docker_container | default('') }}" mkdir -p "$backup_dir/$today/gitea" if [ "$backup_gitea_docker_enabled" = "true" ]; then if [ -z "$backup_gitea_docker_container" ]; then echo "Gitea Docker mode enabled but container name is empty: exiting." exit 1 fi /usr/bin/docker exec -u git -w /tmp gitea bash -c '/usr/local/bin/gitea dump -c "$backup_gitea_config" -q --type tar.gz -f -' > "$backup_dir/$today/gitea/gitea-dump.tar.gz" else sudo su -s /bin/bash - git -c "cd $backup_dir/$today/gitea && /usr/local/bin/gitea dump -c \"$backup_gitea_config\" -q --type tar.gz" fi ## end of gitea ## {% endif %} {% if backup_sshportal_enabled is sameas true %} ## sshportal ## backup_sshportal_db_path="{{ backup_sshportal_db_path | default('/var/lib/docker/volumes/sshportal__var_lib_sshportal/_data/sshportal.db') }}" backup_sshportal_ssh_host="{{ backup_sshportal_ssh_host | default('localhost') }}" backup_sshportal_ssh_port="{{ backup_sshportal_ssh_port | default('2222') }}" backup_sshportal_ssh_user="{{ backup_sshportal_ssh_user | default('admin') }}" backup_sshportal_ssh_key="{{ backup_sshportal_ssh_key | default('/opt/docker-compose/sshportal/ed25519_sshportal_admin') }}" mkdir -p "$backup_dir/$today/sshportal" /usr/bin/sqlite3 "$backup_sshportal_db_path" .dump > "$backup_dir/$today/sshportal/sshportal.sql.bkp" /usr/bin/ssh -i "$backup_sshportal_ssh_key" -p "$backup_sshportal_ssh_port" -l "$backup_sshportal_ssh_user" "$backup_sshportal_ssh_host" config backup > "$backup_dir/$today/sshportal/backup.json" ## end of sshportal ## {% endif %} # purge old backups find $backup_dir -maxdepth 1 -mindepth 1 -type d -mtime +$backup_local_retention -exec rm -rf {} \; ## send to PBS ## /usr/bin/proxmox-backup-client backup \ {% if backup_host_id is defined %} --backup-id {{ backup_host_id }} \ {% endif %} {% if backup_exclude is defined %} {% for item in backup_exclude %} --exclude {{ item }} \ {% endfor %} {% endif %} etc.pxar:/etc \ audit.pxar:$backup_dir/$today/audit \ {% if backup_path_include is defined %} {% for item in backup_path_include %} {{ item | regex_replace('^/', '') | replace('/', '_') }}.pxar:{{ item }} \ {% endfor %} {% endif %} {% if backup_pgsql_enabled is sameas true %} postgresql.pxar:$backup_dir/$today/postgresql \ {% endif %} {% if backup_mysql_enabled is sameas true %} mysql.pxar:$backup_dir/$today/mysql \ {% endif %} {% if backup_mariadb_enabled is sameas true %} mariadb.pxar:$backup_dir/$today/mariadb \ {% endif %} {% if backup_gitea_enabled is sameas true %} gitea.pxar:$backup_dir/$today/gitea \ {% endif %} {% if backup_sshportal_enabled is sameas true %} sshportal.pxar:$backup_dir/$today/sshportal \ {% endif %} --rate ${PBS_RATE} ## end of send to PBS ## ## PBS logout ## # not working at this time, script end with "Error: $XDG_RUNTIME_DIR must be set" #/usr/bin/proxmox-backup-client logout ## end of PBS logout ##