├── .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 | [](https://hub.docker.com/r/ttionya/vaultwarden-backup/tags) [](https://hub.docker.com/r/ttionya/vaultwarden-backup) [](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 |
595 |
596 |
597 |
598 |
599 |
600 | ## License
601 |
602 | MIT
603 |
--------------------------------------------------------------------------------
/README_zh.md:
--------------------------------------------------------------------------------
1 | # vaultwarden backup
2 |
3 | [](https://hub.docker.com/r/ttionya/vaultwarden-backup/tags) [](https://hub.docker.com/r/ttionya/vaultwarden-backup) [](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 |
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 |
--------------------------------------------------------------------------------