update: i have since moved this setup to be run off a raspberry pi in my basement, still using tailscale so that i can connect to it while away from home
this is the topic that originally motivated me to setup this site: i really wanted to talk about it but whenever i did it would cause people visible discomfort with how boring it was.
the gist of this setup is putting vaultwarden, a web server for the bitwarden password manager on but not exposing this sucker to the public internet. keeping it off the public internet is a lot less risky when it comes to security.
that's where tailscale comes in: i sign into tailscale with my github account, and then i can magically visit this machine by putting it's name into my browser's url bar. pretty cool! no dns entries needed. no one else can do that since the machine doesn't have any ports exposed in it's fly configuration
this all seemed to be adequate until i learned that vaultwarden doesn't really work without https. and the web server they ship with doesn't do https that well, so after reading that caddy
is recommended in the vaultwarden wiki
will automatically get me the https certs i need from letsencrypt
sold! the way caddy fits in is that my computer and phone connect to the caddy server via wireguard by way of tailscale and their https support. caddy proxies the request to the vaultwarden server through fly.io's internal wireguard network.
for a variety of reasons this was a huge pain in the ass to get working so i wanted to save some poor schmuck like me a bit of time by publishing my dockerfiles. this should be obvious but please note that i don't know what i'm doing.
FROM vaultwarden/server:alpine
version: '3'
services:
vaultwarden:
image: localvault
container_name: vaultwardenlocal
restart: always
environment:
WEBSOCKET_ENABLED: "true" # Enable WebSocket notifications.
volumes:
- ./vw-data:/data
ports:
- "8082:80"
app = "vaultwarden-machine"
kill_signal = "SIGINT"
kill_timeout = 5
processes = []
[env]
DATA_FOLDER = "vw-data"
ROCKET_ADDRESS = "::"
WEBSOCKET_ENABLED = "true"
WEBSOCKET_ADDRESS = "::"
WEBSOCKET_PORT = 3012
[experimental]
allowed_public_ports = []
auto_rollback = true
[mounts]
source = "your_fly_volume"
destination = "/vw-data"
FROM caddy:2-alpine as builder
WORKDIR /app
COPY . ./
COPY ./Caddyfile /etc/caddy/Caddyfile
FROM alpine:latest as tailscale
WORKDIR /app
COPY . ./
ENV TSFILE=tailscale_1.28.0_amd64.tgz
RUN wget https://pkgs.tailscale.com/stable/${TSFILE} && tar xzf ${TSFILE} --strip-components=1
COPY . ./
# https://docs.docker.com/develop/develop-images/multistage-build/#use-multi-stage-builds
FROM caddy:2-alpine
RUN apk update && apk add ca-certificates iptables ip6tables && rm -rf /var/cache/apk/*
# Copy binary to production image
COPY --from=builder /app/start.sh /app/start.sh
COPY --from=builder /etc/caddy/Caddyfile /etc/caddy/Caddyfile
COPY --from=tailscale /app/tailscaled /app/tailscaled
COPY --from=tailscale /app/tailscale /app/tailscale
RUN mkdir -p /var/run/tailscale /var/cache/tailscale /var/lib/tailscale
#caddy is running on 80
EXPOSE 80
# Run on container startup.
CMD ["/app/start.sh"]
#!/bin/sh
echo "starting tailscaled"
/app/tailscaled --state=/var/lib/tailscale/tailscaled.state --socket=/var/run/tailscale/tailscaled.sock &
echo "tailscale up"
/app/tailscale up --authkey=${TAILSCALE_AUTHKEY} --hostname=schpet-caddyvault
echo "Executing: caddy start -config /etc/caddy/Caddyfile"
caddy run -config /etc/caddy/Caddyfile
# https://github.com/dani-garcia/vaultwarden/wiki/Proxy-examples
vaultwarden-machine.random-whatever.ts.net {
log {
level INFO
output stdout
}
encode gzip
reverse_proxy /notifications/hub/negotiate vaultwarden-machine.internal:80
reverse_proxy /notifications/hub vaultwarden-machine.internal:3012 {
header_up Host {host}
header_up X-Real-IP {remote_host}
header_up X-Forwarded-For {remote_host}
header_up X-Forwarded-Proto {scheme}
}
reverse_proxy vaultwarden-machine.internal:80 {
header_up X-Real-IP {remote_host}
}
}
app = "caddy-machine"
kill_signal = "SIGINT"
kill_timeout = 5
processes = []
[env]
[experimental]
allowed_public_ports = []
auto_rollback = true
[[services]]
http_checks = []
internal_port = 80
processes = ["app"]
protocol = "tcp"
script_checks = []
[services.concurrency]
hard_limit = 25
soft_limit = 20
type = "connections"
# uncomment the following if you want to expose this to the internet
# [[services.ports]]
# force_https = true
# handlers = ["http"]
# port = 80
# [[services.ports]]
# handlers = ["tls", "http"]
# port = 443
[[services.tcp_checks]]
grace_period = "1s"
interval = "15s"
restart_limit = 0
timeout = "2s"
notes
scale-to-zero would be particularly nice for stuff like this so i hope fly adds that like they say they will
i had a problem where every time i deployed caddy i'd need to make a new tailscale key, and haven't bothered figuring it out given i haven't deployed this since i initially got it working a few months ago