Post

Self host password manager with Vaultwarden and Traefik

Vaultwarden is light weight feature rich drop in replacement for Bitwarden server. It’s essentially debloated version of the Bitwarden.

To use Vaultwarden, SSL is required. Otherwise, singing in to the server is impossible. That’s where traefik comes in. Here I’m assuming you have a domain, cloudflare account & domain added to your account, and docker is already set up and ready to go.

Cloudflare

Please keep cloudflare’s Email, API KEY or API TOKEN ready.

Traefik setup

Create required files

directory structure

1
2
3
4
5
touch docker-compose.yml
touch config.yml
mkdir data && cd data
touch acme.json
touch traefik.yml

Directory structure should be like this

1
2
3
4
5
|── docker-compose.yml
├── config.yml
└── data
    ├── acme.json
    └── traefik.yml

Docker network

Create a network with following

1
docker network create -d bridge proxy

Docker compose

First open docker-compose.yml and add following

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
version: '3'

services:
  traefik:
    image: traefik:latest
    container_name: traefik
    restart: unless-stopped
    security_opt:
      - no-new-privileges:true
    networks:
      - proxy
    ports:
      - 80:80
      - 443:443
    dns:
      - 1.1.1.1
      - 8.8.8.8
    environment:
      - [email protected]
    # - CF_API_KEY= # use either api key or api token based on you usecase
    #   - CF_API_TOKEN=
    volumes:
      - /etc/localtime:/etc/localtime:ro
      - /var/run/docker.sock:/var/run/docker.sock:ro
      - /home/user/traefik/data/traefik.yml:/traefik.yml:ro
      - /home/user/traefik/data/acme.json:/acme.json
      - /home/user/traefik/config.yml:/config.yml:ro
    labels:
      - "traefik.enable=true"
      # http entrypoint
      - "traefik.http.routers.traefik.entrypoints=http"
      # Dashboard
      - "traefik.http.routers.traefik.rule=Host(`traefik.internal.example.net`)"
      # To create a user:password pair, the following command can be used:
      # echo $(htpasswd -nb user password) | sed -e s/\\$/\\$\\$/g
      - "traefik.http.middlewares.traefik-auth.basicauth.users=<user & password>"
      # redirect middleware
      - "traefik.http.middlewares.traefik-https-redirect.redirectscheme.scheme=https"
      - "traefik.http.middlewares.sslheader.headers.customrequestheaders.X-Forwarded-Proto=https"
      - "traefik.http.routers.traefik.middlewares=traefik-https-redirect"
      # https entrypoint
      - "traefik.http.routers.traefik-secure.entrypoints=https"
      - "traefik.http.routers.traefik-secure.rule=Host(`traefik.internal.example.net`)"
      - "traefik.http.routers.traefik-secure.middlewares=traefik-auth"
      - "traefik.http.routers.traefik-secure.tls=true"
      - "traefik.http.routers.traefik-secure.tls.certresolver=cloudflare"
      - "traefik.http.routers.traefik-secure.tls.domains[0].main=internal.example.net"
      - "traefik.http.routers.traefik-secure.tls.domains[0].sans=*.internal.example.net"
      - "traefik.http.routers.traefik-secure.service=api@internal"

networks:
  proxy:
    external: true

dns records should already pointed to you docker host. e.g. if docker host ip is 10.20.20.5 A record for traeif.internal should point to 10.20.20.5.

traefik config

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
api:
  dashboard: true
  debug: true
entryPoints:
  http:
    address: ":80"
    http:
      redirections:
        entryPoint:
          to: https
          scheme: https
  https:
    address: ":443"
serversTransport:
  insecureSkipVerify: true
providers:
  docker:
    endpoint: "unix:///var/run/docker.sock"
    exposedByDefault: false
  file:
    filename: /config.yml
