test: trying to get microVMs to work

This commit is contained in:
Nick 2025-11-10 21:50:12 -06:00
parent 7c48cded1d
commit 8f74b8dbc8
10 changed files with 598 additions and 585 deletions

View file

@ -50,7 +50,7 @@ in
# comfyui # comfyui
# filesorter # filesorter
# firefly-iii # firefly-iii
# forgejo forgejo
# glance # glance
jellyfin jellyfin
# logrotate # logrotate

View file

@ -1,6 +1,17 @@
{ flake, ... }: { flake, ... }:
{ let
imports = [ importList =
let
content = builtins.readDir ./.;
dirContent = builtins.filter (n: content.${n} == "directory") (builtins.attrNames content);
in
map (name: ./. + "/${name}") dirContent;
microVMImport = [
flake.inputs.microvm.nixosModules.host flake.inputs.microvm.nixosModules.host
]; ];
in
{
imports = importList ++ microVMImport;
} }

View file

@ -14,7 +14,6 @@ let
host = serviceCfg.domains.url0; host = serviceCfg.domains.url0;
dns0 = instances.web.dns.provider0; dns0 = instances.web.dns.provider0;
dns0Path = "dns/${dns0}"; dns0Path = "dns/${dns0}";
hostSecrets = "/opt/secrets";
in in
{ {
microvm.vms.${serviceCfg.name} = { microvm.vms.${serviceCfg.name} = {

View file

@ -0,0 +1,219 @@
{
config,
flake,
...
}:
let
inherit (flake.config.people) user0;
inherit (flake.config.services) instances;
serviceCfg = flake.config.services.instances.forgejo;
smtpCfg = flake.config.services.instances.smtp;
hostCfg = flake.config.services.instances.web;
host = serviceCfg.domains.url0;
dns0 = instances.web.dns.provider0;
dns0Path = "dns/${dns0}";
in
{
users.users.caddy.extraGroups = [ "acme" ];
security.acme.certs."${host}" = {
dnsProvider = dns0;
environmentFile = config.sops.secrets.${dns0Path}.path;
group = "caddy";
};
microvm.vms = {
${serviceCfg.name} = {
autostart = true;
restartIfChanged = true;
config = {
system.stateVersion = "24.05";
time.timeZone = "America/Winnipeg";
users.users.root.openssh.authorizedKeys.keys = flake.config.people.users.${user0}.sshKeys;
services = {
${serviceCfg.name} = {
enable = true;
lfs.enable = true;
database = {
type = "postgres";
};
secrets = {
mailer.PASSWD = "/run/secrets/smtp";
database.PASSWD = "/run/secrets/database";
};
settings = {
server = {
DOMAIN = host;
ROOT_URL = "https://${host}/";
HTTP_PORT = serviceCfg.ports.port0;
};
# If you need to start from scratch, don't forget to turn this off again
service.DISABLE_REGISTRATION = false;
actions = {
ENABLED = true;
DEFAULT_ACTIONS_URL = "github";
};
mirror = {
ENABLED = true;
};
mailer = {
ENABLED = true;
SMTP_ADDR = smtpCfg.hostname;
FROM = smtpCfg.email.address1;
USER = smtpCfg.email.address1;
PROTOCOL = "${smtpCfg.name}+${smtpCfg.records.record1}";
SMTP_PORT = smtpCfg.ports.port1;
SEND_AS_PLAIN_TEXT = true;
USE_CLIENT_CERT = false;
};
};
};
openssh = {
enable = true;
settings = {
PasswordAuthentication = false;
PermitRootLogin = "prohibit-password";
};
};
postgresql = {
enable = true;
ensureDatabases = [ serviceCfg.name ];
ensureUsers = [
{
name = serviceCfg.name;
ensureDBOwnership = true;
}
];
};
};
networking.firewall.allowedTCPPorts = [
22 # SSH
25 # SMTP
139 # SMTP
587 # SMTP
2525 # SMTP
serviceCfg.ports.port0
];
fileSystems."/tmp" = {
device = "tmpfs";
fsType = "tmpfs";
options = [
"size=4G"
"mode=1777"
];
};
systemd = {
network = {
enable = true;
networks."20-lan" = {
matchConfig.Name = "enp0s5";
addresses = [ { Address = "${serviceCfg.interface.ip}/24"; } ];
routes = [
{
Destination = "${hostCfg.localhost.address1}/0";
Gateway = serviceCfg.interface.gate;
}
];
dns = [
"1.1.1.1"
"8.8.8.8"
];
};
};
tmpfiles.rules = [
"Z ${serviceCfg.varPaths.path0} 0755 ${serviceCfg.name} ${serviceCfg.name} -"
"Z /var/lib/postgresql 0755 postgres postgres -"
];
};
systemd.services.systemd-networkd.wantedBy = [ "multi-user.target" ];
microvm = {
vcpu = 2;
mem = 3072;
hypervisor = "qemu";
interfaces = [
{
type = "tap";
id = serviceCfg.interface.id;
mac = serviceCfg.interface.mac;
}
{
type = "user";
id = serviceCfg.interface.idUser;
mac = serviceCfg.interface.macUser;
}
];
forwardPorts = [
{
from = "host";
host.port = serviceCfg.interface.ssh;
guest.port = 22;
}
];
shares = [
{
mountPoint = "/nix/.ro-store";
proto = "virtiofs";
source = "/nix/store";
tag = "read_only_nix_store";
}
{
mountPoint = "/var/lib/${serviceCfg.name}";
proto = "virtiofs";
source = "${serviceCfg.mntPaths.path0}/data";
tag = "${serviceCfg.name}_data";
}
{
mountPoint = "/var/lib/postgresql";
proto = "virtiofs";
source = "${serviceCfg.mntPaths.path0}/database";
tag = "${serviceCfg.name}_database";
}
{
mountPoint = "/run/secrets";
proto = "virtiofs";
source = "/run/secrets/${serviceCfg.name}";
tag = "host_secrets";
}
];
};
};
};
};
systemd.tmpfiles.rules = [
"d ${serviceCfg.mntPaths.path0}/data 0751 microvm wheel - -"
"d ${serviceCfg.mntPaths.path0}/database 0751 microvm wheel - -"
];
services.caddy.virtualHosts."${host}" = {
extraConfig = ''
reverse_proxy ${serviceCfg.interface.ip}:${toString serviceCfg.ports.port0} {
header_up X-Real-IP {remote_host}
}
tls ${serviceCfg.ssl.cert} ${serviceCfg.ssl.key}
encode zstd gzip
'';
};
sops.secrets = {
"${serviceCfg.name}/smtp" = {
owner = "root";
mode = "0600";
};
"${serviceCfg.name}/database" = {
owner = "root";
mode = "0600";
};
};
}

View file

@ -0,0 +1,169 @@
{
config,
flake,
...
}:
let
inherit (flake.config.people) user0;
inherit (flake.config.services) instances;
serviceCfg = instances.jellyfin;
hostCfg = instances.web;
dns0 = instances.web.dns.provider0;
host = serviceCfg.domains.url0;
dns0Path = "dns/${dns0}";
in
{
microvm.vms = {
${serviceCfg.name} = {
autostart = true;
restartIfChanged = true;
config = {
system.stateVersion = "25.05";
time.timeZone = "America/Winnipeg";
users.users.root.openssh.authorizedKeys.keys = flake.config.people.users.${user0}.sshKeys;
services = {
jellyfin = {
enable = true;
openFirewall = true;
};
openssh = {
enable = true;
settings = {
PasswordAuthentication = false;
PermitRootLogin = "prohibit-password";
};
};
};
networking.firewall.allowedTCPPorts = [
22
serviceCfg.ports.port0
serviceCfg.ports.port1
serviceCfg.ports.port2
];
# fileSystems."/tmp" = {
# device = "tmpfs";
# fsType = "tmpfs";
# options = [
# "size=4G"
# "mode=1777"
# ];
# };
systemd = {
network = {
enable = true;
networks."20-lan" = {
matchConfig.Name = "enp0s6";
addresses = [ { Address = "${serviceCfg.interface.ip}/24"; } ];
routes = [
{
Destination = "${hostCfg.localhost.address1}/0";
Gateway = serviceCfg.interface.gate;
}
];
dns = [
"1.1.1.1"
"8.8.8.8"
];
};
};
tmpfiles.rules = [
"Z ${serviceCfg.varPaths.path0} 0755 ${serviceCfg.name} ${serviceCfg.name} -"
"Z ${serviceCfg.varPaths.path2} 0755 ${serviceCfg.name} ${serviceCfg.name} -"
"d ${serviceCfg.varPaths.path1} 0755 ${serviceCfg.name} ${serviceCfg.name} -"
"Z ${serviceCfg.varPaths.path2} 0755 ${serviceCfg.name} ${serviceCfg.name} -"
];
};
systemd.services.systemd-networkd.wantedBy = [ "multi-user.target" ];
microvm = {
vcpu = 6;
mem = 8192;
hypervisor = "qemu";
interfaces = [
{
type = "tap";
id = serviceCfg.interface.id;
mac = serviceCfg.interface.mac;
}
{
type = "user";
id = serviceCfg.interface.idUser;
mac = serviceCfg.interface.macUser;
}
];
forwardPorts = [
{
from = "host";
host.port = serviceCfg.interface.ssh;
guest.port = 22;
}
];
shares = [
{
mountPoint = "/nix/.ro-store";
proto = "virtiofs";
source = "/nix/store";
tag = "read_only_nix_store";
}
{
mountPoint = serviceCfg.varPaths.path0;
proto = "virtiofs";
source = "${serviceCfg.mntPaths.path0}/data";
tag = "${serviceCfg.name}_data";
}
{
mountPoint = serviceCfg.varPaths.path1;
proto = "virtiofs";
source = "${serviceCfg.mntPaths.path0}/cache";
tag = "${serviceCfg.name}_cache";
}
{
mountPoint = serviceCfg.varPaths.path2;
proto = "virtiofs";
source = "${serviceCfg.mntPaths.path0}/media";
tag = "${serviceCfg.name}_media";
}
];
};
};
};
};
security.acme.certs."${host}" = {
dnsProvider = dns0;
environmentFile = config.sops.secrets.${dns0Path}.path;
group = "caddy";
};
services = {
caddy = {
virtualHosts = {
"${host}" = {
extraConfig = ''
reverse_proxy ${serviceCfg.interface.ip}:${toString serviceCfg.ports.port0} {
header_up X-Real-IP {remote_host}
}
tls ${serviceCfg.ssl.cert} ${serviceCfg.ssl.key}
encode zstd gzip
'';
};
};
};
};
users.users.caddy.extraGroups = [ "acme" ];
systemd.tmpfiles.rules = [
"d ${serviceCfg.mntPaths.path0} 0751 microvm wheel - -"
"d ${serviceCfg.mntPaths.path0}/data 0751 microvm wheel - -"
"d ${serviceCfg.mntPaths.path0}/cache 0751 microvm wheel - -"
"d ${serviceCfg.mntPaths.path0}/media 0751 microvm wheel - -"
];
}

View file

@ -0,0 +1,193 @@
{
config,
flake,
...
}:
let
inherit (flake.config.people) user0;
inherit (flake.config.services) instances;
serviceCfg = instances.vaultwarden;
smtpCfg = instances.smtp;
hostCfg = instances.web;
dns0 = instances.web.dns.provider0;
host = serviceCfg.domains.url0;
dns0Path = "dns/${dns0}";
hostSecrets = "/var/lib/secrets/${serviceCfg.name}";
in
{
microvm.vms = {
vaultwarden = {
autostart = true;
restartIfChanged = true;
config = {
system.stateVersion = "24.05";
time.timeZone = "America/Winnipeg";
users.users.root.openssh.authorizedKeys.keys = flake.config.people.users.${user0}.sshKeys;
services = {
vaultwarden = {
enable = true;
dbBackend = "sqlite";
config = {
# Domain Configuration
DOMAIN = "https://${host}";
# Email Configuration
SMTP_AUTH_MECHANISM = "Plain";
SMTP_EMBED_IMAGES = true;
SMTP_FROM = serviceCfg.email.address0;
SMTP_FROM_NAME = serviceCfg.label;
SMTP_HOST = smtpCfg.hostname;
SMTP_PORT = smtpCfg.ports.port1;
SMTP_SECURITY = smtpCfg.records.record1;
SMTP_USERNAME = smtpCfg.email.address0;
# Security Configuration
DISABLE_ADMIN_TOKEN = false;
# Event and Backup Management
EVENTS_DAYS_RETAIN = 90;
# User Features
SENDS_ALLOWED = true;
SIGNUPS_VERIFY = true;
WEB_VAULT_ENABLED = true;
# Rocket (Web Server) Settings
ROCKET_ADDRESS = "0.0.0.0";
ROCKET_PORT = serviceCfg.ports.port0;
};
# Environment file with secrets (mounted from host)
environmentFile = "/run/secrets/env";
};
openssh = {
enable = true;
settings = {
PasswordAuthentication = false;
PermitRootLogin = "prohibit-password";
};
};
};
networking.firewall.allowedTCPPorts = [
22 # SSH
25 # SMTP
139 # SMTP
587 # SMTP
2525 # SMTP
serviceCfg.ports.port0
];
systemd = {
network = {
enable = true;
networks."20-lan" = {
matchConfig.Name = "enp0s5";
addresses = [
{ Address = "${serviceCfg.interface.ip}/24"; }
];
routes = [
{
Destination = "${hostCfg.localhost.address1}/0";
Gateway = serviceCfg.interface.gate;
}
];
dns = [
"1.1.1.1"
"8.8.8.8"
];
};
};
tmpfiles.rules = [
"Z ${serviceCfg.varPaths.path0} 0755 ${serviceCfg.name} ${serviceCfg.name} -"
# "Z ${serviceCfg.secretPaths.path0} 0755 ${serviceCfg.name} ${serviceCfg.name} -"
];
};
systemd.services.systemd-networkd.wantedBy = [ "multi-user.target" ];
microvm = {
vcpu = 2;
mem = 3072;
hypervisor = "qemu";
interfaces = [
{
type = "tap";
id = serviceCfg.interface.id;
mac = serviceCfg.interface.mac;
}
{
type = "user";
id = serviceCfg.interface.idUser;
mac = serviceCfg.interface.macUser;
}
];
forwardPorts = [
{
from = "host";
host.port = serviceCfg.interface.ssh;
guest.port = 22;
}
];
shares = [
{
mountPoint = "/nix/.ro-store";
proto = "virtiofs";
source = "/nix/store";
tag = "read_only_nix_store";
}
{
mountPoint = "/var/lib/bitwarden_rs";
proto = "virtiofs";
source = serviceCfg.mntPaths.path0;
tag = "vaultwarden_data";
}
{
mountPoint = "/run/secrets";
proto = "virtiofs";
source = "/run/secrets/${serviceCfg.name}";
tag = "host_secrets";
}
];
};
};
};
};
security.acme.certs."${host}" = {
dnsProvider = dns0;
environmentFile = config.sops.secrets.${dns0Path}.path;
group = "caddy";
};
services.caddy.virtualHosts = {
"${host}" = {
extraConfig = ''
reverse_proxy ${serviceCfg.interface.ip}:${toString serviceCfg.ports.port0} {
header_up X-Real-IP {remote_host}
}
tls ${serviceCfg.ssl.cert} ${serviceCfg.ssl.key}
encode zstd gzip
'';
};
};
users.users.caddy.extraGroups = [ "acme" ];
systemd.tmpfiles.rules = [
"d ${serviceCfg.mntPaths.path0} 0751 microvm wheel - -"
];
sops.secrets = {
"${serviceCfg.name}/env" = {
owner = "root";
mode = "0600";
};
};
}

View file

@ -1,219 +0,0 @@
{
config,
flake,
...
}:
let
inherit (flake.config.people) user0;
inherit (flake.config.services) instances;
serviceCfg = flake.config.services.instances.forgejo;
smtpCfg = flake.config.services.instances.smtp;
hostCfg = flake.config.services.instances.web;
host = serviceCfg.domains.url0;
dns0 = instances.web.dns.provider0;
dns0Path = "dns/${dns0}";
hostSecrets = "/opt/secrets";
in
{
users.users.caddy.extraGroups = [ "acme" ];
security.acme.certs."${host}" = {
dnsProvider = dns0;
environmentFile = config.sops.secrets.${dns0Path}.path;
group = "caddy";
};
microvm.vms.forgejo = {
autostart = true;
restartIfChanged = true;
config = {
system.stateVersion = "24.05";
time.timeZone = "America/Winnipeg";
users.users.root.openssh.authorizedKeys.keys = flake.config.people.users.${user0}.sshKeys;
services = {
forgejo = {
enable = true;
lfs.enable = true;
database = {
type = "postgres";
};
secrets = {
mailer.PASSWD = "/run/secrets/smtp";
database.PASSWD = "/run/secrets/database";
};
settings = {
server = {
DOMAIN = host;
ROOT_URL = "https://${host}/";
HTTP_PORT = serviceCfg.ports.port0;
};
# If you need to start from scratch, don't forget to turn this off again
service.DISABLE_REGISTRATION = false;
actions = {
ENABLED = true;
DEFAULT_ACTIONS_URL = "github";
};
mirror = {
ENABLED = true;
};
mailer = {
ENABLED = true;
SMTP_ADDR = smtpCfg.hostname;
FROM = smtpCfg.email.address1;
USER = smtpCfg.email.address1;
PROTOCOL = "${smtpCfg.name}+${smtpCfg.records.record1}";
SMTP_PORT = smtpCfg.ports.port1;
SEND_AS_PLAIN_TEXT = true;
USE_CLIENT_CERT = false;
};
};
};
openssh = {
enable = true;
settings = {
PasswordAuthentication = false;
PermitRootLogin = "prohibit-password";
};
};
postgresql = {
enable = true;
ensureDatabases = [ serviceCfg.name ];
ensureUsers = [
{
name = serviceCfg.name;
ensureDBOwnership = true;
}
];
};
};
networking.firewall.allowedTCPPorts = [
22 # SSH
25 # SMTP
139 # SMTP
587 # SMTP
2525 # SMTP
serviceCfg.ports.port0
];
fileSystems."/tmp" = {
device = "tmpfs";
fsType = "tmpfs";
options = [
"size=4G"
"mode=1777"
];
};
systemd = {
network = {
enable = true;
networks."20-lan" = {
matchConfig.Name = "enp0s5";
addresses = [ { Address = "${serviceCfg.interface.ip}/24"; } ];
routes = [
{
Destination = "${hostCfg.localhost.address1}/0";
Gateway = serviceCfg.interface.gate;
}
];
dns = [
"1.1.1.1"
"8.8.8.8"
];
};
};
tmpfiles.rules = [
"Z ${serviceCfg.varPaths.path0} 0755 ${serviceCfg.name} ${serviceCfg.name} -"
# "Z ${serviceCfg.secretPaths.path0} 0755 ${serviceCfg.name} ${serviceCfg.name} -"
"Z /var/lib/postgresql 0755 postgres postgres -"
];
};
systemd.services.systemd-networkd.wantedBy = [ "multi-user.target" ];
microvm = {
vcpu = 2;
mem = 3072;
hypervisor = "qemu";
interfaces = [
{
type = "tap";
id = serviceCfg.interface.id;
mac = serviceCfg.interface.mac;
}
{
type = "user";
id = serviceCfg.interface.idUser;
mac = serviceCfg.interface.macUser;
}
];
forwardPorts = [
{
from = "host";
host.port = serviceCfg.interface.ssh;
guest.port = 22;
}
];
shares = [
{
mountPoint = "/nix/.ro-store";
proto = "virtiofs";
source = "/nix/store";
tag = "read_only_nix_store";
}
{
mountPoint = "/var/lib/${serviceCfg.name}";
proto = "virtiofs";
source = "${serviceCfg.mntPaths.path0}/data";
tag = "${serviceCfg.name}_data";
}
{
mountPoint = "/var/lib/postgresql";
proto = "virtiofs";
source = "${serviceCfg.mntPaths.path0}/database";
tag = "${serviceCfg.name}_database";
}
{
mountPoint = "/run/secrets";
proto = "virtiofs";
source = "/run/secrets/${serviceCfg.name}";
tag = "host_secrets";
}
];
};
};
};
systemd.tmpfiles.rules = [
"d ${serviceCfg.mntPaths.path0}/data 0751 microvm wheel - -"
"d ${serviceCfg.mntPaths.path0}/database 0751 microvm wheel - -"
];
services.caddy.virtualHosts."${host}" = {
extraConfig = ''
reverse_proxy ${serviceCfg.interface.ip}:${toString serviceCfg.ports.port0} {
header_up X-Real-IP {remote_host}
}
tls ${serviceCfg.ssl.cert} ${serviceCfg.ssl.key}
encode zstd gzip
'';
};
sops.secrets = {
"${serviceCfg.name}/smtp" = {
owner = "root";
mode = "0600";
};
"${serviceCfg.name}/database" = {
owner = "root";
mode = "0600";
};
};
}

View file

@ -1,167 +0,0 @@
{
config,
flake,
...
}:
let
inherit (flake.config.people) user0;
inherit (flake.config.services) instances;
serviceCfg = instances.jellyfin;
hostCfg = instances.web;
dns0 = instances.web.dns.provider0;
host = serviceCfg.domains.url0;
dns0Path = "dns/${dns0}";
in
{
microvm.vms.jellyfin = {
autostart = true;
restartIfChanged = true;
config = {
system.stateVersion = "25.05";
time.timeZone = "America/Winnipeg";
users.users.root.openssh.authorizedKeys.keys = flake.config.people.users.${user0}.sshKeys;
services = {
jellyfin = {
enable = true;
openFirewall = true;
};
openssh = {
enable = true;
settings = {
PasswordAuthentication = false;
PermitRootLogin = "prohibit-password";
};
};
};
networking.firewall.allowedTCPPorts = [
22
serviceCfg.ports.port0
serviceCfg.ports.port1
serviceCfg.ports.port2
];
# fileSystems."/tmp" = {
# device = "tmpfs";
# fsType = "tmpfs";
# options = [
# "size=4G"
# "mode=1777"
# ];
# };
systemd = {
network = {
enable = true;
networks."20-lan" = {
matchConfig.Name = "enp0s6";
addresses = [ { Address = "${serviceCfg.interface.ip}/24"; } ];
routes = [
{
Destination = "${hostCfg.localhost.address1}/0";
Gateway = serviceCfg.interface.gate;
}
];
dns = [
"1.1.1.1"
"8.8.8.8"
];
};
};
tmpfiles.rules = [
"Z ${serviceCfg.varPaths.path0} 0755 ${serviceCfg.name} ${serviceCfg.name} -"
"Z ${serviceCfg.varPaths.path2} 0755 ${serviceCfg.name} ${serviceCfg.name} -"
"d ${serviceCfg.varPaths.path1} 0755 ${serviceCfg.name} ${serviceCfg.name} -"
"Z ${serviceCfg.varPaths.path2} 0755 ${serviceCfg.name} ${serviceCfg.name} -"
];
};
systemd.services.systemd-networkd.wantedBy = [ "multi-user.target" ];
microvm = {
vcpu = 6;
mem = 8192;
hypervisor = "qemu";
interfaces = [
{
type = "tap";
id = serviceCfg.interface.id;
mac = serviceCfg.interface.mac;
}
{
type = "user";
id = serviceCfg.interface.idUser;
mac = serviceCfg.interface.macUser;
}
];
forwardPorts = [
{
from = "host";
host.port = serviceCfg.interface.ssh;
guest.port = 22;
}
];
shares = [
{
mountPoint = "/nix/.ro-store";
proto = "virtiofs";
source = "/nix/store";
tag = "read_only_nix_store";
}
{
mountPoint = serviceCfg.varPaths.path0;
proto = "virtiofs";
source = "${serviceCfg.mntPaths.path0}/data";
tag = "${serviceCfg.name}_data";
}
{
mountPoint = serviceCfg.varPaths.path1;
proto = "virtiofs";
source = "${serviceCfg.mntPaths.path0}/cache";
tag = "${serviceCfg.name}_cache";
}
{
mountPoint = serviceCfg.varPaths.path2;
proto = "virtiofs";
source = "${serviceCfg.mntPaths.path0}/media";
tag = "${serviceCfg.name}_media";
}
];
};
};
};
security.acme.certs."${host}" = {
dnsProvider = dns0;
environmentFile = config.sops.secrets.${dns0Path}.path;
group = "caddy";
};
services = {
caddy = {
virtualHosts = {
"${host}" = {
extraConfig = ''
reverse_proxy ${serviceCfg.interface.ip}:${toString serviceCfg.ports.port0} {
header_up X-Real-IP {remote_host}
}
tls ${serviceCfg.ssl.cert} ${serviceCfg.ssl.key}
encode zstd gzip
'';
};
};
};
};
users.users.caddy.extraGroups = [ "acme" ];
systemd.tmpfiles.rules = [
"d ${serviceCfg.mntPaths.path0} 0751 microvm wheel - -"
"d ${serviceCfg.mntPaths.path0}/data 0751 microvm wheel - -"
"d ${serviceCfg.mntPaths.path0}/cache 0751 microvm wheel - -"
"d ${serviceCfg.mntPaths.path0}/media 0751 microvm wheel - -"
];
}

View file

@ -1,192 +0,0 @@
{
config,
flake,
...
}:
let
inherit (flake.config.people) user0;
inherit (flake.config.services) instances;
serviceCfg = instances.vaultwarden;
smtpCfg = instances.smtp;
hostCfg = instances.web;
dns0 = instances.web.dns.provider0;
host = serviceCfg.domains.url0;
dns0Path = "dns/${dns0}";
hostSecrets = "/var/lib/secrets/${serviceCfg.name}";
in
{
microvm.vms.vaultwarden = {
autostart = true;
restartIfChanged = true;
config = {
system.stateVersion = "24.05";
time.timeZone = "America/Winnipeg";
users.users.root.openssh.authorizedKeys.keys = flake.config.people.users.${user0}.sshKeys;
services = {
vaultwarden = {
enable = true;
dbBackend = "sqlite";
config = {
# Domain Configuration
DOMAIN = "https://${host}";
# Email Configuration
SMTP_AUTH_MECHANISM = "Plain";
SMTP_EMBED_IMAGES = true;
SMTP_FROM = serviceCfg.email.address0;
SMTP_FROM_NAME = serviceCfg.label;
SMTP_HOST = smtpCfg.hostname;
SMTP_PORT = smtpCfg.ports.port1;
SMTP_SECURITY = smtpCfg.records.record1;
SMTP_USERNAME = smtpCfg.email.address0;
# Security Configuration
DISABLE_ADMIN_TOKEN = false;
# Event and Backup Management
EVENTS_DAYS_RETAIN = 90;
# User Features
SENDS_ALLOWED = true;
SIGNUPS_VERIFY = true;
WEB_VAULT_ENABLED = true;
# Rocket (Web Server) Settings
ROCKET_ADDRESS = "0.0.0.0";
ROCKET_PORT = serviceCfg.ports.port0;
};
# Environment file with secrets (mounted from host)
environmentFile = "/run/secrets/env";
};
openssh = {
enable = true;
settings = {
PasswordAuthentication = false;
PermitRootLogin = "prohibit-password";
};
};
};
networking.firewall.allowedTCPPorts = [
22 # SSH
25 # SMTP
139 # SMTP
587 # SMTP
2525 # SMTP
serviceCfg.ports.port0
];
systemd = {
network = {
enable = true;
networks."20-lan" = {
matchConfig.Name = "enp0s5";
addresses = [
{ Address = "${serviceCfg.interface.ip}/24"; }
];
routes = [
{
Destination = "${hostCfg.localhost.address1}/0";
Gateway = serviceCfg.interface.gate;
}
];
dns = [
"1.1.1.1"
"8.8.8.8"
];
};
};
tmpfiles.rules = [
"Z ${serviceCfg.varPaths.path0} 0755 ${serviceCfg.name} ${serviceCfg.name} -"
# "Z ${serviceCfg.secretPaths.path0} 0755 ${serviceCfg.name} ${serviceCfg.name} -"
];
};
systemd.services.systemd-networkd.wantedBy = [ "multi-user.target" ];
microvm = {
vcpu = 2;
mem = 3072;
hypervisor = "qemu";
interfaces = [
{
type = "tap";
id = serviceCfg.interface.id;
mac = serviceCfg.interface.mac;
}
{
type = "user";
id = serviceCfg.interface.idUser;
mac = serviceCfg.interface.macUser;
}
];
forwardPorts = [
{
from = "host";
host.port = serviceCfg.interface.ssh;
guest.port = 22;
}
];
shares = [
{
mountPoint = "/nix/.ro-store";
proto = "virtiofs";
source = "/nix/store";
tag = "read_only_nix_store";
}
{
mountPoint = "/var/lib/bitwarden_rs";
proto = "virtiofs";
source = serviceCfg.mntPaths.path0;
tag = "vaultwarden_data";
}
{
mountPoint = "/run/secrets";
proto = "virtiofs";
source = "/run/secrets/${serviceCfg.name}";
tag = "host_secrets";
}
];
};
};
};
security.acme.certs."${host}" = {
dnsProvider = dns0;
environmentFile = config.sops.secrets.${dns0Path}.path;
group = "caddy";
};
services.caddy.virtualHosts = {
"${host}" = {
extraConfig = ''
reverse_proxy ${serviceCfg.interface.ip}:${toString serviceCfg.ports.port0} {
header_up X-Real-IP {remote_host}
}
tls ${serviceCfg.ssl.cert} ${serviceCfg.ssl.key}
encode zstd gzip
'';
};
};
users.users.caddy.extraGroups = [ "acme" ];
systemd.tmpfiles.rules = [
"d ${serviceCfg.mntPaths.path0} 0751 microvm wheel - -"
];
sops.secrets = {
"${serviceCfg.name}/env" = {
owner = "root";
mode = "0600";
};
};
}

View file

@ -1,7 +1,7 @@
ssh: ssh:
private: ENC[AES256_GCM,data:XJk/gjPkFeSZtPkKYS2vRHqMY/X5zRaDlS4UwzUvjm9MvTgdhoXUlqvFC0Dl5SZhRlY+XXAuG7gIIUESzCFWQKdOoUcto3r0WSuIm9EwLKXnnaHemeFVHYgZU9Rz45PK6yFWUC06+n56b2A1dFXftjeXcCqaQrT/jk3RDSHmhW9u7QgDmhhaybxXOrzkup2U8kjhrMmRBcf4xP//nihuzHcyYX75ONr56bgkjl6gpZTfZrn2ad8b+4iGn+rElzf7RHAG0mwTeEX2kYRyafaanGuc2xTnZubBAYDnc1eM6T99PXC0iWh/lUKc1zG1l18UchWzgvl3sPK0Cb2/5aaFMUk2ET6kVOlpKyGc94MRpyv3iUi8soFjh34sWH3mFtec2OWfIxDhoVfZoc2hmP2Hflfjp7acwaMskFBHaCSO2DGtNmN3hSUhAAeLx8OZupSIJmDVpq00qKUbN+5z4K78AdGuUOP07cE889evNniCHLP6yPav7tIulnBS9lD2U+CbqF7vMtdZx/eYFwJjmMtE,iv:JxSytvXKWLHDedlE0Wq5YpPUnfb0HoQgKJ2bt1Z8yqk=,tag:MjOoUSWsHWHgxp0yu9YQFA==,type:str] private: ENC[AES256_GCM,data:XJk/gjPkFeSZtPkKYS2vRHqMY/X5zRaDlS4UwzUvjm9MvTgdhoXUlqvFC0Dl5SZhRlY+XXAuG7gIIUESzCFWQKdOoUcto3r0WSuIm9EwLKXnnaHemeFVHYgZU9Rz45PK6yFWUC06+n56b2A1dFXftjeXcCqaQrT/jk3RDSHmhW9u7QgDmhhaybxXOrzkup2U8kjhrMmRBcf4xP//nihuzHcyYX75ONr56bgkjl6gpZTfZrn2ad8b+4iGn+rElzf7RHAG0mwTeEX2kYRyafaanGuc2xTnZubBAYDnc1eM6T99PXC0iWh/lUKc1zG1l18UchWzgvl3sPK0Cb2/5aaFMUk2ET6kVOlpKyGc94MRpyv3iUi8soFjh34sWH3mFtec2OWfIxDhoVfZoc2hmP2Hflfjp7acwaMskFBHaCSO2DGtNmN3hSUhAAeLx8OZupSIJmDVpq00qKUbN+5z4K78AdGuUOP07cE889evNniCHLP6yPav7tIulnBS9lD2U+CbqF7vMtdZx/eYFwJjmMtE,iv:JxSytvXKWLHDedlE0Wq5YpPUnfb0HoQgKJ2bt1Z8yqk=,tag:MjOoUSWsHWHgxp0yu9YQFA==,type:str]
public: ENC[AES256_GCM,data:Cn4hutHHeptbefHOKK7zv5TmveGOqfHAwGHogDq9sRmeb+b1lzHwj7qvg8lcnlJtIo4qS+TrKtSj5ZCsPNXOhWG1rkk97gTfPMbcxj5f1O3WJigL2wsrB2cQgc5UsA==,iv:ID4zRdr/efClOAHbXzxG1bNuJR0A2qbydzGlMhvEcRE=,tag:qbIoaGb+RXxRRkkQtuX7/A==,type:str] public: ENC[AES256_GCM,data:Cn4hutHHeptbefHOKK7zv5TmveGOqfHAwGHogDq9sRmeb+b1lzHwj7qvg8lcnlJtIo4qS+TrKtSj5ZCsPNXOhWG1rkk97gTfPMbcxj5f1O3WJigL2wsrB2cQgc5UsA==,iv:ID4zRdr/efClOAHbXzxG1bNuJR0A2qbydzGlMhvEcRE=,tag:qbIoaGb+RXxRRkkQtuX7/A==,type:str]
hosts: ENC[AES256_GCM,data:cgizSZ5PiwkhNm2c3PNYOO29iNUg6U+skkSb5HpOeYNFv3ZkwtVM3SdEXdZEHDF91JhFeoJiNevLF80ol7YR9J493bQsEZPuOyMIpsDIWAHKFBS+S46ekG/oBwN8JZUy0Hnh3x421cg5KOE8J8Um5wJhh8Lun9Y0747nmEhqXt+cAjzaGNBbuFJR7z8XAO8/ZjrBxOr+LcxJt2IYG6PqA6r9ZEFEglXqxDeezTcQsNxIGuflBrB0QKyUp0CGkM4uaW9506UrvUzdHh71hYHm5Ir9pmhYawlWCWuOgydR8aCkIiz2lbatvjukHJvoMd2S1TxSYHMkheXMhFEDmfzQvPW/7LdfQ3PmMyGq23JFuSxsvPXSOLpI2QfqpQBwkCbNhzEkmc0ln50rRAka/z4HfR6Ga77FLW9Jcz9rpwRiI0eF46x1gRxiB2DO74kSwwZgjTNyafPGChQJjDh6Fycs9ivMOFEoltIaipHHSrxDTQvz1nhJkb1S/qgQEkfm9GbfJS9OxYgheHzWJOxtocPVVpUc5wac/9KrS2O1lfgI4IrtNF1J0zti6JcsGewFQZzfDx6usTKw/R9T1okNhtM4njn5qADsJyWnpkuiiqXh2ozO9CLgehxpm6oSbw/FVA3BDME2iU+16CXsZVxxVW3zSSI5ctZXpkDgSJh19DCh7Er5nb6iwsLLCCRqcIKtfAf2ZHPABB2BoZE0CGvf/nnkQd++MfL9W18OwXz3/9aVkW+gYNrDU9Mv8LiGz0BIarpneTdIw4i+LT7wtv5h0RSjvfJS50LP/1eK39vTwP9OiJ5UsCDyURSkB2sVS0MeSca6nVGoIOdWr2ee8IBp3QFS24VWZvLFE+0MY90S4ZNZsOF8nmN+M/SCvS4h/ixqQQ6zr+qtS8Hm1N/EHx2U2CqRbE64HnQhaHnj/hKUkQiABIlt1kz1eR2NjEt5aa1hQyoKl8E4J9V9V/u3t3vd0aIkWFL08SUEQEQWith066Jdf0VMdQvaSUKiSlhfZZ5Wj6ex1Y1hX9vT9utEGUAS12vlh2KMEpPi6eFxvIyOiu7bCMSeicQLpjGKEIsKJOVDPYPjbilwXuz8agjUevoguoAT12CKdCWPlnrjuByCZ34/U/qBHdak7UqnjS9MbbDTIcr8uz/2g8w2dMIAbRA0I9A4CrOUAAecIPQMCFU2scuJt+4xxZxof+HUlyqXQR3YD6Wh89/5rVK+kvgoNjflijegxW0rju7NyU0kU3jZp36V1F4HxzXsz7OOHmN54GJTvRSqhrQfQ39+eOSvyd04pwCM7EAOfJYw0moozVnVtmjnDLVjR49ho9qHrvDVLJ/sEcG/iyDrNtp8PCq9OK6EOEgPGNq4qDEow1GelPz6M1BU7t+KL5QnqDi0azz7L2ctx9N6b34LOHgNoHD5qGWP0CnNo1Pq7lQYoN9/XHYdQbHrc8NWjR56NmBnUnA65Af8+67J7+stLSVDrUyRRf9Ohcp5k6WK5N1yyJeYxa1EDulsXzToEEbadSbbS6REEESEn/jlup7U5NituT5wT9al,iv:ervRE0xkjtsKNYB/1W9oHM59lHwHTsOk3NLhnaRvWCM=,tag:Yj3emxFA5h2ndIilC5L16w==,type:str] hosts: ENC[AES256_GCM,data:QuyhBCfYd/CXRhqbenhO7c8PRuaxMQyXVwetbCN5qOwRv32IPOAYcFByQygmz6J3sAaw47arSYXqNWarzRNX0OPfEvxAusIDyB4Ia0kRnELJKJ+LHOWC8fbKwh+w1IVPFznEUDJsq2dIkMd3uOLw+R70Z2A/uTycjOI8ZtTz+Pw0fbu8wtGUR0yzTjgMVia7L7L6m2k5Owkc6M+MYqd2GlkSarVQa+XiwOmp06hEVqSeWb/rXIGEszb54iC0jbnArfAHVeiZ9moLogDpcZRUFEUxY72wG3Y7DM0bNEG5B3bXm11hemvIGPCY1kqvG4ROtDbvgx6C4XNupxiYG9tUMneJHwfRXAFs4gGO0r7wSsE8qQePWAlGg4YcpZX/R3WraQsUSHjS4rGtqNUAsEMRN195/WChcHGyP2aMjrzl1RSs27fIA7O04pLEH6Uhcv26Rp7c1JwlpvcKgnMZ0GqiHKuqVYYJMPEzLWlWXpRpcODDB5iqad0o0T/9rnVlK7HGhCTEl5rYj0jSYrwu4ktApZ2jNMCsMNIek1p12OJK1RrP9HvIThuSDNXo5vaIreHL1MlIgMb4t8MvsXcczN1+yAiJ0msZhyV/OH/LRO3pfceHMmf3LCB7zIFsJpTcQGAMFrscSA7D6Zise81GMIL2DJVzlbdhQxIwB2DTDa7mf74DdZzUkNbQCbWRP6YnrfiIOCcQGWMVXbm+QVr48l066NXfuxPbFFnCT3hK3cnNwRpMzTqzLc5ol6kxtN+ryVPCQ7hvMe1InoSnMXJhf5GEeEUH26mAOQ2o1eakcKonyCTLi0Ui1Wlw2x9EbM0QCk9Nl5YcxUZLPddEyWdkvCJ8JqGM5IitTNwuzHLUjqkNb+DP0d35ON2ek5vK9kAh8oJOuN8vKVdvbR3XmexEbo9oviGSQwB3dWl274D29Oy7k2H7u+rro+HDeZp1MCkCJ8DgvKGaHYXNjESVNxSJ9/sch0i5h2N2aEs0lK8/WqpW6K6DX6m2yjO7qdZf+j31bilRZD6mwELMtXrpQWwH9OIEYDsCxxjC/Q4soZ+d61U6YRdXrhfG4wwrL6GWr6CQD46kDobNlnt2mrdlWOrWz5tfPhsEAHZ5f7/WNk+xTHC+MuDt/aFUGisXuW5Wv2LmADBIHablsV2V1THDeT+r1753djtI4zn9QGWQnJ+5kgsw+lQScJVROOPriSJYWrvHJmkDMMOg2I5yYyx9jQO1xafPM5RO05FDQo8zJtCodKmZVyikP97mspFfKukBKuUHC9X3OXaIbvUrc3RDh/L7whQ5p3Ih+fgTFm4Pw+yPVq6KJ9hNMRmuvrnZsstWeMBIg5hus5k8g4q+xHmL5uVjLvkDxCFgoKC66gQYENm/XPYdcj3b5bSRW4WYoofk0XjppgU/zGKIrgtr3F7mWo64ZMwv9XyVznReHNVBcyRxQlvLdpAzrb8vczEKFkpyRdM+uIdovNl88vhgaLbLKtIY9LVp7w1p7QIuCkBqS1qAhFzYd20NZKDTAd6TnUfPC+/bswbHslSNEIFYBoUvEwJW,iv:OZdzyvCeRCHMqdUPkHGxiZdXlm/+u0LAagk7HLHWG94=,tag:IkDpOIQmtC5FvKjlmmhH2A==,type:str]
network: network:
server: ENC[AES256_GCM,data:EFsmXNkuf5OAMh8hjfZTixmmdjqBNIME9JjQC8azeCwcMVInm8bWdxE4OqFmxOk9MAU=,iv:pI6WeM2aQC+7vx1Xmp5O2rikqNLgzuEOg+Lo7TqFQxU=,tag:ElcA8mn9dx+IjIf38nKT5A==,type:str] server: ENC[AES256_GCM,data:EFsmXNkuf5OAMh8hjfZTixmmdjqBNIME9JjQC8azeCwcMVInm8bWdxE4OqFmxOk9MAU=,iv:pI6WeM2aQC+7vx1Xmp5O2rikqNLgzuEOg+Lo7TqFQxU=,tag:ElcA8mn9dx+IjIf38nKT5A==,type:str]
fallaryn: ENC[AES256_GCM,data:O77hH3STB6zpl0b9iXsVu9OOrlLKUwfs2qI9hdqX4kMuBs3XgT/xsQ==,iv:RDKsuJoy+LIyADMc3bgOEmLKdXtu6kad2aeVetuZdJI=,tag:MrpCZ+iJUnGIjeHMgcYG6Q==,type:str] fallaryn: ENC[AES256_GCM,data:O77hH3STB6zpl0b9iXsVu9OOrlLKUwfs2qI9hdqX4kMuBs3XgT/xsQ==,iv:RDKsuJoy+LIyADMc3bgOEmLKdXtu6kad2aeVetuZdJI=,tag:MrpCZ+iJUnGIjeHMgcYG6Q==,type:str]
@ -61,7 +61,7 @@ sops:
bXBOa1VSakoyaWxpODJEOU11QUZCaUEK8Ch9Ten3DdrPHF1DTH2qei85AlHUOaLD bXBOa1VSakoyaWxpODJEOU11QUZCaUEK8Ch9Ten3DdrPHF1DTH2qei85AlHUOaLD
aNfzakake7ej+MxJYdKEU0bcWofNMKzIlZa2uM10KZSENDP8d8qlig== aNfzakake7ej+MxJYdKEU0bcWofNMKzIlZa2uM10KZSENDP8d8qlig==
-----END AGE ENCRYPTED FILE----- -----END AGE ENCRYPTED FILE-----
lastmodified: "2025-11-10T05:21:40Z" lastmodified: "2025-11-11T03:26:53Z"
mac: ENC[AES256_GCM,data:1e2wUXNJSCP5108HqTnaEalBrDgGI60CgsLrc/ZWE52OpUxOkzfhXZDb1MZWkPEUbmc3ZeJazF9SHBHspYqnO/wznisEzLAPpGMLJePqSPla6pg81UqZdVawwtqoopurCmtr/SHpdQfGcHk9pR5n7tDdwat7xIEPoOStyZUpoDE=,iv:wg9XTEIr4ozhqr4mQP5auG+DZls1towlDAJSGXb08SI=,tag:PUDxUZfTYqy+FMGd6sLy3g==,type:str] mac: ENC[AES256_GCM,data:Wsc9G4UIX/GfuYdHgf/rMqHNJBKFQp4/pp13IJhFXyONxullcHqH5qZBophGen9loRhaRTLVglRDVPEfQwisFJHIDu6yQ7A2YLLVl5oZ5GB2jkUxK/hnw5FqQG5NTaoAls150niyDQchTe0GO7YzEamIucnJwkiUVsc0+7ZGED4=,iv:ZOk64KSwjvN28stBE3xJu4oxSCyVhTsJnqBeliHUyp8=,tag:IhQeULWdFBuIvqGy9SEcTQ==,type:str]
unencrypted_suffix: _unencrypted unencrypted_suffix: _unencrypted
version: 3.11.0 version: 3.11.0