diff options
Diffstat (limited to 'nixos/modules/virtualisation')
-rw-r--r-- | nixos/modules/virtualisation/lxc-container.nix | 130 | ||||
-rw-r--r-- | nixos/modules/virtualisation/lxc-image-metadata.nix | 104 | ||||
-rw-r--r-- | nixos/modules/virtualisation/lxc-instance-common.nix | 30 | ||||
-rw-r--r-- | nixos/modules/virtualisation/lxd-virtual-machine.nix | 46 | ||||
-rw-r--r-- | nixos/modules/virtualisation/lxd.nix | 129 |
5 files changed, 287 insertions, 152 deletions
diff --git a/nixos/modules/virtualisation/lxc-container.nix b/nixos/modules/virtualisation/lxc-container.nix index 55b285b69147..9402d3bf37d0 100644 --- a/nixos/modules/virtualisation/lxc-container.nix +++ b/nixos/modules/virtualisation/lxc-container.nix @@ -1,96 +1,16 @@ { lib, config, pkgs, ... }: -with lib; - let - templateSubmodule = { ... }: { - options = { - enable = mkEnableOption (lib.mdDoc "this template"); - - target = mkOption { - description = lib.mdDoc "Path in the container"; - type = types.path; - }; - template = mkOption { - description = lib.mdDoc ".tpl file for rendering the target"; - type = types.path; - }; - when = mkOption { - description = lib.mdDoc "Events which trigger a rewrite (create, copy)"; - type = types.listOf (types.str); - }; - properties = mkOption { - description = lib.mdDoc "Additional properties"; - type = types.attrs; - default = {}; - }; - }; - }; - - toYAML = name: data: pkgs.writeText name (generators.toYAML {} data); - cfg = config.virtualisation.lxc; - templates = if cfg.templates != {} then let - list = mapAttrsToList (name: value: { inherit name; } // value) - (filterAttrs (name: value: value.enable) cfg.templates); - in - { - files = map (tpl: { - source = tpl.template; - target = "/templates/${tpl.name}.tpl"; - }) list; - properties = listToAttrs (map (tpl: nameValuePair tpl.target { - when = tpl.when; - template = "${tpl.name}.tpl"; - properties = tpl.properties; - }) list); - } - else { files = []; properties = {}; }; - -in -{ +in { imports = [ - ../installer/cd-dvd/channel.nix - ../profiles/clone-config.nix - ../profiles/minimal.nix + ./lxc-instance-common.nix ]; options = { virtualisation.lxc = { - templates = mkOption { - description = lib.mdDoc "Templates for LXD"; - type = types.attrsOf (types.submodule (templateSubmodule)); - default = {}; - example = literalExpression '' - { - # create /etc/hostname on container creation. also requires networking.hostName = "" to be set - "hostname" = { - enable = true; - target = "/etc/hostname"; - template = builtins.toFile "hostname.tpl" "{{ container.name }}"; - when = [ "create" ]; - }; - # create /etc/nixos/hostname.nix with a configuration for keeping the hostname applied - "hostname-nix" = { - enable = true; - target = "/etc/nixos/hostname.nix"; - template = builtins.toFile "hostname-nix.tpl" "{ ... }: { networking.hostName = \"{{ container.name }}\"; }"; - # copy keeps the file updated when the container is changed - when = [ "create" "copy" ]; - }; - # copy allow the user to specify a custom configuration.nix - "configuration-nix" = { - enable = true; - target = "/etc/nixos/configuration.nix"; - template = builtins.toFile "configuration-nix" "{{ config_get(\"user.user-data\", properties.default) }}"; - when = [ "create" ]; - }; - }; - ''; - }; - - privilegedContainer = mkOption { - type = types.bool; + privilegedContainer = lib.mkOption { + type = lib.types.bool; default = false; description = lib.mdDoc '' Whether this LXC container will be running as a privileged container or not. If set to `true` then @@ -116,24 +36,6 @@ in ${config.nix.package.out}/bin/nix-env -p /nix/var/nix/profiles/system --set /run/current-system ''; - system.build.metadata = pkgs.callPackage ../../lib/make-system-tarball.nix { - contents = [ - { - source = toYAML "metadata.yaml" { - architecture = builtins.elemAt (builtins.match "^([a-z0-9_]+).+" (toString pkgs.system)) 0; - creation_date = 1; - properties = { - description = "${config.system.nixos.distroName} ${config.system.nixos.codeName} ${config.system.nixos.label} ${pkgs.system}"; - os = "${config.system.nixos.distroId}"; - release = "${config.system.nixos.codeName}"; - }; - templates = templates.properties; - }; - target = "/metadata.yaml"; - } - ] ++ templates.files; - }; - # TODO: build rootfs as squashfs for faster unpack system.build.tarball = pkgs.callPackage ../../lib/make-system-tarball.nix { extraArgs = "--owner=0"; @@ -180,7 +82,7 @@ in ProtectKernelTunables=no NoNewPrivileges=no LoadCredential= - '' + optionalString cfg.privilegedContainer '' + '' + lib.optionalString cfg.privilegedContainer '' # Additional settings for privileged containers ProtectHome=no ProtectSystem=no @@ -193,28 +95,8 @@ in }) ]; - # Allow the user to login as root without password. - users.users.root.initialHashedPassword = mkOverride 150 ""; - - system.activationScripts.installInitScript = mkForce '' + system.activationScripts.installInitScript = lib.mkForce '' ln -fs $systemConfig/init /sbin/init ''; - - # Some more help text. - services.getty.helpLine = - '' - - Log in as "root" with an empty password. - ''; - - # Containers should be light-weight, so start sshd on demand. - services.openssh.enable = mkDefault true; - services.openssh.startWhenNeeded = mkDefault true; - - # As this is intended as a standalone image, undo some of the minimal profile stuff - environment.noXlibs = false; - documentation.enable = true; - documentation.nixos.enable = true; - services.logrotate.enable = true; }; } diff --git a/nixos/modules/virtualisation/lxc-image-metadata.nix b/nixos/modules/virtualisation/lxc-image-metadata.nix new file mode 100644 index 000000000000..2c0568b4c468 --- /dev/null +++ b/nixos/modules/virtualisation/lxc-image-metadata.nix @@ -0,0 +1,104 @@ +{ lib, config, pkgs, ... }: + +let + templateSubmodule = {...}: { + options = { + enable = lib.mkEnableOption "this template"; + + target = lib.mkOption { + description = "Path in the container"; + type = lib.types.path; + }; + template = lib.mkOption { + description = ".tpl file for rendering the target"; + type = lib.types.path; + }; + when = lib.mkOption { + description = "Events which trigger a rewrite (create, copy)"; + type = lib.types.listOf (lib.types.str); + }; + properties = lib.mkOption { + description = "Additional properties"; + type = lib.types.attrs; + default = {}; + }; + }; + }; + + toYAML = name: data: pkgs.writeText name (lib.generators.toYAML {} data); + + cfg = config.virtualisation.lxc; + templates = if cfg.templates != {} then let + list = lib.mapAttrsToList (name: value: { inherit name; } // value) + (lib.filterAttrs (name: value: value.enable) cfg.templates); + in + { + files = map (tpl: { + source = tpl.template; + target = "/templates/${tpl.name}.tpl"; + }) list; + properties = lib.listToAttrs (map (tpl: lib.nameValuePair tpl.target { + when = tpl.when; + template = "${tpl.name}.tpl"; + properties = tpl.properties; + }) list); + } + else { files = []; properties = {}; }; + +in { + options = { + virtualisation.lxc = { + templates = lib.mkOption { + description = "Templates for LXD"; + type = lib.types.attrsOf (lib.types.submodule templateSubmodule); + default = {}; + example = lib.literalExpression '' + { + # create /etc/hostname on container creation + "hostname" = { + enable = true; + target = "/etc/hostname"; + template = builtins.writeFile "hostname.tpl" "{{ container.name }}"; + when = [ "create" ]; + }; + # create /etc/nixos/hostname.nix with a configuration for keeping the hostname applied + "hostname-nix" = { + enable = true; + target = "/etc/nixos/hostname.nix"; + template = builtins.writeFile "hostname-nix.tpl" "{ ... }: { networking.hostName = "{{ container.name }}"; }"; + # copy keeps the file updated when the container is changed + when = [ "create" "copy" ]; + }; + # copy allow the user to specify a custom configuration.nix + "configuration-nix" = { + enable = true; + target = "/etc/nixos/configuration.nix"; + template = builtins.writeFile "configuration-nix" "{{ config_get(\"user.user-data\", properties.default) }}"; + when = [ "create" ]; + }; + }; + ''; + }; + }; + }; + + config = { + system.build.metadata = pkgs.callPackage ../../lib/make-system-tarball.nix { + contents = [ + { + source = toYAML "metadata.yaml" { + architecture = builtins.elemAt (builtins.match "^([a-z0-9_]+).+" (toString pkgs.system)) 0; + creation_date = 1; + properties = { + description = "${config.system.nixos.distroName} ${config.system.nixos.codeName} ${config.system.nixos.label} ${pkgs.system}"; + os = "${config.system.nixos.distroId}"; + release = "${config.system.nixos.codeName}"; + }; + templates = templates.properties; + }; + target = "/metadata.yaml"; + } + ] ++ templates.files; + }; + }; +} diff --git a/nixos/modules/virtualisation/lxc-instance-common.nix b/nixos/modules/virtualisation/lxc-instance-common.nix new file mode 100644 index 000000000000..d6a0e05fb1c9 --- /dev/null +++ b/nixos/modules/virtualisation/lxc-instance-common.nix @@ -0,0 +1,30 @@ +{lib, ...}: + +{ + imports = [ + ./lxc-image-metadata.nix + + ../installer/cd-dvd/channel.nix + ../profiles/clone-config.nix + ../profiles/minimal.nix + ]; + + # Allow the user to login as root without password. + users.users.root.initialHashedPassword = lib.mkOverride 150 ""; + + # Some more help text. + services.getty.helpLine = '' + + Log in as "root" with an empty password. + ''; + + # Containers should be light-weight, so start sshd on demand. + services.openssh.enable = lib.mkDefault true; + services.openssh.startWhenNeeded = lib.mkDefault true; + + # As this is intended as a standalone image, undo some of the minimal profile stuff + environment.noXlibs = false; + documentation.enable = true; + documentation.nixos.enable = true; + services.logrotate.enable = true; +} diff --git a/nixos/modules/virtualisation/lxd-virtual-machine.nix b/nixos/modules/virtualisation/lxd-virtual-machine.nix new file mode 100644 index 000000000000..ba729465ec2f --- /dev/null +++ b/nixos/modules/virtualisation/lxd-virtual-machine.nix @@ -0,0 +1,46 @@ +{ config, lib, pkgs, ... }: + +let + serialDevice = + if pkgs.stdenv.hostPlatform.isx86 + then "ttyS0" + else "ttyAMA0"; # aarch64 +in { + imports = [ + ./lxc-instance-common.nix + + ../profiles/qemu-guest.nix + ]; + + config = { + system.build.qemuImage = import ../../lib/make-disk-image.nix { + inherit pkgs lib config; + + partitionTableType = "efi"; + format = "qcow2-compressed"; + copyChannel = true; + }; + + fileSystems = { + "/" = { + device = "/dev/disk/by-label/nixos"; + autoResize = true; + fsType = "ext4"; + }; + "/boot" = { + device = "/dev/disk/by-label/ESP"; + fsType = "vfat"; + }; + }; + + boot.growPartition = true; + boot.loader.systemd-boot.enable = true; + + # image building needs to know what device to install bootloader on + boot.loader.grub.device = "/dev/vda"; + + boot.kernelParams = ["console=tty1" "console=${serialDevice}"]; + + virtualisation.lxd.agent.enable = lib.mkDefault true; + }; +} diff --git a/nixos/modules/virtualisation/lxd.nix b/nixos/modules/virtualisation/lxd.nix index e22ba9a0ae2c..e30fbebb662c 100644 --- a/nixos/modules/virtualisation/lxd.nix +++ b/nixos/modules/virtualisation/lxd.nix @@ -2,21 +2,20 @@ { config, lib, pkgs, ... }: -with lib; - let cfg = config.virtualisation.lxd; + preseedFormat = pkgs.formats.yaml {}; in { imports = [ - (mkRemovedOptionModule [ "virtualisation" "lxd" "zfsPackage" ] "Override zfs in an overlay instead to override it globally") + (lib.mkRemovedOptionModule [ "virtualisation" "lxd" "zfsPackage" ] "Override zfs in an overlay instead to override it globally") ]; ###### interface options = { virtualisation.lxd = { - enable = mkOption { - type = types.bool; + enable = lib.mkOption { + type = lib.types.bool; default = false; description = lib.mdDoc '' This option enables lxd, a daemon that manages @@ -32,28 +31,28 @@ in { ''; }; - package = mkOption { - type = types.package; + package = lib.mkOption { + type = lib.types.package; default = pkgs.lxd; - defaultText = literalExpression "pkgs.lxd"; + defaultText = lib.literalExpression "pkgs.lxd"; description = lib.mdDoc '' The LXD package to use. ''; }; - lxcPackage = mkOption { - type = types.package; + lxcPackage = lib.mkOption { + type = lib.types.package; default = pkgs.lxc; - defaultText = literalExpression "pkgs.lxc"; + defaultText = lib.literalExpression "pkgs.lxc"; description = lib.mdDoc '' The LXC package to use with LXD (required for AppArmor profiles). ''; }; - zfsSupport = mkOption { - type = types.bool; + zfsSupport = lib.mkOption { + type = lib.types.bool; default = config.boot.zfs.enabled; - defaultText = literalExpression "config.boot.zfs.enabled"; + defaultText = lib.literalExpression "config.boot.zfs.enabled"; description = lib.mdDoc '' Enables lxd to use zfs as a storage for containers. @@ -62,8 +61,8 @@ in { ''; }; - recommendedSysctlSettings = mkOption { - type = types.bool; + recommendedSysctlSettings = lib.mkOption { + type = lib.types.bool; default = false; description = lib.mdDoc '' Enables various settings to avoid common pitfalls when @@ -75,8 +74,67 @@ in { ''; }; - startTimeout = mkOption { - type = types.int; + preseed = lib.mkOption { + type = lib.types.nullOr (lib.types.submodule { + freeformType = preseedFormat.type; + }); + + default = null; + + description = lib.mdDoc '' + Configuration for LXD preseed, see + <https://documentation.ubuntu.com/lxd/en/latest/howto/initialize/#initialize-preseed> + for supported values. + + Changes to this will be re-applied to LXD which will overwrite existing entities or create missing ones, + but entities will *not* be removed by preseed. + ''; + + example = lib.literalExpression '' + { + networks = [ + { + name = "lxdbr0"; + type = "bridge"; + config = { + "ipv4.address" = "10.0.100.1/24"; + "ipv4.nat" = "true"; + }; + } + ]; + profiles = [ + { + name = "default"; + devices = { + eth0 = { + name = "eth0"; + network = "lxdbr0"; + type = "nic"; + }; + root = { + path = "/"; + pool = "default"; + size = "35GiB"; + type = "disk"; + }; + }; + } + ]; + storage_pools = [ + { + name = "default"; + driver = "dir"; + config = { + source = "/var/lib/lxd/storage-pools/default"; + }; + } + ]; + } + ''; + }; + + startTimeout = lib.mkOption { + type = lib.types.int; default = 600; apply = toString; description = lib.mdDoc '' @@ -91,13 +149,13 @@ in { Enables the (experimental) LXD UI. ''); - package = mkPackageOption pkgs.lxd-unwrapped "ui" { }; + package = lib.mkPackageOption pkgs.lxd-unwrapped "ui" { }; }; }; }; ###### implementation - config = mkIf cfg.enable { + config = lib.mkIf cfg.enable { environment.systemPackages = [ cfg.package ]; # Note: the following options are also declared in virtualisation.lxc, but @@ -139,19 +197,19 @@ in { wantedBy = [ "multi-user.target" ]; after = [ "network-online.target" - (mkIf config.virtualisation.lxc.lxcfs.enable "lxcfs.service") + (lib.mkIf config.virtualisation.lxc.lxcfs.enable "lxcfs.service") ]; requires = [ "network-online.target" "lxd.socket" - (mkIf config.virtualisation.lxc.lxcfs.enable "lxcfs.service") + (lib.mkIf config.virtualisation.lxc.lxcfs.enable "lxcfs.service") ]; documentation = [ "man:lxd(1)" ]; path = [ pkgs.util-linux ] - ++ optional cfg.zfsSupport config.boot.zfs.package; + ++ lib.optional cfg.zfsSupport config.boot.zfs.package; - environment = mkIf (cfg.ui.enable) { + environment = lib.mkIf (cfg.ui.enable) { "LXD_UI" = cfg.ui.package; }; @@ -173,11 +231,26 @@ in { # By default, `lxd` loads configuration files from hard-coded # `/usr/share/lxc/config` - since this is a no-go for us, we have to # explicitly tell it where the actual configuration files are - Environment = mkIf (config.virtualisation.lxc.lxcfs.enable) + Environment = lib.mkIf (config.virtualisation.lxc.lxcfs.enable) "LXD_LXC_TEMPLATE_CONFIG=${pkgs.lxcfs}/share/lxc/config"; }; }; + systemd.services.lxd-preseed = lib.mkIf (cfg.preseed != null) { + description = "LXD initialization with preseed file"; + wantedBy = ["multi-user.target"]; + requires = ["lxd.service"]; + after = ["lxd.service"]; + + script = '' + ${pkgs.coreutils}/bin/cat ${preseedFormat.generate "lxd-preseed.yaml" cfg.preseed} | ${cfg.package}/bin/lxd init --preseed + ''; + + serviceConfig = { + Type = "oneshot"; + }; + }; + users.groups.lxd = {}; users.users.root = { @@ -185,7 +258,7 @@ in { subGidRanges = [ { startGid = 1000000; count = 65536; } ]; }; - boot.kernel.sysctl = mkIf cfg.recommendedSysctlSettings { + boot.kernel.sysctl = lib.mkIf cfg.recommendedSysctlSettings { "fs.inotify.max_queued_events" = 1048576; "fs.inotify.max_user_instances" = 1048576; "fs.inotify.max_user_watches" = 1048576; @@ -196,7 +269,7 @@ in { "kernel.keys.maxkeys" = 2000; }; - boot.kernelModules = [ "veth" "xt_comment" "xt_CHECKSUM" "xt_MASQUERADE" ] - ++ optionals (!config.networking.nftables.enable) [ "iptable_mangle" ]; + boot.kernelModules = [ "veth" "xt_comment" "xt_CHECKSUM" "xt_MASQUERADE" "vhost_vsock" ] + ++ lib.optionals (!config.networking.nftables.enable) [ "iptable_mangle" ]; }; } |