feat: mail server

This commit is contained in:
DACHXY 2025-05-05 13:37:38 +08:00
parent 396a4d603d
commit 1e77f204d0
13 changed files with 320 additions and 57 deletions

View file

@ -31,3 +31,22 @@ vim.g.mkdp_combine_preview = 1
vim.g.mkdp_echo_preview_url = 1 vim.g.mkdp_echo_preview_url = 1
vim.g.mkdp_open_to_the_world = 1 vim.g.mkdp_open_to_the_world = 1
vim.g.mkdp_port = "20088" vim.g.mkdp_port = "20088"
-- Use osc52 as clipboard provider
local function paste()
return { vim.fn.split(vim.fn.getreg(""), "\n"), vim.fn.getregtype("") }
end
vim.g.clipboard = {
name = "OSC 52",
copy = {
["+"] = require("vim.ui.clipboard.osc52").copy("+"),
["*"] = require("vim.ui.clipboard.osc52").copy("*"),
},
paste = {
["+"] = paste,
["*"] = paste,
},
}
-- To ALWAYS use the clipboard for ALL operations
-- (instead of interacting with the "+" and/or "*" registers explicitly):
vim.opt.clipboard = "unnamedplus"

View file

@ -33,5 +33,7 @@
GTK_CSD = "0"; GTK_CSD = "0";
GTK_USE_PORTAL = "1"; GTK_USE_PORTAL = "1";
GTK_IM_MODULE = ""; GTK_IM_MODULE = "";
MAIL = "~/Mailbox";
}; };
} }

View file

@ -31,9 +31,18 @@ let
tmux switch-client -t $selected_name tmux switch-client -t $selected_name
''; '';
ryank = pkgs.writeShellScriptBin "ryank" ''
# copy via OSC 52
buf=$( cat "$@" )
len=$( printf %s "$buf" | wc -c ) max=74994
test $len -gt $max && echo "$0: input is $(( len - max )) bytes too long" >&2
printf "\033]52;c;$( printf %s "$buf" | head -c $max | base64 | tr -d '\r\n' )\a"
'';
in in
{ {
home.packages = [ home.packages = [
tmuxSessionizer tmuxSessionizer
ryank
]; ];
} }

View file

@ -29,6 +29,10 @@
dataBackupPath = "/mnt/backup_dn"; dataBackupPath = "/mnt/backup_dn";
dbBackupPath = "/mnt/backup_dn"; dbBackupPath = "/mnt/backup_dn";
}) })
(import ../../modules/vaultwarden.nix {
domain = "https://bitwarden.net.dn";
})
(import ../../modules/openldap.nix { })
]; ];
environment.systemPackages = with pkgs; [ environment.systemPackages = with pkgs; [

View file

@ -1,5 +1,6 @@
{ {
config, config,
pkgs,
settings, settings,
... ...
}: }:
@ -16,7 +17,10 @@ let
# fqdn = "dn-server.daccc.info"; # fqdn = "dn-server.daccc.info";
in in
{ {
networking.firewall.allowedTCPPorts = [ 25 ]; networking.firewall.allowedTCPPorts = [
25
587
];
services.postfix = { services.postfix = {
enable = true; enable = true;
@ -29,9 +33,17 @@ in
fqdn fqdn
]; ];
config = {
home_mailbox = "Mailbox";
};
postmasterAlias = "root"; postmasterAlias = "root";
rootAlias = settings.personal.username; rootAlias = settings.personal.username;
config = {
alias_maps = [ "ldap:${config.sops.secrets."postfix/openldap".path}" ];
};
extraAliases = '' extraAliases = ''
mailer-daemon: postmaster mailer-daemon: postmaster
nobody: root nobody: root
@ -44,6 +56,7 @@ in
abuse: root abuse: root
noc: root noc: root
security: root security: root
vaultwarden: root
''; '';
}; };
} }

View file

