dotfiles/modules/nixos/services/vaultwarden.nix
2024-04-20 09:42:06 -04:00

130 lines
3.8 KiB
Nix

# Vaultwarden is an implementation of the Bitwarden password manager backend
# service, which allows for self-hosting the synchronization of a Bitwarden
# password manager client.
{
config,
pkgs,
lib,
...
}:
let
vaultwardenPath = "/var/lib/bitwarden_rs"; # Default service directory
in
{
config = lib.mkIf config.services.vaultwarden.enable {
services.vaultwarden = {
config = {
DOMAIN = "https://${config.hostnames.secrets}";
SIGNUPS_ALLOWED = false;
SIGNUPS_VERIFY = true;
INVITATIONS_ALLOWED = true;
WEB_VAULT_ENABLED = true;
ROCKET_ADDRESS = "127.0.0.1";
ROCKET_PORT = 8222;
WEBSOCKET_ENABLED = true;
WEBSOCKET_ADDRESS = "0.0.0.0";
WEBSOCKET_PORT = 3012;
LOGIN_RATELIMIT_SECONDS = 60;
LOGIN_RATELIMIT_MAX_BURST = 10;
ADMIN_RATELIMIT_SECONDS = 300;
ADMIN_RATELIMIT_MAX_BURST = 3;
};
environmentFile = config.secrets.vaultwarden.dest;
dbBackend = "sqlite";
};
secrets.vaultwarden = {
source = ../../../private/vaultwarden.age;
dest = "${config.secretsDirectory}/vaultwarden";
owner = "vaultwarden";
group = "vaultwarden";
};
networking.firewall.allowedTCPPorts = [ 3012 ];
caddy.routes = [
{
match = [ { host = [ config.hostnames.secrets ]; } ];
handle = [
{
handler = "reverse_proxy";
upstreams = [
{ dial = "localhost:${builtins.toString config.services.vaultwarden.config.ROCKET_PORT}"; }
];
headers.request.add."X-Real-IP" = [ "{http.request.remote.host}" ];
}
];
}
];
# Configure Cloudflare DNS to point to this machine
services.cloudflare-dyndns.domains = [ config.hostnames.secrets ];
## Backup config
# Open to groups, allowing for backups
systemd.services.vaultwarden.serviceConfig.StateDirectoryMode = lib.mkForce "0770";
systemd.tmpfiles.rules = [
"f ${vaultwardenPath}/db.sqlite3 0660 vaultwarden vaultwarden"
"f ${vaultwardenPath}/db.sqlite3-shm 0660 vaultwarden vaultwarden"
"f ${vaultwardenPath}/db.sqlite3-wal 0660 vaultwarden vaultwarden"
];
# Allow litestream and vaultwarden to share a sqlite database
users.users.litestream.extraGroups = [ "vaultwarden" ];
users.users.vaultwarden.extraGroups = [ "litestream" ];
# Backup sqlite database with litestream
services.litestream = {
settings = {
dbs = [
{
path = "${vaultwardenPath}/db.sqlite3";
replicas = [
{ url = "s3://${config.backup.s3.bucket}.${config.backup.s3.endpoint}/vaultwarden"; }
];
}
];
};
};
# Don't start litestream unless vaultwarden is up
systemd.services.litestream = {
after = [ "vaultwarden.service" ];
requires = [ "vaultwarden.service" ];
};
# Run a separate file backup on a schedule
systemd.timers.vaultwarden-backup = {
timerConfig = {
OnCalendar = "*-*-* 06:00:00"; # Once per day
Unit = "vaultwarden-backup.service";
};
wantedBy = [ "timers.target" ];
};
# Backup other Vaultwarden data to object storage
systemd.services.vaultwarden-backup = {
description = "Backup Vaultwarden files";
environment.AWS_ACCESS_KEY_ID = config.backup.s3.accessKeyId;
serviceConfig = {
Type = "oneshot";
User = "vaultwarden";
Group = "backup";
EnvironmentFile = config.secrets.backup.dest;
};
script = ''
${pkgs.awscli2}/bin/aws s3 sync \
${vaultwardenPath}/ \
s3://${config.backup.s3.bucket}/vaultwarden/ \
--endpoint-url=https://${config.backup.s3.endpoint} \
--exclude "*db.sqlite3*" \
--exclude ".db.sqlite3*"
'';
};
};
}