From 0b40b6c3fc5b2bb8f27fef38fe14c89df7596d6d Mon Sep 17 00:00:00 2001 From: Arthur Zapparoli <2992+arthurgeek@users.noreply.github.com> Date: Wed, 12 Jul 2023 10:25:45 +0200 Subject: [PATCH] Initial commit --- .config.sample.env | 88 ++++++++++++++ .github/renovate.json5 | 34 ++++++ .github/renovate/autoMerge.json5 | 21 ++++ .github/renovate/commitMessage.json5 | 12 ++ .github/renovate/labels.json5 | 29 +++++ .github/renovate/semanticCommits.json5 | 62 ++++++++++ .github/workflows/publish.yaml | 49 ++++++++ .gitignore | 1 + .taskfiles/Fly.yaml | 7 ++ .taskfiles/FlyApp.yaml | 22 ++++ .taskfiles/FlyLogs.yaml | 10 ++ .taskfiles/FlySecrets.yaml | 7 ++ .taskfiles/FlyVolume.yaml | 7 ++ Dockerfile | 98 +++++++++++++++ LICENSE | 20 +++ README.md | 161 +++++++++++++++++++++++++ Taskfile.yaml | 13 ++ config/Caddyfile | 46 +++++++ config/Procfile | 4 + config/crontab | 1 + fly.toml | 11 ++ scripts/restic-backup.sh | 54 +++++++++ scripts/setup-msmtp.sh | 17 +++ 23 files changed, 774 insertions(+) create mode 100644 .config.sample.env create mode 100644 .github/renovate.json5 create mode 100644 .github/renovate/autoMerge.json5 create mode 100644 .github/renovate/commitMessage.json5 create mode 100644 .github/renovate/labels.json5 create mode 100644 .github/renovate/semanticCommits.json5 create mode 100644 .github/workflows/publish.yaml create mode 100644 .gitignore create mode 100644 .taskfiles/Fly.yaml create mode 100644 .taskfiles/FlyApp.yaml create mode 100644 .taskfiles/FlyLogs.yaml create mode 100644 .taskfiles/FlySecrets.yaml create mode 100644 .taskfiles/FlyVolume.yaml create mode 100644 Dockerfile create mode 100644 LICENSE create mode 100644 README.md create mode 100644 Taskfile.yaml create mode 100644 config/Caddyfile create mode 100644 config/Procfile create mode 100644 config/crontab create mode 100644 fly.toml create mode 100755 scripts/restic-backup.sh create mode 100755 scripts/setup-msmtp.sh diff --git a/.config.sample.env b/.config.sample.env new file mode 100644 index 0000000..030fd17 --- /dev/null +++ b/.config.sample.env @@ -0,0 +1,88 @@ +# +# E-mail settings +# + +# Your domain name without protocol +# If you don't have your own domain (highly recommended) +# comment this out for first deployment, add your fly.dev domain here +# then reset your secrets and redeploy your app (untested) +DOMAIN_NAME=vw.example.com + +# An account name used in msmtp config +# can be any single word that represents your smtp host +SMTP_ACCOUNT=account + +# the e-mail address used to send e-mails from both vaultwarden and restic +SMTP_FROM=vw@example.com + +# the e-mail address to notify on case of restic backup failure +SMTP_TO=me@example.com + +# Your SMPT host settings +# Check https://github.com/dani-garcia/vaultwarden/wiki/SMTP-Configuration +# for some examples +SMTP_HOST=smtp.example.com +SMTP_PORT=123 +SMTP_USERNAME=user +SMTP_PASSWORD=pass +# SMTP_SECURITY=starttls +# SMTP_AUTH_MECHANISM="Login" + +# +# Restic settings +# + +# Your restic repository location +# You don't need to initialize this repo beforehand +RESTIC_REPOSITORY=repo + +# Your restic repo password +RESTIC_PASSWORD=pass + +# If using S3 (or B2, wasabi, Minio) you'll need those +# Your S3 Access Key +# AWS_ACCESS_KEY_ID=key + +# Your S3 Secret Key +# AWS_SECRET_ACCESS_KEY=secret + +# +# Vaultwarden settings +# + +# You can read more about vaultwarden environment variables here +# https://github.com/dani-garcia/vaultwarden/wiki/Configuration-overview#configuration-options + +# Check https://github.com/dani-garcia/vaultwarden/wiki/Configuration-overview#setting-the-domain-url +# If you don't have your own domain (highly recommended) +# comment this out for first deployment, add your fly.dev domain here +# then reset your secrets and redeploy your app (untested) +DOMAIN=https://vw.example.com + +# Check https://github.com/dani-garcia/vaultwarden/wiki/Disable-registration-of-new-users +# SIGNUPS_ALLOWED=false + +# Check https://github.com/dani-garcia/vaultwarden/wiki/Disable-registration-of-new-users#restricting-registrations-to-certain-email-domains +# SIGNUPS_DOMAINS_WHITELIST=example.com + +# Check https://github.com/dani-garcia/vaultwarden/wiki/Disable-registration-of-new-users#restricting-registrations-to-certain-email-domains +# SIGNUPS_VERIFY=true + +# Check https://github.com/dani-garcia/vaultwarden/wiki/Disable-invitations +# INVITATIONS_ALLOWED=false + +# This is commented by default, because I strongly advise setting this only when needed +# Make sure you read the link below to understand the consequences and to secure the token +# https://github.com/dani-garcia/vaultwarden/wiki/Enabling-admin-page +# ADMIN_TOKEN=secure-token + +# Check https://github.com/dani-garcia/vaultwarden/blob/e7f083dee9743bfe4937f5c8149fa9d8383edb96/.env.template#L261-L267 +# ORG_CREATION_USERS=admin@example.com + +# Check https://github.com/dani-garcia/vaultwarden/wiki/Hardening-Guide#disable-password-hint-display +# SHOW_PASSWORD_HINT=false + +# Check https://github.com/dani-garcia/vaultwarden/wiki/Enabling-WebSocket-notifications +# You don't need to do any additional configuration, as the template +# already uses Caddy to configure the websocket proxy +WEBSOCKET_ENABLED=true diff --git a/.github/renovate.json5 b/.github/renovate.json5 new file mode 100644 index 0000000..16e5412 --- /dev/null +++ b/.github/renovate.json5 @@ -0,0 +1,34 @@ +{ + "$schema": "https://docs.renovatebot.com/renovate-schema.json", + "extends": [ + "config:base", + "docker:enableMajor", + ":disableRateLimiting", + ":dependencyDashboard", + ":semanticCommits", + ":enablePreCommit", + ":automergeDigest", + ":automergeBranch", + "github>arthurgeek/vaultwarden-fly-template//.github/renovate/autoMerge.json5", + "github>arthurgeek/vaultwarden-fly-template//.github/renovate/commitMessage.json5", + "github>arthurgeek/vaultwarden-fly-template//.github/renovate/labels.json5", + "github>arthurgeek/vaultwarden-fly-template//.github/renovate/semanticCommits.json5", + "helpers:pinGitHubActionDigests" + ], + "dependencyDashboard": true, + "dependencyDashboardTitle": "Renovate Dashboard 🤖", + "suppressNotifications": ["prIgnoreNotification"], + "rebaseWhen": "conflicted", + "schedule": ["every saturday"], + "pre-commit": { + "enabled": true + }, + "regexManagers": [ + { + "fileMatch": ["^Dockerfile$"], + "matchStrings": [ + "datasource=(?.*?) depName=(?.*?)\\sARG .*?_VERSION=(?v.*)\\s" + ] + } + ] +} diff --git a/.github/renovate/autoMerge.json5 b/.github/renovate/autoMerge.json5 new file mode 100644 index 0000000..8032cc2 --- /dev/null +++ b/.github/renovate/autoMerge.json5 @@ -0,0 +1,21 @@ +{ + "$schema": "https://docs.renovatebot.com/renovate-schema.json", + "packageRules": [ + { + "description": "Auto merge GitHub Actions", + "matchManagers": ["github-actions"], + "automerge": true, + "automergeType": "branch", + "ignoreTests": true, + "matchUpdateTypes": ["minor", "patch", "digest"] + }, + { + "description": "Auto merge container digests", + "matchDatasources": ["docker"], + "automerge": true, + "automergeType": "branch", + "ignoreTests": true, + "matchUpdateTypes": ["digest"] + } + ] +} diff --git a/.github/renovate/commitMessage.json5 b/.github/renovate/commitMessage.json5 new file mode 100644 index 0000000..52ae185 --- /dev/null +++ b/.github/renovate/commitMessage.json5 @@ -0,0 +1,12 @@ +{ + "$schema": "https://docs.renovatebot.com/renovate-schema.json", + "commitMessageTopic": "{{depName}}", + "commitMessageExtra": "to {{newVersion}}", + "commitMessageSuffix": "", + "packageRules": [ + { + "matchDatasources": ["docker"], + "commitMessageTopic": "image {{depName}}" + } + ] +} diff --git a/.github/renovate/labels.json5 b/.github/renovate/labels.json5 new file mode 100644 index 0000000..fccbfa8 --- /dev/null +++ b/.github/renovate/labels.json5 @@ -0,0 +1,29 @@ +{ + "$schema": "https://docs.renovatebot.com/renovate-schema.json", + "packageRules": [ + { + "matchUpdateTypes": ["major"], + "labels": ["type/major"] + }, + { + "matchUpdateTypes": ["minor"], + "labels": ["type/minor"] + }, + { + "matchUpdateTypes": ["patch"], + "labels": ["type/patch"] + }, + { + "matchDatasources": ["docker"], + "addLabels": ["renovate/container"] + }, + { + "matchDatasources": ["github-releases", "github-tags"], + "addLabels": ["renovate/github-release"] + }, + { + "matchManagers": ["github-actions"], + "addLabels": ["renovate/github-action"] + } + ] +} diff --git a/.github/renovate/semanticCommits.json5 b/.github/renovate/semanticCommits.json5 new file mode 100644 index 0000000..9ba0ef1 --- /dev/null +++ b/.github/renovate/semanticCommits.json5 @@ -0,0 +1,62 @@ +{ + "$schema": "https://docs.renovatebot.com/renovate-schema.json", + "packageRules": [ + { + "matchDatasources": ["docker"], + "matchUpdateTypes": ["major"], + "commitMessagePrefix": "feat(container)!: " + }, + { + "matchDatasources": ["docker"], + "matchUpdateTypes": ["minor"], + "semanticCommitType": "feat", + "semanticCommitScope": "container" + }, + { + "matchDatasources": ["docker"], + "matchUpdateTypes": ["patch"], + "semanticCommitType": "fix", + "semanticCommitScope": "container" + }, + { + "matchDatasources": ["docker"], + "matchUpdateTypes": ["digest"], + "semanticCommitType": "chore", + "semanticCommitScope": "container" + }, + { + "matchDatasources": ["github-releases", "github-tags"], + "matchUpdateTypes": ["major"], + "commitMessagePrefix": "feat(github-release)!: " + }, + { + "matchDatasources": ["github-releases", "github-tags"], + "matchUpdateTypes": ["minor"], + "semanticCommitType": "feat", + "semanticCommitScope": "github-release" + }, + { + "matchDatasources": ["github-releases", "github-tags"], + "matchUpdateTypes": ["patch"], + "semanticCommitType": "fix", + "semanticCommitScope": "github-release" + }, + { + "matchManagers": ["github-actions"], + "matchUpdateTypes": ["major"], + "commitMessagePrefix": "feat(github-action)!: " + }, + { + "matchManagers": ["github-actions"], + "matchUpdateTypes": ["minor"], + "semanticCommitType": "feat", + "semanticCommitScope": "github-action" + }, + { + "matchManagers": ["github-actions"], + "matchUpdateTypes": ["patch"], + "semanticCommitType": "fix", + "semanticCommitScope": "github-action" + } + ] +} diff --git a/.github/workflows/publish.yaml b/.github/workflows/publish.yaml new file mode 100644 index 0000000..a513be4 --- /dev/null +++ b/.github/workflows/publish.yaml @@ -0,0 +1,49 @@ +--- +name: Publish container + +on: + workflow_dispatch: {} + push: + tags: + - "*" + branches: + - main + paths: + - DOCKERFILE + - config/** + - scripts/** + +jobs: + build-push: + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v3 + + - name: Login to ghcr + uses: docker/login-action@v2 + with: + registry: ghcr.io + username: ${{ github.repository_owner }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Container meta + id: meta + uses: docker/metadata-action@v4 + with: + images: | + ghcr.io/${{ github.repository }} + tags: | + type=raw, value=latest, enable={{is_default_branch}} + type=semver, pattern={{version}} + type=ref, event=branch + type=ref, event=tag + type=ref, event=pr + + - name: Build and push to ghcr + uses: docker/build-push-action@v4 + with: + context: . + push: ${{ github.event_name != 'pull_request' }} + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..b79a9f0 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +.config.env \ No newline at end of file diff --git a/.taskfiles/Fly.yaml b/.taskfiles/Fly.yaml new file mode 100644 index 0000000..284e2b4 --- /dev/null +++ b/.taskfiles/Fly.yaml @@ -0,0 +1,7 @@ +--- +version: "3" + +includes: + app: FlyApp.yaml + volume: FlyVolume.yaml + secrets: FlySecrets.yaml \ No newline at end of file diff --git a/.taskfiles/FlyApp.yaml b/.taskfiles/FlyApp.yaml new file mode 100644 index 0000000..1c69f8b --- /dev/null +++ b/.taskfiles/FlyApp.yaml @@ -0,0 +1,22 @@ +--- +version: "3" + +includes: + logs: FlyLogs.yaml + +tasks: + create: + cmds: + - | + fly_app=$(fly apps create --generate-name 2>&1 | tee /dev/tty) + echo $fly_app | awk -F 'New app created:\ ' '{print "\n# Fly app name\nFLY_APP="$2}' >> .config.env + silent: true + destroy: + cmds: + - fly apps destroy {{.FLY_APP}} + deploy: + cmds: + - fly deploy -a {{.FLY_APP}} + ssh: + cmds: + - fly ssh console -a {{.FLY_APP}} \ No newline at end of file diff --git a/.taskfiles/FlyLogs.yaml b/.taskfiles/FlyLogs.yaml new file mode 100644 index 0000000..314eee4 --- /dev/null +++ b/.taskfiles/FlyLogs.yaml @@ -0,0 +1,10 @@ +--- +version: "3" + +tasks: + default: + cmds: + - fly logs -a {{.FLY_APP}} + web: + cmds: + - open https://fly.io/apps/{{.FLY_APP}}/monitoring \ No newline at end of file diff --git a/.taskfiles/FlySecrets.yaml b/.taskfiles/FlySecrets.yaml new file mode 100644 index 0000000..2830ecf --- /dev/null +++ b/.taskfiles/FlySecrets.yaml @@ -0,0 +1,7 @@ +--- +version: "3" + +tasks: + set: + cmds: + - cat .config.env | fly secrets import -a {{.FLY_APP}} \ No newline at end of file diff --git a/.taskfiles/FlyVolume.yaml b/.taskfiles/FlyVolume.yaml new file mode 100644 index 0000000..98c1455 --- /dev/null +++ b/.taskfiles/FlyVolume.yaml @@ -0,0 +1,7 @@ +--- +version: "3" + +tasks: + create: + cmds: + - fly volumes create vw_data --size 1 -a {{.FLY_APP}} \ No newline at end of file diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..0043131 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,98 @@ +# renovate: datasource=github-releases depName=aptible/supercronic +ARG SUPERCRONIC_VERSION=v0.2.25 + +# renovate: datasource=github-releases depName=DarthSim/overmind +ARG OVERMIND_VERSION=v2.4.0 + +# Binary file names +ARG SUPERCRONIC=supercronic-linux-amd64 +ARG OVERMIND=overmind-${OVERMIND_VERSION}-linux-amd64 + +FROM vaultwarden/server:1.28.1-alpine as vaultwarden + +# +# Supercronic +# +FROM alpine:3.18 as supercronic + +ARG SUPERCRONIC_VERSION +ARG OVERMIND_VERSION +ARG SUPERCRONIC +ARG OVERMIND + +ENV SUPERCRONIC_URL=https://github.com/aptible/supercronic/releases/download/${SUPERCRONIC_VERSION}/${SUPERCRONIC} + +WORKDIR / + +RUN wget "$SUPERCRONIC_URL" && chmod +x "$SUPERCRONIC" + +# +# Overmind +# +FROM alpine:3.18 as overmind + +ARG OVERMIND_VERSION +ARG SUPERCRONIC +ARG OVERMIND + +ENV OVERMIND_FILE=overmind-${OVERMIND_VERSION}-linux-amd64.gz + +ENV OVERMIND_URL=https://github.com/DarthSim/overmind/releases/download/${OVERMIND_VERSION}/${OVERMIND_FILE} + +WORKDIR / + +RUN wget "$OVERMIND_URL" && gunzip ${OVERMIND_FILE} && chmod +x "$OVERMIND" + +# +# Fly app +# +FROM caddy:2.6.4-alpine + +ARG SUPERCRONIC +ARG OVERMIND + +ENV ROCKET_PROFILE="release" \ + ROCKET_ADDRESS=0.0.0.0 \ + ROCKET_PORT=8080 \ + SSL_CERT_DIR=/etc/ssl/certs + +ENV OVERMIND_CAN_DIE=setupmsmtp + +RUN apk add --no-cache \ + ca-certificates \ + curl \ + openssl \ + tzdata \ + iptables \ + ip6tables \ + tmux \ + sqlite \ + restic \ + msmtp \ + mailx + +VOLUME /data + +EXPOSE 80 + +WORKDIR / + +RUN ln -sf /usr/bin/msmtp /usr/bin/sendmail +RUN ln -sf /usr/bin/msmtp /usr/sbin/sendmail + +COPY --from=supercronic /${SUPERCRONIC} /usr/bin/supercronic +COPY --from=overmind /${OVERMIND} /usr/bin/overmind + +COPY --from=vaultwarden /web-vault ./web-vault +COPY --from=vaultwarden /vaultwarden . + +COPY --from=vaultwarden /healthcheck.sh . +COPY --from=vaultwarden /start.sh ./vaultwarden.sh + +COPY config/crontab . +COPY config/Procfile . +COPY config/Caddyfile /etc/caddy/Caddyfile +COPY scripts/restic-backup.sh . +COPY scripts/setup-msmtp.sh . + +CMD ["overmind", "start"] \ No newline at end of file diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..53ad878 --- /dev/null +++ b/LICENSE @@ -0,0 +1,20 @@ +Copyright (c) Arthur Zapparoli + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..fc148b0 --- /dev/null +++ b/README.md @@ -0,0 +1,161 @@ +# Template for deploying [Vaultwarden] on [Fly.io] + +This is a template for deploying [Vaultwarden] on [Fly.io] with +[Caddy](https://caddyserver.com) for [websockets] support and +[supercronic](https://github.com/aptible/supercronic) for hourly +[restic](https://restic.net) backups with e-mail failure notification +via [msmtp](https://marlam.de/msmtp/). + +This uses a single fly machine, within Fly's [free allowance](https://fly.io/docs/about/pricing/#free-allowances). + +## Usage + +You first need to create a new repo for your config, by clicking +on the **Use this template** button on this page. + +Then, clone your new repo and `cd` into it. + +### Install dependencies + +1. Install [go-task](https://github.com/go-task/task): + + We use go-task to automate some steps, you can check the task + code under [.taskfiles](.taskfiles). to see which commands each + task run. + + ```sh + brew install go-task/tap/go-task + ``` + +1. Install [flyctl](https://fly.io/docs/hands-on/install-flyctl/): + + ```sh + brew install flyctl + ``` + +### Configuration + +The `.config.env` file contains environment variables needed to deploy +the apps in this template. + +1. Copy the `.config.sample.env` to `.config.env` and fill out all +the environment variables. **All uncommented variables are required**. + +### [Fly.io] setup + +For some commands below, we use a task instead of `flyctl` because we +the task writes (on app creation) and reads (subsequent commands) your +app name from the config file. This is the only way to keep your app +name hidden. + +1. Signup to Fly + + If you already have a Fly account, use `flyctl auth login` instead. + + ```sh + flyctl auth signup + ``` + +1. Create a new fly app + + If this is your first app, you'll be asked to add credit card + information, but, don't worry, you'll not be charged by this app. + + ```sh + task fly:app:create + ``` + +1. Create a new volume + + This will show you a warning about invididual volumes. + It's ok to have a single volume because we're not + concerned about downtime for our Vaultwarden instance. + + ```sh + task fly:volume:create + ``` + +1. Deploy your app + + ```sh + task fly:app:deploy + ``` + +1. Setup your custom domain + + After your app is deployed, follow the steps [here](https://fly.io/docs/app-guides/custom-domains-with-fly/) to setup your custom domain. + +1. Open your new Vaultwarden website + + That's all! Now you can open your custom domain and Vaultwarden should + work. + +## Keeping dependencies up to date + +This template uses [Renovatebot](https://www.mend.io/free-developer-tools/renovate/) to scan and open new PRs when dependencies are out of date. + +To enable this, open their [Github app](https://github.com/apps/renovate) page, click the "Configure" button, then choose your repo. The template already provides Renovate configs and there's no need for further action. + +## Troubleshooting + +If your deployment failed or you can't open Vaultwarden web, you can see +the logs with: + +```sh +task fly:app:logs +``` + +If that command fails (eg, if the machine is stopped), try opening your +logs in the browser: + +```sh +task fly:app:logs:web +``` + +You can also ssh in the machine with: + +```sh +task fly:app:ssh +``` + +and check individual logs using [overmind](https://github.com/DarthSim/overmind): + +```sh +# Run this command inside your fly machine +overmind connect vaultwarden +``` + +This will open a tmux window with vaultwarden logs. +You can scroll your tmux window with `Ctrl-B-]` and use +`Ctrl-B-D` to exit the tmux window. + +Substitute `vaultwarden` with `caddy`, or `backup` to see logs for +other apps. + +## FAQ + +1. Why every `fly` command I run errors with: `Error: the config for your app is missing an app name`? + + For security reasons the app name is not sdaved in the [fly.toml] file. + In that case, you have to add `-a your-app-name` to all `fly` commands. + + Your app name is found in your `.config.env` file. + + Example: + + ```sh + fly secrets list -a your-app-name + ``` + + Or you can add: + + ```yaml + app = "your-app-name" + ``` + + to the beginning of your [fly.toml] file. + +[Vaultwarden]: https://github.com/dani-garcia/vaultwarden +[Fly.io]: https://fly.io +[websockets]: https://github.com/dani-garcia/vaultwarden/wiki/Enabling-WebSocket-notifications +[fly.toml]: fly.toml \ No newline at end of file diff --git a/Taskfile.yaml b/Taskfile.yaml new file mode 100644 index 0000000..a26fc4c --- /dev/null +++ b/Taskfile.yaml @@ -0,0 +1,13 @@ +--- +version: "3" + +dotenv: [".config.env"] + +includes: + fly: .taskfiles/Fly.yaml + +tasks: + default: + cmds: + - task --list-all + silent: true \ No newline at end of file diff --git a/config/Caddyfile b/config/Caddyfile new file mode 100644 index 0000000..3c16e7a --- /dev/null +++ b/config/Caddyfile @@ -0,0 +1,46 @@ +{ + # HTTPS/TLS is handled by Fly or on your domain (eg: Cloudflare) + auto_https off + admin off + persist_config off + + log { + output stdout + format json + } +} + +{$DOMAIN_NAME}:80 { + encode gzip + + header / { + # Enable HTTP Strict Transport Security (HSTS) + Strict-Transport-Security "max-age=31536000;" + # Enable cross-site filter (XSS) and tell browser to block detected attacks + X-XSS-Protection "1; mode=block" + # Disallow the site to be rendered within a frame (clickjacking protection) + X-Frame-Options "DENY" + # Prevent search engines from indexing + X-Robots-Tag "noindex, nofollow" + # Disallow sniffing of X-Content-Type-Options + X-Content-Type-Options "nosniff" + # Server name removing + -Server + # Remove X-Powered-By though this shouldn't be an issue, better opsec to remove + -X-Powered-By + # Remove Last-Modified because etag is the same and is as effective + -Last-Modified + } + + # The negotiation endpoint is also proxied to Rocket + reverse_proxy /notifications/hub/negotiate localhost:8080 + + # Notifications redirected to the websockets server + reverse_proxy /notifications/hub localhost:3012 + + # Proxy everything else to Rocket + reverse_proxy localhost:8080 { + # Send the true remote IP to Rocket, so that vaultwarden can put this in the log + header_up X-Real-IP {remote_host} + } +} diff --git a/config/Procfile b/config/Procfile new file mode 100644 index 0000000..2645b87 --- /dev/null +++ b/config/Procfile @@ -0,0 +1,4 @@ +vaultwarden: /vaultwarden.sh +caddy: caddy run --config /etc/caddy/Caddyfile --adapter caddyfile +setupmsmtp: /setup-msmtp.sh +backup: supercronic /crontab \ No newline at end of file diff --git a/config/crontab b/config/crontab new file mode 100644 index 0000000..a5b31c0 --- /dev/null +++ b/config/crontab @@ -0,0 +1 @@ +@hourly /restic-backup.sh \ No newline at end of file diff --git a/fly.toml b/fly.toml new file mode 100644 index 0000000..a2f9865 --- /dev/null +++ b/fly.toml @@ -0,0 +1,11 @@ +kill_signal = "SIGINT" +kill_timeout = 5 + +[mounts] + source = "vw_data" + destination = "/data" + +[http_service] + internal_port = 80 + force_https = true + min_machines_running = 1 \ No newline at end of file diff --git a/scripts/restic-backup.sh b/scripts/restic-backup.sh new file mode 100755 index 0000000..744ce97 --- /dev/null +++ b/scripts/restic-backup.sh @@ -0,0 +1,54 @@ +#!/bin/sh + +EMAIL_SUBJECT_PREFIX="[Restic] " +LOG="/var/log/restic/$(date +\%Y\%m\%d_\%H\%M\%S).log" + +notify() { + cat "${LOG}" | mail -s "${EMAIL_SUBJECT_PREFIX}${1}" ${SMTP_TO} +} + +# prepare_restic +mkdir -p /var/log/restic/ + +if restic cat config >/dev/null 2>&1; then + echo -e "skipping restic repo init\n------------\n" | tee -a "$LOG" +else + echo -e "initializing restic\n------------\n" | tee -a "$LOG" + restic init | tee -a "$LOG" 2>&1 +fi + +# prepare_backup +sqlite3 /data/db.sqlite3 ".backup '/data/backup.bak'" + +# backup +echo -e "restic backup\n------------\n" | tee -a "$LOG" +restic backup --verbose --exclude="db.*" /data | tee -a "$LOG" 2>&1 +if [ $? -ne 0 ] +then + notify "Failed Vaultwarden backup" + exit 2 +fi + +# check consistency of the repository +echo -e "\n------------\nrestic check\n-----------\n" | tee -a "$LOG" +restic check | tee -a "$LOG" 2>&1 +if [ $? -ne 0 ] +then + notify "Failed Vaultwarden check" + exit 3 +fi + +# remove outdated snapshots +echo -e "\n-------------\nrestic forget\n------------\n" | tee -a "$LOG" +restic forget \ + --keep-daily 7 \ + --keep-weekly 4 \ + --keep-monthly 3 \ + --keep-yearly 3 \ + --prune \ + | tee -a "$LOG" 2>&1 +if [ $? -ne 0 ] +then + notify "Failed Vaultwarden forget" + exit 4 +fi diff --git a/scripts/setup-msmtp.sh b/scripts/setup-msmtp.sh new file mode 100755 index 0000000..a635416 --- /dev/null +++ b/scripts/setup-msmtp.sh @@ -0,0 +1,17 @@ +#!/bin/sh + +cat << EOF > /etc/msmtprc +defaults +auth on +tls on +tls_trust_file /etc/ssl/certs/ca-certificates.crt +logfile /var/log/msmtp.log +account $SMTP_ACCOUNT +host $SMTP_HOST +port $SMTP_PORT +auth on +user $SMTP_USERNAME +password $SMTP_PASSWORD +from $SMTP_FROM +account default : $SMTP_ACCOUNT +EOF \ No newline at end of file