├── Dockerfile ├── .github └── workflows │ └── deploy.yaml ├── README.md └── manage_app.py /Dockerfile: -------------------------------------------------------------------------------- 1 | ARG IMAGE_VERSION=${IMAGE_VERSION:-1.25.2-alpine} 2 | FROM vaultwarden/server:${IMAGE_VERSION} 3 | -------------------------------------------------------------------------------- /.github/workflows/deploy.yaml: -------------------------------------------------------------------------------- 1 | name: VaultwardenOnRender_Deploy 2 | 3 | on: 4 | workflow_dispatch: 5 | 6 | env: 7 | ENABLE_DUO: "true" 8 | SMTP_SECURITY: "" 9 | 10 | jobs: 11 | update_and_deploy_vaultwarden: 12 | name: Create Vaultwarden App 13 | runs-on: ubuntu-latest 14 | steps: 15 | - name: Checkout 16 | uses: actions/checkout@v2 17 | - name: Run Vaultwarden-Render Manage Script 18 | run: | 19 | pip install httpx && python manage_app.py 20 | env: 21 | RENDER_API_KEY: ${{ secrets.RENDER_API_KEY }} 22 | APP_NAME: ${{ secrets.APP_NAME }} 23 | ENABLE_DUO: ${{ env.ENABLE_DUO }} 24 | DUO_IKEY: ${{ secrets.DUO_IKEY }} 25 | DUO_SKEY: ${{ secrets.DUO_SKEY }} 26 | DUO_HOST: ${{ secrets.DUO_HOST }} 27 | DATABASE_URL: ${{ secrets.DATABASE_URL }} 28 | SMTP_HOST: ${{ secrets.SMTP_HOST }} 29 | SMTP_FROM: ${{ secrets.SMTP_FROM }} 30 | SMTP_PORT: ${{ secrets.SMTP_PORT }} 31 | SMTP_SECURITY: ${{ env.SMTP_SECURITY }} 32 | SMTP_USERNAME: ${{ secrets.SMTP_USERNAME }} 33 | SMTP_PASSWORD: ${{ secrets.SMTP_PASSWORD }} 34 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # vaultwarden-render-deploy 2 | Vaultwarden on Render.com infrastructure 3 | 4 | **This is still a work in progress and may reflect in progress changes. PRs are welcome, the Python code is atrocious...** 5 | 6 | ## Notes 7 | 8 | Render's API does not allow us to create new service using the free tier via the API. So we need to create the service manually and then use the API to update the service. 9 | 10 | You will need to supply your own database for this purposes. I am using the free tier of filess.io. You can use any other database provider as long as it is supported by the upstream MySQL project. 11 | 12 | With exception, Planetscale is not supported by the upstream project due to its needs to connect via TLS, which Diesel does not support in Vaultwarden's current status. 13 | 14 | ## Create Process 15 | 16 | 1. Create a new account on Render.com 17 | 2. Create a new `Web Service` on Render.com using this fokred repository in your own account. You may use `Public Git Repository` and enter your own repo URL. 18 | 3. In the Create screen, select a name for your Vaultwarden instance, environment type set to `Docker`, region wherever is closest to you (and your database), branch as `main`, and lastly the `Free` plan. Click create and take note of your application name. 19 | 4. After clicking create, your Vaultwarden instance will be built from the Dockerfile in this repository. This will take a few minutes. 20 | 5. Add environment variables as needed from the below list, you may reference this via the [Vaultwarden Wiki](https://github.com/dani-garcia/vaultwarden/wiki): 21 | 22 | | Name | Description | Example | Required | 23 | | --- | --- | --- | --- | 24 | | `_ENABLE_DUO` | Enable Duo integration for pre-configured databases | `true` | No | 25 | | `DATABASE_URL` | The database URL of your MySQL or PostgresSQL database | `mysql://[[user]:[password]@]host[:port][/database]` | Yes | 26 | | `DUO_SKEY` | Duo Authentication SKEY, found in account | `abc123` | No | 27 | | `DUO_IKEY` | Duo Authentication SKey, found in account | `acb123` | No | 28 | | `DUO_HOST` | Duo Authentication Host, found in account | `api-23423tgf.duosecurity.com` | No | 29 | | `SMTP_HOST` | The hostname for mail sending | `mail.mailserver.ccom` | No | 30 | | `SMTP_FROM` | The email address listed in FROM field received by user | `vaultwarden@mailserver.com` | No | 31 | | `SMTP_PORT` | The port for SMTP sending | `587` | No | 32 | | `SMTP_SECURITY` | The security options to set for SMTP sending, following options: `starttls`, `force_tls` and `off` | `starttls` | No | 33 | | `SMTP_USERNAME` | The username of your SMTP login | `vaultwarden@mailserver.com` | No | 34 | | `SMTP_PASSWORD` | The password of your SMTP login | `mysupersecretpassword` | No | 35 | 36 | -------------------------------------------------------------------------------- /manage_app.py: -------------------------------------------------------------------------------- 1 | import httpx 2 | import os 3 | from secrets import token_bytes 4 | from base64 import b64encode 5 | import json 6 | 7 | def get_current_env_vars(serviceId): 8 | retreive_current_envs = httpx.get(url="https://api.render.com/v1/services/{service_id}/env-vars".format(service_id=serviceId), headers={"Authorization": f"Bearer {os.environ['RENDER_API_KEY']}"}) 9 | return retreive_current_envs.json() 10 | 11 | def get_service_id_and_slug(appName): 12 | retreive_service_id = httpx.get(url="https://api.render.com/v1/services", headers={"Authorization": f"Bearer {os.environ['RENDER_API_KEY']}"}) 13 | for service in retreive_service_id.json(): 14 | if service["service"]["name"] == appName: 15 | return {"service_id": service["service"]["id"], "slug": service["service"]["slug"]} 16 | 17 | def update_or_add_envars(serviceId, envVars): 18 | update_env_vars = httpx.put(url="https://api.render.com/v1/services/{service_id}/env-vars".format(service_id=serviceId), headers={"Authorization": f"Bearer {os.environ['RENDER_API_KEY']}"}, json=envVars) 19 | return update_env_vars.json() 20 | 21 | def build_env_vars(appName, serviceSlug): 22 | staticEnVars = [] 23 | boolsVars = ["I_REALLY_WANT_VOLATILE_STORAGE","ENABLE_DUO"] 24 | stringsVars = ["ADMIN_TOKEN", "DUO_SKEY", "DUO_IKEY", "DUO_HOST", "DATABASE_URL", "DOMAIN", "SMTP_HOST", "SMTP_FROM", "SMTP_PORT", "SMTP_SECURITY", "SMTP_USERNAME", "SMTP_PASSWORD"] 25 | 26 | for varToAddBools in boolsVars: 27 | if varToAddBools == "ENABLE_DUO" and os.environ[varToAddBools] != "": 28 | staticEnVars.append({"key": "_ENABLE_DUO", "value": os.environ[varToAddBools]}) 29 | elif varToAddBools == "I_REALLY_WANT_VOLATILE_STORAGE": 30 | staticEnVars.append({"key": varToAddBools, "value": "true"}) 31 | else: 32 | continue 33 | 34 | for varToAddBools in stringsVars: 35 | if varToAddBools == "ADMIN_TOKEN": 36 | staticEnVars.append({"key": varToAddBools, "value": b64encode(token_bytes(32)).decode()}) 37 | elif varToAddBools == "DUO_SKEY" and os.environ[varToAddBools] != "": 38 | staticEnVars.append({"key": varToAddBools, "value": os.environ[varToAddBools]}) 39 | elif varToAddBools == "DUO_IKEY" and os.environ[varToAddBools] != "": 40 | staticEnVars.append({"key": varToAddBools, "value": os.environ[varToAddBools]}) 41 | elif varToAddBools == "DUO_HOST" and os.environ[varToAddBools] != "": 42 | staticEnVars.append({"key": varToAddBools, "value": os.environ[varToAddBools]}) 43 | elif varToAddBools == "DATABASE_URL" and os.environ[varToAddBools] != "": 44 | staticEnVars.append({"key": varToAddBools, "value": os.environ[varToAddBools]}) 45 | elif varToAddBools == "DOMAIN": 46 | staticEnVars.append({"key": varToAddBools, "value": "https://{app_name}-{service_slug}.onrender.com".format(app_name=appName, service_slug=serviceSlug)}) 47 | elif varToAddBools == "SMTP_HOST" and os.environ[varToAddBools] != "": 48 | staticEnVars.append({"key": varToAddBools, "value": os.environ[varToAddBools]}) 49 | elif varToAddBools == "SMTP_FROM" and os.environ[varToAddBools] != "": 50 | staticEnVars.append({"key": varToAddBools, "value": os.environ[varToAddBools]}) 51 | elif varToAddBools == "SMTP_PORT" and os.environ[varToAddBools] != "": 52 | staticEnVars.append({"key": varToAddBools, "value": os.environ[varToAddBools]}) 53 | elif varToAddBools == "SMTP_SECURITY" and os.environ[varToAddBools] != "": 54 | staticEnVars.append({"key": varToAddBools, "value": os.environ[varToAddBools]}) 55 | elif varToAddBools == "SMTP_USERNAME" and os.environ[varToAddBools] != "": 56 | staticEnVars.append({"key": varToAddBools, "value": os.environ[varToAddBools]}) 57 | elif varToAddBools == "SMTP_PASSWORD" and os.environ[varToAddBools] != "": 58 | staticEnVars.append({"key": varToAddBools, "value": os.environ[varToAddBools]}) 59 | else: 60 | continue 61 | 62 | return staticEnVars 63 | 64 | def kickoff_build(serviceId): 65 | build = httpx.post(url="https://api.render.com/v1/services/{service_id}/deploys".format(service_id=serviceId), headers={"Authorization": f"Bearer {os.environ['RENDER_API_KEY']}"}, json={"clearCache": "clear"}) 66 | return build.json() 67 | 68 | appName = os.environ["APP_NAME"] 69 | serviceDetails = get_service_id_and_slug(appName) 70 | update_or_add_envars(serviceDetails['service_id'], build_env_vars(appName, serviceDetails['slug'])) 71 | kickoff_build(serviceDetails['service_id']) --------------------------------------------------------------------------------