@ -1,16 +1,76 @@
{ {
config, config,
lib,
pkgs, pkgs,
... ...
}: }:
let let
acmeWebRoot = "/var/www/${config.services.nextcloud.hostName}/html/"; mkProxyHost = (
{
domain,
proxyPass,
ssl ? false,
}:
(
if ssl then
{
forceSSL = true;
sslCertificate = "/etc/letsencrypt/live/${domain}/fullchain.pem";
sslCertificateKey = "/etc/letsencrypt/live/${domain}/privkey.pem";
certScript = pkgs.writeShellScriptBin "certbot-nextcloud" '' listen = [
{
addr = "0.0.0.0";
port = 443;
ssl = true;
}
];
}
else
{
listen = [
{
addr = "0.0.0.0";
port = 80;
}
];
}
)
// {
locations."/" = {
proxyPass = proxyPass;
extraConfig = ''
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
'';
};
locations."^~ /.well-known/acme-challenge/" = {
root = "/var/www/${domain}/html";
extraConfig = ''
default_type "text/plain";
'';
};
extraConfig = ''
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers 'TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384';
ssl_prefer_server_ciphers on;
'';
}
);
certScript = pkgs.writeShellScriptBin "genCert" ''
acmeWebRoot="/var/www/$1/html/";
if [ ! -d "$acmeWebRoot" ]; then
mkdir -p "$acmeWebRoot"
fi
REQUESTS_CA_BUNDLE=${../../../system/extra/ca.crt} \
${pkgs.certbot}/bin/certbot certonly --webroot \ ${pkgs.certbot}/bin/certbot certonly --webroot \
--webroot-path ${acmeWebRoot} -v \ --webroot-path $acmeWebRoot -v \
-d ${config.services.nextcloud.hostName} \ -d "$1" \
--server https://ca.net.dn:8443/acme/acme/directory \ --server https://ca.net.dn:8443/acme/acme/directory \
-m admin@mail.net.dn -m admin@mail.net.dn
@ -21,17 +81,24 @@ let
hostname = "pre-nextcloud.net.dn"; hostname = "pre-nextcloud.net.dn";
ip = "10.0.0.130"; ip = "10.0.0.130";
}; };
vaultwarden = {
domain = "bitwarden.net.dn";
};
in in
{ {
environment.systemPackages = [
certScript
];
services.nginx = { services.nginx = {
enable = true; enable = true;
enableReload = true; enableReload = true;
virtualHosts = { virtualHosts = {
# Nextcloud - Server # Nextcloud - Server
${config.services.nextcloud.hostName} = { ${config.services.nextcloud.hostName} = {
listen = lib.mkForce [ listen = [
{ {
addr = "0.0.0.0"; addr = "0.0.0.0";
port = 443; port = 443;
@ -61,46 +128,16 @@ in
''; '';
}; };
${pre7780.hostname} = { ${pre7780.hostname} = mkProxyHost {
listen = [ domain = pre7780.hostname;
{ proxyPass = "http://${pre7780.ip}";
addr = "0.0.0.0"; ssl = true;
port = 443; };
ssl = true;
}
{
addr = "0.0.0.0";
port = 80;
}
];
locations."/" = {
proxyPass = "http://${pre7780.ip}/";
extraConfig = ''
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
'';
};
locations."^~ /.well-known/acme-challenge/" = {
root = "/var/www/${pre7780.hostname}/html";
extraConfig = ''
default_type "text/plain";
'';
};
forceSSL = true;
sslCertificate = "/etc/letsencrypt/live/${pre7780.hostname}/fullchain.pem";
sslCertificateKey = "/etc/letsencrypt/live/${pre7780.hostname}/privkey.pem";
extraConfig = ''
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers 'TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384';
ssl_prefer_server_ciphers on;
'';
${vaultwarden.domain} = mkProxyHost {
domain = vaultwarden.domain;
proxyPass = "http://127.0.0.1:${builtins.toString config.services.vaultwarden.config.ROCKET_PORT}";
ssl = true;
}; };
}; };
}; };

View file

@ -4,6 +4,13 @@ nextcloud:
adminPassword: ENC[AES256_GCM,data:O2rK18+riVrvloqqLsMUXw==,iv:OosiF0g4l1mrgndbwUOvO2YUqxWVk1hvAZY0rHU9GPE=,tag:yh1ccDmthARLND0NwpLTCA==,type:str] adminPassword: ENC[AES256_GCM,data:O2rK18+riVrvloqqLsMUXw==,iv:OosiF0g4l1mrgndbwUOvO2YUqxWVk1hvAZY0rHU9GPE=,tag:yh1ccDmthARLND0NwpLTCA==,type:str]
step_ca: step_ca:
password: ENC[AES256_GCM,data:3EWxpk/ktZHJreqnR9ln5pfdPjgigoCC4lyoRWugHas=,iv:q9cWW8xTxYQnRYohBxnPIsbVSpvkZYVpYLRVeZgmsRM=,tag:UHZagnLvorZUrPq43YU+Gw==,type:str] password: ENC[AES256_GCM,data:3EWxpk/ktZHJreqnR9ln5pfdPjgigoCC4lyoRWugHas=,iv:q9cWW8xTxYQnRYohBxnPIsbVSpvkZYVpYLRVeZgmsRM=,tag:UHZagnLvorZUrPq43YU+Gw==,type:str]
vaultwarden: ENC[AES256_GCM,data:PSKtHBIxw0/z/rmtF83Yg3btHksbVVyWZ80nP0wl4zAHRpFXypvpchZu9/edX7RgREd+9okm21WyjNWRUDoGVTOJYOCFHZCvOUx4KzIL2c/i7jUjXwtvAEmikhL1qlunVrCPhDu0knQ5nvsqpgWyxgcZl52yxuskMSIRAOsMpCRePVwJerWW5tuQ5zteYeOR0GHR8Q0iwBm98YGlCbKvz/37jAjMQVxY5W9DE1Tu1XVyEPBeAVvEwZknFNIZg1ukB+kW9Z/sBwLEVbAGsiBSGjonP6KEsgKmtaIkbBPzpfA3CQ==,iv:X8x3ooFDkFIT2OuHICcP2J1zX8T6xZW8j71ZuaByx6Q=,tag:mfnDFf9riivZ3EBup1l6lw==,type:str]
openldap:
adminPassword: ENC[AES256_GCM,data:dSaynM6RBrhZLOwcN2djaA==,iv:t2xJuRO2irEFgcnNcZS25qCfXiZXHaoqcCZYcR041aY=,tag:K5DiJRp+AumtKafAOR49/w==,type:str]
postfix:
openldap: ENC[AES256_GCM,data:8woTLrSJ5qqZU7jizOIK9VGlaPaBuyhq6FOs6LwiE9WHYJzWCAw3D+449SmCVeEE2t+EZWmfRPaOQBceSeIfUY6WZ5vso1E29CWPq8Tk7AuHT2i/K82EhpapXst61IAgSa/y39MchA7LqwaiTzL3A2CJVM1k5Ay5iHUUDfXvLbUsVmn1NlNfOv2QPPd5g+2yR2oGGx5HTbTPQNfoiU77KtvtFmlrubAs413I3DGdhM4uiOS+FI9WgZ4Ia22BucaOLHp2odfWnEMbP+ZIyJFdu3CBcs1lbTnLLVI=,iv:RvPm2+WsTIPFWLlYzv/OyKKDy/fWhtEfut98mBoM/1A=,tag:wkkWK88D0jKfaudN+KpN0Q==,type:str]
dovecot:
openldap: ENC[AES256_GCM,data:G7jdoSqL2SYDv2alh7q65BaA8Ap898azUPf2KKWd5wbr9pRVsRhFxQxHdZDuTOHDhWcfaa+eqMgc5k9gGLBYIO9EWVyEZ01/QfG4GIHSDjubzZxCElwhJrtsFn1A+Ihv7T1IIGKBCdmQGhUwfBMtwYlIuj8PYZaty4+c/dxIOCfDr5HyM1C6qQ4RCJTDEh6B+Hpx8NlFO0+fRFC9+9tQYX0rjI7JZRSfbg7F23nEdkBATr/xlwQXj8dvXYMLZhUKaswFnRs5TrG97AVQ9t3rMguRHutCAqEROhml2lJvV3Vxb/yMmTrom8qSrbkuw00YfdlDCmUo5/E4Vu9DYL0kv0EnASyQ4vQbmVXz0clYEzEXBLWZIEu4QHGJ7jQWgsKFv+WSTvuunVQyNuij3SFWZLR/zdfJELxU,iv:bsGMMdDo1Mj4GxRbWuRmbH/WrLt25jK3we8JDYQRsLw=,tag:EugvDijjQnYcms70nZq5FQ==,type:str]
sops: sops:
kms: [] kms: []
gcp_kms: [] gcp_kms: []
@ -19,8 +26,8 @@ sops:
Qm0wbmNGZDZwZlNTOVl0WVh5RXNxK2cK1Fwbgl5kKAFyrIIhBP+X4ZKFS4Xl39QY Qm0wbmNGZDZwZlNTOVl0WVh5RXNxK2cK1Fwbgl5kKAFyrIIhBP+X4ZKFS4Xl39QY
11qkglNgro/JBFJ/W7Hj5wtEd8QToiJM1RW0lQaI25sneQ2v6L5pDA== 11qkglNgro/JBFJ/W7Hj5wtEd8QToiJM1RW0lQaI25sneQ2v6L5pDA==
-----END AGE ENCRYPTED FILE----- -----END AGE ENCRYPTED FILE-----
lastmodified: "2025-04-22T06:01:27Z" lastmodified: "2025-05-04T13:11:13Z"
mac: ENC[AES256_GCM,data:HQHGQRuRR93Lwny8Bbv3Po2xSNvKgrLJhXC7txwwwO0Qh4Q3HjngpJqTQZg8KXe6PyNj8iPSnqW3Bt1jjOv9vjPPQVhRiUFy5pMBd/aK7mkBhTbvN4CMj5y1egU6TBMceRvlU5rhtPSsiIzq1egA/YOsfyeAXkLBghDRZmabq8s=,iv:IAvO0Kewx9D7NxWQ5K9heQtdksK7xfvl0eQCU1n0bmU=,tag:KPNbBeqXTeei+CXyL1NaOw==,type:str] mac: ENC[AES256_GCM,data:+V5vP4XbeXQP49gyisV4uQJjUybtK792DaFEWBHzLlKn2HiRj+qqSVR5XQrQMQQ5mKMhzsZXGq7QjjXtzKqgLCz5snItU63HzxQ6OxarNeg5pctk7i8ueNST4JpMxZODKGJncz2Ysq8OGrjZ6Nf4QVjO0XhFxZP6MbZxZL7wbuY=,iv:7jKt3uAY/ks8m/uzpos6XvldkpQjkgCHcLn+oRiY3mk=,tag:d6V+waMu4m2wi/H/J3bMXg==,type:str]
pgp: [] pgp: []
unencrypted_suffix: _unencrypted unencrypted_suffix: _unencrypted
version: 3.9.4 version: 3.9.4

View file

@ -382,10 +382,11 @@ in
IN NS dns.${origin} IN NS dns.${origin}
@ IN A ${serverIP} @ IN A ${serverIP}
IN AAAA fe80::3319:e2bb:fc15:c9df IN AAAA fe80::3319:e2bb:fc15:c9df
IN MX 10 mail.${origin} @ IN MX 10 mail.${origin}
IN TXT "v=spf1 mx" IN TXT "v=spf1 mx"
dns IN A ${serverIP} dns IN A ${serverIP}
nextcloud IN A ${serverIP} nextcloud IN A ${serverIP}
bitwarden IN A ${serverIP}
pre-nextcloud IN A ${serverIP} pre-nextcloud IN A ${serverIP}
ca IN A ${serverIP} ca IN A ${serverIP}
${hostname} IN A ${serverIP} ${hostname} IN A ${serverIP}
@ -406,7 +407,6 @@ in
file = file =
let let
serverIP = getSubAddress personal.ip; serverIP = getSubAddress personal.ip;
mailIP = getSubAddress personal.ip;
hostname = config.networking.hostName; hostname = config.networking.hostName;
in in
pkgs.writeText "${getReverseFilename personal.ip}" '' pkgs.writeText "${getReverseFilename personal.ip}" ''
@ -420,11 +420,12 @@ in
IN NS dns.${personal.domain}. IN NS dns.${personal.domain}.
${serverIP} IN PTR dns.${personal.domain}. ${serverIP} IN PTR dns.${personal.domain}.
${serverIP} IN PTR mail.${personal.domain}.
${serverIP} IN PTR ${hostname}.${personal.domain}. ${serverIP} IN PTR ${hostname}.${personal.domain}.
${serverIP} IN PTR nextcloud.${personal.domain}. ${serverIP} IN PTR nextcloud.${personal.domain}.
${serverIP} IN PTR bitwarden.${personal.domain}.
${serverIP} IN PTR pre-nextcloud.${personal.domain}. ${serverIP} IN PTR pre-nextcloud.${personal.domain}.
${serverIP} IN PTR ca.${personal.domain}. ${serverIP} IN PTR ca.${personal.domain}.
${mailIP} IN PTR mail.${personal.domain}.
${dnsReversedRecords} ${dnsReversedRecords}
''; '';

View file

@ -1,9 +1,16 @@
{ config, ... }:
{ {
sops = { sops = {
secrets = { secrets = {
"wireguard/privateKey" = { }; "wireguard/privateKey" = { };
"nextcloud/adminPassword" = { }; "nextcloud/adminPassword" = { };
"step_ca/password" = { }; "step_ca/password" = { };
vaultwarden = { };
"postfix/openldap" = { };
"openldap/adminPassword" = {
owner = config.users.users.openldap.name;
group = config.users.users.openldap.group;
};
}; };
}; };
} }

View file

@ -25,13 +25,14 @@
ExecStart = ''${pkgs.certbot}/bin/certbot renew''; ExecStart = ''${pkgs.certbot}/bin/certbot renew'';
ExecStartPost = "${pkgs.busybox}/bin/chown nginx:nginx -R /etc/letsencrypt"; ExecStartPost = "${pkgs.busybox}/bin/chown nginx:nginx -R /etc/letsencrypt";
}; };
unitConfig = {
OnSuccess = "nginx-reload-after-certbot.service";
};
}; };
systemd.services."nginx-reload-after-certbot" = { systemd.services."nginx-reload-after-certbot" = {
after = [ "certbot-renew.service" ];
requires = [ "certbot-renew.service" ];
wantedBy = [ "certbot-renew.service" ];
serviceConfig = { serviceConfig = {
Type = "oneshot";
User = "nginx"; User = "nginx";
# This config file path refers to "services.nginx.enableReload" # This config file path refers to "services.nginx.enableReload"
ExecStart = ''${pkgs.nginx}/bin/nginx -s reload -c /etc/nginx/nginx.conf''; ExecStart = ''${pkgs.nginx}/bin/nginx -s reload -c /etc/nginx/nginx.conf'';

129
system/modules/openldap.nix Normal file
View file

@ -0,0 +1,129 @@
{
urlList ? [ "ldap:///" ],
}:
{
pkgs,
config,
...
}:
let
create_ldap_user = pkgs.writeShellScriptBin "create_ldap_user" ''
# Base DN for LDAP directory
BASE_DN="dc=net,dc=dn"
# Organizational Unit (OU) where users are stored
OU="people"
# Prompt for username
read -p "Please enter the username: " USERNAME
# Prompt for password (hidden input)
read -s -p "Please enter the password: " USER_PASSWORD
echo
# Prompt for password confirmation (hidden input)
read -s -p "Please confirm the password: " USER_PASSWORD_CONFIRM
echo
# Check if the entered passwords match
if [ "$USER_PASSWORD" != "$USER_PASSWORD_CONFIRM" ]; then
echo " Passwords do not match. Please run the script again."
exit 1
fi
# Hash the password using slappasswd
PASSWORD_HASH=$(slappasswd -s "$USER_PASSWORD")
# Construct the Distinguished Name (DN) for the user
USER_DN="uid=$USERNAME,ou=$OU,$BASE_DN"
# Check if the base DN (dc=net,dc=dn) exists, if not, create it
ldapsearch -x -b "$BASE_DN" > /dev/null 2>&1
if [ $? -ne 0 ]; then
echo " $BASE_DN does not exist. Creating it now..."
cat <<EOF | ldapadd -x -D "cn=admin,$BASE_DN" -W
dn: $BASE_DN
objectClass: top
objectClass: domain
dc: net
EOF
fi
# Check if the OU exists, if not, create it
ldapsearch -x -b "ou=$OU,$BASE_DN" > /dev/null 2>&1
if [ $? -ne 0 ]; then
echo " OU=$OU does not exist. Creating it now..."
cat <<EOF | ldapadd -x -D "cn=admin,$BASE_DN" -W
dn: ou=$OU,$BASE_DN
objectClass: organizationalUnit
ou: $OU
EOF
fi
# Add the user entry to the LDAP directory
cat <<EOF | ldapadd -x -D "cn=admin,$BASE_DN" -W
dn: $USER_DN
objectClass: inetOrgPerson
objectClass: organizationalPerson
objectClass: person
objectClass: top
uid: $USERNAME
cn: $USERNAME
sn: $USERNAME
userPassword: $PASSWORD_HASH
EOF
# Confirm the user was successfully created
echo " User $USERNAME has been successfully created." '';
in
{
environment.systemPackages = [
create_ldap_user
];
services.openldap = {
enable = true;
urlList = urlList;
settings = {
attrs = {
olcLogLevel = "conns config";
};
children = {
"cn=schema".includes = [
"${pkgs.openldap}/etc/schema/core.ldif"
"${pkgs.openldap}/etc/schema/cosine.ldif"
"${pkgs.openldap}/etc/schema/inetorgperson.ldif"
];
"olcDatabase={1}mdb".attrs = {
objectClass = [
"olcDatabaseConfig"
"olcMdbConfig"
];
olcDatabase = "{1}mdb";
olcDbDirectory = "/var/lib/openldap/data";
olcSuffix = "dc=net,dc=dn";
olcRootDN = "cn=admin,dc=net,dc=dn";
olcRootPW.path = config.sops.secrets."openldap/adminPassword".path;
olcAccess = [
# custom access rules for userPassword attributes
''
{0}to attrs=userPassword
by self write
by anonymous auth
by * none''
# allow read on anything else
''
{1}to *
by * read''
];
};
};
};
};
}

View file

@ -36,7 +36,10 @@ in
]; ];
extraConfig = '' extraConfig = ''
set -gq allow-passthrough on set -g allow-passthrough on
set -s set-clipboard on
set-option -s set-clipboard on
set -g status "on" set -g status "on"
set -g status-style fg=default,bg=default set -g status-style fg=default,bg=default
set -g status-position top set -g status-position top
@ -48,7 +51,6 @@ in
setw -g window-status-format "#[fg=#171616,bg=default] #[fg=#495361,bg=default]#(${getIconScript}/get-icon #I)#W" setw -g window-status-format "#[fg=#171616,bg=default] #[fg=#495361,bg=default]#(${getIconScript}/get-icon #I)#W"
setw -g window-status-current-format "#[fg=#7e93a9,bg=default] #[fg=#7e93a9,bg=default,bold]#(${getIconScript}/get-icon #I) #W" setw -g window-status-current-format "#[fg=#7e93a9,bg=default] #[fg=#7e93a9,bg=default,bold]#(${getIconScript}/get-icon #I) #W"
set -g default-terminal "xterm-256color" set -g default-terminal "xterm-256color"
set -ga terminal-overrides ",*256col*:Tc" set -ga terminal-overrides ",*256col*:Tc"
set -ga terminal-overrides '*:Ss=\E[%p1%d q:Se=\E[ q' set -ga terminal-overrides '*:Ss=\E[%p1%d q:Se=\E[ q'

View file

@ -0,0 +1,32 @@
{ domain }:
{ config, ... }:
{
services.postgresql = {
enable = true;
ensureUsers = [
{
name = "vaultwarden";
ensureDBOwnership = true;
}
];
ensureDatabases = [
"vaultwarden"
];
};
services.vaultwarden = {
enable = true;
dbBackend = "postgresql";
environmentFile = config.sops.secrets.vaultwarden.path;
config = {
DOMAIN = domain;
SIGNUPS_ALLOWED = true;
SIGNUPS_VERIFY = true;
ROCKET_PORT = 8222;
ROCKET_ADDRESS = "127.0.0.1";
ROCKET_LOG = "critical";
DATABASE_URL = "postgresql:///vaultwarden";
};
};
}