mirror of
https://gitlab.com/upRootNutrition/dotfiles.git
synced 2025-12-07 21:42:16 -06:00
test: vaultwarden microVM
This commit is contained in:
parent
e90d05f83d
commit
7ba592c0c5
43 changed files with 4005 additions and 267 deletions
335
example/host/default.nix
Executable file
335
example/host/default.nix
Executable file
|
|
@ -0,0 +1,335 @@
|
|||
{
|
||||
pkgs,
|
||||
config,
|
||||
lib,
|
||||
...
|
||||
}:
|
||||
let
|
||||
inherit (config.microvm) stateDir;
|
||||
microvmCommand = import ../../pkgs/microvm-command.nix {
|
||||
inherit pkgs;
|
||||
};
|
||||
user = "microvm";
|
||||
group = "kvm";
|
||||
in
|
||||
{
|
||||
imports = [ ./options.nix ];
|
||||
|
||||
config = lib.mkIf config.microvm.host.enable {
|
||||
assertions = lib.concatMap (vmName: [
|
||||
{
|
||||
assertion =
|
||||
config.microvm.vms.${vmName}.config != null -> config.microvm.vms.${vmName}.flake == null;
|
||||
message = "vm ${vmName}: Fully-declarative VMs cannot also set a flake!";
|
||||
}
|
||||
{
|
||||
assertion =
|
||||
config.microvm.vms.${vmName}.config != null -> config.microvm.vms.${vmName}.updateFlake == null;
|
||||
message = "vm ${vmName}: Fully-declarative VMs cannot set a updateFlake!";
|
||||
}
|
||||
]) (builtins.attrNames config.microvm.vms);
|
||||
|
||||
boot.kernelModules = [ "tun" ];
|
||||
|
||||
system.activationScripts.microvm-host = ''
|
||||
mkdir -p ${stateDir}
|
||||
chown ${user}:${group} ${stateDir}
|
||||
chmod g+w ${stateDir}
|
||||
'';
|
||||
|
||||
environment.systemPackages = [
|
||||
microvmCommand
|
||||
];
|
||||
|
||||
users.users.${user} = {
|
||||
isSystemUser = true;
|
||||
inherit group;
|
||||
};
|
||||
|
||||
security.pam.loginLimits = [
|
||||
{
|
||||
domain = user;
|
||||
item = "memlock";
|
||||
type = "hard";
|
||||
value = "infinity";
|
||||
}
|
||||
{
|
||||
domain = user;
|
||||
item = "memlock";
|
||||
type = "soft";
|
||||
value = "infinity";
|
||||
}
|
||||
];
|
||||
|
||||
systemd.services =
|
||||
builtins.foldl'
|
||||
(
|
||||
result: name:
|
||||
result
|
||||
// (
|
||||
let
|
||||
microvmConfig = config.microvm.vms.${name};
|
||||
inherit (microvmConfig) flake updateFlake;
|
||||
isFlake = flake != null;
|
||||
guestConfig =
|
||||
if isFlake then flake.nixosConfigurations.${name}.config else microvmConfig.config.config;
|
||||
runner = guestConfig.microvm.declaredRunner;
|
||||
in
|
||||
{
|
||||
"install-microvm-${name}" = {
|
||||
description = "Install MicroVM '${name}'";
|
||||
before = [
|
||||
"microvm@${name}.service"
|
||||
"microvm-tap-interfaces@${name}.service"
|
||||
"microvm-pci-devices@${name}.service"
|
||||
"microvm-virtiofsd@${name}.service"
|
||||
];
|
||||
partOf = [ "microvm@${name}.service" ];
|
||||
wantedBy = [ "microvms.target" ];
|
||||
# Run on every rebuild for fully-declarative MicroVMs and flake-based MicroVMs without updateFlake.
|
||||
# For MicroVMs with updateFlake set, only run on initial installation.
|
||||
unitConfig.ConditionPathExists = lib.mkIf (isFlake && updateFlake != null) "!${stateDir}/${name}";
|
||||
serviceConfig.Type = "oneshot";
|
||||
script = ''
|
||||
mkdir -p ${stateDir}/${name}
|
||||
cd ${stateDir}/${name}
|
||||
|
||||
ln -sTf ${runner} current
|
||||
chown -h ${user}:${group} . current
|
||||
''
|
||||
# Including the toplevel here is crucial to have the service definition
|
||||
# change when the host is rebuilt and the vm definition changed.
|
||||
+ lib.optionalString (!isFlake) ''
|
||||
ln -sTf ${guestConfig.system.build.toplevel} toplevel
|
||||
''
|
||||
# Declarative deployment requires storing just the flake
|
||||
+ lib.optionalString isFlake ''
|
||||
echo '${if updateFlake != null then updateFlake else flake}' > flake
|
||||
chown -h ${user}:${group} flake
|
||||
'';
|
||||
serviceConfig.SyslogIdentifier = "install-microvm-${name}";
|
||||
};
|
||||
"microvm@${name}" = {
|
||||
# restartIfChanged is opt-out, so we have to include the definition unconditionally
|
||||
serviceConfig.X-RestartIfChanged = [
|
||||
""
|
||||
microvmConfig.restartIfChanged
|
||||
];
|
||||
path = lib.mkForce [ ];
|
||||
# If the given declarative microvm wants to be restarted on change,
|
||||
# We have to make sure this service group is restarted. To make sure
|
||||
# that this service is also changed when the microvm configuration changes,
|
||||
# we also have to include a trigger here.
|
||||
restartTriggers = [ guestConfig.system.build.toplevel ];
|
||||
overrideStrategy = "asDropin";
|
||||
serviceConfig.Type =
|
||||
if guestConfig.microvm.declaredRunner.supportsNotifySocket then "notify" else "simple";
|
||||
};
|
||||
"microvm-tap-interfaces@${name}" = {
|
||||
serviceConfig.X-RestartIfChanged = [
|
||||
""
|
||||
microvmConfig.restartIfChanged
|
||||
];
|
||||
path = lib.mkForce [ ];
|
||||
overrideStrategy = "asDropin";
|
||||
};
|
||||
"microvm-pci-devices@${name}" = {
|
||||
serviceConfig.X-RestartIfChanged = [
|
||||
""
|
||||
microvmConfig.restartIfChanged
|
||||
];
|
||||
path = lib.mkForce [ ];
|
||||
overrideStrategy = "asDropin";
|
||||
};
|
||||
"microvm-virtiofsd@${name}" = {
|
||||
serviceConfig.X-RestartIfChanged = [
|
||||
""
|
||||
microvmConfig.restartIfChanged
|
||||
];
|
||||
path = lib.mkForce [ ];
|
||||
overrideStrategy = "asDropin";
|
||||
};
|
||||
}
|
||||
)
|
||||
)
|
||||
{
|
||||
"microvm-tap-interfaces@" = {
|
||||
description = "Setup MicroVM '%i' TAP interfaces";
|
||||
before = [ "microvm@%i.service" ];
|
||||
partOf = [ "microvm@%i.service" ];
|
||||
after = [ "network.target" ];
|
||||
unitConfig.ConditionPathExists = "${stateDir}/%i/current/bin/tap-up";
|
||||
restartIfChanged = false;
|
||||
serviceConfig = {
|
||||
Type = "oneshot";
|
||||
RemainAfterExit = true;
|
||||
SyslogIdentifier = "microvm-tap-interfaces@%i";
|
||||
ExecStart = "${stateDir}/%i/current/bin/tap-up";
|
||||
ExecStop = "${stateDir}/%i/booted/bin/tap-down";
|
||||
};
|
||||
};
|
||||
|
||||
"microvm-macvtap-interfaces@" = {
|
||||
description = "Setup MicroVM '%i' MACVTAP interfaces";
|
||||
before = [ "microvm@%i.service" ];
|
||||
partOf = [ "microvm@%i.service" ];
|
||||
unitConfig.ConditionPathExists = "${stateDir}/%i/current/bin/macvtap-up";
|
||||
restartIfChanged = false;
|
||||
serviceConfig = {
|
||||
Type = "oneshot";
|
||||
RemainAfterExit = true;
|
||||
SyslogIdentifier = "microvm-macvtap-interfaces@%i";
|
||||
ExecStart = "${stateDir}/%i/current/bin/macvtap-up";
|
||||
ExecStop = "${stateDir}/%i/booted/bin/macvtap-down";
|
||||
};
|
||||
};
|
||||
|
||||
"microvm-pci-devices@" = {
|
||||
description = "Setup MicroVM '%i' devices for passthrough";
|
||||
before = [ "microvm@%i.service" ];
|
||||
partOf = [ "microvm@%i.service" ];
|
||||
unitConfig.ConditionPathExists = "${stateDir}/%i/current/bin/pci-setup";
|
||||
restartIfChanged = false;
|
||||
serviceConfig = {
|
||||
Type = "oneshot";
|
||||
RemainAfterExit = true;
|
||||
SyslogIdentifier = "microvm-pci-devices@%i";
|
||||
ExecStart = "${stateDir}/%i/current/bin/pci-setup";
|
||||
};
|
||||
};
|
||||
|
||||
"microvm-virtiofsd@" =
|
||||
let
|
||||
runFromBootedOrCurrent = pkgs.writeShellScript "microvm-runFromBootedOrCurrent" ''
|
||||
NAME="$1"
|
||||
VM="$2"
|
||||
cd "${stateDir}/$VM"
|
||||
|
||||
if [ -e booted ]; then
|
||||
exec booted/bin/$NAME
|
||||
else
|
||||
exec current/bin/$NAME
|
||||
fi
|
||||
'';
|
||||
|
||||
in
|
||||
{
|
||||
description = "VirtioFS daemons for MicroVM '%i'";
|
||||
before = [ "microvm@%i.service" ];
|
||||
after = [ "local-fs.target" ];
|
||||
partOf = [ "microvm@%i.service" ];
|
||||
unitConfig.ConditionPathExists = "${stateDir}/%i/current/bin/virtiofsd-run";
|
||||
restartIfChanged = false;
|
||||
serviceConfig = {
|
||||
WorkingDirectory = "${stateDir}/%i";
|
||||
ExecStart = "${stateDir}/%i/current/bin/virtiofsd-run";
|
||||
ExecStop = "${runFromBootedOrCurrent} virtiofsd-shutdown %i";
|
||||
LimitNOFILE = 1048576;
|
||||
NotifyAccess = "all";
|
||||
PrivateTmp = "yes";
|
||||
Restart = "always";
|
||||
RestartSec = "5s";
|
||||
SyslogIdentifier = "microvm-virtiofsd@%i";
|
||||
Type = "notify";
|
||||
};
|
||||
};
|
||||
|
||||
"microvm@" = {
|
||||
description = "MicroVM '%i'";
|
||||
requires = [
|
||||
"microvm-tap-interfaces@%i.service"
|
||||
"microvm-macvtap-interfaces@%i.service"
|
||||
"microvm-pci-devices@%i.service"
|
||||
"microvm-virtiofsd@%i.service"
|
||||
];
|
||||
after = [
|
||||
"network.target"
|
||||
"microvm-tap-interfaces@%i.service"
|
||||
"microvm-macvtap-interfaces@%i.service"
|
||||
"microvm-pci-devices@%i.service"
|
||||
"microvm-virtiofsd@%i.service"
|
||||
];
|
||||
unitConfig.ConditionPathExists = "${stateDir}/%i/current/bin/microvm-run";
|
||||
restartIfChanged = false;
|
||||
preStart = ''
|
||||
rm -f booted
|
||||
ln -s $(readlink current) booted
|
||||
'';
|
||||
postStop = ''
|
||||
rm booted
|
||||
'';
|
||||
serviceConfig = {
|
||||
Type = if config.microvm.host.useNotifySockets then "notify" else "simple";
|
||||
WorkingDirectory = "${stateDir}/%i";
|
||||
ExecStart = "${stateDir}/%i/current/bin/microvm-run";
|
||||
ExecStop = "${stateDir}/%i/booted/bin/microvm-shutdown";
|
||||
TimeoutSec = config.microvm.host.startupTimeout;
|
||||
Restart = "always";
|
||||
RestartSec = "5s";
|
||||
User = user;
|
||||
Group = group;
|
||||
SyslogIdentifier = "microvm@%i";
|
||||
LimitNOFILE = 1048576;
|
||||
NotifyAccess = "all";
|
||||
LimitMEMLOCK = "infinity";
|
||||
};
|
||||
};
|
||||
}
|
||||
(builtins.attrNames config.microvm.vms);
|
||||
|
||||
microvm.autostart = builtins.filter (vmName: config.microvm.vms.${vmName}.autostart) (
|
||||
builtins.attrNames config.microvm.vms
|
||||
);
|
||||
# Starts all the containers after boot
|
||||
systemd.targets.microvms = {
|
||||
wantedBy = [ "multi-user.target" ];
|
||||
wants = map (name: "microvm@${name}.service") config.microvm.autostart;
|
||||
};
|
||||
|
||||
# This helper creates tap interfaces and attaches them to a bridge
|
||||
# for qemu regardless if it is run as root or not.
|
||||
security.wrappers.qemu-bridge-helper = lib.mkIf (!config.virtualisation.libvirtd.enable) {
|
||||
source = "${pkgs.qemu-utils}/libexec/qemu-bridge-helper";
|
||||
owner = "root";
|
||||
group = "root";
|
||||
capabilities = "cap_net_admin+ep";
|
||||
};
|
||||
|
||||
# You must define this file with your bridge interfaces if you
|
||||
# intend to use qemu-bridge-helper through a `type = "bridge"`
|
||||
# interface.
|
||||
environment.etc."qemu/bridge.conf".text = lib.mkDefault ''
|
||||
allow all
|
||||
'';
|
||||
|
||||
# Enable Kernel Same-Page Merging
|
||||
hardware.ksm.enable = lib.mkDefault true;
|
||||
|
||||
# TODO: remove in 2026
|
||||
system.activationScripts.microvm-update-check = ''
|
||||
if [ -d ${stateDir} ]; then
|
||||
_outdated_microvms=""
|
||||
|
||||
for dir in ${stateDir}/*; do
|
||||
if [ -e $dir/current/share/microvm/virtiofs ] &&
|
||||
[ ! -e $dir/current/bin/virtiofsd-run ]; then
|
||||
_outdated_microvms="$_outdated_microvms $(basename $dir)"
|
||||
elif [ -e $dir/current/share/microvm/tap-interfaces ] &&
|
||||
[ ! -e $dir/current/bin/tap-up ]; then
|
||||
_outdated_microvms="$_outdated_microvms $(basename $dir)"
|
||||
elif [ -e $dir/current/share/microvm/macvtap-interfaces ] &&
|
||||
[ ! -e $dir/current/bin/macvtap-up ]; then
|
||||
_outdated_microvms="$_outdated_microvms $(basename $dir)"
|
||||
elif [ -e $dir/current/share/microvm/pci-devices ] &&
|
||||
[ ! -e $dir/current/bin/pci-setup ]; then
|
||||
_outdated_microvms="$_outdated_microvms $(basename $dir)"
|
||||
fi
|
||||
done
|
||||
|
||||
if [ "$_outdated_microvms" != "" ]; then
|
||||
echo "The following MicroVMs must be updated to follow the new virtiofsd/tap/macvtap/pci setup scheme: $_outdated_microvms"
|
||||
fi
|
||||
fi
|
||||
'';
|
||||
};
|
||||
}
|
||||
182
example/host/options.nix
Executable file
182
example/host/options.nix
Executable file
|
|
@ -0,0 +1,182 @@
|
|||
{ pkgs, lib, ... }:
|
||||
|
||||
{
|
||||
options.microvm = with lib; {
|
||||
host.enable = mkOption {
|
||||
type = types.bool;
|
||||
default = true;
|
||||
description = ''
|
||||
Whether to enable the microvm.nix host module.
|
||||
'';
|
||||
};
|
||||
host.startupTimeout = mkOption {
|
||||
description = "Start up timeout for the VMs in seconds";
|
||||
type = types.ints.positive;
|
||||
default = 150;
|
||||
};
|
||||
|
||||
host.useNotifySockets = mkOption {
|
||||
type = types.bool;
|
||||
default = false;
|
||||
description = ''
|
||||
Enable if all your MicroVMs run with a Hypervisor that sends
|
||||
readiness notification over a VSOCK.
|
||||
|
||||
**Danger!** If one of your MicroVMs doesn't do this, its
|
||||
systemd service will not start up successfully!
|
||||
'';
|
||||
};
|
||||
|
||||
vms = mkOption {
|
||||
type =
|
||||
with types;
|
||||
attrsOf (
|
||||
submodule (
|
||||
{ config, name, ... }:
|
||||
{
|
||||
options = {
|
||||
config = mkOption {
|
||||
description = ''
|
||||
A specification of the desired configuration of this MicroVM,
|
||||
as a NixOS module, for building **without** a flake.
|
||||
'';
|
||||
default = null;
|
||||
type = nullOr (
|
||||
lib.mkOptionType {
|
||||
name = "Toplevel NixOS config";
|
||||
merge =
|
||||
loc: defs:
|
||||
(import "${config.nixpkgs}/nixos/lib/eval-config.nix" {
|
||||
modules =
|
||||
let
|
||||
extraConfig = (
|
||||
{ lib, ... }:
|
||||
{
|
||||
_file = "module at ${__curPos.file}:${toString __curPos.line}";
|
||||
config = {
|
||||
networking.hostName = lib.mkDefault name;
|
||||
};
|
||||
}
|
||||
);
|
||||
in
|
||||
[
|
||||
extraConfig
|
||||
../microvm
|
||||
]
|
||||
++ (map (x: x.value) defs);
|
||||
prefix = [
|
||||
"microvm"
|
||||
"vms"
|
||||
name
|
||||
"config"
|
||||
];
|
||||
inherit (config) specialArgs pkgs;
|
||||
system =
|
||||
if config.pkgs != null then
|
||||
config.pkgs.stdenv.hostPlatform.system
|
||||
else
|
||||
pkgs.stdenv.hostPlatform.system;
|
||||
});
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
nixpkgs = mkOption {
|
||||
type = types.path;
|
||||
default = if config.pkgs != null then config.pkgs.path else pkgs.path;
|
||||
defaultText = literalExpression "pkgs.path";
|
||||
description = ''
|
||||
This option is only respected when `config` is
|
||||
specified.
|
||||
|
||||
The nixpkgs path to use for the MicroVM. Defaults to the
|
||||
host's nixpkgs.
|
||||
'';
|
||||
};
|
||||
|
||||
pkgs = mkOption {
|
||||
type = types.nullOr types.unspecified;
|
||||
default = pkgs;
|
||||
defaultText = literalExpression "pkgs";
|
||||
description = ''
|
||||
This option is only respected when `config` is specified.
|
||||
|
||||
The package set to use for the MicroVM. Must be a
|
||||
nixpkgs package set with the microvm overlay. Determines
|
||||
the system of the MicroVM.
|
||||
|
||||
If set to null, a new package set will be instantiated.
|
||||
'';
|
||||
};
|
||||
|
||||
specialArgs = mkOption {
|
||||
type = types.attrsOf types.unspecified;
|
||||
default = { };
|
||||
description = ''
|
||||
This option is only respected when `config` is specified.
|
||||
|
||||
A set of special arguments to be passed to NixOS modules.
|
||||
This will be merged into the `specialArgs` used to evaluate
|
||||
the NixOS configurations.
|
||||
'';
|
||||
};
|
||||
|
||||
flake = mkOption {
|
||||
description = "Source flake for declarative build";
|
||||
type = nullOr path;
|
||||
default = null;
|
||||
defaultText = literalExpression ''flakeInputs.my-infra'';
|
||||
};
|
||||
|
||||
updateFlake = mkOption {
|
||||
description = "Source flakeref to store for later imperative update";
|
||||
type = nullOr str;
|
||||
default = null;
|
||||
defaultText = literalExpression ''"git+file:///home/user/my-infra"'';
|
||||
};
|
||||
|
||||
autostart = mkOption {
|
||||
description = "Add this MicroVM to config.microvm.autostart?";
|
||||
type = bool;
|
||||
default = true;
|
||||
};
|
||||
|
||||
restartIfChanged = mkOption {
|
||||
type = types.bool;
|
||||
default = config.config != null;
|
||||
description = ''
|
||||
Restart this MicroVM's services if the systemd units are changed,
|
||||
i.e. if it has been updated by rebuilding the host.
|
||||
|
||||
Defaults to true for fully-declarative MicroVMs.
|
||||
'';
|
||||
};
|
||||
};
|
||||
}
|
||||
)
|
||||
);
|
||||
default = { };
|
||||
description = ''
|
||||
The MicroVMs that shall be built declaratively with the host NixOS.
|
||||
'';
|
||||
};
|
||||
|
||||
stateDir = mkOption {
|
||||
type = types.path;
|
||||
default = "/var/lib/microvms";
|
||||
description = ''
|
||||
Directory that contains the MicroVMs
|
||||
'';
|
||||
};
|
||||
|
||||
autostart = mkOption {
|
||||
type = with types; listOf str;
|
||||
default = [ ];
|
||||
description = ''
|
||||
MicroVMs to start by default.
|
||||
|
||||
This includes declarative `config.microvm.vms` as well as MicroVMs that are managed through the `microvm` command.
|
||||
'';
|
||||
};
|
||||
};
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue