├── .editorconfig ├── .env ├── .gitattributes ├── .github ├── dependabot.yml └── workflows │ ├── docker-description.yml │ ├── docker-publish.yml │ ├── docker-test.yml │ ├── github-release.yml │ └── sync.yml ├── .gitignore ├── CHANGELOG.md ├── Dockerfile ├── LICENSE ├── README.md ├── README_zh.md ├── docker-bake.hcl ├── docker-compose.yml ├── docs ├── manually-trigger-a-backup.md ├── multiple-remote-destinations.md ├── run-as-non-root-user.md ├── using-the-mysql-or-mariadb-backend.md └── using-the-postgresql-backend.md ├── scripts ├── backup.sh ├── entrypoint.sh ├── includes.sh └── restore.sh ├── tests ├── Dockerfile ├── fixtures │ └── source │ │ ├── bitwarden │ │ └── data │ │ │ ├── attachments │ │ │ └── 31ba771a-dde9-4b5c-aced-9c5688017f4e │ │ │ │ └── c45510b00a849da3dd9e │ │ │ ├── config.json │ │ │ ├── db.sqlite3 │ │ │ ├── db.sqlite3-shm │ │ │ ├── db.sqlite3-wal │ │ │ ├── rsa_key.pem │ │ │ ├── rsa_key.pub.pem │ │ │ └── sends │ │ │ └── d48d0c9a-5711-40d9-b7a5-e0706c5cecce │ │ │ └── 473c1e5667b047fd26bf16bd060769a525299deaff3989a85e5637758a74f2bd │ │ └── config │ │ └── rclone │ │ └── rclone.conf ├── test.sh └── units │ ├── backup-7z-file │ └── test.sh │ ├── backup-cron │ └── test.sh │ ├── backup-unpackage │ └── test.sh │ ├── backup-zip-file │ └── test.sh │ ├── check-rclone-connection-initializing │ └── test.sh │ └── env-priority │ └── test.sh └── version /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | indent_style = space 6 | indent_size = 2 7 | end_of_line = lf 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | 11 | [*.md] 12 | trim_trailing_whitespace = false 13 | 14 | [*.sh] 15 | indent_size = 4 16 | -------------------------------------------------------------------------------- /.env: -------------------------------------------------------------------------------- 1 | # 1. Please put the value in double quotes to avoid problems. 2 | # 2. To use the file, you need to map the file to `/.env` in the container. 3 | 4 | # RCLONE_REMOTE_NAME="BitwardenBackup" 5 | # RCLONE_REMOTE_DIR="/BitwardenBackup/" 6 | # RCLONE_GLOBAL_FLAG="" 7 | # CRON="5 * * * *" 8 | # ZIP_ENABLE="TRUE" 9 | # ZIP_PASSWORD="WHEREISMYPASSWORD?" 10 | # ZIP_TYPE="zip" 11 | # BACKUP_FILE_SUFFIX="%Y%m%d" 12 | # BACKUP_KEEP_DAYS="0" 13 | # PING_URL="" 14 | # PING_URL_CURL_OPTIONS="" 15 | # PING_URL_WHEN_START="" 16 | # PING_URL_WHEN_START_CURL_OPTIONS="" 17 | # PING_URL_WHEN_SUCCESS="" 18 | # PING_URL_WHEN_SUCCESS_CURL_OPTIONS="" 19 | # PING_URL_WHEN_FAILURE="" 20 | # PING_URL_WHEN_FAILURE_CURL_OPTIONS="" 21 | # MAIL_SMTP_ENABLE="FALSE" 22 | # MAIL_SMTP_VARIABLES="" 23 | # MAIL_TO="" 24 | # MAIL_WHEN_SUCCESS="TRUE" 25 | # MAIL_WHEN_FAILURE="TRUE" 26 | # TIMEZONE="UTC" 27 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | /*.hcl linguist-detectable=false 2 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | 3 | updates: 4 | 5 | - package-ecosystem: docker 6 | directory: / 7 | schedule: 8 | interval: daily 9 | commit-message: 10 | prefix: 'feat:' 11 | 12 | - package-ecosystem: 'github-actions' 13 | directory: / 14 | schedule: 15 | interval: monthly 16 | commit-message: 17 | prefix: 'chore:' 18 | -------------------------------------------------------------------------------- /.github/workflows/docker-description.yml: -------------------------------------------------------------------------------- 1 | name: 'Docker Publish Description' 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | paths: 8 | - README.md 9 | - .github/workflows/docker-description.yml 10 | 11 | jobs: 12 | sync: 13 | name: Docker README.md Sync 14 | 15 | runs-on: ubuntu-latest 16 | 17 | steps: 18 | - 19 | name: Checkout 20 | uses: actions/checkout@v4 21 | - 22 | name: DockerHub Description 23 | uses: peter-evans/dockerhub-description@v4 24 | with: 25 | username: ${{ secrets.DOCKERHUB_USERNAME }} 26 | password: ${{ secrets.DOCKERHUB_TOKEN }} 27 | repository: 'ttionya/vaultwarden-backup' 28 | enable-url-completion: true 29 | - 30 | name: DockerHub Description 31 | uses: peter-evans/dockerhub-description@v4 32 | with: 33 | username: ${{ secrets.DOCKERHUB_USERNAME }} 34 | password: ${{ secrets.DOCKERHUB_TOKEN }} 35 | repository: 'ttionya/bitwardenrs-backup' 36 | enable-url-completion: true 37 | -------------------------------------------------------------------------------- /.github/workflows/docker-publish.yml: -------------------------------------------------------------------------------- 1 | name: 'Docker Publish' 2 | 3 | on: 4 | push: 5 | tags: 6 | - 'v*.*.*' 7 | schedule: 8 | - cron: '0 0 10,20,30 * *' 9 | 10 | permissions: 11 | packages: write 12 | 13 | jobs: 14 | publish-stable: 15 | name: Docker Publish 16 | 17 | runs-on: ubuntu-latest 18 | 19 | if: ${{ github.event_name != 'schedule' && !contains(github.ref, '-') }} 20 | 21 | steps: 22 | - 23 | name: Checkout 24 | uses: actions/checkout@v4 25 | - 26 | name: Prepare 27 | run: echo "VERSION=${GITHUB_REF_NAME#v}" >> $GITHUB_ENV 28 | - 29 | name: Set up QEMU 30 | uses: docker/setup-qemu-action@v3 31 | - 32 | name: Set up Docker Buildx 33 | uses: docker/setup-buildx-action@v3 34 | - 35 | name: Login to DockerHub 36 | uses: docker/login-action@v3 37 | with: 38 | username: ${{ secrets.DOCKERHUB_USERNAME }} 39 | password: ${{ secrets.DOCKERHUB_TOKEN }} 40 | - 41 | name: Login to ghcr.io 42 | uses: docker/login-action@v3 43 | with: 44 | registry: ghcr.io 45 | username: ${{ github.actor }} 46 | password: ${{ secrets.GITHUB_TOKEN }} 47 | - 48 | name: Docker meta 49 | id: meta 50 | uses: docker/metadata-action@v5 51 | with: 52 | labels: | 53 | org.opencontainers.image.title=vaultwarden-backup 54 | org.opencontainers.image.description=Backup vaultwarden SQLite3/PostgreSQL/MySQL/MariaDB database by rclone 55 | org.opencontainers.image.authors=ttionya 56 | org.opencontainers.image.version=${{ env.VERSION }} 57 | - 58 | name: Build and push 59 | uses: docker/bake-action@v6 60 | env: 61 | VERSION: ${{ env.VERSION }} 62 | with: 63 | source: . 64 | files: | 65 | ./docker-bake.hcl 66 | ${{ steps.meta.outputs.bake-file-labels }} 67 | targets: image-stable 68 | push: true 69 | 70 | publish-beta: 71 | name: Docker Publish Beta 72 | 73 | runs-on: ubuntu-latest 74 | 75 | if: ${{ github.event_name != 'schedule' && contains(github.ref, '-') }} 76 | 77 | steps: 78 | - 79 | name: Checkout 80 | uses: actions/checkout@v4 81 | - 82 | name: Prepare 83 | run: echo "VERSION=${GITHUB_REF_NAME#v}" >> $GITHUB_ENV 84 | - 85 | name: Set up QEMU 86 | uses: docker/setup-qemu-action@v3 87 | - 88 | name: Set up Docker Buildx 89 | uses: docker/setup-buildx-action@v3 90 | - 91 | name: Login to DockerHub 92 | uses: docker/login-action@v3 93 | with: 94 | username: ${{ secrets.DOCKERHUB_USERNAME }} 95 | password: ${{ secrets.DOCKERHUB_TOKEN }} 96 | - 97 | name: Docker meta 98 | id: meta 99 | uses: docker/metadata-action@v5 100 | with: 101 | labels: | 102 | org.opencontainers.image.title=vaultwarden-backup 103 | org.opencontainers.image.description=Backup vaultwarden SQLite3/PostgreSQL/MySQL/MariaDB database by rclone 104 | org.opencontainers.image.authors=ttionya 105 | org.opencontainers.image.version=${{ env.VERSION }} 106 | - 107 | name: Build and push 108 | uses: docker/bake-action@v6 109 | env: 110 | VERSION: ${{ env.VERSION }} 111 | with: 112 | source: . 113 | files: | 114 | ./docker-bake.hcl 115 | ${{ steps.meta.outputs.bake-file-labels }} 116 | targets: image-beta 117 | push: true 118 | 119 | publish-schedule: 120 | name: Docker Publish Schedule 121 | 122 | runs-on: ubuntu-latest 123 | 124 | if: ${{ github.event_name == 'schedule' }} 125 | 126 | steps: 127 | - 128 | name: Get Tag 129 | id: tag 130 | uses: pozetroninc/github-action-get-latest-release@v0.8.0 131 | with: 132 | repository: ${{ github.repository }} 133 | excludes: prerelease, draft 134 | - 135 | name: Checkout 136 | uses: actions/checkout@v4 137 | with: 138 | ref: refs/tags/${{ steps.tag.outputs.release }} 139 | - 140 | name: Prepare 141 | run: | 142 | TAG=${{ steps.tag.outputs.release }} 143 | echo "VERSION=${TAG#v}" >> $GITHUB_ENV 144 | - 145 | name: Set up QEMU 146 | uses: docker/setup-qemu-action@v3 147 | - 148 | name: Set up Docker Buildx 149 | uses: docker/setup-buildx-action@v3 150 | - 151 | name: Login to DockerHub 152 | uses: docker/login-action@v3 153 | with: 154 | username: ${{ secrets.DOCKERHUB_USERNAME }} 155 | password: ${{ secrets.DOCKERHUB_TOKEN }} 156 | - 157 | name: Login to ghcr.io 158 | uses: docker/login-action@v3 159 | with: 160 | registry: ghcr.io 161 | username: ${{ github.actor }} 162 | password: ${{ secrets.GITHUB_TOKEN }} 163 | - 164 | name: Docker meta 165 | id: meta 166 | uses: docker/metadata-action@v5 167 | with: 168 | labels: | 169 | org.opencontainers.image.title=vaultwarden-backup 170 | org.opencontainers.image.description=Backup vaultwarden SQLite3/PostgreSQL/MySQL/MariaDB database by rclone 171 | org.opencontainers.image.authors=ttionya 172 | org.opencontainers.image.version=${{ env.VERSION }} 173 | - 174 | name: Build and push 175 | uses: docker/bake-action@v6 176 | env: 177 | VERSION: ${{ env.VERSION }} 178 | with: 179 | source: . 180 | files: | 181 | ./docker-bake.hcl 182 | ${{ steps.meta.outputs.bake-file-labels }} 183 | targets: image-schedule 184 | push: true 185 | -------------------------------------------------------------------------------- /.github/workflows/docker-test.yml: -------------------------------------------------------------------------------- 1 | name: 'Docker Test' 2 | 3 | on: 4 | workflow_dispatch: 5 | push: 6 | paths: 7 | - .github/workflows/docker-test.yml 8 | - Dockerfile 9 | - scripts/** 10 | - tests/** 11 | 12 | jobs: 13 | test: 14 | name: Docker Test 15 | 16 | runs-on: ubuntu-latest 17 | 18 | services: 19 | registry: 20 | image: registry:2 21 | ports: 22 | - 5000:5000 23 | 24 | steps: 25 | - 26 | name: Checkout 27 | uses: actions/checkout@v4 28 | - 29 | name: Set up QEMU 30 | uses: docker/setup-qemu-action@v3 31 | - 32 | name: Set up Docker Buildx 33 | uses: docker/setup-buildx-action@v3 34 | with: 35 | # network=host driver-opt needed to push to local registry 36 | # https://docs.docker.com/build/ci/github-actions/named-contexts/#using-with-a-container-builder 37 | driver-opts: network=host 38 | - 39 | name: Build base image 40 | uses: docker/bake-action@v6 41 | with: 42 | source: . 43 | files: ./docker-bake.hcl 44 | targets: image-test-base 45 | push: true 46 | - 47 | name: Build test image 48 | uses: docker/bake-action@v6 49 | with: 50 | source: . 51 | files: ./docker-bake.hcl 52 | targets: image-test 53 | load: true 54 | - 55 | name: Test 56 | run: | 57 | sudo apt-get update 58 | sudo apt-get install -y p7zip-full 59 | bash tests/test.sh 60 | -------------------------------------------------------------------------------- /.github/workflows/github-release.yml: -------------------------------------------------------------------------------- 1 | name: 'GitHub Release' 2 | 3 | on: 4 | push: 5 | tags: 6 | - 'v*.*.*' 7 | - '!v*.*.*-*' 8 | 9 | permissions: 10 | contents: write 11 | 12 | jobs: 13 | release: 14 | name: Create Release 15 | 16 | runs-on: ubuntu-latest 17 | 18 | steps: 19 | - 20 | name: Checkout 21 | uses: actions/checkout@v4 22 | - 23 | name: Release 24 | uses: softprops/action-gh-release@v2 25 | with: 26 | body: | 27 | [CHANGELOG](https://github.com/ttionya/vaultwarden-backup/blob/master/CHANGELOG.md) 28 | -------------------------------------------------------------------------------- /.github/workflows/sync.yml: -------------------------------------------------------------------------------- 1 | name: 'Sync' 2 | 3 | on: 4 | push: 5 | delete: 6 | schedule: 7 | - cron: '0 9 * * *' 8 | 9 | jobs: 10 | gitee: 11 | name: Sync to Gitee 12 | 13 | runs-on: ubuntu-latest 14 | 15 | if: ${{ github.actor != 'dependabot[bot]' }} 16 | 17 | steps: 18 | - 19 | name: Checkout 20 | uses: actions/checkout@v4 21 | with: 22 | fetch-depth: 0 23 | - 24 | name: Sync 25 | uses: ttionya/Repository-Sync-Hub@v1 26 | with: 27 | target_repository: 'https://gitee.com/ttionya/vaultwarden-backup.git' 28 | http_access_name: 'ttionya' 29 | http_access_token: ${{ secrets.GITEE_HTTP_ACCESS_TOKEN }} 30 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # OS 2 | .DS_Store 3 | 4 | # IDE 5 | .vscode 6 | .idea 7 | 8 | # Log 9 | *.log 10 | *.log.* 11 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## v1.24.3 (20250522) 4 | 5 | ### Feature 6 | 7 | - Update Dockerfile base image to `rclone/rclone:1.69.3` 8 | 9 |
10 | 11 | 12 | 13 | ## v1.24.2 (20250512) 14 | 15 | ### Fixed 16 | 17 | - Fix `directory not found` error (fixed [#199](https://github.com/ttionya/vaultwarden-backup/issues/199)) 18 | 19 |
20 | 21 | 22 | 23 | ## v1.24.1 (20250503) 24 | 25 | ### Feature 26 | 27 | - Update Dockerfile base image to `rclone/rclone:1.69.2` 28 | 29 |
30 | 31 | 32 | 33 | ## v1.24.0 (20250316) 34 | 35 | ### Feature 36 | 37 | - Add environment variable `DISPLAY_NAME` to customize the display name (close [#189](https://github.com/ttionya/vaultwarden-backup/issues/189)) 38 | 39 |
40 | 41 | 42 | 43 | ## v1.23.2 (20250312) 44 | 45 | ### Fixed 46 | 47 | - Fix the issue where errors occur due to spaces in `MAIL_SMTP_VARIABLES` in some cases (fixed [#186](https://github.com/ttionya/vaultwarden-backup/issues/186)) 48 | 49 |
50 | 51 | 52 | 53 | ## v1.23.1 (20250220) 54 | 55 | ### Feature 56 | 57 | - Add environment variables `MYSQL_SSL` and `MYSQL_SSL_VERIFY_SERVER_CERT` to resolve the MySQL TLS connection error 58 | 59 |
60 | 61 | 62 | 63 | ## v1.23.0 (20250218) 64 | 65 | ### Feature 66 | 67 | - Update Dockerfile base image to `rclone/rclone:1.69.1` 68 | - Support MySQL SSL connection (close [#185](https://github.com/ttionya/vaultwarden-backup/pull/185)) 69 | - Optimize Rclone connection detection logic (close [#183](https://github.com/ttionya/vaultwarden-backup/issues/183)) 70 | 71 | ### Chore 72 | 73 | - Compatible with docker/bake-action@v6 74 | 75 |
76 | 77 | 78 | 79 | ## v1.22.0 (20250116) 80 | 81 | ### Feature 82 | 83 | - Update Dockerfile base image to `rclone/rclone:1.69.0` 84 | - Support PostgreSQL 17 (close [#178](https://github.com/ttionya/vaultwarden-backup/issues/178)) 85 | - Allow partial storage system connection failures during backup verification (close [#175](https://github.com/ttionya/vaultwarden-backup/issues/175)) 86 | 87 |
88 | 89 | 90 | 91 | ## v1.21.3 (20241117) 92 | 93 | ### Feature 94 | 95 | - Update Dockerfile base image to `rclone/rclone:1.68.2` 96 | - Use standardized docker labels 97 | 98 |
99 | 100 | 101 | 102 | ## v1.21.2 (20240925) 103 | 104 | ### Feature 105 | 106 | - Update Dockerfile base image to `rclone/rclone:1.68.1` 107 | 108 |
109 | 110 | 111 | 112 | ## v1.21.1 (20240914) 113 | 114 | ### Feature 115 | 116 | - Update Dockerfile base image to `rclone/rclone:1.68.0` 117 | 118 |
119 | 120 | 121 | 122 | ## v1.21.0 (20240907) 123 | 124 | ### Feature 125 | 126 | - Enhance ping functionality to support customizable curl options. (close [#164](https://github.com/ttionya/vaultwarden-backup/issues/164)) 127 | 128 |
129 | 130 | 131 | 132 | ## v1.20.0 (20240721) 133 | 134 | ### Feature 135 | 136 | - Add support for fine-grained ping messages 137 | - Update Dockerfile base image to `rclone/rclone:1.67.0` 138 | - Support testing 139 | 140 |
141 | 142 | 143 | 144 | ## v1.19.10 (20240313) 145 | 146 | ### Feature 147 | 148 | - Update Dockerfile base image to `rclone/rclone:1.66.0` 149 | 150 |
151 | 152 | 153 | 154 | ## v1.19.9 (20240223) 155 | 156 | ### Feature 157 | 158 | - Update Dockerfile base image to `rclone/rclone:1.65.2` 159 | 160 |
161 | 162 | 163 | 164 | ## v1.19.8 (20240112) 165 | 166 | ### Fixed 167 | 168 | - Fix `restore` command parameter parsing error (fixed [#141](https://github.com/ttionya/vaultwarden-backup/issues/141)) 169 | 170 |
171 | 172 | 173 | 174 | ## v1.19.7 (20240111) 175 | 176 | ### Feature 177 | 178 | - Update Dockerfile base image to `rclone/rclone:1.65.1` 179 | 180 |
181 | 182 | 183 | 184 | ## v1.19.6 (20231214) 185 | 186 | ### Feature 187 | 188 | - Support PostgreSQL 16 (close [#137](https://github.com/ttionya/vaultwarden-backup/issues/137)) 189 | 190 |
191 | 192 | 193 | 194 | ## v1.19.4 (20231128) 195 | 196 | ### Feature 197 | 198 | - Update Dockerfile base image to `rclone/rclone:1.65.0` 199 | 200 |
201 | 202 | 203 | 204 | ## v1.19.3 (20231021) 205 | 206 | ### Feature 207 | 208 | - Update Dockerfile base image to `rclone/rclone:1.64.2` 209 | 210 |
211 | 212 | 213 | 214 | ## v1.19.2 (20230912) 215 | 216 | ### Feature 217 | 218 | - Update Dockerfile base image to `rclone/rclone:1.64.0` 219 | 220 |
221 | 222 | 223 | 224 | ## v1.19.1 (20230722) 225 | 226 | ### Feature 227 | 228 | - Update Dockerfile base image to `rclone/rclone:1.63.1` 229 | 230 |
231 | 232 | 233 | 234 | ## v1.19.0 (20230704) 235 | 236 | **Reminder**: We are utilizing [`s-nail`](https://www.sdaoden.eu/code-nail.html) to send emails. In case you encounter any errors during the email sending process, please modify the `MAIL_SMTP_VARIABLES` environment variables accordingly. 237 | 238 | ### Feature 239 | 240 | - Using `s-nail` instead of `heirloom-mailx` to send emails 241 | - Update Dockerfile base image to `rclone/rclone:1.63.0` 242 | 243 |
244 | 245 | 246 | 247 | ## v1.18.0 (20230408) 248 | 249 | ### Feature 250 | 251 | - Add environment variable `BACKUP_FILE_SUFFIX` 252 | 253 |
254 | 255 | 256 | 257 | ## v1.17.0 (20230318) 258 | 259 | ### Feature 260 | 261 | - Update Dockerfile base image to `rclone/rclone:1.62.2` 262 | - Support manually trigger a backup (close [#94](https://github.com/ttionya/vaultwarden-backup/issues/94)) 263 | 264 |
265 | 266 | 267 | 268 | ## v1.16.0 (20230129) 269 | 270 | ### Feature 271 | 272 | - Support PostgreSQL/MySQL/MariaDB backend (close [#88](https://github.com/ttionya/vaultwarden-backup/issues/88)) 273 | 274 |
275 | 276 | 277 | 278 | ## v1.15.3 (20221225) 279 | 280 | ### Feature 281 | 282 | - Update Dockerfile base image to `rclone/rclone:1.61.1` 283 | - Replace `p7zip` with `7zip` package (close [#86](https://github.com/ttionya/vaultwarden-backup/issues/68)) 284 | 285 |
286 | 287 | 288 | 289 | ## v1.15.2 (20221119) 290 | 291 | ### Feature 292 | 293 | - Update Dockerfile base image to `rclone/rclone:1.60.1` 294 | 295 |
296 | 297 | 298 | 299 | ## v1.15.1 (20221022) 300 | 301 | ### Feature 302 | 303 | - Update Dockerfile base image to `rclone/rclone:1.60.0` 304 | 305 |
306 | 307 | 308 | 309 | ## v1.15.0 (20221018) 310 | 311 | ### Feature 312 | 313 | - Execute `supercronic` as PID 1 process 314 | 315 |
316 | 317 | 318 | 319 | ## v1.14.4 (20220916) 320 | 321 | ### Feature 322 | 323 | - Update Dockerfile base image to `rclone/rclone:1.59.2` 324 | 325 |
326 | 327 | 328 | 329 | ## v1.14.3 (20220908) 330 | 331 | ### Fixed 332 | 333 | - Fix arm/v6 can't use the latest rclone (fixed [#81](https://github.com/ttionya/vaultwarden-backup/issues/81)) 334 | 335 |
336 | 337 | 338 | 339 | ## v1.14.2 (20220826) 340 | 341 | ### Feature 342 | 343 | - Add hidden environment variable `BACKUP_FILE_DATE` 344 | 345 |
346 | 347 | 348 | 349 | ## v1.14.1 (20220809) 350 | 351 | ### Feature 352 | 353 | - Update Dockerfile base image to `rclone/rclone:1.59.1` 354 | 355 | ### Chore 356 | 357 | - Skip sync when dependabot push branch 358 | 359 |
360 | 361 | 362 | 363 | ## v1.14.0 (20220731) 364 | 365 | ### Feature 366 | 367 | - Support backup to multiple remote storage 368 | 369 |
370 | 371 | 372 | 373 | ## v1.13.0 (20220728) 374 | 375 | ### Feature 376 | 377 | - Support force restore without asking for confirmation 378 | 379 |
380 | 381 | 382 | 383 | ## v1.12.2 (20220718) 384 | 385 | ### Feature 386 | 387 | - Support `arm/v6` platform by using multistage build (close [#56](https://github.com/ttionya/vaultwarden-backup/issues/56)) 388 | 389 |
390 | 391 | 392 | 393 | ## v1.12.1 (20220711) 394 | 395 | ### Feature 396 | 397 | - Support `arm/v6` platform (close [#56](https://github.com/ttionya/vaultwarden-backup/issues/56)) 398 | - Update Dockerfile base image to `rclone/rclone:1.59.0` 399 | 400 | ### Chore 401 | 402 | - Add GitHub Actions dependabot 403 | - Update GitHub Actions 404 | 405 |
406 | 407 | 408 | 409 | ## v1.12.0 (20220702) 410 | 411 | ### Feature 412 | 413 | - Support start the container as non-root user (close [#45](https://github.com/ttionya/vaultwarden-backup/issues/45), close [#47](https://github.com/ttionya/vaultwarden-backup/issues/47)) 414 | - Cron tool switched from BusyBox `crond` to [`supercronic`](https://github.com/aptible/supercronic) 415 | 416 |
417 | 418 | 419 | 420 | ## v1.11.1 (20220430) 421 | 422 | ### Feature 423 | 424 | - Update Dockerfile base image to `rclone/rclone:1.58.1` 425 | 426 | ### Chore 427 | 428 | - Add dependabot 429 | 430 |
431 | 432 | 433 | 434 | ## v1.11.0 (20220321) 435 | 436 | ### Feature 437 | 438 | - Update Dockerfile base image to `rclone/rclone:1.58.0` 439 | - support Rclone global flags (close [#49](https://github.com/ttionya/vaultwarden-backup/issues/49)) 440 | 441 |
442 | 443 | 444 | 445 | ## v1.10.0 (20211231) 446 | 447 | ### Feature 448 | 449 | - Encrypt file/dirname for 7z format 450 | 451 | ### Fixed 452 | 453 | - Fix Mail Test error 454 | - Fix the problem that `MAIL_SMTP_VARIABLES` does not work with `.env` files (fixed [#36](https://github.com/ttionya/vaultwarden-backup/issues/36), fixed [#38](https://github.com/ttionya/vaultwarden-backup/issues/38)) 455 | 456 |
457 | 458 | 459 | 460 | ## v1.9.6 (20211106) 461 | 462 | ### Feature 463 | 464 | - Update Dockerfile base image to `rclone/rclone:1.57.0` 465 | - Display the time of running the backup program 466 | 467 |
468 | 469 | 470 | 471 | ## v1.9.5 (20211010) 472 | 473 | ### Feature 474 | 475 | - Update Dockerfile base image to `rclone/rclone:1.56.2` 476 | 477 |
478 | 479 | 480 | 481 | ## v1.9.4 (20210925) 482 | 483 | ### Fixed 484 | 485 | - Fix the wrong rsa_key compressed file name for searching when restoring (fixed [#32](https://github.com/ttionya/vaultwarden-backup/issues/32)) 486 | 487 |
488 | 489 | 490 | 491 | ## v1.9.3 (20210922) 492 | 493 | ### Feature 494 | 495 | - Update Dockerfile base image to `rclone/rclone:1.56.1` 496 | 497 | ### Chore 498 | 499 | - On the 10th, 20th and 30th of every month, republish the Docker image to update the alpine packages 500 | 501 |
502 | 503 | 504 | 505 | ## v1.9.2 (20210731) 506 | 507 | ### Feature 508 | 509 | - Update Dockerfile base image to `rclone/rclone:1.56.0` (close [#31](https://github.com/ttionya/vaultwarden-backup/issues/31)) 510 | 511 |
512 | 513 | 514 | 515 | ## v1.9.1 (20210609) 516 | 517 | ### Feature 518 | 519 | - Increase the number of ping retries and timeout time 520 | 521 |
522 | 523 | 524 | 525 | ## v1.9.0 (20210608) 526 | 527 | ### Feature 528 | 529 | - Support for pinging, such as healthchecks.io (close [#30](https://github.com/ttionya/vaultwarden-backup/issues/30)) 530 | 531 | ### Chore 532 | 533 | - Don't support linux/386 platform anymore because vaultwarden not support it 534 | 535 |
536 | 537 | 538 | 539 | ## v1.8.1 (20210512) 540 | 541 | ### Feature 542 | 543 | - Update the `docker-compose.yml` file to use the new docker image 544 | - Add Rclone configuration verification (fixed [#29](https://github.com/ttionya/vaultwarden-backup/issues/29)) 545 | 546 |
547 | 548 | 549 | 550 | ## v1.8.0 (20210506) 551 | 552 | **Reminder**: If you are still using the `ttionya/bitwardenrs-backup` Docker images, you need to migrate to the new `ttionya/vaultwarden-backup` image. 553 | 554 | ### Feature 555 | 556 | - Rename the Docker image to `vaultwarden-backup` 557 | 558 | ### Chore 559 | 560 | - Build both `bitwardenrs-backup` and `vaultwarden-backup` Docker images 561 | 562 |
563 | 564 | 565 | 566 | ## before v1.8.0 567 | 568 | outdated. 569 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | # syntax=docker/dockerfile:1 2 | 3 | FROM rclone/rclone:1.69.3 4 | 5 | ARG USER_NAME="backuptool" 6 | ARG USER_ID="1100" 7 | 8 | ENV LOCALTIME_FILE="/tmp/localtime" 9 | 10 | COPY scripts/*.sh /app/ 11 | 12 | RUN chmod +x /app/*.sh \ 13 | && mkdir -m 777 /bitwarden \ 14 | && apk add --no-cache 7zip bash curl mariadb-client postgresql17-client sqlite supercronic s-nail tzdata \ 15 | && apk info --no-cache -Lq mariadb-client | grep -vE '/bin/mariadb$' | grep -vE '/bin/mariadb-dump$' | xargs -I {} rm -f "/{}" \ 16 | && ln -sf "${LOCALTIME_FILE}" /etc/localtime \ 17 | && addgroup -g "${USER_ID}" "${USER_NAME}" \ 18 | && adduser -u "${USER_ID}" -Ds /bin/sh -G "${USER_NAME}" "${USER_NAME}" 19 | 20 | ENTRYPOINT ["/app/entrypoint.sh"] 21 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 ttionya 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # vaultwarden backup 2 | 3 | [![Docker Image Version (latest by date)](https://img.shields.io/docker/v/ttionya/vaultwarden-backup?label=Version&logo=docker)](https://hub.docker.com/r/ttionya/vaultwarden-backup/tags) [![Docker Pulls](https://img.shields.io/docker/pulls/ttionya/vaultwarden-backup?label=Docker%20Pulls&logo=docker)](https://hub.docker.com/r/ttionya/vaultwarden-backup) [![GitHub](https://img.shields.io/github/license/ttionya/vaultwarden-backup?label=License&logo=github)](https://github.com/ttionya/vaultwarden-backup/blob/master/LICENSE) 4 | 5 | README | [中文文档](README_zh.md) 6 | 7 | Docker containers for [vaultwarden](https://github.com/dani-garcia/vaultwarden) (formerly known as **`bitwarden_rs`**) backup to remote. 8 | 9 | - [Docker Hub](https://hub.docker.com/r/ttionya/vaultwarden-backup) 10 | - [GitHub Packages](https://github.com/ttionya/vaultwarden-backup/pkgs/container/vaultwarden-backup) 11 | - [GitHub](https://github.com/ttionya/vaultwarden-backup) 12 | 13 |
14 | 15 | 16 | 17 | ## Feature 18 | 19 | This tool supports backing up the following files or directories. 20 | 21 | - `db.sqlite3` (for SQLite database) 22 | - `db.dump` (for PostgreSQL database) 23 | - `db.sql` (for MySQL / MariaDB database) 24 | - `config.json` 25 | - `rsa_key*` (multiple files) 26 | - `attachments` (directory) 27 | - `sends` (directory) 28 | 29 | And the following ways of notifying backup results are supported. 30 | 31 | - Ping (send on completion, start, success, or failure) 32 | - Mail (SMTP based, send on success and on failure) 33 | 34 |
35 | 36 | 37 | 38 | ## Usage 39 | 40 | > **Important:** We assume you already read the `vaultwarden` [documentation](https://github.com/dani-garcia/vaultwarden/wiki). 41 | 42 | ### Configure Rclone (⚠️ MUST READ ⚠️) 43 | 44 | > **For backup, you need to configure Rclone first, otherwise the backup tool will not work.** 45 | > 46 | > **For restore, it is not necessary.** 47 | 48 | We upload the backup files to the storage system by [Rclone](https://rclone.org/). 49 | 50 | Visit [GitHub](https://github.com/rclone/rclone) for more storage system tutorials. Different systems get tokens differently. 51 | 52 | #### Configure and Check 53 | 54 | You can get the token by the following command. 55 | 56 | ```shell 57 | docker run --rm -it \ 58 | --mount type=volume,source=vaultwarden-rclone-data,target=/config/ \ 59 | ttionya/vaultwarden-backup:latest \ 60 | rclone config 61 | ``` 62 | 63 | **We recommend setting the remote name to `BitwardenBackup`, otherwise you need to specify the environment variable `RCLONE_REMOTE_NAME` as the remote name you set.** 64 | 65 | After setting, check the configuration content by the following command. 66 | 67 | ```shell 68 | docker run --rm -it \ 69 | --mount type=volume,source=vaultwarden-rclone-data,target=/config/ \ 70 | ttionya/vaultwarden-backup:latest \ 71 | rclone config show 72 | 73 | # Microsoft Onedrive Example 74 | # [BitwardenBackup] 75 | # type = onedrive 76 | # token = {"access_token":"access token","token_type":"token type","refresh_token":"refresh token","expiry":"expiry time"} 77 | # drive_id = driveid 78 | # drive_type = personal 79 | ``` 80 | 81 |
82 | 83 | 84 | 85 | ### Backup 86 | 87 | #### Use Docker Compose (Recommend) 88 | 89 | If you are a new user or are rebuilding vaultwarden, it is recommended to use the `docker-compose.yml` from the project. 90 | 91 | Download `docker-compose.yml` to you machine, edit environment variables and start it. 92 | 93 | You need to go to the directory where the `docker-compose.yml` file is saved. 94 | 95 | ```shell 96 | # Start 97 | docker-compose up -d 98 | 99 | # Stop 100 | docker-compose stop 101 | 102 | # Restart 103 | docker-compose restart 104 | 105 | # Remove 106 | docker-compose down 107 | ``` 108 | 109 | #### Automatic Backups 110 | 111 | If you have a running vaultwarden but don't want to use `docker-compose.yml`, we also provide a backup method for you. 112 | 113 | Make sure that your vaultwarden container is named `vaultwarden` otherwise you have to replace the container name in the `--volumes-from` section of the docker run call. 114 | 115 | By default the data folder for vaultwarden is `/data`, you need to explicitly specify the data folder using the environment variable `DATA_DIR`. 116 | 117 | Start the backup container with default settings. (automatic backup at 5 minute every hour) 118 | 119 | ```shell 120 | docker run -d \ 121 | --restart=always \ 122 | --name vaultwarden_backup \ 123 | --volumes-from=vaultwarden \ 124 | --mount type=volume,source=vaultwarden-rclone-data,target=/config/ \ 125 | -e DATA_DIR="/data" \ 126 | ttionya/vaultwarden-backup:latest 127 | ``` 128 | 129 |
130 | 131 | 132 | 133 | ### Restore 134 | 135 | > **Important:** Restore will overwrite the existing files. 136 | 137 | You need to stop the Docker container before the restore. 138 | 139 | You also need to download the backup files to your local machine. 140 | 141 | Because the host's files are not accessible in the Docker container, you need to map the directory where the backup files that need to be restored are located to the docker container. 142 | 143 | **And go to the directory where your backup files to be restored are located.** 144 | 145 | If you use the `docker-compose.yml` provided with this project, you can use the following command. 146 | 147 | ```shell 148 | docker run --rm -it \ 149 | --mount type=volume,source=vaultwarden-data,target=/bitwarden/data/ \ 150 | --mount type=bind,source=$(pwd),target=/bitwarden/restore/ \ 151 | ttionya/vaultwarden-backup:latest restore \ 152 | [OPTIONS] 153 | ``` 154 | 155 | If you are using "automatic backups", please confirm the vaultwarden volume and replace the `--mount` `source` section. 156 | 157 | Also don't forget to use the environment variable `DATA_DIR` to specify the data directory (`-e DATA_DIR="/data"`). 158 | 159 | ```shell 160 | docker run --rm -it \ 161 | \ # If you are mapping the local folder to a docker container, like `vw-data` 162 | --mount type=bind,source="the absolution path to your local folder",target=/data/ \ 163 | \ # If you are using docker volume 164 | --mount type=volume,source="docker volume name",target=/data/ \ 165 | --mount type=bind,source=$(pwd),target=/bitwarden/restore/ \ 166 | -e DATA_DIR="/data" \ 167 | ttionya/vaultwarden-backup:latest restore \ 168 | [OPTIONS] 169 | ``` 170 | 171 | See [Options](#options) for options information. 172 | 173 | #### Options 174 | 175 | ##### -f / --force-restore 176 | 177 | For restore without asking for confirmation. 178 | 179 | USE WITH CAUTION!! 180 | 181 |
182 | ※ You have the compressed file named backup 183 | 184 | ##### --zip-file \ 185 | 186 | You need to use this option to specify the `backup` compressed package. 187 | 188 | Make sure the file name in the compressed package has not been changed. 189 | 190 | ##### -p / --password 191 | 192 | THIS IS INSECURE! 193 | 194 | If the `backup` compressed package has a password, you can use this option to set the password to extract it. 195 | 196 | If not, the password will be asked for interactively. 197 | 198 |
199 | 200 |
201 | ※ You have multiple independent backup files 202 | 203 | ##### --db-file \ 204 | 205 | You need to use this option to specify the `db.*` file. 206 | 207 | ##### --config-file \ 208 | 209 | You need to use this option to specify the `config.json` file. 210 | 211 | ##### --rsakey-file \ 212 | 213 | You need to use this option to specify the `rsakey.tar` file. 214 | 215 | ##### --attachments-file \ 216 | 217 | You need to use this option to specify the `attachments.tar` file. 218 | 219 | ##### --sends-file \ 220 | 221 | You need to use this option to specify the `sends.tar` file. 222 | 223 |
224 | 225 |
226 | 227 | 228 | 229 | ## Environment Variables 230 | 231 | > **Note:** All environment variables have default values, you can use the docker image without setting any environment variables. 232 | 233 | #### RCLONE_REMOTE_NAME 234 | 235 | The name of the Rclone remote, which needs to be consistent with the remote name in the rclone config. 236 | 237 | You can view the current remote name with the following command. 238 | 239 | ```shell 240 | docker run --rm -it \ 241 | --mount type=volume,source=vaultwarden-rclone-data,target=/config/ \ 242 | ttionya/vaultwarden-backup:latest \ 243 | rclone config show 244 | 245 | # [BitwardenBackup] <- this 246 | # ... 247 | ``` 248 | 249 | Default: `BitwardenBackup` 250 | 251 | #### RCLONE_REMOTE_DIR 252 | 253 | The folder where backup files are stored in the storage system. 254 | 255 | Default: `/BitwardenBackup/` 256 | 257 | #### RCLONE_GLOBAL_FLAG 258 | 259 | Rclone global flags, see [flags](https://rclone.org/flags/). 260 | 261 | **Do not add flags that will change the output, such as `-P`, which will affect the deletion of outdated backup files.** 262 | 263 | Default: `''` 264 | 265 | #### CRON 266 | 267 | Schedule to run the backup script, based on [`supercronic`](https://github.com/aptible/supercronic). You can test the rules [here](https://crontab.guru/#5_*_*_*_*). 268 | 269 | Default: `5 * * * *` (run the script at 5 minute every hour) 270 | 271 | #### ZIP_ENABLE 272 | 273 | Pack all backup files into a compressed file. When set to `'FALSE'`, each backup file will be uploaded independently. 274 | 275 | Default: `TRUE` 276 | 277 | #### ZIP_PASSWORD 278 | 279 | The password for the compressed file. Note that the password will always be used when packing the backup files. 280 | 281 | Default: `WHEREISMYPASSWORD?` 282 | 283 | #### ZIP_TYPE 284 | 285 | Because the `zip` format is less secure, we offer archives in `7z` format for those who seek security. 286 | 287 | It should be noted that the password for vaultwarden is encrypted before it is sent to the server. The server does not have plaintext passwords, so the `zip` format is good enough for basic encryption needs. 288 | 289 | Default: `zip` (only support `zip` and `7z` formats) 290 | 291 | #### BACKUP_KEEP_DAYS 292 | 293 | Only keep last a few days backup files in the storage system. Set to `0` to keep all backup files. 294 | 295 | Default: `0` 296 | 297 | #### BACKUP_FILE_SUFFIX 298 | 299 | Each backup file is suffixed by default with `%Y%m%d`. If you back up your vault multiple times a day, that suffix is not unique anymore. This environment variable allows you to append a unique suffix to that date to create a unique backup name. 300 | 301 | You can use any character except for `/` since it cannot be used in Linux file names. 302 | 303 | This environment variable combines the functionalities of [`BACKUP_FILE_DATE`](#backup_file_date) and [`BACKUP_FILE_DATE_SUFFIX`](#backup_file_date_suffix), and has a higher priority. You can directly use this environment variable to control the suffix of the backup files. 304 | 305 | Please use the [date man page](https://man7.org/linux/man-pages/man1/date.1.html) for the format notation. 306 | 307 | Default: `%Y%m%d` 308 | 309 | #### TIMEZONE 310 | 311 | Set your timezone name. 312 | 313 | Here is timezone list at [wikipedia](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones). 314 | 315 | Default: `UTC` 316 | 317 | #### DISPLAY_NAME 318 | 319 | A custom name to identify your vaultwarden instance in notifications and logs. 320 | 321 | This doesn't affect functionality, it only affects the display in the notification title and partial log output. 322 | 323 | Default: `vaultwarden` 324 | 325 | #### DATA_DIR 326 | 327 | This folder stores the data of vaultwarden. 328 | 329 | When using `Docker Compose`, this does not need to be changed. However, when using automatic backup, you need to change it to `/data`. 330 | 331 | Default: `/bitwarden/data` 332 | 333 | ※ Please refer to the [`Notification`](#notification) section for notification-related environment variables. 334 | 335 |
336 | ※ Other environment variables 337 | 338 | > **You don't need to change these environment variables unless you know what you are doing.** 339 | 340 | #### BACKUP_FILE_DATE 341 | 342 | You should use the [`BACKUP_FILE_SUFFIX`](#backup_file_suffix) environment variable instead. 343 | 344 | Edit this environment variable only if you explicitly want to change the time prefix of the backup file (e.g. 20220101). **Incorrect configuration may result in the backup file being overwritten by mistake.** 345 | 346 | Same rule as [`BACKUP_FILE_DATE_SUFFIX`](#backup_file_date_suffix). 347 | 348 | Default: `%Y%m%d` 349 | 350 | #### BACKUP_FILE_DATE_SUFFIX 351 | 352 | You should use the [`BACKUP_FILE_SUFFIX`](#backup_file_suffix) environment variable instead. 353 | 354 | Each backup file is suffixed by default with `%Y%m%d`. If you back up your vault multiple times a day, that suffix is not unique anymore. 355 | This environment variable allows you to append a unique suffix to that date (`%Y%m%d${BACKUP_FILE_DATE_SUFFIX}`) to create a unique backup name. 356 | 357 | Note that only numbers, upper and lower case letters, `-`, `_`, `%` are supported. 358 | 359 | Please use the [date man page](https://man7.org/linux/man-pages/man1/date.1.html) for the format notation. 360 | 361 | Default: `''` 362 | 363 | #### DATA_DB 364 | 365 | Set the path for the sqlite database file. 366 | 367 | Default: `${DATA_DIR}/db.sqlite3` 368 | 369 | #### DATA_RSAKEY 370 | 371 | Set the path for the rsa_key file. 372 | 373 | Default: `${DATA_DIR}/rsa_key` 374 | 375 | #### DATA_ATTACHMENTS 376 | 377 | Set the path for the attachment folder. 378 | 379 | Default: `${DATA_DIR}/attachments` 380 | 381 | #### DATA_SENDS 382 | 383 | Set the path for the sends folder. 384 | 385 | Default: `${DATA_DIR}/sends` 386 | 387 |
388 | 389 |
390 | 391 | 392 | 393 | ## Notification 394 | 395 | ### Ping 396 | 397 | We provide functionality to send notifications when the backup is completed, started, successful, or failed. 398 | 399 | **Using a [healthcheck.io](https://healthchecks.io/) address or other similar cron monitoring addresses is a good choice, and it is also recommended.** For more complex notification scenarios, you can use environment variables with the `_CURL_OPTIONS` suffix to set curl options. For example, you can add request headers, change the request method, etc. 400 | 401 | For different notification scenarios, **the backup tool provides `%{subject}` and `%{content}` placeholders to replace the actual title and content**. You can use them in the following environment variables. Note that the title and content may contain spaces. For the four environment variables containing `_CURL_OPTIONS`, the placeholders will be directly replaced, retaining spaces. For other `PING_URL` environment variables, spaces will be replaced with `+` to comply with URL rules. 402 | 403 | | Environment Variable | Trigger Status | Test Identifier | Description | 404 | |------------------------------------|-------------|--------------|----------------------------------------------------------------------| 405 | | PING_URL | completion (success or failure) | `completion` | The URL to which the request is sent after the backup is completed. | 406 | | PING_URL_CURL_OPTIONS | | | Curl options used with `PING_URL` | 407 | | PING_URL_WHEN_START | start | `start` | The URL to which the request is sent when the backup starts. | 408 | | PING_URL_WHEN_START_CURL_OPTIONS | | | Curl options used with `PING_URL_WHEN_START` | 409 | | PING_URL_WHEN_SUCCESS | success | `success` | The URL to which the request is sent after the backup is successful. | 410 | | PING_URL_WHEN_SUCCESS_CURL_OPTIONS | | | Curl options used with `PING_URL_WHEN_SUCCESS` | 411 | | PING_URL_WHEN_FAILURE | failure | `failure` | The URL to which the request is sent after the backup fails. | 412 | | PING_URL_WHEN_FAILURE_CURL_OPTIONS | | | Curl options used with `PING_URL_WHEN_FAILURE` | 413 | 414 |
415 | 416 | 417 | 418 | ### Ping Test 419 | 420 | You can use the following command to test the Ping sending. 421 | 422 | The "test identifier" is the identifier in the table in the [previous section](#ping). You can use `completion`, `start`, `success`, or `failure`, which determines which set of environment variables to use. 423 | 424 | ```shell 425 | docker run --rm -it \ 426 | -e PING_URL='' \ 427 | -e PING_URL_CURL_OPTIONS='' \ 428 | -e PING_URL_WHEN_START='' \ 429 | -e PING_URL_WHEN_START_CURL_OPTIONS='' \ 430 | -e PING_URL_WHEN_SUCCESS='' \ 431 | -e PING_URL_WHEN_SUCCESS_CURL_OPTIONS='' \ 432 | -e PING_URL_WHEN_FAILURE='' \ 433 | -e PING_URL_WHEN_FAILURE_CURL_OPTIONS='' \ 434 | ttionya/vaultwarden-backup:latest ping 435 | ``` 436 | 437 |
438 | 439 | 440 | 441 | ### Mail 442 | 443 | Starting from v1.19.0, we will be using [`s-nail`](https://www.sdaoden.eu/code-nail.html) instead of [`heirloom-mailx`](https://www.systutorials.com/docs/linux/man/1-heirloom-mailx/) to send emails. 444 | 445 | Please note that `heirloom-mailx` is a stub for `s-nail`, and most of its functionality is compatible. Therefore, you may not need to modify any environment variables for this change. 446 | 447 | | Environment Variable | Default Value | Description | 448 | | --- | --- |-------------------------------------------------------| 449 | | MAIL_SMTP_ENABLE | `FALSE` | Enable sending mail. | 450 | | MAIL_SMTP_VARIABLES | | Mail sending options. | 451 | | MAIL_TO | | The recipient of the notification email. | 452 | | MAIL_WHEN_SUCCESS | `TRUE` | Send an email when the backup completes successfully. | 453 | | MAIL_WHEN_FAILURE | `TRUE` | Send an email if the backup fails. | 454 | 455 | For `MAIL_SMTP_VARIABLES`, you need to configure the mail sending options yourself. **We will set the email subject based on the usage scenario, so you should not use the `-s` flag.** 456 | 457 | ```text 458 | # My example: 459 | 460 | # For Zoho 461 | -S smtp-use-starttls \ 462 | -S smtp=smtp://smtp.zoho.com:587 \ 463 | -S smtp-auth=login \ 464 | -S smtp-auth-user= \ 465 | -S smtp-auth-password= \ 466 | -S from= 467 | ``` 468 | 469 | Console showing warnings? Check [issue #177](https://github.com/ttionya/vaultwarden-backup/issues/117#issuecomment-1691443179) for more details. 470 | 471 |
472 | 473 | 474 | 475 | ### Mail Test 476 | 477 | You can use the following command to test mail sending. We will add the `-v` flag to display detailed information, so you do not need to set it again in `MAIL_SMTP_VARIABLES`. 478 | 479 | ```shell 480 | docker run --rm -it -e MAIL_SMTP_VARIABLES='' ttionya/vaultwarden-backup:latest mail 481 | 482 | # Or 483 | 484 | docker run --rm -it -e MAIL_SMTP_VARIABLES='' -e MAIL_TO='' ttionya/vaultwarden-backup:latest mail 485 | ``` 486 | 487 |
488 | 489 | 490 | 491 | ## Environment Variables Considerations 492 | 493 | ### Using `.env` file 494 | 495 | If you prefer using an env file instead of environment variables, you can map the env file containing the environment variables to the `/.env` file in the container. 496 | 497 | ```shell 498 | docker run -d \ 499 | --mount type=bind,source=/path/to/env,target=/.env \ 500 | ttionya/vaultwarden-backup:latest 501 | ``` 502 | 503 | **Please do not use the `--env-file` flag directly**; make sure to map the environment variables by mounting the file. The `--env-file` flag incorrectly handles quotes, which can lead to unexpected situations. For more information, please see [docker/cli#3630](https://github.com/docker/cli/issues/3630). 504 | 505 |
506 | 507 | 508 | 509 | ### Docker Secrets 510 | 511 | As an alternative to passing sensitive information via environment variables, you can append `_FILE` to the previously listed environment variables. This causes the initialization script to load the values for those variables from files present in the container. In particular, this can be used to load passwords from Docker secrets stored in `/run/secrets/` files. 512 | 513 | ```shell 514 | docker run -d \ 515 | -e ZIP_PASSWORD_FILE=/run/secrets/zip-password \ 516 | ttionya/vaultwarden-backup:latest 517 | ``` 518 | 519 |
520 | 521 | 522 | 523 | ### About Priority 524 | 525 | We look for environment variables in the following order: 526 | 527 | 1. Directly read the value of the environment variable 528 | 2. Read the content of the file pointed to by the environment variable ending in `_FILE` 529 | 3. Read the content of the file pointed to by the environment variable ending in `_FILE` in the `.env` file 530 | 4. Read the value of the environment variable in the `.env` file 531 | 532 | Example: 533 | 534 | ```txt 535 | # For 1 536 | MY_ENV="example1" 537 | 538 | # For 2 539 | MY_ENV_FILE="/path/to/example2" 540 | 541 | # For 3 (.env file) 542 | MY_ENV_FILE="/path/to/example3" 543 | 544 | # For 4 (.env file) 545 | MY_ENV="example4" 546 | ``` 547 | 548 |
549 | 550 | 551 | 552 | ## Migration 553 | 554 | **Unofficial Bitwarden compatible server written in Rust, formerly known as `bitwarden_rs`, has been renamed to `vaultwarden`. Consequently, this backup tool has also been renamed from `bitwardenrs-backup` to `vaultwarden-backup`.** 555 | 556 | The old image can still be used, just **DEPRECATED**. Please migrate to the new image as soon as possible. 557 | 558 | **Migration Instructions** 559 | 560 | If you use automatic backups, you only need to replace the image with `ttionya/vaultwarden-backup`. Note the name of your volume. 561 | 562 | If you use `docker-compose`, you need to update `bitwardenrs/server` to `vaultwarden/server` and `ttionya/bitwardenrs-backup` to `ttionya/vaultwarden-backup`. 563 | 564 | We recommend re-downloading the [`docker-compose.yml`](./docker-compose.yml) file, updating your environment variables, and paying attention to the `volumes` section, which you may need to modify. 565 | 566 |
567 | 568 | 569 | 570 | ## Advance 571 | 572 | - [Run as non-root user](docs/run-as-non-root-user.md) 573 | - [Multiple remote destinations](docs/multiple-remote-destinations.md) 574 | - [Manually trigger a backup](docs/manually-trigger-a-backup.md) 575 | - [Using the PostgreSQL backend](docs/using-the-postgresql-backend.md) 576 | - [Using the MySQL(MariaDB) backend](docs/using-the-mysql-or-mariadb-backend.md) 577 | 578 |
579 | 580 | 581 | 582 | ## Changelog 583 | 584 | Check out the [CHANGELOG](CHANGELOG.md) file. 585 | 586 |
587 | 588 | 589 | 590 | ## Thanks 591 | 592 | I am grateful for the OSS license provided by [JetBrains](https://www.jetbrains.com/). 593 | 594 | JetBrains logo. 595 | 596 |
597 | 598 | 599 | 600 | ## License 601 | 602 | MIT 603 | -------------------------------------------------------------------------------- /README_zh.md: -------------------------------------------------------------------------------- 1 | # vaultwarden backup 2 | 3 | [![Docker Image Version (latest by date)](https://img.shields.io/docker/v/ttionya/vaultwarden-backup?label=Version&logo=docker)](https://hub.docker.com/r/ttionya/vaultwarden-backup/tags) [![Docker Pulls](https://img.shields.io/docker/pulls/ttionya/vaultwarden-backup?label=Docker%20Pulls&logo=docker)](https://hub.docker.com/r/ttionya/vaultwarden-backup) [![GitHub](https://img.shields.io/github/license/ttionya/vaultwarden-backup?label=License&logo=github)](https://github.com/ttionya/vaultwarden-backup/blob/master/LICENSE) 4 | 5 | [README](README.md) | 中文文档 6 | 7 | 备份 [vaultwarden](https://github.com/dani-garcia/vaultwarden) (之前叫 `bitwarden_rs`) 数据并通过 [Rclone](https://rclone.org/) 同步到其他存储系统。 8 | 9 | - [Docker Hub](https://hub.docker.com/r/ttionya/vaultwarden-backup) 10 | - [GitHub Packages](https://github.com/ttionya/vaultwarden-backup/pkgs/container/vaultwarden-backup) 11 | - [GitHub](https://github.com/ttionya/vaultwarden-backup) 12 | 13 |
14 | 15 | 16 | 17 | ## 功能 18 | 19 | 本工具会备份以下文件或目录。 20 | 21 | - `db.sqlite3` (SQLite 数据库) 22 | - `db.dump` (PostgreSQL 数据库) 23 | - `db.sql` (MySQL / MariaDB 数据库) 24 | - `config.json` 25 | - `rsa_key*` (多个文件) 26 | - `attachments` (目录) 27 | - `sends` (目录) 28 | 29 | 并且支持以下通知备份结果的方式。 30 | 31 | - Ping (完成,开始,成功或失败时发送) 32 | - Mail (基于 SMTP,成功时和失败时都会发送) 33 | 34 |
35 | 36 | 37 | 38 | ## 使用方法 39 | 40 | > **重要:** 我们假设你已经完整阅读了 `vaultwarden` [文档](https://github.com/dani-garcia/vaultwarden/wiki) 。 41 | 42 | ### 配置 Rclone (⚠️ 必读 ⚠️) 43 | 44 | > **对于备份,你需要先配置 Rclone,否则备份工具不会工作。** 45 | > 46 | > **对于还原,它不是必要的。** 47 | 48 | 我们通过 [Rclone](https://rclone.org/) 同步备份文件到远程存储系统。 49 | 50 | 访问 [GitHub](https://github.com/rclone/rclone) 了解更多存储系统使用教程,不同的系统获得 Token 的方式不同。 51 | 52 | #### 配置和检查 53 | 54 | 你可以通过下面的命令获得 Token。 55 | 56 | ```shell 57 | docker run --rm -it \ 58 | --mount type=volume,source=vaultwarden-rclone-data,target=/config/ \ 59 | ttionya/vaultwarden-backup:latest \ 60 | rclone config 61 | ``` 62 | 63 | **我们建议将远程名称设置为 `BitwardenBackup`,否则你需要指定环境变量 `RCLONE_REMOTE_NAME` 为你设置的远程名称。** 64 | 65 | 完成设置后,可以通过以下命令检查配置情况。 66 | 67 | ```shell 68 | docker run --rm -it \ 69 | --mount type=volume,source=vaultwarden-rclone-data,target=/config/ \ 70 | ttionya/vaultwarden-backup:latest \ 71 | rclone config show 72 | 73 | # Microsoft Onedrive Example 74 | # [BitwardenBackup] 75 | # type = onedrive 76 | # token = {"access_token":"access token","token_type":"token type","refresh_token":"refresh token","expiry":"expiry time"} 77 | # drive_id = driveid 78 | # drive_type = personal 79 | ``` 80 | 81 |
82 | 83 | 84 | 85 | ### 备份 86 | 87 | #### 使用 Docker Compose (推荐) 88 | 89 | 如果你是新用户或正在重新搭建 vaultwarden,推荐使用项目中的 `docker-compose.yml`. 90 | 91 | 下载 `docker-compose.yml`,根据实际情况编辑环境变量后启动它。 92 | 93 | 你需要进入 `docker-compose.yml` 文件所在目录执行操作。 94 | 95 | ```shell 96 | # Start 97 | docker-compose up -d 98 | 99 | # Stop 100 | docker-compose stop 101 | 102 | # Restart 103 | docker-compose restart 104 | 105 | # Remove 106 | docker-compose down 107 | ``` 108 | 109 | #### 自动备份 110 | 111 | 如果你有一个正在运行的 vaultwarden,但是不想使用 `docker-compose.yml`,我们同样为你提供了备份方法。 112 | 113 | 确保你的 vaultwarden 容器被命名为 `vaultwarden`,否则你需要自行替换 docker run 的 `--volumes-from` 部分。 114 | 115 | 默认情况下 vaultwarden 的数据文件夹是 `/data`,你需要显式使用环境变量 `DATA_DIR` 指定数据文件夹。 116 | 117 | 使用默认设置启动容器(每小时的 05 分自动备份)。 118 | 119 | ```shell 120 | docker run -d \ 121 | --restart=always \ 122 | --name vaultwarden_backup \ 123 | --volumes-from=vaultwarden \ 124 | --mount type=volume,source=vaultwarden-rclone-data,target=/config/ \ 125 | -e DATA_DIR="/data" \ 126 | ttionya/vaultwarden-backup:latest 127 | ``` 128 | 129 |
130 | 131 | 132 | 133 | ### 还原备份 134 | 135 | > **重要:** 还原备份会覆盖已存在的文件。 136 | 137 | 你需要在还原备份前停止 Docker 容器。 138 | 139 | 你也需要下载备份文件到本地计算机。 140 | 141 | 因为主机的文件无法在 Docker 容器中直接访问,所以要将需要还原的备份文件所在目录映射到 Docker 容器中。 142 | 143 | **首先进入待还原的备份文件所在目录。** 144 | 145 | 如果你使用的是本项目提供的 `docker-compose.yml`,你可以执行下面的命令。 146 | 147 | ```shell 148 | docker run --rm -it \ 149 | --mount type=volume,source=vaultwarden-data,target=/bitwarden/data/ \ 150 | --mount type=bind,source=$(pwd),target=/bitwarden/restore/ \ 151 | ttionya/vaultwarden-backup:latest restore \ 152 | [OPTIONS] 153 | ``` 154 | 155 | 如果你使用的是“自动备份”,请确认 vaultwarden 卷的命名,并替换 `--mount` `source` 部分。 156 | 157 | 同时不要忘记使用环境变量 `DATA_DIR` 指定数据目录(`-e DATA_DIR="/data"`)。 158 | 159 | ```shell 160 | docker run --rm -it \ 161 | \ # 如果你将本地目录映射到 Docker 容器中,就像 `vw-data` 一样 162 | --mount type=bind,source="本地目录的绝对路径",target=/data/ \ 163 | \ # 如果你使用 Docker 卷 164 | --mount type=volume,source="Docker 卷名称",target=/data/ \ 165 | --mount type=bind,source=$(pwd),target=/bitwarden/restore/ \ 166 | -e DATA_DIR="/data" \ 167 | ttionya/vaultwarden-backup:latest restore \ 168 | [OPTIONS] 169 | ``` 170 | 171 | 选项已在下面列出。 172 | 173 | #### 选项 174 | 175 | ##### -f / --force-restore 176 | 177 | 强制还原,没有交互式确认。请谨慎使用!! 178 | 179 |
180 | ※ 你有一个名为 backup 的压缩文件 181 | 182 | ##### --zip-file \ 183 | 184 | 你需要使用这个选项来指定 `backup` 压缩文件。 185 | 186 | 请确保压缩文件中的文件名没有被更改。 187 | 188 | ##### -p / --password 189 | 190 | **这是不安全的!!** 191 | 192 | 如果 `backup` 压缩文件设置了密码,你可以用这个选项指定备份文件的密码。 193 | 194 | 不建议使用该选项,因为在没有使用该选项且存在密码时,程序会交互式地询问密码。 195 | 196 |
197 | 198 |
199 | ※ 你有多个独立的备份文件 200 | 201 | ##### --db-file \ 202 | 203 | 你需要用这个选项来指定 `db.*` 文件。 204 | 205 | ##### --config-file \ 206 | 207 | 你需要用这个选项来指定 `config.json` 文件。 208 | 209 | ##### --rsakey-file \ 210 | 211 | 你需要用这个选项来指定 `rsakey.tar` 文件。 212 | 213 | ##### --attachments-file \ 214 | 215 | 你需要用这个选项来指定 `attachments.tar` 文件。 216 | 217 | ##### --sends-file \ 218 | 219 | 你需要用这个选项来指定 `sends.tar` 文件。 220 | 221 |
222 | 223 |
224 | 225 | 226 | 227 | ## 环境变量 228 | 229 | > **注意:** 所有的环境变量都有默认值,你可以在不设置任何环境变量的情况下使用 Docker 镜像。 230 | 231 | #### RCLONE_REMOTE_NAME 232 | 233 | Rclone 远程名称,它需要和 rclone config 中的远程名称保持一致。 234 | 235 | 你可以通过以下命令查看当前远程名称。 236 | 237 | ```shell 238 | docker run --rm -it \ 239 | --mount type=volume,source=vaultwarden-rclone-data,target=/config/ \ 240 | ttionya/vaultwarden-backup:latest \ 241 | rclone config show 242 | 243 | # [BitwardenBackup] <- 就是它 244 | # ... 245 | ``` 246 | 247 | 默认值:`BitwardenBackup` 248 | 249 | #### RCLONE_REMOTE_DIR 250 | 251 | 远程存储系统中存放备份文件的文件夹路径。 252 | 253 | 默认值:`/BitwardenBackup/` 254 | 255 | #### RCLONE_GLOBAL_FLAG 256 | 257 | Rclone 全局参数,详见 [flags](https://rclone.org/flags/)。 258 | 259 | **不要添加会改变输出的全局参数,比如 `-P`,它会影响删除过期备份文件的操作。** 260 | 261 | 默认值:`''` 262 | 263 | #### CRON 264 | 265 | `crond` 的规则,它基于 [`supercronic`](https://github.com/aptible/supercronic)。你可以在 [这里](https://crontab.guru/#5_*_*_*_*) 进行测试。 266 | 267 | 默认值:`5 * * * *` (每小时的 05 分自动备份) 268 | 269 | #### ZIP_ENABLE 270 | 271 | 将所有备份文件打包为压缩文件。当设置为 `'FALSE'` 时,会单独上传每个备份文件。 272 | 273 | 默认值:`TRUE` 274 | 275 | #### ZIP_PASSWORD 276 | 277 | 压缩文件的密码。请注意,打包备份文件时始终会使用密码。 278 | 279 | 默认值:`WHEREISMYPASSWORD?` 280 | 281 | #### ZIP_TYPE 282 | 283 | 因为 `zip` 格式安全性较低,我们为追求安全的人提供 `7z` 格式的存档。 284 | 285 | 需要说明的是,vaultwarden 的密码在发送到服务器前就已经加密了。服务器没有保存明文密码,所以 `zip` 格式已经可以满足基本的加密需求。 286 | 287 | 默认值:`zip` (只支持 `zip` 和 `7z` 格式) 288 | 289 | #### BACKUP_KEEP_DAYS 290 | 291 | 在远程存储系统中保留最近 X 天的备份文件。设置为 `0` 会保留所有备份文件。 292 | 293 | 默认值:`0` 294 | 295 | #### BACKUP_FILE_SUFFIX 296 | 297 | 每个备份文件都默认添加 `%Y%m%d` 后缀。如果你在一天内多次进行备份,每次备份都会被覆盖之前同名的文件。这个环境变量允许你自定义日期信息以便每次备份生成不同的文件。 298 | 299 | 你可以使用除了 `/` 外的任何字符,无法使用的原因是 Linux 不能使用 `/` 作为文件名。 300 | 301 | 这个环境变量合并了 [`BACKUP_FILE_DATE`](#backup_file_date) 和 [`BACKUP_FILE_DATE_SUFFIX`](#backup_file_date_suffix) 的功能,并且优先级更高。现在你可以直接通过它控制备份文件后缀。 302 | 303 | 在 [这里](https://man7.org/linux/man-pages/man1/date.1.html) 查看时间格式化说明。 304 | 305 | 默认值:`%Y%m%d` 306 | 307 | #### TIMEZONE 308 | 309 | 设置你的时区名称。 310 | 311 | 在 [wikipedia](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones) 查看所有时区名称。(PS: 北京时区TIMEZONE设置为Asia/Shanghai) 312 | 313 | 默认值:`UTC` 314 | 315 | #### DISPLAY_NAME 316 | 317 | 用于在通知和日志中标识 vaultwarden 实例的自定义名称。 318 | 319 | 这不会影响功能,仅影响通知标题和部分日志输出中的显示。 320 | 321 | 默认值:`vaultwarden` 322 | 323 | #### DATA_DIR 324 | 325 | 指定存放 vaultwarden 数据的目录。 326 | 327 | 当使用 `Docker Compose` 时,你一般不需要修改它,但是当你使用自动备份时,你通常需要将它修改为 `/data`。 328 | 329 | 默认值:`/bitwarden/data` 330 | 331 | ※ 通知相关环境变量请查看[通知](#通知)部分。 332 | 333 |
334 | ※ 其他环境变量 335 | 336 | > **你无需修改这些环境变量,除非你知道你在做什么。** 337 | 338 | #### BACKUP_FILE_DATE 339 | 340 | 你应该使用 [`BACKUP_FILE_SUFFIX`](#backup_file_suffix) 环境变量替代。 341 | 342 | 只有在你确定想修改备份文件的时间前缀(如 20220101)时编辑该环境变量。**错误的配置可能导致备份文件被错误的覆盖。** 343 | 344 | 规则同 [`BACKUP_FILE_DATE_SUFFIX`](#backup_file_date_suffix)。 345 | 346 | Default: `%Y%m%d` 347 | 348 | #### BACKUP_FILE_DATE_SUFFIX 349 | 350 | 你应该使用 [`BACKUP_FILE_SUFFIX`](#backup_file_suffix) 环境变量替代。 351 | 352 | 每个备份文件都默认添加 `%Y%m%d` 后缀。如果你在一天内多次进行备份,每次备份都会被覆盖之前同名的文件。这个环境变量允许你追加日期信息 (`%Y%m%d${BACKUP_FILE_DATE_SUFFIX}`) 以便每次备份生成不同的文件。 353 | 354 | 注意:只支持数字、大小写字母、`-`、`_` 和 `%`。 355 | 356 | 在 [这里](https://man7.org/linux/man-pages/man1/date.1.html) 查看时间格式化说明。 357 | 358 | 默认值:`''` 359 | 360 | #### DATA_DB 361 | 362 | 指定 sqlite 数据库文件的路径。 363 | 364 | 默认值:`${DATA_DIR}/db.sqlite3` 365 | 366 | #### DATA_RSAKEY 367 | 368 | 指定 rsa_key 文件的路径。 369 | 370 | 默认值:`${DATA_DIR}/rsa_key` 371 | 372 | #### DATA_ATTACHMENTS 373 | 374 | 指定 attachments 文件夹路径。 375 | 376 | 默认值:`${DATA_DIR}/attachments` 377 | 378 | #### DATA_SENDS 379 | 380 | 指定 sends 文件夹路径。 381 | 382 | 默认值:`${DATA_DIR}/sends` 383 | 384 |
385 | 386 |
387 | 388 | 389 | 390 | ## 通知 391 | 392 | ### Ping 393 | 394 | 我们提供了在备份完成、开始、成功、失败时发送通知的功能。 395 | 396 | **搭配 [healthcheck.io](https://healthchecks.io/) 地址或者其他类似的 cron 监控地址是一个不错的选择,这也是我们推荐的。** 对于一些更复杂的通知场景,你可以使用 `_CURL_OPTIONS` 后缀的环境变量来设置 curl 选项。比如你可以添加请求头,改变请求方法等。 397 | 398 | 对于不同的通知场景,**备份工具提供了 `%{subject}` 和 `%{content}` 占位符用于替换实际的标题和内容**,你可以在以下环境变量中随意使用它们。请注意,标题和内容可能包含空格。对于包含 `_CURL_OPTIONS` 的四个环境变量,将直接替换占位符,保留空格。对于其他 `PING_URL` 环境变量,空格将被替换为 `+`,以符合 URL 规则。 399 | 400 | | 环境变量 | 触发状态 | 测试标识 | 描述 | 401 | |------------------------------------|-------------|--------------|-----------------------------------------| 402 | | PING_URL | 完成(不论成功或失败) | `completion` | 备份完成后发送请求的地址 | 403 | | PING_URL_CURL_OPTIONS | | | 与 `PING_URL` 搭配使用的 curl 选项 | 404 | | PING_URL_WHEN_START | 开始 | `start` | 备份开始时发送请求的地址 | 405 | | PING_URL_WHEN_START_CURL_OPTIONS | | | 与 `PING_URL_WHEN_START` 搭配使用的 curl 选项 | 406 | | PING_URL_WHEN_SUCCESS | 成功 | `success` | 备份成功后发送请求的地址 | 407 | | PING_URL_WHEN_SUCCESS_CURL_OPTIONS | | | 与 `PING_URL_WHEN_SUCCESS` 搭配使用的 curl 选项 | 408 | | PING_URL_WHEN_FAILURE | 失败 | `failure` | 备份失败后发送请求的地址 | 409 | | PING_URL_WHEN_FAILURE_CURL_OPTIONS | | | 与 `PING_URL_WHEN_FAILURE` 搭配使用的 curl 选项 | 410 | 411 |
412 | 413 | 414 | 415 | ### Ping 发送测试 416 | 417 | 你可以使用下面的命令测试 Ping 发送功能。 418 | 419 | “test identifier”是[上一节](#ping)表格中的测试标识,你可以使用 `completion`、`start`、`success` 或 `failure`,它决定了使用哪一组环境变量。 420 | 421 | ```shell 422 | docker run --rm -it \ 423 | -e PING_URL='' \ 424 | -e PING_URL_CURL_OPTIONS='' \ 425 | -e PING_URL_WHEN_START='' \ 426 | -e PING_URL_WHEN_START_CURL_OPTIONS='' \ 427 | -e PING_URL_WHEN_SUCCESS='' \ 428 | -e PING_URL_WHEN_SUCCESS_CURL_OPTIONS='' \ 429 | -e PING_URL_WHEN_FAILURE='' \ 430 | -e PING_URL_WHEN_FAILURE_CURL_OPTIONS='' \ 431 | ttionya/vaultwarden-backup:latest ping 432 | ``` 433 | 434 |
435 | 436 | 437 | 438 | ### Mail 439 | 440 | 从 v1.19.0 开始,本工具使用 [`s-nail`](https://www.sdaoden.eu/code-nail.html) 代替 [`heirloom-mailx`](https://www.systutorials.com/docs/linux/man/1-heirloom-mailx/) 发送邮件。 441 | 442 | 请注意,`heirloom-mailx` 是 `s-nail` 的存根,它们大部分功能是兼容的。因此你可能不需要为这个改变修改任何环境变量。 443 | 444 | | 环境变量 | 默认值 | 描述 | 445 | | --- |--------|-----------| 446 | | MAIL_SMTP_ENABLE | `FALSE` | 启用邮件发送功能 | 447 | | MAIL_SMTP_VARIABLES | | 邮件发送参数 | 448 | | MAIL_TO | | 接收邮件的地址 | 449 | | MAIL_WHEN_SUCCESS | `TRUE` | 备份成功后发送邮件 | 450 | | MAIL_WHEN_FAILURE | `TRUE` | 备份失败后发送邮件 | 451 | 452 | 对于 `MAIL_SMTP_VARIABLES`,你需要自行配置邮件发送参数。**我们会根据使用场景设置邮件主题,所以你不应该使用 `-s` 标志。** 453 | 454 | ```text 455 | # 提供一个能正常使用的例子: 456 | 457 | # For Zoho 458 | -S smtp-use-starttls \ 459 | -S smtp=smtp://smtp.zoho.com:587 \ 460 | -S smtp-auth=login \ 461 | -S smtp-auth-user= \ 462 | -S smtp-auth-password= \ 463 | -S from= 464 | ``` 465 | 466 | 控制台有警告?查看 [issue #177](https://github.com/ttionya/vaultwarden-backup/issues/117#issuecomment-1691443179) 了解更多。 467 | 468 |
469 | 470 | 471 | 472 | ### 邮件发送测试 473 | 474 | 你可以使用下面的命令测试邮件发送功能。我们会增加 `-v` 标志以显示详细信息,你无需在 `MAIL_SMTP_VARIABLES` 中重复设置。 475 | 476 | ```shell 477 | docker run --rm -it -e MAIL_SMTP_VARIABLES='' ttionya/vaultwarden-backup:latest mail 478 | 479 | # Or 480 | 481 | docker run --rm -it -e MAIL_SMTP_VARIABLES='' -e MAIL_TO='' ttionya/vaultwarden-backup:latest mail 482 | ``` 483 | 484 |
485 | 486 | 487 | 488 | ## 环境变量注意事项 489 | 490 | ### 使用 `.env` 文件 491 | 492 | 如果你喜欢使用 env 文件而不是环境变量,可以将包含环境变量的 env 文件映射到容器中的 `/.env` 文件。 493 | 494 | ```shell 495 | docker run -d \ 496 | --mount type=bind,source=/path/to/env,target=/.env \ 497 | ttionya/vaultwarden-backup:latest 498 | ``` 499 | 500 | 请不要直接使用 `--env-file` 标志,务必通过挂载文件的方式映射环境变量。`--env-file` 标志会错误地处理引号,导致发生意外情况。更多信息请参见 [docker/cli#3630](https://github.com/docker/cli/issues/3630)。 501 | 502 |
503 | 504 | 505 | 506 | ### Docker Secrets 507 | 508 | 作为通过环境变量传递敏感信息的替代方法,可以在前面列出的环境变量后面追加 `_FILE`,使初始化脚本从容器中存在的文件加载这些变量的值。特别是这可以用来从存储在 `/run/secrets/` 文件中的 Docker Secrets 中加载密码。 509 | 510 | ```shell 511 | docker run -d \ 512 | -e ZIP_PASSWORD_FILE=/run/secrets/zip-password \ 513 | ttionya/vaultwarden-backup:latest 514 | ``` 515 | 516 |
517 | 518 | 519 | 520 | ### 关于优先级 521 | 522 | 我们按以下顺序查找环境变量: 523 | 524 | 1. 直接读取环境变量的值 525 | 2. 读取以 `_FILE` 结尾的环境变量指向的文件的内容 526 | 3. 读取 `.env` 文件中以 `_FILE` 结尾的环境变量指向的文件的内容 527 | 4. 读取 `.env` 文件中环境变量的值 528 | 529 | 示例: 530 | 531 | ```txt 532 | # 对于 1 533 | MY_ENV="example1" 534 | 535 | # 对于 2 536 | MY_ENV_FILE="/path/to/example2" 537 | 538 | # 对于 3 (.env 文件) 539 | MY_ENV_FILE="/path/to/example3" 540 | 541 | # 对于 4 (.env 文件) 542 | MY_ENV="example4" 543 | ``` 544 | 545 |
546 | 547 | 548 | 549 | ## 迁移 550 | 551 | **用 Rust 编写的非官方 Bitwarden 服务器,以前称为 `bitwarden_rs`,已经改名为 `vaultwarden`。所以这个备份工具也由 `bitwardenrs-backup` 重命名为 `vaultwarden-backup`。** 552 | 553 | 旧的镜像仍然可以使用,只是被标记为 **DEPRECATED** 了,请尽快迁移到新的镜像。 554 | 555 | **迁移说明** 556 | 557 | 如果你使用自动备份,你只需要把镜像名改为 `ttionya/vaultwarden-backup`。注意你的卷的名称。 558 | 559 | 如果你使用 `docker-compose`,你需要将 `bitwardenrs/server` 更新为 `vaultwarden/server`,`ttionya/bitwardenrs-backup` 更新为 `ttionya/vaultwarden-backup`。 560 | 561 | 我们建议重新下载 [`docker-compose.yml`](./docker-compose.yml) 文件,更新你的环境变量,并注意 `volumes` 部分,你可能需要修改它。 562 | 563 |
564 | 565 | 566 | 567 | ## 高级 568 | 569 | - [以非 root 用户运行](docs/run-as-non-root-user.md) 570 | - [备份到多个远程目标](docs/multiple-remote-destinations.md) 571 | - [手动触发备份](docs/manually-trigger-a-backup.md) 572 | - [使用 PostgreSQL 数据库](docs/using-the-postgresql-backend.md) 573 | - [使用 MySQL(MariaDB) 数据库](docs/using-the-mysql-or-mariadb-backend.md) 574 | 575 |
576 | 577 | 578 | 579 | ## 更新日志 580 | 581 | 请查看 [CHANGELOG](CHANGELOG.md) 文件。 582 | 583 |
584 | 585 | 586 | 587 | ## 感谢 588 | 589 | 感谢 [JetBrains](https://www.jetbrains.com/) 提供的 OSS 许可证。 590 | 591 | JetBrains logo. 592 | 593 |
594 | 595 | 596 | 597 | ## 许可证 598 | 599 | MIT 600 | -------------------------------------------------------------------------------- /docker-bake.hcl: -------------------------------------------------------------------------------- 1 | variable "VERSION" { 2 | default = "latest" 3 | } 4 | 5 | variable "TEST_BASE_TAG" { 6 | default = "localhost:5000/base:dev" 7 | } 8 | 9 | target "docker-metadata-action" {} 10 | 11 | target "_common" { 12 | inherits = ["docker-metadata-action"] 13 | context = "." 14 | dockerfile = "Dockerfile" 15 | } 16 | 17 | target "_common_multi_platforms" { 18 | platforms = [ 19 | "linux/amd64", 20 | "linux/arm64", 21 | "linux/arm/v6", 22 | "linux/arm/v7" 23 | ] 24 | } 25 | 26 | target "_common_tags" { 27 | tags = [ 28 | "ttionya/vaultwarden-backup:latest", 29 | "ttionya/vaultwarden-backup:${VERSION}", 30 | "ttionya/bitwardenrs-backup:latest", 31 | "ttionya/bitwardenrs-backup:${VERSION}", 32 | "ghcr.io/ttionya/vaultwarden-backup:latest", 33 | "ghcr.io/ttionya/vaultwarden-backup:${VERSION}" 34 | ] 35 | } 36 | 37 | target "image-stable" { 38 | inherits = ["_common", "_common_multi_platforms", "_common_tags"] 39 | } 40 | 41 | target "image-schedule" { 42 | inherits = ["image-stable"] 43 | } 44 | 45 | target "image-beta" { 46 | inherits = ["_common", "_common_multi_platforms"] 47 | tags = [ 48 | "ttionya/vaultwarden-backup:${VERSION}" 49 | ] 50 | } 51 | 52 | target "image-test-base" { 53 | inherits = ["_common", "_common_multi_platforms"] 54 | tags = [ 55 | "${TEST_BASE_TAG}" 56 | ] 57 | } 58 | 59 | target "image-test" { 60 | inherits = ["_common"] 61 | dockerfile = "./tests/Dockerfile" 62 | contexts = { 63 | base = "docker-image://${TEST_BASE_TAG}" 64 | } 65 | tags = [ 66 | "ttionya/vaultwarden-backup:test" 67 | ] 68 | } 69 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3.4' 2 | 3 | services: 4 | 5 | vaultwarden: 6 | image: vaultwarden/server:latest 7 | restart: always 8 | # environment: 9 | # SIGNUPS_ALLOWED: 'false' 10 | # ADMIN_TOKEN: 'your authentication token' 11 | ports: 12 | - '127.0.0.1:8200:80' 13 | volumes: 14 | - vaultwarden-data:/data/ 15 | 16 | backup: 17 | image: ttionya/vaultwarden-backup:latest 18 | restart: always 19 | # environment: 20 | # RCLONE_REMOTE_NAME: 'BitwardenBackup' 21 | # RCLONE_REMOTE_DIR: '/BitwardenBackup/' 22 | # RCLONE_GLOBAL_FLAG: '' 23 | # CRON: '5 * * * *' 24 | # ZIP_ENABLE: 'TRUE' 25 | # ZIP_PASSWORD: 'WHEREISMYPASSWORD?' 26 | # ZIP_TYPE: 'zip' 27 | # BACKUP_FILE_SUFFIX: '%Y%m%d' 28 | # BACKUP_KEEP_DAYS: 0 29 | # PING_URL: '' 30 | # PING_URL_CURL_OPTIONS: '' 31 | # PING_URL_WHEN_START: '' 32 | # PING_URL_WHEN_START_CURL_OPTIONS: '' 33 | # PING_URL_WHEN_SUCCESS: '' 34 | # PING_URL_WHEN_SUCCESS_CURL_OPTIONS: '' 35 | # PING_URL_WHEN_FAILURE: '' 36 | # PING_URL_WHEN_FAILURE_CURL_OPTIONS: '' 37 | # MAIL_SMTP_ENABLE: 'FALSE' 38 | # MAIL_SMTP_VARIABLES: '' 39 | # MAIL_TO: '' 40 | # MAIL_WHEN_SUCCESS: 'TRUE' 41 | # MAIL_WHEN_FAILURE: 'TRUE' 42 | # TIMEZONE: 'UTC' 43 | volumes: 44 | - vaultwarden-data:/bitwarden/data/ 45 | - vaultwarden-rclone-data:/config/ 46 | # - /path/to/env:/.env 47 | 48 | volumes: 49 | vaultwarden-data: 50 | # Specify the name of the volume where you save the vaultwarden data, 51 | # use vaultwarden-data for new users 52 | # and bitwardenrs-data for migrated users 53 | name: vaultwarden-data 54 | # name: bitwardenrs-data 55 | vaultwarden-rclone-data: 56 | external: true 57 | # Specify the name of the volume where you save the rclone configuration, 58 | # use vaultwarden-rclone-data for new users 59 | # and bitwardenrs-rclone-data for migrated users 60 | name: vaultwarden-rclone-data 61 | # name: bitwardenrs-rclone-data 62 | -------------------------------------------------------------------------------- /docs/manually-trigger-a-backup.md: -------------------------------------------------------------------------------- 1 | # Manually trigger a backup 2 | 3 | Sometimes, it's necessary to manually trigger backup actions. 4 | 5 | This can be useful when other programs are used to consistently schedule tasks or to verify that environment variables are properly configured. 6 | 7 | If your container is already running (with the container name `vaultwarden_backup`) and you want to execute an adhoc backup you can do so with the command `docker exec vaultwarden_backup bash /app/backup.sh`. 8 | 9 |
10 | 11 | 12 | 13 | ## Usage 14 | 15 | Previously, performing an immediate backup required overwriting the entrypoint of the image. However, with the new setup, you can perform a backup directly with a parameterless command. 16 | 17 | ```shell 18 | docker run \ 19 | --rm \ 20 | --name vaultwarden_backup \ 21 | --volumes-from=vaultwarden \ 22 | --mount type=volume,source=vaultwarden-rclone-data,target=/config/ \ 23 | -e ... \ 24 | ttionya/vaultwarden-backup:latest backup 25 | ``` 26 | 27 | You also need to mount the rclone config file and set the environment variables. 28 | 29 | The only difference is that the environment variable `CRON` does not work because it does not start the CRON program, but exits the container after the backup is done. 30 | 31 |
32 | 33 | 34 | 35 | ## IMPORTANT 36 | 37 | **Manually triggering a backup only verifies that the environment variables are configured correctly, not that CRON is working properly. This is the [issue](https://github.com/ttionya/vaultwarden-backup/issues/53) that CRON may not work properly on ARM devices.** 38 | 39 | -------------------------------------------------------------------------------- /docs/multiple-remote-destinations.md: -------------------------------------------------------------------------------- 1 | # Multiple remote destinations 2 | 3 | Some users want to upload their backups to multiple remote destinations. 4 | 5 | You can achieve this by setting the following environment variables. 6 | 7 |
8 | 9 | 10 | 11 | ## Usage 12 | 13 | > **Don't forget to add the new Rclone remote before running with the new environment variables.** 14 | > 15 | > Find more information on how to configure Rclone [here](https://github.com/ttionya/vaultwarden-backup#configure-rclone-%EF%B8%8F-must-read-%EF%B8%8F). 16 | 17 | To set additional remote destinations, use the environment variables `RCLONE_REMOTE_NAME_N` and `RCLONE_REMOTE_DIR_N`, where: 18 | 19 | - `N` is a serial number, starting from 1 and increasing consecutively for each additional destination 20 | - `RCLONE_REMOTE_NAME_N` and `RCLONE_REMOTE_DIR_N` cannot be empty 21 | 22 | Note that if the serial number is not consecutive or the value is empty, the script will break parsing the environment variables for remote destinations. 23 | 24 |
25 | 26 | 27 | 28 | #### Example 29 | 30 | ```yml 31 | ... 32 | environment: 33 | # they have default values 34 | # RCLONE_REMOTE_NAME: BitwardenBackup 35 | # RCLONE_REMOTE_DIR: /BitwardenBackup/ 36 | RCLONE_REMOTE_NAME_1: extraRemoteName1 37 | RCLONE_REMOTE_DIR_1: extraRemoteDir1 38 | ... 39 | ``` 40 | 41 | With the above example, both remote destinations are available: `BitwardenBackup:/BitwardenBackup/` and `extraRemoteName1:extraRemoteDir1`. 42 | 43 |
44 | 45 | ```yml 46 | ... 47 | environment: 48 | RCLONE_REMOTE_NAME: remoteName 49 | RCLONE_REMOTE_DIR: remoteDir 50 | RCLONE_REMOTE_NAME_1: extraRemoteName1 51 | RCLONE_REMOTE_DIR_1: extraRemoteDir1 52 | RCLONE_REMOTE_NAME_2: extraRemoteName2 53 | RCLONE_REMOTE_DIR_2: extraRemoteDir2 54 | RCLONE_REMOTE_NAME_3: extraRemoteName3 55 | RCLONE_REMOTE_DIR_3: extraRemoteDir3 56 | RCLONE_REMOTE_NAME_4: extraRemoteName4 57 | RCLONE_REMOTE_DIR_4: extraRemoteDir4 58 | ... 59 | ``` 60 | 61 | With the above example, all 5 remote destinations are available. 62 | 63 |
64 | 65 | ```yml 66 | ... 67 | environment: 68 | RCLONE_REMOTE_NAME: remoteName 69 | RCLONE_REMOTE_DIR: remoteDir 70 | RCLONE_REMOTE_NAME_1: extraRemoteName1 71 | RCLONE_REMOTE_DIR_1: extraRemoteDir1 72 | RCLONE_REMOTE_NAME_2: extraRemoteName2 73 | # RCLONE_REMOTE_DIR_2: extraRemoteDir2 74 | RCLONE_REMOTE_NAME_3: extraRemoteName3 75 | RCLONE_REMOTE_DIR_3: extraRemoteDir3 76 | RCLONE_REMOTE_NAME_4: extraRemoteName4 77 | RCLONE_REMOTE_DIR_4: extraRemoteDir4 78 | ... 79 | ``` 80 | 81 | With the above example, only the remote destinations before `RCLONE_REMOTE_DIR_2` are available: `remoteName:remoteDir` and `extraRemoteName1:extraRemoteDir1`. 82 | 83 |
84 | 85 | 86 | 87 | ## Notification 88 | 89 | - success: **all** remote destinations were uploaded successfully 90 | - failure: **any** of the remote destinations failed to upload 91 | -------------------------------------------------------------------------------- /docs/run-as-non-root-user.md: -------------------------------------------------------------------------------- 1 | # Run as non-root user 2 | 3 | By default, the container runs the backup script as the root user. If you wish to run the container as a non-root user, there are a few things you need to set. 4 | 5 | You can use the built-in non-root user and group, named `backuptool`, with the UID and GID of `1100`. 6 | 7 |
8 | 9 | 10 | 11 | ## Backup 12 | 13 | 1. Make sure that the rclone config file in the mounted `vaultwarden-rclone-data` volume is writable by `backuptool` user. 14 | 15 | ```shell 16 | # enter the container 17 | docker run --rm -it \ 18 | --mount type=volume,source=vaultwarden-rclone-data,target=/config/ \ 19 | --entrypoint=bash \ 20 | ttionya/vaultwarden-backup:latest 21 | 22 | # modify the rclone config file owner in the container 23 | chown -R 1100:1100 /config/ 24 | 25 | # exit the container 26 | exit 27 | ``` 28 | 29 | 2. If you want a full backup of the `rsa_key*`, you need to allow the `backuptool` user to read the `rsa_key*`. 30 | 31 | **With Docker Compose** 32 | 33 | ```shell 34 | # enter the container 35 | docker run --rm -it \ 36 | --mount type=volume,source=vaultwarden-data,target=/bitwarden/data/ \ 37 | --entrypoint=bash \ 38 | ttionya/vaultwarden-backup:latest 39 | 40 | # make files readable for all users in the container 41 | chmod -R +r /bitwarden/data/ 42 | 43 | # exit the container 44 | exit 45 | ``` 46 | 47 | **With Automatic Backups** 48 | 49 | ```shell 50 | # enter the container 51 | docker run --rm -it \ 52 | --volumes-from=vaultwarden \ 53 | --entrypoint=bash \ 54 | ttionya/vaultwarden-backup:latest 55 | 56 | # make files readable for all users in the container 57 | chmod -R +r /data/ 58 | 59 | # exit the container 60 | exit 61 | ``` 62 | 63 | 3. Start the container with proper parameters. 64 | 65 | **With Docker Compose** 66 | 67 | ```shell 68 | # docker-compose.yml 69 | services: 70 | backup: 71 | image: ttionya/vaultwarden-backup:latest 72 | user: 'backuptool:backuptool' 73 | ... 74 | ``` 75 | 76 | **With Automatic Backups** 77 | 78 | ```shell 79 | docker run -d \ 80 | ... 81 | --user backuptool:backuptool \ 82 | ... 83 | ttionya/vaultwarden-backup:latest 84 | ``` 85 | 86 |
87 | 88 | 89 | 90 | ## Restore 91 | 92 | Perform the restore normally, nothing special. 93 | -------------------------------------------------------------------------------- /docs/using-the-mysql-or-mariadb-backend.md: -------------------------------------------------------------------------------- 1 | # Using the MySQL(MariaDB) backend 2 | 3 | Now supports MySQL(MariaDB) backend. 4 | 5 |
6 | 7 | 8 | 9 | ## Environment Variables 10 | 11 | #### DB_TYPE 12 | 13 | Set to `mysql` switch to MySQL(MariaDB) database. 14 | 15 | Default: `sqlite` 16 | 17 | #### MYSQL_HOST 18 | 19 | MySQL(MariaDB) host, **required**. 20 | 21 | #### MYSQL_PORT 22 | 23 | MySQL(MariaDB) port. 24 | 25 | Default: `3306` 26 | 27 | #### MYSQL_DATABASE 28 | 29 | MySQL(MariaDB) database name. 30 | 31 | Default: `vaultwarden` 32 | 33 | #### MYSQL_USERNAME 34 | 35 | MySQL(MariaDB) username. 36 | 37 | Default: `vaultwarden` 38 | 39 | #### MYSQL_PASSWORD 40 | 41 | MySQL(MariaDB) password, **required**. 42 | 43 | #### MYSQL_SSL 44 | 45 | Enable SSL for connection. 46 | 47 | No default value is set; it uses the default provided by `mariadb-dump`, and starting from version `10.11`, the default is `TRUE`. 48 | 49 | #### MYSQL_SSL_VERIFY_SERVER_CERT 50 | 51 | Verify server's certificate. 52 | 53 | No default value is set; it uses the default provided by `mariadb-dump`, and starting from version `11.4`, the default is `TRUE`. 54 | 55 | If you encounter any TLS-related connection errors, you can try disabling it by setting values such as `0` or `FALSE`. 56 | 57 | #### MYSQL_SSL_CA 58 | 59 | The path to the CA certificate for TLS connection (optional). 60 | 61 | #### MYSQL_SSL_CERT 62 | 63 | The path to the client certificate for TLS connection (optional). 64 | 65 | #### MYSQL_SSL_KEY 66 | 67 | The path to the client key for TLS connection (optional). 68 | 69 |
70 | 71 | 72 | 73 | ## Backup 74 | 75 | Specify the above environment variables to switch to the MySQL(MariaDB) database. 76 | 77 |
78 | 79 | 80 | 81 | ## Restore 82 | 83 | When restoring, also specify the above environment variables to switch to the MySQL(MariaDB) database. 84 | 85 | 1. Ensure that the database is accessible. 86 | 87 | Perhaps you will use the `docker-compose up -d [services name]` command to start the database separately. 88 | 89 | 2. Verify that the `MYSQL_HOST` you are using is accessible to. 90 | 91 | If your database is running in docker-compose, you need to find the corresponding network name via `docker network ls` and add `--network=[name]` to the restore command to specify the network name. 92 | 93 | 3. Restore and restart the container. 94 | -------------------------------------------------------------------------------- /docs/using-the-postgresql-backend.md: -------------------------------------------------------------------------------- 1 | # Using the PostgreSQL backend 2 | 3 | Now supports PostgreSQL backend. 4 | 5 | ~~Because upstream Rclone image is based on `alpine 3.16`(and `linux/arm/v6` platform is based on `alpine 3.15`), it **only supports PostgreSQL 14 and previous versions**, see [Alpine 3.16 Packages](https://pkgs.alpinelinux.org/packages?name=postgresql*-client&branch=v3.16) and [Alpine 3.15 Packages](https://pkgs.alpinelinux.org/packages?name=postgresql*-client&branch=v3.15).~~ 6 | 7 | We support PostgreSQL 17 and previous versions. 8 | 9 | If the `postgresql*-client` is not updated promptly, please create an issue to inform us. 10 | 11 |
12 | 13 | 14 | 15 | ## Environment Variables 16 | 17 | #### DB_TYPE 18 | 19 | Set to `postgresql` switch to PostgreSQL database. 20 | 21 | Default: `sqlite` 22 | 23 | #### PG_HOST 24 | 25 | PostgreSQL host, **required**. 26 | 27 | #### PG_PORT 28 | 29 | PostgreSQL port. 30 | 31 | Default: `5432` 32 | 33 | #### PG_DBNAME 34 | 35 | PostgreSQL database name. 36 | 37 | Default: `vaultwarden` 38 | 39 | #### PG_USERNAME 40 | 41 | PostgreSQL username. 42 | 43 | Default: `vaultwarden` 44 | 45 | #### PG_PASSWORD 46 | 47 | PostgreSQL password, **required**. 48 | 49 | The login information will be saved in the `~/.pgpass` file. 50 | 51 |
52 | 53 | 54 | 55 | ## Backup 56 | 57 | Specify the above environment variables to switch to the PostgreSQL database. 58 | 59 |
60 | 61 | 62 | 63 | ## Restore 64 | 65 | When restoring, also specify the above environment variables to switch to the PostgreSQL database. 66 | 67 | 1. Ensure that the database is accessible. 68 | 69 | Perhaps you will use the `docker-compose up -d [services name]` command to start the database separately. 70 | 71 | 2. Verify that the `PG_HOST` you are using is accessible to. 72 | 73 | If your database is running in docker-compose, you need to find the corresponding network name via `docker network ls` and add `--network=[name]` to the restore command to specify the network name. 74 | 75 | 3. Restore and restart the container. 76 | -------------------------------------------------------------------------------- /scripts/backup.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | . /app/includes.sh 4 | 5 | function clear_dir() { 6 | rm -rf "${BACKUP_DIR}" 7 | } 8 | 9 | function backup_init() { 10 | NOW="$(date +"${BACKUP_FILE_DATE_FORMAT}")" 11 | # backup vaultwarden database file (sqlite) 12 | BACKUP_FILE_DB_SQLITE="${BACKUP_DIR}/db.${NOW}.sqlite3" 13 | # backup vaultwarden database file (postgresql) 14 | BACKUP_FILE_DB_POSTGRESQL="${BACKUP_DIR}/db.${NOW}.dump" 15 | # backup vaultwarden database file (mysql) 16 | BACKUP_FILE_DB_MYSQL="${BACKUP_DIR}/db.${NOW}.sql" 17 | # backup vaultwarden config file 18 | BACKUP_FILE_CONFIG="${BACKUP_DIR}/config.${NOW}.json" 19 | # backup vaultwarden rsakey files 20 | BACKUP_FILE_RSAKEY="${BACKUP_DIR}/rsakey.${NOW}.tar" 21 | # backup vaultwarden attachments directory 22 | BACKUP_FILE_ATTACHMENTS="${BACKUP_DIR}/attachments.${NOW}.tar" 23 | # backup vaultwarden sends directory 24 | BACKUP_FILE_SENDS="${BACKUP_DIR}/sends.${NOW}.tar" 25 | # backup zip file 26 | BACKUP_FILE_ZIP="${BACKUP_DIR}/backup.${NOW}.${ZIP_TYPE}" 27 | } 28 | 29 | function backup_db_sqlite() { 30 | color blue "backup vaultwarden sqlite database" 31 | 32 | if [[ -f "${DATA_DB}" ]]; then 33 | sqlite3 "${DATA_DB}" ".backup '${BACKUP_FILE_DB_SQLITE}'" 34 | else 35 | color yellow "not found vaultwarden sqlite database, skipping" 36 | fi 37 | } 38 | 39 | function backup_db_postgresql() { 40 | color blue "backup vaultwarden postgresql database" 41 | 42 | pg_dump -Fc -h "${PG_HOST}" -p "${PG_PORT}" -d "${PG_DBNAME}" -U "${PG_USERNAME}" -f "${BACKUP_FILE_DB_POSTGRESQL}" 43 | if [[ $? != 0 ]]; then 44 | color red "backup vaultwarden postgresql database failed" 45 | 46 | send_notification "failure" "Backup failed at $(date +"%Y-%m-%d %H:%M:%S %Z"). Reason: Backup postgresql database failed." 47 | 48 | exit 1 49 | fi 50 | } 51 | 52 | function backup_db_mysql() { 53 | color blue "backup vaultwarden mysql database" 54 | 55 | local EXTRA_OPTIONS=() 56 | if [[ -n "${MYSQL_SSL}" ]]; then 57 | EXTRA_OPTIONS+=("--ssl=\"${MYSQL_SSL}\"") 58 | fi 59 | if [[ -n "${MYSQL_SSL_VERIFY_SERVER_CERT}" ]]; then 60 | EXTRA_OPTIONS+=("--ssl-verify-server-cert=\"${MYSQL_SSL_VERIFY_SERVER_CERT}\"") 61 | fi 62 | if [[ -n "${MYSQL_SSL_CA}" ]]; then 63 | EXTRA_OPTIONS+=("--ssl-ca=\"${MYSQL_SSL_CA}\"") 64 | fi 65 | if [[ -n "${MYSQL_SSL_CERT}" ]]; then 66 | EXTRA_OPTIONS+=("--ssl-cert=\"${MYSQL_SSL_CERT}\"") 67 | fi 68 | if [[ -n "${MYSQL_SSL_KEY}" ]]; then 69 | EXTRA_OPTIONS+=("--ssl-key=\"${MYSQL_SSL_KEY}\"") 70 | fi 71 | 72 | eval "mariadb-dump -h \"${MYSQL_HOST}\" -P \"${MYSQL_PORT}\" -u \"${MYSQL_USERNAME}\" -p\"${MYSQL_PASSWORD}\" ${EXTRA_OPTIONS[@]} \"${MYSQL_DATABASE}\" > \"${BACKUP_FILE_DB_MYSQL}\"" 73 | if [[ $? != 0 ]]; then 74 | color red "backup vaultwarden mysql database failed" 75 | 76 | send_notification "failure" "Backup failed at $(date +"%Y-%m-%d %H:%M:%S %Z"). Reason: Backup mysql database failed." 77 | 78 | exit 1 79 | fi 80 | } 81 | 82 | function backup_config() { 83 | color blue "backup vaultwarden config" 84 | 85 | if [[ -f "${DATA_CONFIG}" ]]; then 86 | cp -f "${DATA_CONFIG}" "${BACKUP_FILE_CONFIG}" 87 | else 88 | color yellow "not found vaultwarden config, skipping" 89 | fi 90 | } 91 | 92 | function backup_rsakey() { 93 | color blue "backup vaultwarden rsakey" 94 | 95 | local FIND_RSAKEY=$(find "${DATA_RSAKEY_DIRNAME}" -name "${DATA_RSAKEY_BASENAME}*" | xargs -I {} basename {}) 96 | local FIND_RSAKEY_COUNT=$(echo "${FIND_RSAKEY}" | wc -l) 97 | 98 | if [[ "${FIND_RSAKEY_COUNT}" -gt 0 ]]; then 99 | echo "${FIND_RSAKEY}" | tar -c -C "${DATA_RSAKEY_DIRNAME}" -f "${BACKUP_FILE_RSAKEY}" -T - 100 | 101 | color blue "display rsakey tar file list" 102 | 103 | tar -tf "${BACKUP_FILE_RSAKEY}" 104 | else 105 | color yellow "not found vaultwarden rsakey, skipping" 106 | fi 107 | } 108 | 109 | function backup_attachments() { 110 | color blue "backup vaultwarden attachments" 111 | 112 | if [[ -d "${DATA_ATTACHMENTS}" ]]; then 113 | tar -c -C "${DATA_ATTACHMENTS_DIRNAME}" -f "${BACKUP_FILE_ATTACHMENTS}" "${DATA_ATTACHMENTS_BASENAME}" 114 | 115 | color blue "display attachments tar file list" 116 | 117 | tar -tf "${BACKUP_FILE_ATTACHMENTS}" 118 | else 119 | color yellow "not found vaultwarden attachments directory, skipping" 120 | fi 121 | } 122 | 123 | function backup_sends() { 124 | color blue "backup vaultwarden sends" 125 | 126 | if [[ -d "${DATA_SENDS}" ]]; then 127 | tar -c -C "${DATA_SENDS_DIRNAME}" -f "${BACKUP_FILE_SENDS}" "${DATA_SENDS_BASENAME}" 128 | 129 | color blue "display sends tar file list" 130 | 131 | tar -tf "${BACKUP_FILE_SENDS}" 132 | else 133 | color yellow "not found vaultwarden sends directory, skipping" 134 | fi 135 | } 136 | 137 | function backup() { 138 | mkdir -p "${BACKUP_DIR}" 139 | 140 | case "${DB_TYPE}" in 141 | SQLITE) backup_db_sqlite ;; 142 | POSTGRESQL) backup_db_postgresql ;; 143 | MYSQL) backup_db_mysql ;; 144 | esac 145 | 146 | backup_config 147 | backup_rsakey 148 | backup_attachments 149 | backup_sends 150 | 151 | ls -lah "${BACKUP_DIR}" 152 | } 153 | 154 | function backup_package() { 155 | if [[ "${ZIP_ENABLE}" == "TRUE" ]]; then 156 | color blue "package backup file" 157 | 158 | UPLOAD_FILE="${BACKUP_FILE_ZIP}" 159 | 160 | if [[ "${ZIP_TYPE}" == "zip" ]]; then 161 | 7z a -tzip -mx=9 -p"${ZIP_PASSWORD}" "${BACKUP_FILE_ZIP}" "${BACKUP_DIR}"/* 162 | else 163 | 7z a -t7z -m0=lzma2 -mx=9 -mfb=64 -md=32m -ms=on -mhe=on -p"${ZIP_PASSWORD}" "${BACKUP_FILE_ZIP}" "${BACKUP_DIR}"/* 164 | fi 165 | 166 | ls -lah "${BACKUP_DIR}" 167 | 168 | color blue "display backup ${ZIP_TYPE} file list" 169 | 170 | 7z l -p"${ZIP_PASSWORD}" "${BACKUP_FILE_ZIP}" 171 | else 172 | color yellow "skip package backup files" 173 | 174 | UPLOAD_FILE="${BACKUP_DIR}" 175 | fi 176 | } 177 | 178 | function upload() { 179 | # upload file not exist 180 | if [[ ! -e "${UPLOAD_FILE}" ]]; then 181 | color red "upload file not found" 182 | 183 | send_notification "failure" "File upload failed at $(date +"%Y-%m-%d %H:%M:%S %Z"). Reason: Upload file not found." 184 | 185 | exit 1 186 | fi 187 | 188 | # upload 189 | local HAS_ERROR="FALSE" 190 | 191 | for RCLONE_REMOTE_X in "${RCLONE_REMOTE_LIST[@]}" 192 | do 193 | color blue "upload backup file to storage system $(color yellow "[${RCLONE_REMOTE_X}]")" 194 | 195 | rclone ${RCLONE_GLOBAL_FLAG} copy "${UPLOAD_FILE}" "${RCLONE_REMOTE_X}" 196 | if [[ $? != 0 ]]; then 197 | color red "upload failed" 198 | 199 | HAS_ERROR="TRUE" 200 | fi 201 | done 202 | 203 | if [[ "${HAS_ERROR}" == "TRUE" ]]; then 204 | send_notification "failure" "File upload failed at $(date +"%Y-%m-%d %H:%M:%S %Z")." 205 | 206 | exit 1 207 | fi 208 | } 209 | 210 | function clear_history() { 211 | if [[ "${BACKUP_KEEP_DAYS}" -gt 0 ]]; then 212 | for RCLONE_REMOTE_X in "${RCLONE_REMOTE_LIST[@]}" 213 | do 214 | color blue "delete ${BACKUP_KEEP_DAYS} days ago backup files $(color yellow "[${RCLONE_REMOTE_X}]")" 215 | 216 | mapfile -t RCLONE_DELETE_LIST < <(rclone ${RCLONE_GLOBAL_FLAG} lsf "${RCLONE_REMOTE_X}" --min-age "${BACKUP_KEEP_DAYS}d") 217 | 218 | for RCLONE_DELETE_FILE in "${RCLONE_DELETE_LIST[@]}" 219 | do 220 | color yellow "deleting \"${RCLONE_DELETE_FILE}\"" 221 | 222 | rclone ${RCLONE_GLOBAL_FLAG} delete "${RCLONE_REMOTE_X}/${RCLONE_DELETE_FILE}" 223 | if [[ $? != 0 ]]; then 224 | color red "delete \"${RCLONE_DELETE_FILE}\" failed" 225 | fi 226 | done 227 | done 228 | fi 229 | } 230 | 231 | color blue "running the backup program at $(date +"%Y-%m-%d %H:%M:%S %Z")" 232 | 233 | init_env 234 | 235 | send_notification "start" "Start backup at $(date +"%Y-%m-%d %H:%M:%S %Z")" 236 | 237 | check_rclone_connection any 238 | 239 | clear_dir 240 | backup_init 241 | backup 242 | backup_package 243 | upload 244 | clear_dir 245 | clear_history 246 | 247 | send_notification "success" "The file was successfully uploaded at $(date +"%Y-%m-%d %H:%M:%S %Z")." 248 | 249 | color none "" 250 | -------------------------------------------------------------------------------- /scripts/entrypoint.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | . /app/includes.sh 4 | 5 | # rclone command 6 | if [[ "$1" == "rclone" ]]; then 7 | $* 8 | 9 | exit 0 10 | fi 11 | 12 | # mail test 13 | if [[ "$1" == "mail" ]]; then 14 | export_env_file 15 | init_env_display 16 | init_env_mail 17 | 18 | MAIL_SMTP_ENABLE="TRUE" 19 | MAIL_DEBUG="TRUE" 20 | 21 | if [[ -n "$2" ]]; then 22 | MAIL_TO="$2" 23 | fi 24 | 25 | send_mail "${DISPLAY_NAME} Backup Test" "Your SMTP configuration looks correct." 26 | 27 | exit 0 28 | fi 29 | 30 | # ping test 31 | if [[ "$1" == "ping" ]]; then 32 | export_env_file 33 | init_env_display 34 | init_env_ping 35 | 36 | PING_DEBUG="TRUE" 37 | 38 | send_ping "$2" "${DISPLAY_NAME} Backup Test" "Your PING configuration looks correct." 39 | 40 | exit 0 41 | fi 42 | 43 | # restore 44 | if [[ "$1" == "restore" ]]; then 45 | . /app/restore.sh 46 | 47 | shift 48 | restore $* 49 | 50 | exit 0 51 | fi 52 | 53 | function configure_timezone() { 54 | ln -sf "/usr/share/zoneinfo/${TIMEZONE}" "${LOCALTIME_FILE}" 55 | } 56 | 57 | function configure_cron() { 58 | local FIND_CRON_COUNT="$(grep -c 'backup.sh' "${CRON_CONFIG_FILE}" 2> /dev/null)" 59 | if [[ "${FIND_CRON_COUNT}" -eq 0 ]]; then 60 | echo "${CRON} bash /app/backup.sh" >> "${CRON_CONFIG_FILE}" 61 | fi 62 | } 63 | 64 | init_env 65 | check_rclone_connection all 66 | configure_postgresql 67 | configure_timezone 68 | configure_cron 69 | 70 | # backup manually 71 | if [[ "$1" == "backup" ]]; then 72 | color yellow "Manually triggering a backup will only execute the backup script once, and the container will exit upon completion." 73 | 74 | bash "/app/backup.sh" 75 | 76 | exit 0 77 | fi 78 | 79 | # foreground run crond 80 | exec /usr/bin/supercronic -passthrough-logs -quiet "${CRON_CONFIG_FILE}" 81 | -------------------------------------------------------------------------------- /scripts/includes.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | ENV_FILE="/.env" 4 | CRON_CONFIG_FILE="${HOME}/crontabs" 5 | BACKUP_DIR="/bitwarden/backup" 6 | RESTORE_DIR="/bitwarden/restore" 7 | RESTORE_EXTRACT_DIR="/bitwarden/extract" 8 | 9 | #################### Function #################### 10 | ######################################## 11 | # Print colorful message. 12 | # Arguments: 13 | # color 14 | # message 15 | # Outputs: 16 | # colorful message 17 | ######################################## 18 | function color() { 19 | case $1 in 20 | red) echo -e "\033[31m$2\033[0m" ;; 21 | green) echo -e "\033[32m$2\033[0m" ;; 22 | yellow) echo -e "\033[33m$2\033[0m" ;; 23 | blue) echo -e "\033[34m$2\033[0m" ;; 24 | none) echo "$2" ;; 25 | esac 26 | } 27 | 28 | ######################################## 29 | # Check storage system connection success. 30 | # Arguments: 31 | # success strategy (all / any) 32 | ######################################## 33 | function check_rclone_connection() { 34 | # check if the configuration exists 35 | rclone ${RCLONE_GLOBAL_FLAG} config show "${RCLONE_REMOTE_NAME}" > /dev/null 2>&1 36 | if [[ $? != 0 ]]; then 37 | color red "rclone configuration information not found" 38 | color blue "Please configure rclone first, check https://github.com/ttionya/vaultwarden-backup/blob/master/README.md#backup" 39 | exit 1 40 | fi 41 | 42 | # check connection 43 | local ERROR_COUNT=0 44 | 45 | for RCLONE_REMOTE_X in "${RCLONE_REMOTE_LIST[@]}" 46 | do 47 | rclone ${RCLONE_GLOBAL_FLAG} lsd "${RCLONE_REMOTE_X}" > /dev/null 48 | if [[ $? != 0 ]]; then 49 | color red "storage system connection may not be initialized, try initializing $(color yellow "[${RCLONE_REMOTE_X}]")" 50 | 51 | rclone ${RCLONE_GLOBAL_FLAG} mkdir "${RCLONE_REMOTE_X}" 52 | if [[ $? != 0 ]]; then 53 | color red "storage system connection failure $(color yellow "[${RCLONE_REMOTE_X}]")" 54 | 55 | ((ERROR_COUNT++)) 56 | fi 57 | fi 58 | done 59 | 60 | if [[ "${ERROR_COUNT}" -gt 0 ]]; then 61 | if [[ "$1" == "all" ]]; then 62 | color red "storage system connection failure exists" 63 | exit 1 64 | elif [[ "$1" == "any" ]]; then 65 | if [[ "${ERROR_COUNT}" -eq "${#RCLONE_REMOTE_LIST[@]}" ]]; then 66 | color red "all storage system connections failed" 67 | exit 1 68 | else 69 | color yellow "some storage system connections failed, but the backup will continue" 70 | fi 71 | fi 72 | fi 73 | } 74 | 75 | ######################################## 76 | # Check if the file exists. 77 | # Arguments: 78 | # file 79 | ######################################## 80 | function check_file_exist() { 81 | if [[ ! -f "$1" ]]; then 82 | color red "cannot access $1: No such file" 83 | exit 1 84 | fi 85 | } 86 | 87 | ######################################## 88 | # Check if the directory exists. 89 | # Arguments: 90 | # directory 91 | ######################################## 92 | function check_dir_exist() { 93 | if [[ ! -d "$1" ]]; then 94 | color red "cannot access $1: No such directory" 95 | exit 1 96 | fi 97 | } 98 | 99 | ######################################## 100 | # Send mail by s-nail. 101 | # Arguments: 102 | # mail subject 103 | # mail content 104 | # Outputs: 105 | # send mail result 106 | ######################################## 107 | function send_mail() { 108 | if [[ "${MAIL_DEBUG}" == "TRUE" ]]; then 109 | local MAIL_VERBOSE="-v" 110 | fi 111 | 112 | echo "$2" | eval "mail ${MAIL_VERBOSE} -s \"$1\" ${MAIL_SMTP_VARIABLES} \"${MAIL_TO}\"" 113 | if [[ $? != 0 ]]; then 114 | color red "mail sending has failed" 115 | else 116 | color blue "mail has been sent successfully" 117 | fi 118 | } 119 | 120 | ######################################## 121 | # Send health check or notification ping. 122 | # Arguments: 123 | # ping status (completion / start / success / failure) 124 | # ping subject 125 | # ping content 126 | # Outputs: 127 | # send ping result 128 | ######################################## 129 | function send_ping() { 130 | local CURL_URL="" 131 | local CURL_OPTIONS="" 132 | 133 | case "$1" in 134 | completion) CURL_URL="${PING_URL}" CURL_OPTIONS="${PING_URL_CURL_OPTIONS}" ;; 135 | start) CURL_URL="${PING_URL_WHEN_START}" CURL_OPTIONS="${PING_URL_WHEN_START_CURL_OPTIONS}" ;; 136 | success) CURL_URL="${PING_URL_WHEN_SUCCESS}" CURL_OPTIONS="${PING_URL_WHEN_SUCCESS_CURL_OPTIONS}" ;; 137 | failure) CURL_URL="${PING_URL_WHEN_FAILURE}" CURL_OPTIONS="${PING_URL_WHEN_FAILURE_CURL_OPTIONS}" ;; 138 | *) color red "illegal identifier, only supports completion, start, success, failure" ;; 139 | esac 140 | 141 | if [[ -z "${CURL_URL}" ]]; then 142 | return 143 | fi 144 | 145 | CURL_URL=$(echo "${CURL_URL}" | sed "s/%{subject}/$(echo "$2" | tr ' ' '+')/g") 146 | CURL_URL=$(echo "${CURL_URL}" | sed "s/%{content}/$(echo "$3" | tr ' ' '+')/g") 147 | CURL_OPTIONS=$(echo "${CURL_OPTIONS}" | sed "s/%{subject}/$2/g") 148 | CURL_OPTIONS=$(echo "${CURL_OPTIONS}" | sed "s/%{content}/$3/g") 149 | 150 | local CURL_COMMAND="curl -m 15 --retry 10 --retry-delay 1 -o /dev/null -s${CURL_OPTIONS:+" ${CURL_OPTIONS}"} \"${CURL_URL}\"" 151 | 152 | if [[ "${PING_DEBUG}" == "TRUE" ]]; then 153 | color yellow "curl command: ${CURL_COMMAND}" 154 | fi 155 | 156 | eval "${CURL_COMMAND}" 157 | if [[ $? != 0 ]]; then 158 | color red "$1 ping sending has failed" 159 | else 160 | color blue "$1 ping has been sent successfully" 161 | fi 162 | } 163 | 164 | ######################################## 165 | # Send notification. 166 | # Arguments: 167 | # status (start / success / failure) 168 | # notification content 169 | ######################################## 170 | function send_notification() { 171 | local SUBJECT_START="${DISPLAY_NAME} Backup Start" 172 | local SUBJECT_SUCCESS="${DISPLAY_NAME} Backup Success" 173 | local SUBJECT_FAILURE="${DISPLAY_NAME} Backup Failed" 174 | 175 | case "$1" in 176 | start) 177 | # ping 178 | send_ping "start" "${SUBJECT_START}" "$2" 179 | ;; 180 | success) 181 | # mail 182 | if [[ "${MAIL_SMTP_ENABLE}" == "TRUE" && "${MAIL_WHEN_SUCCESS}" == "TRUE" ]]; then 183 | send_mail "${SUBJECT_SUCCESS}" "$2" 184 | fi 185 | # ping 186 | send_ping "success" "${SUBJECT_SUCCESS}" "$2" 187 | send_ping "completion" "${SUBJECT_SUCCESS}" "$2" 188 | ;; 189 | failure) 190 | # mail 191 | if [[ "${MAIL_SMTP_ENABLE}" == "TRUE" && "${MAIL_WHEN_FAILURE}" == "TRUE" ]]; then 192 | send_mail "${SUBJECT_FAILURE}" "$2" 193 | fi 194 | # ping 195 | send_ping "failure" "${SUBJECT_FAILURE}" "$2" 196 | send_ping "completion" "${SUBJECT_FAILURE}" "$2" 197 | ;; 198 | esac 199 | } 200 | 201 | ######################################## 202 | # Configure PostgreSQL password file. 203 | # Arguments: 204 | # None 205 | ######################################## 206 | function configure_postgresql() { 207 | if [[ "${DB_TYPE}" == "POSTGRESQL" ]]; then 208 | echo "${PG_HOST}:${PG_PORT}:${PG_DBNAME}:${PG_USERNAME}:${PG_PASSWORD}" > ~/.pgpass 209 | chmod 0600 ~/.pgpass 210 | fi 211 | } 212 | 213 | ######################################## 214 | # Export variables from .env file. 215 | # Arguments: 216 | # None 217 | # Outputs: 218 | # variables with prefix 'DOTENV_' 219 | # Reference: 220 | # https://gist.github.com/judy2k/7656bfe3b322d669ef75364a46327836#gistcomment-3632918 221 | ######################################## 222 | function export_env_file() { 223 | if [[ -f "${ENV_FILE}" ]]; then 224 | color blue "find \"${ENV_FILE}\" file and export variables" 225 | set -a 226 | source <(cat "${ENV_FILE}" | sed -e '/^#/d;/^\s*$/d' -e 's/\(\w*\)[ \t]*=[ \t]*\(.*\)/DOTENV_\1=\2/') 227 | set +a 228 | fi 229 | } 230 | 231 | ######################################## 232 | # Get variables from 233 | # environment variables, 234 | # secret file in environment variables, 235 | # secret file in .env file, 236 | # environment variables in .env file. 237 | # Arguments: 238 | # variable name 239 | # Outputs: 240 | # variable value 241 | ######################################## 242 | function get_env() { 243 | local VAR="$1" 244 | local VAR_FILE="${VAR}_FILE" 245 | local VAR_DOTENV="DOTENV_${VAR}" 246 | local VAR_DOTENV_FILE="DOTENV_${VAR_FILE}" 247 | local VALUE="" 248 | 249 | if [[ -n "${!VAR:-}" ]]; then 250 | VALUE="${!VAR}" 251 | elif [[ -n "${!VAR_FILE:-}" ]]; then 252 | VALUE="$(cat "${!VAR_FILE}")" 253 | elif [[ -n "${!VAR_DOTENV_FILE:-}" ]]; then 254 | VALUE="$(cat "${!VAR_DOTENV_FILE}")" 255 | elif [[ -n "${!VAR_DOTENV:-}" ]]; then 256 | VALUE="${!VAR_DOTENV}" 257 | fi 258 | 259 | export "${VAR}=${VALUE}" 260 | } 261 | 262 | ######################################## 263 | # Get RCLONE_REMOTE_LIST variables. 264 | # Arguments: 265 | # None 266 | # Outputs: 267 | # variable value 268 | ######################################## 269 | function get_rclone_remote_list() { 270 | # RCLONE_REMOTE_LIST 271 | RCLONE_REMOTE_LIST=() 272 | 273 | local i=0 274 | local RCLONE_REMOTE_NAME_X_REFER 275 | local RCLONE_REMOTE_DIR_X_REFER 276 | local RCLONE_REMOTE_X 277 | 278 | # for multiple 279 | while true; do 280 | RCLONE_REMOTE_NAME_X_REFER="RCLONE_REMOTE_NAME_${i}" 281 | RCLONE_REMOTE_DIR_X_REFER="RCLONE_REMOTE_DIR_${i}" 282 | get_env "${RCLONE_REMOTE_NAME_X_REFER}" 283 | get_env "${RCLONE_REMOTE_DIR_X_REFER}" 284 | 285 | if [[ -z "${!RCLONE_REMOTE_NAME_X_REFER}" || -z "${!RCLONE_REMOTE_DIR_X_REFER}" ]]; then 286 | break 287 | fi 288 | 289 | RCLONE_REMOTE_X=$(echo "${!RCLONE_REMOTE_NAME_X_REFER}:${!RCLONE_REMOTE_DIR_X_REFER}" | sed 's@\(/*\)$@@') 290 | RCLONE_REMOTE_LIST=(${RCLONE_REMOTE_LIST[@]} "${RCLONE_REMOTE_X}") 291 | 292 | ((i++)) 293 | done 294 | } 295 | 296 | ######################################## 297 | # Initialization environment variables. 298 | # Arguments: 299 | # None 300 | # Outputs: 301 | # environment variables 302 | ######################################## 303 | function init_env() { 304 | # export 305 | export_env_file 306 | 307 | init_env_dir 308 | init_env_db 309 | init_env_display 310 | init_env_ping 311 | init_env_mail 312 | 313 | # CRON 314 | get_env CRON 315 | CRON="${CRON:-"5 * * * *"}" 316 | 317 | # RCLONE_REMOTE_NAME 318 | get_env RCLONE_REMOTE_NAME 319 | RCLONE_REMOTE_NAME="${RCLONE_REMOTE_NAME:-"BitwardenBackup"}" 320 | RCLONE_REMOTE_NAME_0="${RCLONE_REMOTE_NAME}" 321 | 322 | # RCLONE_REMOTE_DIR 323 | get_env RCLONE_REMOTE_DIR 324 | RCLONE_REMOTE_DIR="${RCLONE_REMOTE_DIR:-"/BitwardenBackup/"}" 325 | RCLONE_REMOTE_DIR_0="${RCLONE_REMOTE_DIR}" 326 | 327 | # get RCLONE_REMOTE_LIST 328 | get_rclone_remote_list 329 | 330 | # RCLONE_GLOBAL_FLAG 331 | get_env RCLONE_GLOBAL_FLAG 332 | RCLONE_GLOBAL_FLAG="${RCLONE_GLOBAL_FLAG:-""}" 333 | 334 | # ZIP_ENABLE 335 | get_env ZIP_ENABLE 336 | if [[ "${ZIP_ENABLE^^}" == "FALSE" ]]; then 337 | ZIP_ENABLE="FALSE" 338 | else 339 | ZIP_ENABLE="TRUE" 340 | fi 341 | 342 | # ZIP_PASSWORD 343 | get_env ZIP_PASSWORD 344 | ZIP_PASSWORD="${ZIP_PASSWORD:-"WHEREISMYPASSWORD?"}" 345 | 346 | # ZIP_TYPE 347 | get_env ZIP_TYPE 348 | if [[ "${ZIP_TYPE,,}" == "7z" ]]; then 349 | ZIP_TYPE="7z" 350 | else 351 | ZIP_TYPE="zip" 352 | fi 353 | 354 | # BACKUP_KEEP_DAYS 355 | get_env BACKUP_KEEP_DAYS 356 | BACKUP_KEEP_DAYS="${BACKUP_KEEP_DAYS:-"0"}" 357 | 358 | # BACKUP_FILE_DATE_FORMAT 359 | get_env BACKUP_FILE_SUFFIX 360 | get_env BACKUP_FILE_DATE 361 | get_env BACKUP_FILE_DATE_SUFFIX 362 | BACKUP_FILE_DATE="$(echo "${BACKUP_FILE_DATE:-"%Y%m%d"}${BACKUP_FILE_DATE_SUFFIX}" | sed 's/[^0-9a-zA-Z%_-]//g')" 363 | BACKUP_FILE_DATE_FORMAT="$(echo "${BACKUP_FILE_SUFFIX:-"${BACKUP_FILE_DATE}"}" | sed 's/\///g')" 364 | 365 | # TIMEZONE 366 | get_env TIMEZONE 367 | local TIMEZONE_MATCHED_COUNT=$(ls "/usr/share/zoneinfo/${TIMEZONE}" 2> /dev/null | wc -l) 368 | if [[ "${TIMEZONE_MATCHED_COUNT}" -ne 1 ]]; then 369 | TIMEZONE="UTC" 370 | fi 371 | 372 | color yellow "========================================" 373 | color yellow "DATA_DIR: ${DATA_DIR}" 374 | color yellow "DATA_CONFIG: ${DATA_CONFIG}" 375 | color yellow "DATA_RSAKEY: ${DATA_RSAKEY}" 376 | color yellow "DATA_ATTACHMENTS: ${DATA_ATTACHMENTS}" 377 | color yellow "DATA_SENDS: ${DATA_SENDS}" 378 | color yellow "========================================" 379 | color yellow "DB_TYPE: ${DB_TYPE}" 380 | 381 | if [[ "${DB_TYPE}" == "POSTGRESQL" ]]; then 382 | color yellow "DB_URL: postgresql://${PG_USERNAME}:***(${#PG_PASSWORD} Chars)@${PG_HOST}:${PG_PORT}/${PG_DBNAME}" 383 | elif [[ "${DB_TYPE}" == "MYSQL" ]]; then 384 | color yellow "DB_URL: mysql://${MYSQL_USERNAME}:***(${#MYSQL_PASSWORD} Chars)@${MYSQL_HOST}:${MYSQL_PORT}/${MYSQL_DATABASE}" 385 | else 386 | color yellow "DATA_DB: ${DATA_DB}" 387 | fi 388 | 389 | color yellow "========================================" 390 | color yellow "CRON: ${CRON}" 391 | 392 | for RCLONE_REMOTE_X in "${RCLONE_REMOTE_LIST[@]}" 393 | do 394 | color yellow "RCLONE_REMOTE: ${RCLONE_REMOTE_X}" 395 | done 396 | 397 | color yellow "RCLONE_GLOBAL_FLAG: ${RCLONE_GLOBAL_FLAG}" 398 | color yellow "ZIP_ENABLE: ${ZIP_ENABLE}" 399 | color yellow "ZIP_PASSWORD: ${#ZIP_PASSWORD} Chars" 400 | color yellow "ZIP_TYPE: ${ZIP_TYPE}" 401 | color yellow "BACKUP_FILE_DATE_FORMAT: ${BACKUP_FILE_DATE_FORMAT} (example \"[filename].$(date +"${BACKUP_FILE_DATE_FORMAT}").[ext]\")" 402 | color yellow "BACKUP_KEEP_DAYS: ${BACKUP_KEEP_DAYS}" 403 | if [[ -n "${PING_URL}" ]]; then 404 | color yellow "PING_URL: curl${PING_URL_CURL_OPTIONS:+" ${PING_URL_CURL_OPTIONS}"} \"${PING_URL}\"" 405 | fi 406 | if [[ -n "${PING_URL_WHEN_START}" ]]; then 407 | color yellow "PING_URL_WHEN_START: curl${PING_URL_WHEN_START_CURL_OPTIONS:+" ${PING_URL_WHEN_START_CURL_OPTIONS}"} \"${PING_URL_WHEN_START}\"" 408 | fi 409 | if [[ -n "${PING_URL_WHEN_SUCCESS}" ]]; then 410 | color yellow "PING_URL_WHEN_SUCCESS: curl${PING_URL_WHEN_SUCCESS_CURL_OPTIONS:+" ${PING_URL_WHEN_SUCCESS_CURL_OPTIONS}"} \"${PING_URL_WHEN_SUCCESS}\"" 411 | fi 412 | if [[ -n "${PING_URL_WHEN_FAILURE}" ]]; then 413 | color yellow "PING_URL_WHEN_FAILURE: curl${PING_URL_WHEN_FAILURE_CURL_OPTIONS:+" ${PING_URL_WHEN_FAILURE_CURL_OPTIONS}"} \"${PING_URL_WHEN_FAILURE}\"" 414 | fi 415 | color yellow "MAIL_SMTP_ENABLE: ${MAIL_SMTP_ENABLE}" 416 | if [[ "${MAIL_SMTP_ENABLE}" == "TRUE" ]]; then 417 | color yellow "MAIL_TO: ${MAIL_TO}" 418 | color yellow "MAIL_WHEN_SUCCESS: ${MAIL_WHEN_SUCCESS}" 419 | color yellow "MAIL_WHEN_FAILURE: ${MAIL_WHEN_FAILURE}" 420 | fi 421 | color yellow "TIMEZONE: ${TIMEZONE}" 422 | color yellow "DISPLAY_NAME: ${DISPLAY_NAME}" 423 | color yellow "========================================" 424 | } 425 | 426 | function init_env_dir() { 427 | # DATA_DIR 428 | get_env DATA_DIR 429 | DATA_DIR="${DATA_DIR:-"/bitwarden/data"}" 430 | check_dir_exist "${DATA_DIR}" 431 | 432 | # DATA_DB 433 | get_env DATA_DB 434 | DATA_DB="${DATA_DB:-"${DATA_DIR}/db.sqlite3"}" 435 | 436 | # DATA_CONFIG 437 | DATA_CONFIG="${DATA_DIR}/config.json" 438 | 439 | # DATA_RSAKEY 440 | get_env DATA_RSAKEY 441 | DATA_RSAKEY="${DATA_RSAKEY:-"${DATA_DIR}/rsa_key"}" 442 | DATA_RSAKEY_DIRNAME="$(dirname "${DATA_RSAKEY}")" 443 | DATA_RSAKEY_BASENAME="$(basename "${DATA_RSAKEY}")" 444 | 445 | # DATA_ATTACHMENTS 446 | get_env DATA_ATTACHMENTS 447 | DATA_ATTACHMENTS="$(dirname "${DATA_ATTACHMENTS:-"${DATA_DIR}/attachments"}/useless")" 448 | DATA_ATTACHMENTS_DIRNAME="$(dirname "${DATA_ATTACHMENTS}")" 449 | DATA_ATTACHMENTS_BASENAME="$(basename "${DATA_ATTACHMENTS}")" 450 | 451 | # DATA_SEND 452 | get_env DATA_SENDS 453 | DATA_SENDS="$(dirname "${DATA_SENDS:-"${DATA_DIR}/sends"}/useless")" 454 | DATA_SENDS_DIRNAME="$(dirname "${DATA_SENDS}")" 455 | DATA_SENDS_BASENAME="$(basename "${DATA_SENDS}")" 456 | } 457 | 458 | function init_env_db() { 459 | # DB_TYPE 460 | get_env DB_TYPE 461 | 462 | if [[ "${DB_TYPE^^}" == "POSTGRESQL" ]]; then # postgresql 463 | DB_TYPE="POSTGRESQL" 464 | 465 | # PG_HOST 466 | get_env PG_HOST 467 | 468 | # PG_PORT 469 | get_env PG_PORT 470 | PG_PORT="${PG_PORT:-"5432"}" 471 | 472 | # PG_DBNAME 473 | get_env PG_DBNAME 474 | PG_DBNAME="${PG_DBNAME:-"vaultwarden"}" 475 | 476 | # PG_USERNAME 477 | get_env PG_USERNAME 478 | PG_USERNAME="${PG_USERNAME:-"vaultwarden"}" 479 | 480 | # PG_PASSWORD 481 | get_env PG_PASSWORD 482 | elif [[ "${DB_TYPE^^}" == "MYSQL" ]]; then # mysql 483 | DB_TYPE="MYSQL" 484 | 485 | # MYSQL_HOST 486 | get_env MYSQL_HOST 487 | 488 | # MYSQL_PORT 489 | get_env MYSQL_PORT 490 | MYSQL_PORT="${MYSQL_PORT:-"3306"}" 491 | 492 | # MYSQL_DATABASE 493 | get_env MYSQL_DATABASE 494 | MYSQL_DATABASE="${MYSQL_DATABASE:-"vaultwarden"}" 495 | 496 | # MYSQL_USERNAME 497 | get_env MYSQL_USERNAME 498 | MYSQL_USERNAME="${MYSQL_USERNAME:-"vaultwarden"}" 499 | 500 | # MYSQL_PASSWORD 501 | get_env MYSQL_PASSWORD 502 | 503 | # MYSQL_SSL 504 | get_env MYSQL_SSL 505 | 506 | # MYSQL_SSL_VERIFY_SERVER_CERT 507 | get_env MYSQL_SSL_VERIFY_SERVER_CERT 508 | 509 | # MYSQL_SSL_CA 510 | get_env MYSQL_SSL_CA 511 | 512 | # MYSQL_SSL_CERT 513 | get_env MYSQL_SSL_CERT 514 | 515 | # MYSQL_SSL_KEY 516 | get_env MYSQL_SSL_KEY 517 | else # sqlite 518 | DB_TYPE="SQLITE" 519 | fi 520 | } 521 | 522 | function init_env_display() { 523 | # DISPLAY_NAME 524 | get_env DISPLAY_NAME 525 | DISPLAY_NAME="${DISPLAY_NAME:-"vaultwarden"}" 526 | } 527 | 528 | function init_env_ping() { 529 | # PING_URL 530 | get_env PING_URL 531 | PING_URL="${PING_URL:-""}" 532 | 533 | # PING_URL_CURL_OPTIONS 534 | get_env PING_URL_CURL_OPTIONS 535 | PING_URL_CURL_OPTIONS="${PING_URL_CURL_OPTIONS:-""}" 536 | 537 | # PING_URL_WHEN_START 538 | get_env PING_URL_WHEN_START 539 | PING_URL_WHEN_START="${PING_URL_WHEN_START:-""}" 540 | 541 | # PING_URL_WHEN_START_CURL_OPTIONS 542 | get_env PING_URL_WHEN_START_CURL_OPTIONS 543 | PING_URL_WHEN_START_CURL_OPTIONS="${PING_URL_WHEN_START_CURL_OPTIONS:-""}" 544 | 545 | # PING_URL_WHEN_SUCCESS 546 | get_env PING_URL_WHEN_SUCCESS 547 | PING_URL_WHEN_SUCCESS="${PING_URL_WHEN_SUCCESS:-""}" 548 | 549 | # PING_URL_WHEN_SUCCESS_CURL_OPTIONS 550 | get_env PING_URL_WHEN_SUCCESS_CURL_OPTIONS 551 | PING_URL_WHEN_SUCCESS_CURL_OPTIONS="${PING_URL_WHEN_SUCCESS_CURL_OPTIONS:-""}" 552 | 553 | # PING_URL_WHEN_FAILURE 554 | get_env PING_URL_WHEN_FAILURE 555 | PING_URL_WHEN_FAILURE="${PING_URL_WHEN_FAILURE:-""}" 556 | 557 | # PING_URL_WHEN_FAILURE_CURL_OPTIONS 558 | get_env PING_URL_WHEN_FAILURE_CURL_OPTIONS 559 | PING_URL_WHEN_FAILURE_CURL_OPTIONS="${PING_URL_WHEN_FAILURE_CURL_OPTIONS:-""}" 560 | } 561 | 562 | function init_env_mail() { 563 | # MAIL_SMTP_ENABLE 564 | # MAIL_TO 565 | get_env MAIL_SMTP_ENABLE 566 | get_env MAIL_TO 567 | if [[ "${MAIL_SMTP_ENABLE^^}" == "TRUE" && "${MAIL_TO}" ]]; then 568 | MAIL_SMTP_ENABLE="TRUE" 569 | else 570 | MAIL_SMTP_ENABLE="FALSE" 571 | fi 572 | 573 | # MAIL_SMTP_VARIABLES 574 | get_env MAIL_SMTP_VARIABLES 575 | MAIL_SMTP_VARIABLES="${MAIL_SMTP_VARIABLES:-""}" 576 | 577 | # MAIL_WHEN_SUCCESS 578 | get_env MAIL_WHEN_SUCCESS 579 | if [[ "${MAIL_WHEN_SUCCESS^^}" == "FALSE" ]]; then 580 | MAIL_WHEN_SUCCESS="FALSE" 581 | else 582 | MAIL_WHEN_SUCCESS="TRUE" 583 | fi 584 | 585 | # MAIL_WHEN_FAILURE 586 | get_env MAIL_WHEN_FAILURE 587 | if [[ "${MAIL_WHEN_FAILURE^^}" == "FALSE" ]]; then 588 | MAIL_WHEN_FAILURE="FALSE" 589 | else 590 | MAIL_WHEN_FAILURE="TRUE" 591 | fi 592 | } 593 | -------------------------------------------------------------------------------- /scripts/restore.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | . /app/includes.sh 4 | 5 | RESTORE_FILE_DB="" 6 | RESTORE_FILE_CONFIG="" 7 | RESTORE_FILE_RSAKEY="" 8 | RESTORE_FILE_ATTACHMENTS="" 9 | RESTORE_FILE_SENDS="" 10 | RESTORE_FILE_ZIP="" 11 | RESTORE_FILE_DIR="${RESTORE_DIR}" 12 | ZIP_PASSWORD="" 13 | FORCE_RESTORE="FALSE" 14 | 15 | function clear_extract_dir() { 16 | rm -rf "${RESTORE_EXTRACT_DIR}" 17 | } 18 | 19 | function restore_zip() { 20 | color blue "restore vaultwarden backup zip file" 21 | 22 | local FIND_FILE_DB 23 | local FIND_FILE_CONFIG 24 | local FIND_FILE_RSAKEY 25 | local FIND_FILE_ATTACHMENTS 26 | local FIND_FILE_SENDS 27 | 28 | if [[ -n "${ZIP_PASSWORD}" ]]; then 29 | 7z e -aoa -p"${ZIP_PASSWORD}" -o"${RESTORE_EXTRACT_DIR}" "${RESTORE_FILE_ZIP}" 30 | else 31 | 7z e -aoa -o"${RESTORE_EXTRACT_DIR}" "${RESTORE_FILE_ZIP}" 32 | fi 33 | 34 | if [[ $? == 0 ]]; then 35 | color green "extract vaultwarden backup zip file successful" 36 | else 37 | color red "extract vaultwarden backup zip file failed" 38 | exit 1 39 | fi 40 | 41 | # get restore db file 42 | FIND_FILE_DB="$( basename "$(ls ${RESTORE_EXTRACT_DIR}/db.*.* 2>/dev/null)" )" 43 | RESTORE_FILE_DB="${FIND_FILE_DB:-}" 44 | 45 | # get restore config file 46 | FIND_FILE_CONFIG="$( basename "$(ls ${RESTORE_EXTRACT_DIR}/config.*.json 2>/dev/null)" )" 47 | RESTORE_FILE_CONFIG="${FIND_FILE_CONFIG:-}" 48 | 49 | # get restore rsakey file 50 | FIND_FILE_RSAKEY="$( basename "$(ls ${RESTORE_EXTRACT_DIR}/rsakey.*.tar 2>/dev/null)" )" 51 | RESTORE_FILE_RSAKEY="${FIND_FILE_RSAKEY:-}" 52 | 53 | # get restore attachments file 54 | FIND_FILE_ATTACHMENTS="$( basename "$(ls ${RESTORE_EXTRACT_DIR}/attachments.*.tar 2>/dev/null)" )" 55 | RESTORE_FILE_ATTACHMENTS="${FIND_FILE_ATTACHMENTS:-}" 56 | 57 | # get restore sends file 58 | FIND_FILE_SENDS="$( basename "$(ls ${RESTORE_EXTRACT_DIR}/sends.*.tar 2>/dev/null)" )" 59 | RESTORE_FILE_SENDS="${FIND_FILE_SENDS:-}" 60 | 61 | RESTORE_FILE_ZIP="" 62 | RESTORE_FILE_DIR="${RESTORE_EXTRACT_DIR}" 63 | restore_file 64 | } 65 | 66 | function restore_db_sqlite() { 67 | color blue "restore vaultwarden sqlite database" 68 | 69 | cp -f "${RESTORE_FILE_DB}" "${DATA_DB}" 70 | 71 | if [[ $? == 0 ]]; then 72 | color green "restore vaultwarden sqlite database successful" 73 | else 74 | color red "restore vaultwarden sqlite database failed" 75 | fi 76 | } 77 | 78 | function restore_db_postgresql() { 79 | color blue "restore vaultwarden postgresql database" 80 | 81 | pg_restore -h "${PG_HOST}" -p "${PG_PORT}" -d "${PG_DBNAME}" -U "${PG_USERNAME}" -c "${RESTORE_FILE_DB}" 82 | 83 | if [[ $? == 0 ]]; then 84 | color green "restore vaultwarden postgresql database successful" 85 | else 86 | color red "restore vaultwarden postgresql database failed" 87 | fi 88 | } 89 | 90 | function restore_db_mysql() { 91 | color blue "restore vaultwarden mysql database" 92 | 93 | local EXTRA_OPTIONS=() 94 | if [[ -n "${MYSQL_SSL}" ]]; then 95 | EXTRA_OPTIONS+=("--ssl=\"${MYSQL_SSL}\"") 96 | fi 97 | if [[ -n "${MYSQL_SSL_VERIFY_SERVER_CERT}" ]]; then 98 | EXTRA_OPTIONS+=("--ssl-verify-server-cert=\"${MYSQL_SSL_VERIFY_SERVER_CERT}\"") 99 | fi 100 | if [[ -n "${MYSQL_SSL_CA}" ]]; then 101 | EXTRA_OPTIONS+=("--ssl-ca=\"${MYSQL_SSL_CA}\"") 102 | fi 103 | if [[ -n "${MYSQL_SSL_CERT}" ]]; then 104 | EXTRA_OPTIONS+=("--ssl-cert=\"${MYSQL_SSL_CERT}\"") 105 | fi 106 | if [[ -n "${MYSQL_SSL_KEY}" ]]; then 107 | EXTRA_OPTIONS+=("--ssl-key=\"${MYSQL_SSL_KEY}\"") 108 | fi 109 | 110 | eval "mariadb -h \"${MYSQL_HOST}\" -P \"${MYSQL_PORT}\" -u \"${MYSQL_USERNAME}\" -p\"${MYSQL_PASSWORD}\" ${EXTRA_OPTIONS[@]} \"${MYSQL_DATABASE}\" < \"${RESTORE_FILE_DB}\"" 111 | 112 | if [[ $? == 0 ]]; then 113 | color green "restore vaultwarden mysql database successful" 114 | else 115 | color red "restore vaultwarden mysql database failed" 116 | fi 117 | } 118 | 119 | function restore_config() { 120 | color blue "restore vaultwarden config" 121 | 122 | cp -f "${RESTORE_FILE_CONFIG}" "${DATA_CONFIG}" 123 | 124 | if [[ $? == 0 ]]; then 125 | color green "restore vaultwarden config successful" 126 | else 127 | color red "restore vaultwarden config failed" 128 | fi 129 | } 130 | 131 | function restore_rsakey() { 132 | color blue "restore vaultwarden rsakey" 133 | 134 | mkdir -p "${DATA_RSAKEY_DIRNAME}" 135 | tar -x -C "${DATA_RSAKEY_DIRNAME}" -f "${RESTORE_FILE_RSAKEY}" 136 | 137 | if [[ $? == 0 ]]; then 138 | color green "restore vaultwarden rsakey successful" 139 | else 140 | color red "restore vaultwarden rsakey failed" 141 | fi 142 | } 143 | 144 | function restore_attachments() { 145 | color blue "restore vaultwarden attachments" 146 | 147 | # When customizing the attachments folder, the root directory of the tar file 148 | # is the directory name at the time of packing 149 | local RESTORE_FILE_ATTACHMENTS_DIRNAME=$(tar -tf "${RESTORE_FILE_ATTACHMENTS}" | head -n 1 | xargs basename) 150 | local DATA_ATTACHMENTS_EXTRACT="${DATA_ATTACHMENTS}.extract" 151 | 152 | rm -rf "${DATA_ATTACHMENTS}" "${DATA_ATTACHMENTS_EXTRACT}" 153 | mkdir -p "${DATA_ATTACHMENTS_EXTRACT}" 154 | tar -x -C "${DATA_ATTACHMENTS_EXTRACT}" -f "${RESTORE_FILE_ATTACHMENTS}" 155 | mv "${DATA_ATTACHMENTS_EXTRACT}/${RESTORE_FILE_ATTACHMENTS_DIRNAME}" "${DATA_ATTACHMENTS}" 156 | rm -rf "${DATA_ATTACHMENTS_EXTRACT}" 157 | 158 | if [[ $? == 0 ]]; then 159 | color green "restore vaultwarden attachments successful" 160 | else 161 | color red "restore vaultwarden attachments failed" 162 | fi 163 | } 164 | 165 | function restore_sends() { 166 | color blue "restore vaultwarden sends" 167 | 168 | # When customizing the sends folder, the root directory of the tar file 169 | # is the directory name at the time of packing 170 | local RESTORE_FILE_SENDS_DIRNAME=$(tar -tf "${RESTORE_FILE_SENDS}" | head -n 1 | xargs basename) 171 | local DATA_SENDS_EXTRACT="${DATA_SENDS}.extract" 172 | 173 | rm -rf "${DATA_SENDS}" "${DATA_SENDS_EXTRACT}" 174 | mkdir -p "${DATA_SENDS_EXTRACT}" 175 | tar -x -C "${DATA_SENDS_EXTRACT}" -f "${RESTORE_FILE_SENDS}" 176 | mv "${DATA_SENDS_EXTRACT}/${RESTORE_FILE_SENDS_DIRNAME}" "${DATA_SENDS}" 177 | rm -rf "${DATA_SENDS_EXTRACT}" 178 | 179 | if [[ $? == 0 ]]; then 180 | color green "restore vaultwarden sends successful" 181 | else 182 | color red "restore vaultwarden sends failed" 183 | fi 184 | } 185 | 186 | function check_restore_file_exist() { 187 | if [[ ! -f "${RESTORE_FILE_DIR}/$1" ]]; then 188 | color red "$2: cannot access $1: No such file" 189 | exit 1 190 | fi 191 | } 192 | 193 | function restore_file() { 194 | if [[ -n "${RESTORE_FILE_ZIP}" ]]; then 195 | check_restore_file_exist "${RESTORE_FILE_ZIP}" "--zip-file" 196 | 197 | RESTORE_FILE_ZIP="${RESTORE_FILE_DIR}/${RESTORE_FILE_ZIP}" 198 | 199 | clear_extract_dir 200 | restore_zip 201 | clear_extract_dir 202 | else 203 | if [[ -n "${RESTORE_FILE_DB}" ]]; then 204 | check_restore_file_exist "${RESTORE_FILE_DB}" "--db-file" 205 | 206 | RESTORE_FILE_DB="${RESTORE_FILE_DIR}/${RESTORE_FILE_DB}" 207 | fi 208 | 209 | if [[ -n "${RESTORE_FILE_CONFIG}" ]]; then 210 | check_restore_file_exist "${RESTORE_FILE_CONFIG}" "--config-file" 211 | 212 | RESTORE_FILE_CONFIG="${RESTORE_FILE_DIR}/${RESTORE_FILE_CONFIG}" 213 | fi 214 | 215 | if [[ -n "${RESTORE_FILE_RSAKEY}" ]]; then 216 | check_restore_file_exist "${RESTORE_FILE_RSAKEY}" "--rsakey-file" 217 | 218 | RESTORE_FILE_RSAKEY="${RESTORE_FILE_DIR}/${RESTORE_FILE_RSAKEY}" 219 | fi 220 | 221 | if [[ -n "${RESTORE_FILE_ATTACHMENTS}" ]]; then 222 | check_restore_file_exist "${RESTORE_FILE_ATTACHMENTS}" "--attachments-file" 223 | 224 | RESTORE_FILE_ATTACHMENTS="${RESTORE_FILE_DIR}/${RESTORE_FILE_ATTACHMENTS}" 225 | fi 226 | 227 | if [[ -n "${RESTORE_FILE_SENDS}" ]]; then 228 | check_restore_file_exist "${RESTORE_FILE_SENDS}" "--sends-file" 229 | 230 | RESTORE_FILE_SENDS="${RESTORE_FILE_DIR}/${RESTORE_FILE_SENDS}" 231 | fi 232 | 233 | if [[ -n "${RESTORE_FILE_DB}" ]]; then 234 | case "${DB_TYPE}" in 235 | SQLITE) restore_db_sqlite ;; 236 | POSTGRESQL) restore_db_postgresql ;; 237 | MYSQL) restore_db_mysql ;; 238 | esac 239 | fi 240 | if [[ -n "${RESTORE_FILE_CONFIG}" ]]; then 241 | restore_config 242 | fi 243 | if [[ -n "${RESTORE_FILE_RSAKEY}" ]]; then 244 | restore_rsakey 245 | fi 246 | if [[ -n "${RESTORE_FILE_ATTACHMENTS}" ]]; then 247 | restore_attachments 248 | fi 249 | if [[ -n "${RESTORE_FILE_SENDS}" ]]; then 250 | restore_sends 251 | fi 252 | fi 253 | } 254 | 255 | function check_empty_input() { 256 | if [[ -z "${RESTORE_FILE_ZIP}${RESTORE_FILE_DB}${RESTORE_FILE_CONFIG}${RESTORE_FILE_RSAKEY}${RESTORE_FILE_ATTACHMENTS}${RESTORE_FILE_SENDS}" ]]; then 257 | color yellow "Empty input" 258 | color none "" 259 | color none "Find out more at https://github.com/ttionya/vaultwarden-backup#restore" 260 | exit 0 261 | fi 262 | } 263 | 264 | function check_data_dir_exist() { 265 | if [[ ! -d "${DATA_DIR}" ]]; then 266 | color red "vaultwarden data directory not found" 267 | exit 1 268 | fi 269 | } 270 | 271 | function restore() { 272 | local READ_RESTORE_CONTINUE 273 | 274 | while [[ $# -gt 0 ]]; do 275 | case "$1" in 276 | -p|--password) 277 | shift 278 | ZIP_PASSWORD="$1" 279 | shift 280 | ;; 281 | --zip-file) 282 | shift 283 | RESTORE_FILE_ZIP="$(basename "$1")" 284 | shift 285 | ;; 286 | --db-file) 287 | shift 288 | RESTORE_FILE_DB="$(basename "$1")" 289 | shift 290 | ;; 291 | --config-file) 292 | shift 293 | RESTORE_FILE_CONFIG="$(basename "$1")" 294 | shift 295 | ;; 296 | --rsakey-file) 297 | shift 298 | RESTORE_FILE_RSAKEY="$(basename "$1")" 299 | shift 300 | ;; 301 | --attachments-file) 302 | shift 303 | RESTORE_FILE_ATTACHMENTS="$(basename "$1")" 304 | shift 305 | ;; 306 | --sends-file) 307 | shift 308 | RESTORE_FILE_SENDS="$(basename "$1")" 309 | shift 310 | ;; 311 | -f|--force-restore) 312 | shift 313 | FORCE_RESTORE="TRUE" 314 | ;; 315 | *) 316 | color red "Illegal input" 317 | exit 1 318 | ;; 319 | esac 320 | done 321 | 322 | init_env_dir 323 | init_env_db 324 | configure_postgresql 325 | check_empty_input 326 | check_data_dir_exist 327 | 328 | if [[ "${FORCE_RESTORE}" == "TRUE" ]]; then 329 | restore_file 330 | else 331 | color yellow "Restore will overwrite the existing files, continue? (y/N)" 332 | read -p "(Default: n): " READ_RESTORE_CONTINUE 333 | if [[ $(echo "${READ_RESTORE_CONTINUE:-n}" | tr [a-z] [A-Z]) == "Y" ]]; then 334 | restore_file 335 | fi 336 | fi 337 | } 338 | -------------------------------------------------------------------------------- /tests/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM base 2 | 3 | COPY tests/fixtures/source / 4 | 5 | ENTRYPOINT ["/app/entrypoint.sh"] 6 | -------------------------------------------------------------------------------- /tests/fixtures/source/bitwarden/data/attachments/31ba771a-dde9-4b5c-aced-9c5688017f4e/c45510b00a849da3dd9e: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ttionya/vaultwarden-backup/0ac377b2f11dad89022f1ec17b65ecfe10814f53/tests/fixtures/source/bitwarden/data/attachments/31ba771a-dde9-4b5c-aced-9c5688017f4e/c45510b00a849da3dd9e -------------------------------------------------------------------------------- /tests/fixtures/source/bitwarden/data/config.json: -------------------------------------------------------------------------------- 1 | {} 2 | -------------------------------------------------------------------------------- /tests/fixtures/source/bitwarden/data/db.sqlite3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ttionya/vaultwarden-backup/0ac377b2f11dad89022f1ec17b65ecfe10814f53/tests/fixtures/source/bitwarden/data/db.sqlite3 -------------------------------------------------------------------------------- /tests/fixtures/source/bitwarden/data/db.sqlite3-shm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ttionya/vaultwarden-backup/0ac377b2f11dad89022f1ec17b65ecfe10814f53/tests/fixtures/source/bitwarden/data/db.sqlite3-shm -------------------------------------------------------------------------------- /tests/fixtures/source/bitwarden/data/db.sqlite3-wal: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ttionya/vaultwarden-backup/0ac377b2f11dad89022f1ec17b65ecfe10814f53/tests/fixtures/source/bitwarden/data/db.sqlite3-wal -------------------------------------------------------------------------------- /tests/fixtures/source/bitwarden/data/rsa_key.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | MIIEpAIBAAKCAQEAssB6zmcI4PeE1ycqPbNZStcN0AmgDKBrCbucj3p1NziWtCI0 3 | sHopiPq5O1Ij25VlNOjb4pLuiQN6wunpXBiGpAqU7asltqH2YwfTdu8C+EMvgexE 4 | uxN+O+xbHavjAkc628Q7fQUJlrV00q7+Y+24NOoUwa6KeNAMCyKB5nwDzR7hZ8qm 5 | z8P4lC3xvwnekVdzmtNydfroWWyWaR8B5c8dGGDU6xWHl/YxzLgfsQAwHvdWctDj 6 | panw6EA/fncp4besarWDu6ZCQnW94mVCWP+T97KDLVXNRv0q4/n9lgglfh8A44AV 7 | ED8BH8IcffSGconiKImVlY2uuuXW0eAQjl2jNQIDAQABAoIBAAOjZ3HVQIOcyEMS 8 | solXGUFO6Yj+jFwEXgDXC3oCAmn2uaKK+7FMI8ivo1p6WTGx2gXWRaYlBXgs3Tjf 9 | V/cV2YLrhY0pqaFzJHv3ZhRGYQEHaVHvVUAiV8V9ItGjuQG/VL32H6zq5SGJB9d/ 10 | B2f70qEm9Pf/thPSPQtzG8HkwYH8Akd01A1r3saIyEBQwVhUQGnM5h242Bhb+yEh 11 | KcmbTJFp7ivBzBnFGp5fJvtaFkHwei2oVjyTLyn9vohIs7YbQTU6JlY9S31/I+U9 12 | pFgR7sdgXqUgMgTNUYWwL6wLYDV3AFVe2O7zRYYa5UALgrpmLfb0Z0cTL8d1BcUA 13 | YA+BJvUCgYEA28SF91yL3fpfhmJzJKl8cBXw06wX4fbxwPMhIWB5z23v3M9JoSHw 14 | Ja+A9ZEFxxCB75eGbUNdtukFnMBHl5AHWZiES3OZ5Jn3xfcVizBFbJEFp/uyB3YV 15 | OL5HRGjlia3EWJwtpXQZUwXGZfqzzd9B9l/SWMWWgVhZedUQKnipST8CgYEA0DjZ 16 | y05wEQej5ONjPkwPiEmqLOChO9ASz/n/alvm0THMXIOJzpFTXXiSbAidj4yfUZeg 17 | JyqJVK19zq+K6RnPXbF5A+dk/gXYhxCX0F4qsgtLuyOIYMsO7oMteptI0JhaDmui 18 | wEcxRS4ugHszMJpFgoaQGABG7RxvforQLl6noosCgYEApcuzTZRSKJsUqetn9oau 19 | zFihIO+57M1CQVCq8+U2wFiuFqWDZL6Xz7aB0cEg3LcFb92empux/aX6h/E/kYYl 20 | JWC8nbeOqDnIBV+Mrz8xgOA/piVqf9qD1BUo6uFAGggwErFwdlwKJuo6bQEf2PbP 21 | arGLnVEjZF1k01b8JS52eD0CgYEAilssRM5C4t37xkxdlnh93aZtIFLGb/MLfQx1 22 | 7htQ3PJFA7gXqp2gEjzatlRnNYpQFqw0q7G0/QIm1V6JY+hVhME3UyO/VJdX0C9z 23 | YO4hWprs4FV0+jQTIOMjJhPmp0yEko5s32yuzXQpTBAQ7Jul0lxNhNUyS72YTDI6 24 | sIUOyI8CgYBt0kbg/cs5H4WpeeN1BWQE11OunjVYv7BmpWJl8i6yC03+q/wZ+ues 25 | Y0qGHD2gkE4OCMj7pTWfKRSxkE7mVILU6V64Sc4u96uxUN8Vup8gOkdfDo4yE/y1 26 | Pdidy5RfGp8T0zytF+r6RhsAi+SxtLRhx86ncd62fzvRaJqb32gX+A== 27 | -----END RSA PRIVATE KEY----- 28 | -------------------------------------------------------------------------------- /tests/fixtures/source/bitwarden/data/rsa_key.pub.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN PUBLIC KEY----- 2 | MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAssB6zmcI4PeE1ycqPbNZ 3 | StcN0AmgDKBrCbucj3p1NziWtCI0sHopiPq5O1Ij25VlNOjb4pLuiQN6wunpXBiG 4 | pAqU7asltqH2YwfTdu8C+EMvgexEuxN+O+xbHavjAkc628Q7fQUJlrV00q7+Y+24 5 | NOoUwa6KeNAMCyKB5nwDzR7hZ8qmz8P4lC3xvwnekVdzmtNydfroWWyWaR8B5c8d 6 | GGDU6xWHl/YxzLgfsQAwHvdWctDjpanw6EA/fncp4besarWDu6ZCQnW94mVCWP+T 7 | 97KDLVXNRv0q4/n9lgglfh8A44AVED8BH8IcffSGconiKImVlY2uuuXW0eAQjl2j 8 | NQIDAQAB 9 | -----END PUBLIC KEY----- 10 | -------------------------------------------------------------------------------- /tests/fixtures/source/bitwarden/data/sends/d48d0c9a-5711-40d9-b7a5-e0706c5cecce/473c1e5667b047fd26bf16bd060769a525299deaff3989a85e5637758a74f2bd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ttionya/vaultwarden-backup/0ac377b2f11dad89022f1ec17b65ecfe10814f53/tests/fixtures/source/bitwarden/data/sends/d48d0c9a-5711-40d9-b7a5-e0706c5cecce/473c1e5667b047fd26bf16bd060769a525299deaff3989a85e5637758a74f2bd -------------------------------------------------------------------------------- /tests/fixtures/source/config/rclone/rclone.conf: -------------------------------------------------------------------------------- 1 | [BitwardenBackup] 2 | type = local 3 | -------------------------------------------------------------------------------- /tests/test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | 4 | DOCKER_IMAGE="ttionya/vaultwarden-backup:test" 5 | ERROR_NUM=0 6 | 7 | DATA_DIR="$(pwd)/tests/fixtures/source/bitwarden/data" 8 | OUTPUT_DIR="output" 9 | EXTRACT_DIR="extract" 10 | TEMP_DIR="tmp" 11 | REMOTE_DIR="/${OUTPUT_DIR}" 12 | 13 | ######################################## 14 | # Print colorful message. 15 | # Arguments: 16 | # color 17 | # message 18 | # Outputs: 19 | # colorful message 20 | ######################################## 21 | function color() { 22 | case $1 in 23 | red) echo -e "\033[31m$2\033[0m" ;; 24 | green) echo -e "\033[32m$2\033[0m" ;; 25 | yellow) echo -e "\033[33m$2\033[0m" ;; 26 | blue) echo -e "\033[34m$2\033[0m" ;; 27 | none) echo "$2" ;; 28 | esac 29 | } 30 | 31 | ######################################## 32 | # Check if the files in two folders are the same. 33 | # Arguments: 34 | # folder1 35 | # folder2 36 | # Outputs: 37 | # progress 38 | # Returns: 39 | # same or not 40 | ######################################## 41 | function check_files_same_in_folders() { 42 | function generate_hash_list() { 43 | find "$1" -type f -not -name "db.*" -exec sha1sum {} \; | sort | sed "s|$1||g" > "$2" 44 | } 45 | 46 | color blue "Calculating file hash in folder \"$1\" and \"$2\"" 47 | 48 | local FOLDER1="$1" 49 | local FOLDER2="$2" 50 | local FOLDER1_HASH_LIST="/tmp/folder1_hash_list" 51 | local FOLDER2_HASH_LIST="/tmp/folder2_hash_list" 52 | generate_hash_list "${FOLDER1}" "${FOLDER1_HASH_LIST}" 53 | generate_hash_list "${FOLDER2}" "${FOLDER2_HASH_LIST}" 54 | 55 | color blue "Calculating differences" 56 | 57 | local RETURN_CODE 58 | local FOLDER_HASH_DIFF="/tmp/folder_hash_diff" 59 | if diff "${FOLDER1_HASH_LIST}" "${FOLDER2_HASH_LIST}" > "${FOLDER_HASH_DIFF}"; then 60 | RETURN_CODE=0 61 | else 62 | RETURN_CODE=1 63 | cat "${FOLDER_HASH_DIFF}" 64 | fi 65 | 66 | rm -rf "${FOLDER1_HASH_LIST}" "${FOLDER2_HASH_LIST}" "${FOLDER_HASH_DIFF}" 67 | 68 | return "${RETURN_CODE}" 69 | } 70 | 71 | ######################################## 72 | # Show test result. 73 | # Arguments: 74 | # test name 75 | # failed number 76 | # Outputs: 77 | # test result 78 | ######################################## 79 | function test_result() { 80 | if [[ "$2" == "0" ]]; then 81 | color green "Test case \"$1\" passed" 82 | return 83 | fi 84 | 85 | ((ERROR_NUM++)) 86 | 87 | color red "Test case \"$1\" failed" 88 | } 89 | 90 | . tests/units/env-priority/test.sh 91 | . tests/units/check-rclone-connection-initializing/test.sh 92 | . tests/units/backup-zip-file/test.sh 93 | . tests/units/backup-7z-file/test.sh 94 | . tests/units/backup-unpackage/test.sh 95 | . tests/units/backup-cron/test.sh 96 | 97 | if [[ "${ERROR_NUM}" == "0" ]]; then 98 | color green "All tests passed" 99 | else 100 | color red "Some tests failed" 101 | exit 1 102 | fi 103 | -------------------------------------------------------------------------------- /tests/units/backup-7z-file/test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | TEST_NAME="backup-7z-file" 4 | TEST_OUTPUT_DIR="$(pwd)/${OUTPUT_DIR}/${TEST_NAME}" 5 | TEST_EXTRACT_DIR="$(pwd)/${EXTRACT_DIR}/${TEST_NAME}" 6 | 7 | PASSWORD="8a0726b7-d0f5-4034-bcb0-40a7efdb3f8b" 8 | BACKUP_FILE="${TEST_OUTPUT_DIR}/backup.test.7z" 9 | 10 | FAILED_NUM=0 11 | 12 | color yellow "Starting test case \"${TEST_NAME}\"" 13 | 14 | function prepare() { 15 | mkdir -p "${TEST_OUTPUT_DIR}" "${TEST_EXTRACT_DIR}" 16 | } 17 | 18 | function start() { 19 | docker run --rm \ 20 | --mount "type=bind,source=${TEST_OUTPUT_DIR},target=${REMOTE_DIR}" \ 21 | -e "RCLONE_REMOTE_DIR=${REMOTE_DIR}" \ 22 | -e "ZIP_PASSWORD=${PASSWORD}" \ 23 | -e "ZIP_TYPE=7z" \ 24 | -e "BACKUP_FILE_SUFFIX=test" \ 25 | "${DOCKER_IMAGE}" \ 26 | backup 27 | } 28 | 29 | function test() { 30 | color blue "Testing..." 31 | 32 | ls -l "${BACKUP_FILE}" 33 | 34 | 7z l -p"${PASSWORD}" "${BACKUP_FILE}" 35 | 36 | docker run --rm \ 37 | --mount "type=bind,source=${TEST_EXTRACT_DIR},target=/bitwarden/data/" \ 38 | --mount "type=bind,source=${TEST_OUTPUT_DIR},target=/bitwarden/restore/" \ 39 | "${DOCKER_IMAGE}" \ 40 | restore \ 41 | -f \ 42 | -p "${PASSWORD}" \ 43 | --zip-file "$(basename "${BACKUP_FILE}")" 44 | 45 | check_files_same_in_folders "${DATA_DIR}" "${TEST_EXTRACT_DIR}" 46 | if [[ $? != 0 ]]; then 47 | ((FAILED_NUM++)) 48 | fi 49 | } 50 | 51 | function cleanup() { 52 | sudo rm -rf "${TEST_OUTPUT_DIR}" "${TEST_EXTRACT_DIR}" 53 | 54 | unset TEST_OUTPUT_DIR 55 | unset TEST_EXTRACT_DIR 56 | unset PASSWORD 57 | unset BACKUP_FILE 58 | } 59 | 60 | prepare 61 | start 62 | test 63 | cleanup 64 | 65 | test_result "${TEST_NAME}" "${FAILED_NUM}" 66 | -------------------------------------------------------------------------------- /tests/units/backup-cron/test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | TEST_NAME="backup-cron" 4 | TEST_CONTAINER_NAME="${TEST_NAME}" 5 | TEST_OUTPUT_DIR="$(pwd)/${OUTPUT_DIR}/${TEST_NAME}" 6 | 7 | PASSWORD="faedea12-7f9e-4d84-983f-d049d9b82a36" 8 | BACKUP_FILE="${TEST_OUTPUT_DIR}/backup.test.zip" 9 | 10 | FAILED_NUM=0 11 | 12 | color yellow "Starting test case \"${TEST_NAME}\"" 13 | 14 | function prepare() { 15 | mkdir -p "${TEST_OUTPUT_DIR}" 16 | } 17 | 18 | function start() { 19 | docker run --rm -d \ 20 | --name "${TEST_CONTAINER_NAME}" \ 21 | --mount "type=bind,source=${TEST_OUTPUT_DIR},target=${REMOTE_DIR}" \ 22 | -e "RCLONE_REMOTE_DIR=${REMOTE_DIR}" \ 23 | -e "ZIP_PASSWORD=${PASSWORD}" \ 24 | -e "BACKUP_FILE_SUFFIX=test" \ 25 | -e "CRON=* * * * *" \ 26 | "${DOCKER_IMAGE}" 27 | } 28 | 29 | function test() { 30 | color blue "Testing..." 31 | 32 | local TIMER=0 33 | local SUCCESS=FALSE 34 | 35 | # wait 120s 36 | while [[ "${TIMER}" -lt 120 ]]; do 37 | if [[ -f "${BACKUP_FILE}" && -s "${BACKUP_FILE}" ]]; then 38 | SUCCESS=TRUE 39 | break 40 | fi 41 | 42 | sleep 1 43 | ((TIMER++)) 44 | done 45 | 46 | ls -l "${BACKUP_FILE}" 47 | 48 | if [[ "${SUCCESS}" == "FALSE" ]]; then 49 | ((FAILED_NUM++)) 50 | fi 51 | } 52 | 53 | function cleanup() { 54 | # stop the container 55 | docker stop "${TEST_CONTAINER_NAME}" 56 | 57 | sudo rm -rf "${TEST_OUTPUT_DIR}" 58 | 59 | unset TEST_CONTAINER_NAME 60 | unset TEST_OUTPUT_DIR 61 | unset PASSWORD 62 | unset BACKUP_FILE 63 | } 64 | 65 | prepare 66 | start 67 | test 68 | cleanup 69 | 70 | test_result "${TEST_NAME}" "${FAILED_NUM}" 71 | -------------------------------------------------------------------------------- /tests/units/backup-unpackage/test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | TEST_NAME="backup-unpackage" 4 | TEST_OUTPUT_DIR="$(pwd)/${OUTPUT_DIR}/${TEST_NAME}" 5 | TEST_EXTRACT_DIR="$(pwd)/${EXTRACT_DIR}/${TEST_NAME}" 6 | 7 | FAILED_NUM=0 8 | 9 | color yellow "Starting test case \"${TEST_NAME}\"" 10 | 11 | function prepare() { 12 | mkdir -p "${TEST_OUTPUT_DIR}" "${TEST_EXTRACT_DIR}" 13 | } 14 | 15 | function start() { 16 | docker run --rm \ 17 | --mount "type=bind,source=${TEST_OUTPUT_DIR},target=${REMOTE_DIR}" \ 18 | -e "RCLONE_REMOTE_DIR=${REMOTE_DIR}" \ 19 | -e "ZIP_ENABLE=FALSE" \ 20 | -e "BACKUP_FILE_SUFFIX=test" \ 21 | "${DOCKER_IMAGE}" \ 22 | backup 23 | } 24 | 25 | function test() { 26 | color blue "Testing..." 27 | 28 | ls -l "${TEST_OUTPUT_DIR}" 29 | 30 | docker run --rm \ 31 | --mount "type=bind,source=${TEST_EXTRACT_DIR},target=/bitwarden/data/" \ 32 | --mount "type=bind,source=${TEST_OUTPUT_DIR},target=/bitwarden/restore/" \ 33 | "${DOCKER_IMAGE}" \ 34 | restore \ 35 | -f \ 36 | --db-file "db.test.sqlite3" \ 37 | --config-file "config.test.json" \ 38 | --rsakey-file "rsakey.test.tar" \ 39 | --attachments-file "attachments.test.tar" \ 40 | --sends-file "sends.test.tar" 41 | 42 | check_files_same_in_folders "${DATA_DIR}" "${TEST_EXTRACT_DIR}" 43 | if [[ $? != 0 ]]; then 44 | ((FAILED_NUM++)) 45 | fi 46 | } 47 | 48 | function cleanup() { 49 | sudo rm -rf "${TEST_OUTPUT_DIR}" "${TEST_EXTRACT_DIR}" 50 | 51 | unset TEST_OUTPUT_DIR 52 | unset TEST_EXTRACT_DIR 53 | } 54 | 55 | prepare 56 | start 57 | test 58 | cleanup 59 | 60 | test_result "${TEST_NAME}" "${FAILED_NUM}" 61 | -------------------------------------------------------------------------------- /tests/units/backup-zip-file/test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | TEST_NAME="backup-zip-file" 4 | TEST_OUTPUT_DIR="$(pwd)/${OUTPUT_DIR}/${TEST_NAME}" 5 | TEST_EXTRACT_DIR="$(pwd)/${EXTRACT_DIR}/${TEST_NAME}" 6 | 7 | PASSWORD="71ad8764-2f69-4c0c-8452-61e08b9f489d" 8 | BACKUP_FILE="${TEST_OUTPUT_DIR}/backup.test.zip" 9 | 10 | FAILED_NUM=0 11 | 12 | color yellow "Starting test case \"${TEST_NAME}\"" 13 | 14 | function prepare() { 15 | mkdir -p "${TEST_OUTPUT_DIR}" "${TEST_EXTRACT_DIR}" 16 | } 17 | 18 | function start() { 19 | docker run --rm \ 20 | --mount "type=bind,source=${TEST_OUTPUT_DIR},target=${REMOTE_DIR}" \ 21 | -e "RCLONE_REMOTE_DIR=${REMOTE_DIR}" \ 22 | -e "ZIP_PASSWORD=${PASSWORD}" \ 23 | -e "BACKUP_FILE_SUFFIX=test" \ 24 | "${DOCKER_IMAGE}" \ 25 | backup 26 | } 27 | 28 | function test() { 29 | color blue "Testing..." 30 | 31 | ls -l "${BACKUP_FILE}" 32 | 33 | 7z l -p"${PASSWORD}" "${BACKUP_FILE}" 34 | 35 | docker run --rm \ 36 | --mount "type=bind,source=${TEST_EXTRACT_DIR},target=/bitwarden/data/" \ 37 | --mount "type=bind,source=${TEST_OUTPUT_DIR},target=/bitwarden/restore/" \ 38 | "${DOCKER_IMAGE}" \ 39 | restore \ 40 | -f \ 41 | -p "${PASSWORD}" \ 42 | --zip-file "$(basename "${BACKUP_FILE}")" 43 | 44 | check_files_same_in_folders "${DATA_DIR}" "${TEST_EXTRACT_DIR}" 45 | if [[ $? != 0 ]]; then 46 | ((FAILED_NUM++)) 47 | fi 48 | } 49 | 50 | function cleanup() { 51 | sudo rm -rf "${TEST_OUTPUT_DIR}" "${TEST_EXTRACT_DIR}" 52 | 53 | unset TEST_OUTPUT_DIR 54 | unset TEST_EXTRACT_DIR 55 | unset PASSWORD 56 | unset BACKUP_FILE 57 | } 58 | 59 | prepare 60 | start 61 | test 62 | cleanup 63 | 64 | test_result "${TEST_NAME}" "${FAILED_NUM}" 65 | -------------------------------------------------------------------------------- /tests/units/check-rclone-connection-initializing/test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | TEST_NAME="check-rclone-connection-initializing" 4 | TEST_OUTPUT_DIR="$(pwd)/${OUTPUT_DIR}/${TEST_NAME}" 5 | TEST_EXTRACT_DIR="$(pwd)/${EXTRACT_DIR}/${TEST_NAME}" 6 | TEST_DIR="/folder1/folder2/folder3" 7 | TEST_REMOTE_DIR="${REMOTE_DIR}${TEST_DIR}" 8 | 9 | PASSWORD="9eeef525-24e9-496b-9e32-990211dce6eb" 10 | BACKUP_FILE="${TEST_OUTPUT_DIR}${TEST_DIR}/backup.test.zip" 11 | 12 | FAILED_NUM=0 13 | 14 | color yellow "Starting test case \"${TEST_NAME}\"" 15 | 16 | function prepare() { 17 | mkdir -p "${TEST_OUTPUT_DIR}" "${TEST_EXTRACT_DIR}" 18 | } 19 | 20 | function start() { 21 | docker run --rm \ 22 | --mount "type=bind,source=${TEST_OUTPUT_DIR},target=${REMOTE_DIR}" \ 23 | -e "RCLONE_REMOTE_DIR=${TEST_REMOTE_DIR}" \ 24 | -e "ZIP_PASSWORD=${PASSWORD}" \ 25 | -e "BACKUP_FILE_SUFFIX=test" \ 26 | "${DOCKER_IMAGE}" \ 27 | backup 28 | } 29 | 30 | function test() { 31 | color blue "Testing..." 32 | 33 | ls -l "${BACKUP_FILE}" 34 | 35 | 7z l -p"${PASSWORD}" "${BACKUP_FILE}" 36 | 37 | docker run --rm \ 38 | --mount "type=bind,source=${TEST_EXTRACT_DIR},target=/bitwarden/data/" \ 39 | --mount "type=bind,source=$(dirname "${BACKUP_FILE}"),target=/bitwarden/restore/" \ 40 | "${DOCKER_IMAGE}" \ 41 | restore \ 42 | -f \ 43 | -p "${PASSWORD}" \ 44 | --zip-file "$(basename "${BACKUP_FILE}")" 45 | 46 | check_files_same_in_folders "${DATA_DIR}" "${TEST_EXTRACT_DIR}" 47 | if [[ $? != 0 ]]; then 48 | ((FAILED_NUM++)) 49 | fi 50 | } 51 | 52 | function cleanup() { 53 | sudo rm -rf "${TEST_OUTPUT_DIR}" "${TEST_EXTRACT_DIR}" 54 | 55 | unset TEST_OUTPUT_DIR 56 | unset TEST_EXTRACT_DIR 57 | unset TEST_DIR 58 | unset TEST_REMOTE_DIR 59 | unset PASSWORD 60 | unset BACKUP_FILE 61 | } 62 | 63 | prepare 64 | start 65 | test 66 | cleanup 67 | 68 | test_result "${TEST_NAME}" "${FAILED_NUM}" 69 | -------------------------------------------------------------------------------- /tests/units/env-priority/test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | TEST_NAME="env-priority" 4 | TEST_OUTPUT_DIR="$(pwd)/${OUTPUT_DIR}/${TEST_NAME}" 5 | TEST_TEMP_DIR="$(pwd)/${TEMP_DIR}/${TEST_NAME}" 6 | 7 | PASSWORD1="32aeec18-3bce-43af-b41d-7be04b0f7810" # For 1 8 | PASSWORD2="ce3cbc42-186b-4d1b-a1a9-3f10f0ec59c7" # For 2 9 | PASSWORD3="0e0fdbc1-5b2f-44db-ac7d-f0a8764992a7" # For 3 10 | PASSWORD4="0ddd9b27-ca9b-4912-b6fb-76569ec5cac1" # For 4 11 | PASSWORD2_FILE="${TEST_TEMP_DIR}/password2" 12 | PASSWORD3_FILE="${TEST_TEMP_DIR}/password3" 13 | BACKUP_FILE1="${TEST_OUTPUT_DIR}/backup.test1.zip" 14 | BACKUP_FILE2="${TEST_OUTPUT_DIR}/backup.test2.zip" 15 | BACKUP_FILE3="${TEST_OUTPUT_DIR}/backup.test3.zip" 16 | BACKUP_FILE4="${TEST_OUTPUT_DIR}/backup.test4.zip" 17 | ENV_FILE1="${TEST_TEMP_DIR}/.env1" 18 | ENV_FILE4="${TEST_TEMP_DIR}/.env4" 19 | 20 | FAILED_NUM=0 21 | 22 | color yellow "Starting test case \"${TEST_NAME}\"" 23 | 24 | function prepare() { 25 | mkdir -p "${TEST_OUTPUT_DIR}" "${TEST_TEMP_DIR}" 26 | 27 | echo "${PASSWORD2}" > "${PASSWORD2_FILE}" 28 | echo "${PASSWORD3}" > "${PASSWORD3_FILE}" 29 | 30 | cat > "${ENV_FILE1}" << EOF 31 | ZIP_PASSWORD_FILE="/password3" 32 | ZIP_PASSWORD="${PASSWORD4}" 33 | EOF 34 | cat > "${ENV_FILE4}" << EOF 35 | ZIP_PASSWORD="${PASSWORD4}" 36 | EOF 37 | } 38 | 39 | function start() { 40 | docker run --rm \ 41 | --mount "type=bind,source=${TEST_OUTPUT_DIR},target=${REMOTE_DIR}" \ 42 | --mount "type=bind,source=${PASSWORD2_FILE},target=/password2" \ 43 | --mount "type=bind,source=${PASSWORD3_FILE},target=/password3" \ 44 | --mount "type=bind,source=${ENV_FILE1},target=/.env" \ 45 | -e "RCLONE_REMOTE_DIR=${REMOTE_DIR}" \ 46 | -e "ZIP_PASSWORD=${PASSWORD1}" \ 47 | -e "ZIP_PASSWORD_FILE=/password2" \ 48 | -e "BACKUP_FILE_SUFFIX=test1" \ 49 | "${DOCKER_IMAGE}" \ 50 | backup 51 | 52 | docker run --rm \ 53 | --mount "type=bind,source=${TEST_OUTPUT_DIR},target=${REMOTE_DIR}" \ 54 | --mount "type=bind,source=${PASSWORD2_FILE},target=/password2" \ 55 | --mount "type=bind,source=${PASSWORD3_FILE},target=/password3" \ 56 | --mount "type=bind,source=${ENV_FILE1},target=/.env" \ 57 | -e "RCLONE_REMOTE_DIR=${REMOTE_DIR}" \ 58 | -e "ZIP_PASSWORD_FILE=/password2" \ 59 | -e "BACKUP_FILE_SUFFIX=test2" \ 60 | "${DOCKER_IMAGE}" \ 61 | backup 62 | 63 | docker run --rm \ 64 | --mount "type=bind,source=${TEST_OUTPUT_DIR},target=${REMOTE_DIR}" \ 65 | --mount "type=bind,source=${PASSWORD2_FILE},target=/password2" \ 66 | --mount "type=bind,source=${PASSWORD3_FILE},target=/password3" \ 67 | --mount "type=bind,source=${ENV_FILE1},target=/.env" \ 68 | -e "RCLONE_REMOTE_DIR=${REMOTE_DIR}" \ 69 | -e "BACKUP_FILE_SUFFIX=test3" \ 70 | "${DOCKER_IMAGE}" \ 71 | backup 72 | 73 | docker run --rm \ 74 | --mount "type=bind,source=${TEST_OUTPUT_DIR},target=${REMOTE_DIR}" \ 75 | --mount "type=bind,source=${PASSWORD2_FILE},target=/password2" \ 76 | --mount "type=bind,source=${PASSWORD3_FILE},target=/password3" \ 77 | --mount "type=bind,source=${ENV_FILE4},target=/.env" \ 78 | -e "RCLONE_REMOTE_DIR=${REMOTE_DIR}" \ 79 | -e "BACKUP_FILE_SUFFIX=test4" \ 80 | "${DOCKER_IMAGE}" \ 81 | backup 82 | } 83 | 84 | function test() { 85 | color blue "Testing..." 86 | 87 | ls -l "${TEST_OUTPUT_DIR}" 88 | 89 | 7z t -p"${PASSWORD1}" "${BACKUP_FILE1}" 90 | if [[ $? != 0 ]]; then 91 | ((FAILED_NUM++)) 92 | fi 93 | 94 | 7z t -p"${PASSWORD2}" "${BACKUP_FILE2}" 95 | if [[ $? != 0 ]]; then 96 | ((FAILED_NUM++)) 97 | fi 98 | 99 | 7z t -p"${PASSWORD3}" "${BACKUP_FILE3}" 100 | if [[ $? != 0 ]]; then 101 | ((FAILED_NUM++)) 102 | fi 103 | 104 | 7z t -p"${PASSWORD4}" "${BACKUP_FILE4}" 105 | if [[ $? != 0 ]]; then 106 | ((FAILED_NUM++)) 107 | fi 108 | } 109 | 110 | function cleanup() { 111 | sudo rm -rf "${TEST_OUTPUT_DIR}" "${TEST_TEMP_DIR}" 112 | 113 | unset TEST_OUTPUT_DIR 114 | unset TEST_TEMP_DIR 115 | unset PASSWORD1 116 | unset PASSWORD2 117 | unset PASSWORD3 118 | unset PASSWORD4 119 | unset PASSWORD2_FILE 120 | unset PASSWORD3_FILE 121 | unset BACKUP_FILE1 122 | unset BACKUP_FILE2 123 | unset BACKUP_FILE3 124 | unset BACKUP_FILE4 125 | unset ENV_FILE1 126 | unset ENV_FILE4 127 | } 128 | 129 | prepare 130 | start 131 | test 132 | cleanup 133 | 134 | test_result "${TEST_NAME}" "${FAILED_NUM}" 135 | -------------------------------------------------------------------------------- /version: -------------------------------------------------------------------------------- 1 | v1.24.3 2 | --------------------------------------------------------------------------------