1
0
mirror of https://github.com/xzeldon/vwdump.git synced 2025-06-27 23:08:14 +03:00

feat: initial refactor and feature implementation

This is the first major commit after forking the original project. The
script has been almost completely rewritten to add new features.

- Implement AES-256 encryption with configurable PBKDF2 iterations (default 600k).
- Add Telegram integration for status updates and optional file uploads.
- Rework backup logic to use safe `sqlite3 .backup` method.
- Add GitHub Action to build/publish multi-arch Docker images and create
  GitHub Releases on git tags.

BREAKING CHANGE: The project is not backward-compatible, configuration
and behavior are entirely different from the original version.
This commit is contained in:
Timofey Gelazoniya 2025-06-14 11:57:05 +03:00
parent 1d523c8734
commit 12ca3f2ac7
Signed by: zeldon
GPG Key ID: D99707D1FF69FAB0
5 changed files with 532 additions and 133 deletions

View File

@ -1,42 +1,68 @@
name: Dockerize name: Dockerize
# When to run.
# In this case, manual only.
on: on:
workflow_dispatch: push:
tags:
- "v*"
# Instructions on what to do.
# In this case, 'build' is the job and Ubuntu is the OS to run the job on.
jobs: jobs:
main: release-and-publish:
runs-on: ubuntu-latest runs-on: ubuntu-latest
env:
USERNAME: jmqm permissions:
REPOSITORY: vaultwarden_backup contents: write
TAG: latest packages: write
steps: steps:
- name: Check out repo # Step 1: Check out the repository code
uses: actions/checkout@v2.3.4 - name: Check out repository
uses: actions/checkout@v4
- name: Log in to Docker Hub # Step 2: Set up QEMU for multi-architecture builds (e.g., for arm64)
uses: docker/login-action@v1.10.0 - name: Set up QEMU
with: uses: docker/setup-qemu-action@v3
username: ${{ env.USERNAME }}
password: ${{ secrets.DOCKER_HUB_PASSWORD }}
- name: Update README and description
uses: peter-evans/dockerhub-description@v2.4.3
with:
username: ${{ env.USERNAME }}
password: ${{ secrets.DOCKER_HUB_PASSWORD }} # This action does not support tokens.
repository: ${{ env.USERNAME }}/${{ env.REPOSITORY }}
short-description: ${{ github.event.repository.description }}
readme-filepath: ./README.md
- name: Build image and push to Docker hub # Step 3: Set up Docker Buildx, which is required for multi-platform builds
uses: docker/build-push-action@v2.6.1 - name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
# Step 4: Log in to the GitHub Container Registry
- name: Log in to GitHub Container Registry
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
# Step 5: Extract metadata for Docker tags and labels
- name: Extract Docker metadata
id: meta
uses: docker/metadata-action@v5
with:
images: ghcr.io/${{ github.repository }}
tags: |
type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}}
type=semver,pattern={{major}}
flavor: |
latest=auto
# Step 6: Build and push the multi-platform Docker image
- name: Build and push Docker image
id: build-and-push
uses: docker/build-push-action@v6
with: with:
context: . context: .
platforms: linux/amd64,linux/arm64
push: true push: true
tags: ${{ env.USERNAME }}/${{ env.REPOSITORY }}:${{ env.TAG }} tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=gha
cache-to: type=gha,mode=max
# Step 7: Create a GitHub Release
- name: Create GitHub Release
uses: ncipollo/release-action@v1
with:
generateReleaseNotes: true
token: ${{ secrets.GITHUB_TOKEN }}

View File