certificatesResolvers:
  cloudflare:
    acme:
      email: [email protected]
      storage: acme.json
      dnsChallenge:
        provider: cloudflare
        disablePropagationCheck: true
        resolvers:
          - "1.1.1.1:53"
          - "1.0.0.1:53"

To spin up the traefik docker container, run

1
docker-compose up -d

Once docker container created, traefik will generate ssl certs for internel.example.net & wildcard cert for *.internel.example.net. traefik dashboard will be available at traefik.internal.example.net.

Vaultwarden

Volume

I’m using named volumes here for the sake. You can use any directory on the host and bind that.

1
docker volume create vaultwarden

Reason for creating volume outside the compose file, in case, container destroyed with rm, data would be still available from the volume.

Docker-compose

Create a new directory in your home vaultwarden and add new file docker-compose.yml.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
version: '3'

services:
  vaultwarden:
    image: vaultwarden/server:latest
    container_name: vaultwarden
    restart: unless-stopped
    security_opt:
      - no-new-privileges:true
    networks:
      - proxy
    ports:
      - 8100:80
    volumes:
      - /etc/localtime:/etc/localtime:ro
      - vaultwarden:/data
    environment:
      - DOMAIN=https://vaultwarden.internal.example.net
      - SMTP_HOST=smtp.example.com
      - [email protected]
      - SMTP_FROM_NAME=Vaultwarden
      - SMTP_SECURITY=SECURITYMETHOD
      - SMTP_PORT=XXXX
      - [email protected]
      - SMTP_PASSWORD=YourReallyStrongPasswordHere
      - SMTP_AUTH_MECHANISM="Mechanism"
    labels:
      - traefik.enable=true
      - traefik.docker.network=proxy
      - traefik.http.middlewares.redirect-https.redirectScheme.scheme=https
      - traefik.http.middlewares.redirect-https.redirectScheme.permanent=true
      - traefik.http.routers.vaultwarden-https.rule=Host(`vaultwarden.internal.example.net`)
      - traefik.http.routers.vaultwarden-https.entrypoints=https
      - traefik.http.routers.vaultwarden-https.tls=true
      - traefik.http.routers.vaultwarden-https.service=vaultwarden
      - traefik.http.routers.vaultwarden-http.rule=Host(`vaultwarden.internal.example.net`)
      - traefik.http.routers.vaultwarden-http.entrypoints=http
      - traefik.http.routers.vaultwarden-http.middlewares=redirect-https
      - traefik.http.routers.vaultwarden-http.service=vaultwarden
      - traefik.http.services.vaultwarden.loadbalancer.server.port=80
      - traefik.http.routers.vaultwarden-websocket-https.rule=Host(`vaultwarden.internal.example.net`) && Path(`/notifications/hub`)
      - traefik.http.routers.vaultwarden-websocket-https.entrypoints=https
      - traefik.http.routers.vaultwarden-websocket-https.tls=true
      - traefik.http.routers.vaultwarden-websocket-https.service=vaultwarden-websocket
      - traefik.http.routers.vaultwarden-websocket-http.rule=Host(`vaultwarden.internal.example.net`) && Path(`/notifications/hub`)
      - traefik.http.routers.vaultwarden-websocket-http.entrypoints=http
      - traefik.http.routers.vaultwarden-websocket-http.middlewares=redirect-https
      - traefik.http.routers.vaultwarden-websocket-http.service=vaultwarden-websocket
      - traefik.http.services.vaultwarden-websocket.loadbalancer.server.port=3012

networks:
  proxy:
    external: true

volumes:
  vaultwarden:
    external: true

dns records should already pointed to you docker host. e.g. if docker host ip is 10.20.20.5 A record for vaultwarden.internal should point to 10.20.20.5.

To spin up the vaultwarden docker container, run

1
docker-compose up -d

Conclusion

For more details and documentation, visit Official github repo. Any queries, feel free to drop a comment. Au Revoir.

This post is licensed under MIT by the author.