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:
parent
1d523c8734
commit
12ca3f2ac7
84
.github/workflows/dockerize.yml
vendored
84
.github/workflows/dockerize.yml
vendored
@ -1,42 +1,68 @@
|
||||
name: Dockerize
|
||||
|
||||
# When to run.
|
||||
# In this case, manual only.
|
||||
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:
|
||||
main:
|
||||
release-and-publish:
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
USERNAME: jmqm
|
||||
REPOSITORY: vaultwarden_backup
|
||||
TAG: latest
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
packages: write
|
||||
|
||||
steps:
|
||||
- name: Check out repo
|
||||
uses: actions/checkout@v2.3.4
|
||||
# Step 1: Check out the repository code
|
||||
- name: Check out repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Log in to Docker Hub
|
||||
uses: docker/login-action@v1.10.0
|
||||
with:
|
||||
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
|
||||
# Step 2: Set up QEMU for multi-architecture builds (e.g., for arm64)
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v3
|
||||
|
||||
- name: Build image and push to Docker hub
|
||||
uses: docker/build-push-action@v2.6.1
|
||||
# Step 3: Set up Docker Buildx, which is required for multi-platform builds
|
||||
- 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:
|
||||
context: .
|
||||
platforms: linux/amd64,linux/arm64
|
||||
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 }}
|
||||
|
46
Dockerfile
46
Dockerfile
@ -1,25 +1,33 @@
|
||||
ARG ARCH=
|
||||
FROM ${ARCH}alpine:latest
|
||||
FROM ${ARCH}alpine:3.22
|
||||
|
||||
RUN addgroup -S app && adduser -S -G app app
|
||||
|
||||
RUN apk add --no-cache \
|
||||
busybox-suid \
|
||||
su-exec \
|
||||
xz \
|
||||
tzdata
|
||||
|
||||
ENV CRON_TIME "0 */12 * * *"
|
||||
ENV UID 100
|
||||
ENV GID 100
|
||||
ENV DELETE_AFTER 0
|
||||
ENV CRON_TIME="0 */12 * * *"
|
||||
ENV UID=100
|
||||
ENV GID=100
|
||||
ENV DELETE_AFTER=0
|
||||
ENV BACKUP_ENCRYPTION_KEY=""
|
||||
ENV TG_TOKEN=""
|
||||
ENV TG_CHAT_ID=""
|
||||
ENV VWDUMP_DEBUG="false"
|
||||
ENV DISABLE_WARNINGS="false"
|
||||
ENV DISABLE_TELEGRAM_UPLOAD="false"
|
||||
ENV PBKDF2_ITERATIONS=600000
|
||||
|
||||
COPY entrypoint.sh /usr/local/bin/entrypoint.sh
|
||||
COPY script.sh /app/
|
||||
COPY script.sh /app/script.sh
|
||||
|
||||
RUN mkdir /app/log/ \
|
||||
&& chown -R app:app /app/ \
|
||||
&& chmod -R 777 /app/ \
|
||||
&& chmod +x /usr/local/bin/entrypoint.sh
|
||||
RUN addgroup -S app && adduser -S -G app app \
|
||||
&& apk add --no-cache \
|
||||
busybox-suid \
|
||||
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
217
README.md
@ -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:
|
||||
- db.sqlite3
|
||||
- config.json
|
||||
- rsa_key.der
|
||||
- rsa_key.pem
|
||||
- rsa_key.pub.der
|
||||
- /attachments
|
||||
- /sends
|
||||
A Docker-based backup solution for [Vaultwarden](https://github.com/dani-garcia/vaultwarden) with encryption and Telegram integration.
|
||||
|
||||
## Usage
|
||||
> Support only **sqlite3** database backend
|
||||
|
||||
#### Automatic Backups
|
||||
Refer to the `docker-compose` section below. By default, backing up is automatic.
|
||||
## ✨ Key Features
|
||||
|
||||
#### Manual Backups
|
||||
Pass `manual` to `docker run` or `docker-compose` as a `command`.
|
||||
- **Automated Backups**: Configurable backup schedule using cron.
|
||||
- **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:
|
||||
vaultwarden:
|
||||
# Vaultwarden configuration here.
|
||||
backup:
|
||||
image: jmqm/vaultwarden_backup:latest
|
||||
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.
|
||||
vaultwarden-backup:
|
||||
build: .
|
||||
container_name: vaultwarden-backup
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
- DELETE_AFTER=30
|
||||
- CRON_TIME=0 0 * * * # Runs at 12:00 AM.
|
||||
- UID=1024
|
||||
- GID=100
|
||||
# Basic Config
|
||||
- CRON_TIME=0 2 * * * # Daily at 2 AM
|
||||
- DELETE_AFTER=30 # Keep 30 days of local backups
|
||||
- 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)_
|
||||
`/data` _(read)_- Vaultwarden's `/data` directory. Recommend setting mount as read-only.
|
||||
## ⚡ Manual Backups
|
||||
|
||||
`/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`
|
||||
If you want to make them the owner of the backups directory do:
|
||||
```
|
||||
chown ${UID}:${GID} /path/to/backups
|
||||
### Option 1: Triggering a Backup in a Running Container
|
||||
|
||||
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.
|
||||
|
||||
```bash
|
||||
docker exec vaultwarden-backup /app/script.sh
|
||||
```
|
||||
|
||||
## Environment Variables
|
||||
#### ⭐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. |
|
||||
### Option 2: Standalone One-Time Backup
|
||||
|
||||
#### Optional
|
||||
| Environment Variable | Info |
|
||||
| -------------------- | -------------------------------------------------------------------------------------------- |
|
||||
| TZ ¹ | Timezone inside the container. Can mount `/etc/localtime` instead as well _(recommended)_. |
|
||||
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.
|
||||
|
||||
¹ 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
|
||||
#### Unexpected timestamp
|
||||
Mount `/etc/localtime` _(recommend mounting as read-only)_ or set `TZ` environment variable.
|
||||
```bash
|
||||
docker run --rm \
|
||||
-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
|
||||
[cron-editor]: https://crontab.guru/
|
||||
## 📦 File Formats & Restoration
|
||||
|
||||
### 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
5
SECURITY.md
Normal 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
313
script.sh
@ -2,27 +2,151 @@
|
||||
|
||||
# --------------- [ 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 ] ------------------
|
||||
|
||||
cd /data || exit 1 # Exit with error if opening vw data file fails
|
||||
BACKUP_LOCATION="/backups/$(date +"%F_%H-%M-%S").${EXTENSION}"
|
||||
cd /data || exit 1 # Exit with error if opening vw data file fails
|
||||
|
||||
BACKUP_DB="db.sqlite3" # file
|
||||
BACKUP_RSA="rsa_key*" # files
|
||||
BACKUP_CONFIG="config.json" # file
|
||||
BACKUP_ATTACHMENTS="attachments" # directory
|
||||
BACKUP_SENDS="sends" # directory
|
||||
# Ensure backup directory exists
|
||||
mkdir -p /backups
|
||||
BACKUP_LOCATION="/backups/${BACKUP_FILENAME}.${EXTENSION}"
|
||||
|
||||
# Create list of backup items to archive
|
||||
BACKUP_ITEMS="$BACKUP_DB $BACKUP_RSA $BACKUP_CONFIG $BACKUP_ATTACHMENTS $BACKUP_SENDS"
|
||||
|
||||
# Verify which items are available to be backed up
|
||||
BACKUP_ITEMS="db.sqlite3 rsa_key* config.json attachments sends"
|
||||
WARNING_STATUS=""
|
||||
FILES_TO_BACKUP=""
|
||||
WARNING=""
|
||||
|
||||
# Verify which items are available to be backed up
|
||||
for ITEM in $BACKUP_ITEMS; do
|
||||
if [ -e "$ITEM" ] || [ -d "$ITEM" ]; then
|
||||
FILES_TO_BACKUP="$FILES_TO_BACKUP $ITEM"
|
||||
@ -31,38 +155,171 @@ for ITEM in $BACKUP_ITEMS; do
|
||||
fi
|
||||
done
|
||||
|
||||
# Print the warnings out in the docker logs
|
||||
# Print and set warnings
|
||||
if [ -n "$WARNING" ]; then
|
||||
echo "[WARNING] The following expected files/directories are missing:$WARNING" >&2
|
||||
if [ "$DISABLE_WARNINGS" != "true" ]; then
|
||||
WARNING_STATUS="⚠️ Missing files:$WARNING"
|
||||
fi
|
||||
fi
|
||||
|
||||
|
||||
# Back up files and folders, only if there are files to back up
|
||||
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
|
||||
# Exit if there are no files to back up
|
||||
if [ -z "$FILES_TO_BACKUP" ]; then
|
||||
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
|
||||
|
||||
# --- [ 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
|
||||
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)
|
||||
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" ] \
|
||||
&& echo "deleted $(echo "$TO_DELETE" | wc -l) archives older than ${DELETE_AFTER} days" \
|
||||
|| echo "no archives older than ${DELETE_AFTER} days to delete")"
|
||||
if [ "$DELETED_COUNT" -gt 0 ]; then
|
||||
if [ "$DELETED_COUNT" -eq 1 ]; then
|
||||
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
|
||||
|
||||
# --- [ 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 ] ------------------
|
||||
|
||||
echo "[$(date +"%F %r")] ${OUTPUT}."
|
||||
# Append the cleanup message to the final console log message
|
||||
echo "[$(date +"%F %r")] ${OUTPUT}${OUTPUT_SUFFIX}."
|
Loading…
x
Reference in New Issue
Block a user