@ -1,25 +1,33 @@
ARG ARCH= ARG ARCH=
FROM ${ARCH}alpine:latest FROM ${ARCH}alpine:3.22
RUN addgroup -S app && adduser -S -G app app ENV CRON_TIME="0 */12 * * *"
ENV UID=100
RUN apk add --no-cache \ ENV GID=100
busybox-suid \ ENV DELETE_AFTER=0
su-exec \ ENV BACKUP_ENCRYPTION_KEY=""
xz \ ENV TG_TOKEN=""
tzdata ENV TG_CHAT_ID=""
ENV VWDUMP_DEBUG="false"
ENV CRON_TIME "0 */12 * * *" ENV DISABLE_WARNINGS="false"
ENV UID 100 ENV DISABLE_TELEGRAM_UPLOAD="false"
ENV GID 100 ENV PBKDF2_ITERATIONS=600000
ENV DELETE_AFTER 0
COPY entrypoint.sh /usr/local/bin/entrypoint.sh COPY entrypoint.sh /usr/local/bin/entrypoint.sh
COPY script.sh /app/ COPY script.sh /app/script.sh
RUN mkdir /app/log/ \ RUN addgroup -S app && adduser -S -G app app \
&& chown -R app:app /app/ \ && apk add --no-cache \
&& chmod -R 777 /app/ \ busybox-suid \
&& chmod +x /usr/local/bin/entrypoint.sh su-exec \
xz \
tzdata \
openssl \
curl \
sqlite \
&& mkdir -p /app/log/ \
&& chown -R app:app /app \
&& chmod +x /usr/local/bin/entrypoint.sh \
&& chmod +x /app/script.sh
ENTRYPOINT ["entrypoint.sh"] ENTRYPOINT ["entrypoint.sh"]

217
README.md
View File

