feat: wip: add container backend to guests

This commit is contained in:
oddlama 2023-12-17 02:04:20 +01:00
parent 83f1908e21
commit abb8330d86
No known key found for this signature in database
GPG key ID: 14EFE510775FE39A
23 changed files with 256 additions and 208 deletions

View file

@ -9,6 +9,6 @@
''This is \e{cyan}\n\e{reset} [\e{lightblue}\l\e{reset}] (\s \m \r)''
]
# Disabled for guests because of frequent redraws (-> pushed to syslog on the host)
++ lib.optional (!config.guests.isGuest) ''\e{halfbright}\4\e{reset} \e{halfbright}\6\e{reset}''
++ lib.optional (config.node.type == "host") ''\e{halfbright}\4\e{reset} \e{halfbright}\6\e{reset}''
++ [""]);
}

View file

@ -25,8 +25,9 @@
./config/system.nix
./config/users.nix
./guests
./meta/kanidm.nix
./meta/microvms.nix
./meta/nginx.nix
./meta/oauth2-proxy.nix
./meta/promtail.nix

View file

@ -0,0 +1,31 @@
_guestName: guestCfg: {lib, ...}: let
inherit (lib) mkForce;
in {
node.name = guestCfg.nodeName;
node.type = guestCfg.backend;
nix = {
settings.auto-optimise-store = mkForce false;
optimise.automatic = mkForce false;
gc.automatic = mkForce false;
};
systemd.network.networks = {
"10-${guestCfg.networking.mainLinkName}" = {
DHCP = "yes";
dhcpV4Config.UseDNS = false;
dhcpV6Config.UseDNS = false;
ipv6AcceptRAConfig.UseDNS = false;
networkConfig = {
IPv6PrivacyExtensions = "yes";
MulticastDNS = true;
IPv6AcceptRA = true;
};
linkConfig.RequiredForOnline = "routable";
};
};
networking.nftables.firewall = {
zones.untrusted.interfaces = [guestCfg.networking.mainLinkName];
};
}

View file

@ -0,0 +1,29 @@
guestName: guestCfg: {
config,
lib,
...
} @ attrs: let
inherit (lib) mkMerge;
in {
autoStart = guestCfg.autostart;
specialArgs =
attrs
// {
parentNode = config;
};
macvlans = [guestCfg.container.macvlan];
ephemeral = true;
privateNetwork = true;
config = mkMerge (guestCfg.modules
++ [
(import ./common-guest-config.nix guestName guestCfg)
{
systemd.network.networks = {
"10-${guestCfg.networking.mainLinkName}" = {
matchConfig.OriginalName = "mv-${guestCfg.container.macvlan}";
linkConfig.Name = guestCfg.networking.mainLinkName;
};
};
}
]);
}

View file

@ -4,12 +4,10 @@
lib,
pkgs,
utils,
minimal,
...
}: let
} @ attrs: let
inherit
(lib)
attrNames
attrValues
any
disko
@ -17,19 +15,13 @@
makeBinPath
mapAttrsToList
mergeToplevelConfigs
mkDefault
mkEnableOption
mkForce
mkIf
mkOption
net
optional
types
;
cfg = config.guests;
nodeName = config.node.name;
inherit (cfg) guests;
# Configuration required on the host for a specific guest
defineGuest = guestName: guestCfg: {
@ -79,115 +71,18 @@
requires = [fsMountUnit "zfs-chown-${utils.escapeSystemdPath guestCfg.zfs.mountpoint}.service"];
after = [fsMountUnit "zfs-chown-${utils.escapeSystemdPath guestCfg.zfs.mountpoint}.service"];
};
};
microvm.vms.${guestName} = let
mac = (net.mac.assignMacs "02:01:27:00:00:00" 24 [] (attrNames guests)).${guestName};
in
mkIf (guestCfg.backend == "microvm") {
# Allow children microvms to know which node is their parent
specialArgs = {
parentNode = config;
inherit (inputs.self) nodes;
inherit (inputs.self.pkgs.${guestCfg.microvm.system}) lib;
inherit inputs;
inherit minimal;
};
pkgs = inputs.self.pkgs.${guestCfg.microvm.system};
inherit (guestCfg) autostart;
config = {
imports = guestCfg.modules;
node.name = guestCfg.nodeName;
node.isGuest = true;
# TODO needed because of https://github.com/NixOS/nixpkgs/issues/102137
environment.noXlibs = mkForce false;
lib.microvm.mac = mac;
microvm = {
hypervisor = mkDefault "qemu";
# Give them some juice by default
mem = mkDefault (2 * 1024);
# MACVTAP bridge to the host's network
interfaces = [
{
type = "macvtap";
id = "vm-${guestName}";
inherit mac;
macvtap = {
link = cfg.networking.macvtapInterface;
mode = "bridge";
};
}
];
shares =
[
# Share the nix-store of the host
{
source = "/nix/store";
mountPoint = "/nix/.ro-store";
tag = "ro-store";
proto = "virtiofs";
}
{
source = "/state/guests/${guestName}";
mountPoint = "/state";
tag = "state";
proto = "virtiofs";
}
]
# Mount persistent data from the host
++ optional guestCfg.zfs.enable {
source = guestCfg.zfs.mountpoint;
mountPoint = "/persist";
tag = "persist";
proto = "virtiofs";
};
};
# FIXME this should be changed in microvm.nix to mkDefault in oder to not require mkForce here
fileSystems."/state".neededForBoot = mkForce true;
fileSystems."/persist".neededForBoot = mkForce true;
# Add a writable store overlay, but since this is always ephemeral
# disable any store optimization from nix.
microvm.writableStoreOverlay = "/nix/.rw-store";
nix = {
settings.auto-optimise-store = mkForce false;
optimise.automatic = mkForce false;
gc.automatic = mkForce false;
};
networking.renameInterfacesByMac.${guestCfg.networking.mainLinkName} = mac;
systemd.network.networks = {
"10-${guestCfg.networking.mainLinkName}" = {
matchConfig.MACAddress = mac;
DHCP = "yes";
dhcpV4Config.UseDNS = false;
dhcpV6Config.UseDNS = false;
ipv6AcceptRAConfig.UseDNS = false;
networkConfig = {
IPv6PrivacyExtensions = "yes";
MulticastDNS = true;
IPv6AcceptRA = true;
};
linkConfig.RequiredForOnline = "routable";
};
};
networking.nftables.firewall = {
zones.untrusted.interfaces = [guestCfg.networking.mainLinkName];
};
"container@${guestName}" = mkIf (guestCfg.backend == "container") {
requires = [fsMountUnit "zfs-chown-${utils.escapeSystemdPath guestCfg.zfs.mountpoint}.service"];
after = [fsMountUnit "zfs-chown-${utils.escapeSystemdPath guestCfg.zfs.mountpoint}.service"];
};
};
microvm.vms.${guestName} =
mkIf (guestCfg.backend == "microvm") (import ./microvm.nix guestName guestCfg attrs);
containers.${guestName} =
mkIf (guestCfg.backend == "microvm") {
};
mkIf (guestCfg.backend == "container") (import ./container.nix guestName guestCfg attrs);
};
in {
imports = [
@ -198,31 +93,16 @@ in {
microvm.host.enable =
any
(guestCfg: guestCfg.backend == "microvm")
(attrValues guests);
(attrValues config.guests);
}
];
options.node.isGuest = mkOption {
type = types.bool;
description = "Whether this machine is a guest on another machine.";
default = false;
options.node.type = mkOption {
type = types.enum ["host" "microvm" "container"];
description = "The type of this machine.";
default = "host";
};
# networking = {
# baseMac = mkOption {
# type = types.net.mac;
# description = ''
# This MAC address will be used as a base address to derive all MicroVM MAC addresses from.
# A good practise is to use the physical address of the macvtap interface.
# '';
# };
#
# macvtapInterface = mkOption {
# type = types.str;
# description = "The macvtap interface to which MicroVMs should be attached";
# };
# };
options.guests = mkOption {
default = {};
description = "Defines the actual vms and handles the necessary base setup for them.";
@ -252,6 +132,19 @@ in {
type = types.str;
description = "The system that this microvm should use";
};
macvtapInterface = mkOption {
type = types.str;
description = "The host macvtap interface to which the microvm should be attached";
};
};
# Options for the container backend
container = {
macvlan = mkOption {
type = types.str;
description = "The host interface to which the container should be attached";
};
};
networking = {
@ -298,5 +191,7 @@ in {
}));
};
config = mkIf (guests != {}) (mergeToplevelConfigs ["disko" "microvm" "systemd"] (mapAttrsToList defineGuest guests));
config =
mkIf (config.guests != {})
(mergeToplevelConfigs ["containers" "disko" "microvm" "systemd"] (mapAttrsToList defineGuest config.guests));
}

101
modules/guests/microvm.nix Normal file
View file

@ -0,0 +1,101 @@
guestName: guestCfg: {
config,
inputs,
lib,
pkgs,
minimal,
...
}: let
inherit
(lib)
attrNames
mkDefault
mkForce
net
optional
;
mac = (net.mac.assignMacs "02:01:27:00:00:00" 24 [] (attrNames config.guests)).${guestName};
in {
specialArgs = {
parentNode = config;
inherit (inputs.self) nodes;
inherit (inputs.self.pkgs.${guestCfg.microvm.system}) lib;
inherit inputs;
inherit minimal;
};
pkgs = inputs.self.pkgs.${guestCfg.microvm.system};
inherit (guestCfg) autostart;
config = {
imports = guestCfg.modules ++ [(import ./common-guest-config.nix guestName guestCfg)];
# TODO needed because of https://github.com/NixOS/nixpkgs/issues/102137
environment.noXlibs = mkForce false;
lib.microvm.mac = mac;
microvm = {
hypervisor = mkDefault "qemu";
# Give them some juice by default
mem = mkDefault (2 * 1024);
# MACVTAP bridge to the host's network
interfaces = [
{
type = "macvtap";
id = "vm-${guestName}";
inherit mac;
macvtap = {
link = guestCfg.microvm.macvtapInterface;
mode = "bridge";
};
}
];
shares =
[
# Share the nix-store of the host
{
source = "/nix/store";
mountPoint = "/nix/.ro-store";
tag = "ro-store";
proto = "virtiofs";
}
{
source = "/state/guests/${guestName}";
mountPoint = "/state";
tag = "state";
proto = "virtiofs";
}
]
# Mount persistent data from the host
++ optional guestCfg.zfs.enable {
source = guestCfg.zfs.mountpoint;
mountPoint = "/persist";
tag = "persist";
proto = "virtiofs";
};
};
# FIXME this should be changed in microvm.nix to mkDefault in oder to not require mkForce here
fileSystems."/state".neededForBoot = mkForce true;
fileSystems."/persist".neededForBoot = mkForce true;
# Add a writable store overlay, but since this is always ephemeral
# disable any store optimization from nix.
microvm.writableStoreOverlay = "/nix/.rw-store";
nix = {
settings.auto-optimise-store = mkForce false;
optimise.automatic = mkForce false;
gc.automatic = mkForce false;
};
networking.renameInterfacesByMac.${guestCfg.networking.mainLinkName} = mac;
systemd.network.networks = {
"10-${guestCfg.networking.mainLinkName}" = {
matchConfig.MACAddress = mac;
};
};
};
}