@ -1,74 +1,177 @@
Backs up vaultwarden files and directories to `tar.xz` archives automatically. `tar.xz` archives can be opened using data compression programs like [7-Zip](https://www.7-zip.org/) and [WinRAR](https://www.win-rar.com/). # VWdump
Files and directories that are backed up: A Docker-based backup solution for [Vaultwarden](https://github.com/dani-garcia/vaultwarden) with encryption and Telegram integration.
- db.sqlite3
- config.json
- rsa_key.der
- rsa_key.pem
- rsa_key.pub.der
- /attachments
- /sends
## Usage > Support only **sqlite3** database backend
#### Automatic Backups ## ✨ Key Features
Refer to the `docker-compose` section below. By default, backing up is automatic.
#### Manual Backups - **Automated Backups**: Configurable backup schedule using cron.
Pass `manual` to `docker run` or `docker-compose` as a `command`. - **SQLite Safety**: Creates proper SQLite backups using `.backup` command.
- **Encryption**: AES256 encryption with configurable PBKDF2 key derivation (defaults to **600,000** iterations).
- **Compression**: Uses XZ compression for all backups, resulting in smaller file sizes.
- **Telegram Integration**: Get detailed notifications. Encrypted backups are uploaded automatically (configurable) if under 50MB.
- **Reliable Networking**: Automatic retries and timeouts for all Telegram communications ensure messages and files get through on unstable networks.
- **Automated Cleanup**: Configurable retention policy to automatically delete old local backups.
## docker-compose ## 🗄️ What Gets Backed Up
```
- `db.sqlite3` - Main SQLite database
- `rsa_key*` - RSA key files are used to sign authentication tokens
- `config.json` - Stores admin page config; only exists if the admin page has been enabled before.
- `attachments/` - Attachment store
- `sends/` - Send attachment store
## 🚀 Setup
### 1. Create a Telegram Bot (Optional)
1. Message `@BotFather` on Telegram.
2. Send `/newbot` and follow the instructions to get your **bot token**.
3. Add the bot to your target chat or group.
4. Get your **chat ID** by messaging `@userinfobot`.
### 2. Docker Compose Setup
```yaml
services: services:
vaultwarden: vaultwarden-backup:
# Vaultwarden configuration here. build: .
backup: container_name: vaultwarden-backup
image: jmqm/vaultwarden_backup:latest restart: unless-stopped
container_name: vaultwarden_backup
network_mode: none
restart: always
volumes:
- /vaultwarden_data_directory:/data:ro # Read-only
- /backup_directory:/backups
- /etc/localtime:/etc/localtime:ro # Container uses date from host.
environment: environment:
- DELETE_AFTER=30 # Basic Config
- CRON_TIME=0 0 * * * # Runs at 12:00 AM. - CRON_TIME=0 2 * * * # Daily at 2 AM
- UID=1024 - DELETE_AFTER=30 # Keep 30 days of local backups
- GID=100 - UID=1000 # Set to the owner of your vaultwarden data directory
- GID=1000 # Set to the group of your vaultwarden data directory
# Encryption (HIGHLY RECOMMENDED)
- BACKUP_ENCRYPTION_KEY=your_very_secure_password_here
# - PBKDF2_ITERATIONS=350000 # Optional: Lower for very slow CPUs (e.g., old Raspberry Pi), Bitwarden recommended 600,000 or more
# Telegram Integration
- TG_TOKEN=1234567890:ABCdefGHIjklMNOpqrsTUVwxyz
- TG_CHAT_ID=123456789 # Can be user id, message to @userinfobot to get it
# Optional Tweaks
# - VWDUMP_DEBUG=true # Uncomment for detailed script logging
# - DISABLE_WARNINGS=true # Uncomment to hide warnings for missing files
# - DISABLE_TELEGRAM_UPLOAD=true # Uncomment to disable file uploads to Telegram
volumes:
- /path/to/vaultwarden/data:/data:ro
- /path/to/backups:/backups
- /etc/timezone:/etc/timezone:ro
- /etc/localtime:/etc/localtime:ro
``` ```
## Volumes _(permissions required)_ ## ⚡ Manual Backups
`/data` _(read)_- Vaultwarden's `/data` directory. Recommend setting mount as read-only.
`/backups` _(write)_ - Where to store backups to. There are two ways to run a manual backup, depending on your needs.
User specified in compose environment (`UID`/`GID`) vars must have write access to `/backups` ### Option 1: Triggering a Backup in a Running Container
If you want to make them the owner of the backups directory do:
``` If you are running the backup tool via `docker compose` and want to trigger an immediate backup without waiting for the cron schedule, this is the command to use. It executes the backup script inside your already-running container.
chown ${UID}:${GID} /path/to/backups
```bash
docker exec vaultwarden-backup /app/script.sh
``` ```
## Environment Variables ### Option 2: Standalone One-Time Backup
#### ⭐Required, 👍 Recommended
| Environment Variable | Info |
| -------------------- | ------------------------------------------------------------------------------------------------------------------------------------- |
| UID ⭐| User ID to run the cron job as. |
| GID ⭐| Group ID to run the cron job as. |
| CRON_TIME 👍| When to run _(default is every 12 hours)_. Info [here][cron-format-wiki] and editor [here][cron-editor]. |
| DELETE_AFTER 👍| _(exclusive to automatic mode)_ Delete backups _X_ days old. Requires `read` and `write` permissions. |
#### Optional This method is useful for testing your configuration, scripting, or running the backup from a different system. It starts a new, temporary container that runs the backup once and then removes itself.
| Environment Variable | Info |
| -------------------- | -------------------------------------------------------------------------------------------- |
| TZ ¹ | Timezone inside the container. Can mount `/etc/localtime` instead as well _(recommended)_. |
¹ See <https://en.wikipedia.org/wiki/List_of_tz_database_time_zones> for more information Make sure to replace the placeholder values with your actual configuration.
## Errors ```bash
#### Unexpected timestamp docker run --rm \
Mount `/etc/localtime` _(recommend mounting as read-only)_ or set `TZ` environment variable. -v /path/to/vaultwarden/data:/data:ro \
-v /path/to/backups:/backups \
-e UID=1000 \
-e GID=1000 \
-e BACKUP_ENCRYPTION_KEY="your_very_secure_password_here" \
-e TG_TOKEN="your_telegram_token" \
-e TG_CHAT_ID="your_telegram_chat_id" \
-e VWDUMP_DEBUG=true \
your-image-name manual
```
[cron-format-wiki]: https://www.ibm.com/docs/en/db2oc?topic=task-unix-cron-format ## 📦 File Formats & Restoration
[cron-editor]: https://crontab.guru/
### Encrypted Backup (Recommended)
- **Format**: `vaultwarden_backup_YYYY-MM-DD_HH-MM-SS.tar.xz.enc`
- **To Restore**: This command will prompt you to enter your encryption password securely.
> **Important**: If you have changed the `PBKDF2_ITERATIONS` variable, you **must** use that same number in the restore command below.
```bash
# Create a directory for the restored files
mkdir vaultwarden-restore
# Decrypt and extract the XZ archive. You will be prompted for the password.
# Replace 600000 with your custom value if you changed it.
openssl enc -d -aes256 -salt -pbkdf2 -iter 600000 \
-in /path/to/backup.tar.xz.enc | \
tar xJ -C vaultwarden-restore
```
### Unencrypted Backup
- **Format**: `vaultwarden_backup_YYYY-MM-DD_HH-MM-SS.tar.xz`
- **To Restore**:
```bash
tar -xJf /path/to/backup.tar.xz -C /path/to/restore/location
```
## 🕵️ Monitoring and Troubleshooting
### Logs
- Primary logs are available via `docker logs -f vaultwarden-backup`.
- For deep troubleshooting, set `VWDUMP_DEBUG=true` to see more logs.
## 🐳 Building the Image
### Simple Local Build
To build the image for your current system architecture:
```bash
docker build -t vaultwarden-backup .
```
## ⚙️ Environment Variables
### Basic Configuration
- `CRON_TIME`: Backup schedule in cron format (default: `0 */12 * * *`).
- `UID`: User ID for file ownership (default: `100`).
- `GID`: Group ID for file ownership (default: `100`).
- `DELETE_AFTER`: Days to keep local backups (default: `0` - no deletion).
### Encryption (Recommended)
- `BACKUP_ENCRYPTION_KEY`: Password for AES256 encryption. **Setting this enables encryption and Telegram file uploads.**
- `PBKDF2_ITERATIONS`: The number of iterations for the PBKDF2 key derivation. Higher numbers are more secure but slower. (Default: `600000`)
### Telegram Integration (Optional)
- `TG_TOKEN`: Your Telegram bot token.
- `TG_CHAT_ID`: The chat ID for notifications and uploads.
- `VWDUMP_DEBUG`: Set to `true` for verbose debug output from the script (default: `"false"`).
- `DISABLE_WARNINGS`: Set to `true` to suppress Telegram warnings for missing files (e.g., missing files and directories) (default: `"false"`).
- `DISABLE_TELEGRAM_UPLOAD`: Set to `true` to prevent the script from uploading backup files to Telegram. A detailed text notification will be sent instead. (Default: `"false"`)
> You can check default values of each variable in [Dockerfile](./Dockerfile)
---
## Acknowledgements
This project is a deeply modified [vaultwarden_backup](https://github.com/jmqm/vaultwarden_backup) script by [jmqm](https://github.com/jmqm)
## Disclaimer
This software is provided "as is" and without any warranty. The author and contributors are not liable for any damages or data loss that may arise from its use. You assume all responsibility and risk for the use of this software. Always test your backups.

5
SECURITY.md Normal file
View File

@ -0,0 +1,5 @@
# Reporting a Vulnerability
If you believe you have found a security vulnerability, let us know by sending email to timofey@z4n.me We will investigate that and do our best to quickly fix the problem.
Please don't open an issue to or discuss this security vulnerability in a public place. Thanks for understanding!

313
script.sh
View File

@ -2,27 +2,151 @@
# --------------- [ PREREQUISITES ] --------------- # --------------- [ PREREQUISITES ] ---------------
EXTENSION="tar.xz" if [ -n "$BACKUP_ENCRYPTION_KEY" ]; then
EXTENSION="tar.xz.enc"
else
EXTENSION="tar.xz"
fi
TIMESTAMP=$(date +"%F_%H-%M-%S")
BACKUP_FILENAME="vaultwarden_backup_${TIMESTAMP}"
# New line defenition
NL='
'
# Error statuses:
# 0 = Success
# 1 = Network/API error
# 2 = File too large
send_telegram_message() {
local message="$1"
if [ -n "$TG_TOKEN" ] && [ -n "$TG_CHAT_ID" ]; then
if [ "$VWDUMP_DEBUG" = "true" ]; then
echo "[DEBUG] Sending Telegram message..."
fi
# Add timeout and retry logic
local max_retries=3
local retry_count=0
while [ $retry_count -lt $max_retries ]; do
response=$(curl -S -s --max-time 30 --connect-timeout 10 -X POST "https://api.telegram.org/bot${TG_TOKEN}/sendMessage" \
-d "chat_id=${TG_CHAT_ID}" \
-d "text=${message}" \
-d "parse_mode=HTML" 2>&1)
# Check if curl command succeeded
if [ $? -eq 0 ] && echo "$response" | grep -q '"ok":true'; then
if [ "$VWDUMP_DEBUG" = "true" ]; then echo "[DEBUG] Telegram message sent successfully"; fi
return 0
else
echo "[ERROR] Telegram message send failed. Details: $response" >&2
fi
retry_count=$((retry_count + 1))
if [ $retry_count -lt $max_retries ]; then
echo "[INFO] Retrying Telegram message send (attempt $((retry_count + 1))/$max_retries)..." >&2
sleep 5
fi
done
echo "[ERROR] Failed to send Telegram message after $max_retries attempts" >&2
return 1
fi
return 1
}
send_telegram_file() {
local file_path="$1"
local caption="$2"
if [ -n "$TG_TOKEN" ] && [ -n "$TG_CHAT_ID" ] && [ -f "$file_path" ]; then
# Check file size, Telegram limit is 50MB
# See: https://core.telegram.org/bots/api#sending-files
file_size=$(stat -c%s "$file_path" 2>/dev/null || stat -f%z "$file_path" 2>/dev/null)
if [ "$file_size" -ge 52428800 ]; then # 50MB in bytes
echo "[ERROR] File too large for Telegram ($(echo "$file_size" | awk '{print int($1/1024/1024)}')MB)" >&2
return 2
fi
echo "[INFO] Uploading file to Telegram ($(echo "$file_size" | awk '{print int($1/1024)}')KB)..."
# Add timeout and retry logic for file uploads
local max_retries=2
local retry_count=0
while [ $retry_count -lt $max_retries ]; do
response=$(curl -S -s --max-time 120 --connect-timeout 30 -X POST "https://api.telegram.org/bot${TG_TOKEN}/sendDocument" \
-F "chat_id=${TG_CHAT_ID}" \
-F "document=@${file_path}" \
-F "caption=${caption}" \
-F "parse_mode=HTML" 2>&1)
# Check if curl command succeeded
if [ $? -eq 0 ] && echo "$response" | grep -q '"ok":true'; then
if [ "$VWDUMP_DEBUG" = "true" ]; then echo "[DEBUG] Telegram file upload successful"; fi
return 0
else
echo "[ERROR] Telegram file upload failed. Details: $response" >&2
if echo "$response" | grep -q "Request Entity Too Large\|File too large"; then return 2; fi
fi
retry_count=$((retry_count + 1))
if [ $retry_count -lt $max_retries ]; then
echo "[INFO] Retrying Telegram file upload (attempt $((retry_count + 1))/$max_retries)..." >&2
sleep 10
fi
done
echo "[ERROR] Failed to upload file to Telegram after $max_retries attempts" >&2
return 1
fi
return 1
}
# ------------------ [ PRINT CONFIGURATION ] ------------------
if [ "$VWDUMP_DEBUG" = "true" ]; then
echo "[DEBUG] ----- VWDUMP CONFIGURATION -----"
echo "[DEBUG] Cron Schedule: ${CRON_TIME}"
echo "[DEBUG] User ID (UID): ${UID}"
echo "[DEBUG] Group ID (GID): ${GID}"
echo "[DEBUG] Delete After: ${DELETE_AFTER} days"
if [ -n "$BACKUP_ENCRYPTION_KEY" ]; then
echo "[DEBUG] Encryption: Enabled"
echo "[DEBUG] PBKDF2 Iterations: ${PBKDF2_ITERATIONS}"
else
echo "[DEBUG] Encryption: Disabled"
fi
if [ -n "$TG_TOKEN" ] && [ -n "$TG_CHAT_ID" ]; then
echo "[DEBUG] Telegram Notifications: Enabled"
echo "[DEBUG] Telegram Chat ID: ${TG_CHAT_ID}"
echo "[DEBUG] Telegram Token: [set, masked]"
echo "[DEBUG] Disable Warnings: ${DISABLE_WARNINGS}"
echo "[DEBUG] Disable Uploads: ${DISABLE_TELEGRAM_UPLOAD}"
else
echo "[DEBUG] Telegram Notifications: Disabled"
fi
echo "[DEBUG] --------------------------------"
fi
# ------------------ [ BACKUP ] ------------------ # ------------------ [ BACKUP ] ------------------
cd /data || exit 1 # Exit with error if opening vw data file fails cd /data || exit 1 # Exit with error if opening vw data file fails
BACKUP_LOCATION="/backups/$(date +"%F_%H-%M-%S").${EXTENSION}"
BACKUP_DB="db.sqlite3" # file # Ensure backup directory exists
BACKUP_RSA="rsa_key*" # files mkdir -p /backups
BACKUP_CONFIG="config.json" # file BACKUP_LOCATION="/backups/${BACKUP_FILENAME}.${EXTENSION}"
BACKUP_ATTACHMENTS="attachments" # directory
BACKUP_SENDS="sends" # directory
# Create list of backup items to archive BACKUP_ITEMS="db.sqlite3 rsa_key* config.json attachments sends"
BACKUP_ITEMS="$BACKUP_DB $BACKUP_RSA $BACKUP_CONFIG $BACKUP_ATTACHMENTS $BACKUP_SENDS" WARNING_STATUS=""
# Verify which items are available to be backed up
FILES_TO_BACKUP="" FILES_TO_BACKUP=""
WARNING="" WARNING=""
# Verify which items are available to be backed up
for ITEM in $BACKUP_ITEMS; do for ITEM in $BACKUP_ITEMS; do
if [ -e "$ITEM" ] || [ -d "$ITEM" ]; then if [ -e "$ITEM" ] || [ -d "$ITEM" ]; then
FILES_TO_BACKUP="$FILES_TO_BACKUP $ITEM" FILES_TO_BACKUP="$FILES_TO_BACKUP $ITEM"
@ -31,38 +155,171 @@ for ITEM in $BACKUP_ITEMS; do
fi fi
done done
# Print the warnings out in the docker logs # Print and set warnings
if [ -n "$WARNING" ]; then if [ -n "$WARNING" ]; then
echo "[WARNING] The following expected files/directories are missing:$WARNING" >&2 echo "[WARNING] The following expected files/directories are missing:$WARNING" >&2
if [ "$DISABLE_WARNINGS" != "true" ]; then
WARNING_STATUS="⚠️ Missing files:$WARNING"
fi
fi fi
# Exit if there are no files to back up
# Back up files and folders, only if there are files to back up if [ -z "$FILES_TO_BACKUP" ]; then
if [ -n "$FILES_TO_BACKUP" ]; then
echo "[INFO] Backing up:$FILES_TO_BACKUP"
tar -Jcf "$BACKUP_LOCATION" $FILES_TO_BACKUP
OUTPUT="New backup created"
else
OUTPUT="No files to back up" OUTPUT="No files to back up"
ERROR_MSG="<b>⚠️ Vaultwarden Backup Warning</b>${NL}No files found to back up"
send_telegram_message "$ERROR_MSG"
echo "[$(date +"%F %r")] ${OUTPUT}."
exit 0
fi fi
# --- [ Backup Preparation ] ---
echo "[INFO] Creating backup..."
# ------------------ [ DELETE ] ------------------ # Prepare a temporary directory for temp backup files
if [ "$VWDUMP_DEBUG" = "true" ]; then
echo "[DEBUG] Preparing a backup temp directory"
fi
TEMP_DIR="/tmp/vw_backup_$$"
mkdir -p "$TEMP_DIR"
# Always use safe .backup for sqlite3 if it exists
if [ -f "db.sqlite3" ]; then
if [ "$VWDUMP_DEBUG" = "true" ]; then
echo "[DEBUG] Creating SQLite backup..."
fi
sqlite3 db.sqlite3 ".backup '$TEMP_DIR/db.sqlite3'"
fi
# Copy all other files to the temp directory, preserving permissions
for ITEM in $FILES_TO_BACKUP; do
if [ "$ITEM" != "db.sqlite3" ]; then
cp -a "$ITEM" "$TEMP_DIR/"
fi
done
# --- [ Archive Creation ] ---
# Create tar archive from temp directory
if [ -n "$BACKUP_ENCRYPTION_KEY" ]; then
# Create encrypted backup
if [ "$VWDUMP_DEBUG" = "true" ]; then
echo "[DEBUG] Creating archive..."
fi
tar -Jcf - -C "$TEMP_DIR" . | openssl enc -e -aes256 -salt -pbkdf2 -iter "$PBKDF2_ITERATIONS" -pass pass:"$BACKUP_ENCRYPTION_KEY" -out "$BACKUP_LOCATION"
BACKUP_SUCCESS=$?
else
# Create unencrypted backup
if [ "$VWDUMP_DEBUG" = "true" ]; then
echo "[DEBUG] Creating archive..."
fi
tar -Jcf "$BACKUP_LOCATION" -C "$TEMP_DIR" .
BACKUP_SUCCESS=$?
fi
# --- [ Cleanup ] ---
echo "[INFO] Cleaning up..."
rm -rf "$TEMP_DIR"
# --- [ Build clean up message ] ---
CLEANUP_STATUS_MSG=""
if [ -n "$DELETE_AFTER" ] && [ "$DELETE_AFTER" -gt 0 ]; then if [ -n "$DELETE_AFTER" ] && [ "$DELETE_AFTER" -gt 0 ]; then
cd /backups cd /backups
# Find all archives older than x days, store them in a variable, delete them.
TO_DELETE=$(find . -iname "*.${EXTENSION}" -type f -mtime +$DELETE_AFTER) TO_DELETE=$(find . -iname "*.${EXTENSION}" -type f -mtime +$DELETE_AFTER)
find . -iname "*.${EXTENSION}" -type f -mtime +$DELETE_AFTER -exec rm -f {} \; DELETED_COUNT=0
if [ -n "$TO_DELETE" ]; then DELETED_COUNT=$(echo "$TO_DELETE" | wc -l); fi
OUTPUT="${OUTPUT}, $([ ! -z "$TO_DELETE" ] \ if [ "$DELETED_COUNT" -gt 0 ]; then
&& echo "deleted $(echo "$TO_DELETE" | wc -l) archives older than ${DELETE_AFTER} days" \ if [ "$DELETED_COUNT" -eq 1 ]; then
|| echo "no archives older than ${DELETE_AFTER} days to delete")" archive_word="archive"
file_word="file"
else
archive_word="archives"
file_word="files"
fi
echo "$TO_DELETE" | xargs rm -f
OUTPUT_SUFFIX=", deleted ${DELETED_COUNT} ${archive_word} older than ${DELETE_AFTER} days"
CLEANUP_STATUS_MSG="🗑️ Deleted ${DELETED_COUNT} old backup ${file_word} (older than ${DELETE_AFTER} days)."
else
OUTPUT_SUFFIX=", no archives older than ${DELETE_AFTER} days to delete"
fi
fi fi
# --- [ Telegram Notification ] ---
if [ $BACKUP_SUCCESS -eq 0 ]; then
file_size=$(stat -c%s "$BACKUP_LOCATION" 2>/dev/null || stat -f%z "$file_path" 2>/dev/null)
file_size_kb=$(echo "$file_size" | awk '{print int($1/1024)}')
if [ "$file_size_kb" -ge 1024 ]; then
size_display="$(echo "$file_size_kb" | awk '{print int($1/1024)}')MB"
else
size_display="${file_size_kb}KB"
fi
if [ -n "$BACKUP_ENCRYPTION_KEY" ]; then
# Encrypted backup notification logic
decrypt_cmd="openssl enc -d -aes256 -salt -pbkdf2 -iter ${PBKDF2_ITERATIONS} -in ${BACKUP_FILENAME}.${EXTENSION} | tar xJ -C restore-dir"
file_caption="✅ <b>Vaultwarden Backup Complete</b>${NL}${NL}📁 File: <code>${BACKUP_FILENAME}.${EXTENSION}</code>${NL}📏 Size: ${size_display}"
if [ -n "$WARNING_STATUS" ]; then file_caption="${file_caption}${NL}${WARNING_STATUS}"; fi
if [ -n "$CLEANUP_STATUS_MSG" ]; then file_caption="${file_caption}${NL}${NL}${CLEANUP_STATUS_MSG}"; fi
file_caption="${file_caption}${NL}${NL}🔓 <b>Decrypt with:</b>${NL}<code>${decrypt_cmd}</code>"
if [ "$DISABLE_TELEGRAM_UPLOAD" = "true" ]; then
echo "[INFO] Telegram file upload is disabled by user setting."
upload_result=3 # Use a unique code for "disabled by user"
else
send_telegram_file "$BACKUP_LOCATION" "$file_caption"
upload_result=$?
fi
if [ $upload_result -eq 0 ]; then
OUTPUT="New backup created and sent to Telegram"
else
# Build the fallback text-only notification
TELEGRAM_MSG="<b>✅ Vaultwarden Backup Complete</b>${NL}"
TELEGRAM_MSG="${TELEGRAM_MSG}${NL}📁 File: <code>${BACKUP_FILENAME}.${EXTENSION}</code>"
TELEGRAM_MSG="${TELEGRAM_MSG}${NL}📏 Size: ${size_display}"
if [ $upload_result -eq 2 ]; then
TELEGRAM_MSG="${TELEGRAM_MSG}${NL}⚠️ <b>File too large for Telegram</b>, saved locally only."
OUTPUT="New backup created (too large for Telegram)"
elif [ $upload_result -eq 3 ]; then
TELEGRAM_MSG="${TELEGRAM_MSG}${NL} <b>File upload disabled by user</b>, saved locally only."
OUTPUT="New backup created (upload disabled by user)"
else
TELEGRAM_MSG="${TELEGRAM_MSG}${NL}❌ <b>File upload failed</b>, saved locally only."
OUTPUT="New backup created (Telegram upload failed)"
fi
if [ -n "$WARNING_STATUS" ]; then TELEGRAM_MSG="${TELEGRAM_MSG}${NL}${WARNING_STATUS}"; fi
if [ -n "$CLEANUP_STATUS_MSG" ]; then TELEGRAM_MSG="${TELEGRAM_MSG}${NL}${NL}${CLEANUP_STATUS_MSG}"; fi
TELEGRAM_MSG="${TELEGRAM_MSG}${NL}${NL}🔓 <b>Decrypt with:</b>${NL}<code>${decrypt_cmd}</code>"
send_telegram_message "$TELEGRAM_MSG"
fi
else
# Unencrypted backup notification logic
TELEGRAM_MSG="<b>✅ Vaultwarden Backup Complete</b>"
TELEGRAM_MSG="${TELEGRAM_MSG}${NL}📁 File: <code>${BACKUP_FILENAME}.${EXTENSION}</code>"
TELEGRAM_MSG="${TELEGRAM_MSG}${NL}📏 Size: ${size_display}"
if [ -n "$WARNING_STATUS" ]; then TELEGRAM_MSG="${TELEGRAM_MSG}${NL}${WARNING_STATUS}"; fi
if [ -n "$CLEANUP_STATUS_MSG" ]; then TELEGRAM_MSG="${TELEGRAM_MSG}${NL}${NL}${CLEANUP_STATUS_MSG}"; fi
if send_telegram_message "$TELEGRAM_MSG"; then
OUTPUT="New backup created (notification sent)"
else
OUTPUT="New backup created (notification failed)"
fi
fi
else
OUTPUT="Failed to create backup"
ERROR_MSG="<b>❌ Vaultwarden Backup Failed</b>${NL}Could not create archive: ${BACKUP_FILENAME}"
if [ -n "$WARNING_STATUS" ]; then ERROR_MSG="${ERROR_MSG}${NL}${WARNING_STATUS}"; fi
send_telegram_message "$ERROR_MSG"
fi
# ------------------ [ EXIT ] ------------------ # ------------------ [ EXIT ] ------------------
echo "[$(date +"%F %r")] ${OUTPUT}." # Append the cleanup message to the final console log message
echo "[$(date +"%F %r")] ${OUTPUT}${OUTPUT_SUFFIX}."