From 543bb867d9e4f04a131d653fd063ac1f39bbe591 Mon Sep 17 00:00:00 2001 From: codgician <15964984+codgician@users.noreply.github.com> Date: Thu, 5 Dec 2024 22:52:22 +0800 Subject: [PATCH] waagent: init module --- nixos/modules/module-list.nix | 1 + nixos/modules/virtualisation/azure-agent.nix | 333 ++++-------------- nixos/modules/virtualisation/waagent.nix | 334 +++++++++++++++++++ pkgs/by-name/wa/waagent/package.nix | 10 +- 4 files changed, 413 insertions(+), 265 deletions(-) create mode 100644 nixos/modules/virtualisation/waagent.nix diff --git a/nixos/modules/module-list.nix b/nixos/modules/module-list.nix index 3b31c12df77aaf..4c616a59a5cc54 100644 --- a/nixos/modules/module-list.nix +++ b/nixos/modules/module-list.nix @@ -1756,6 +1756,7 @@ ./virtualisation/virtualbox-host.nix ./virtualisation/vmware-guest.nix ./virtualisation/vmware-host.nix + ./virtualisation/waagent.nix ./virtualisation/waydroid.nix ./virtualisation/xe-guest-utilities.nix ./virtualisation/xen-dom0.nix diff --git a/nixos/modules/virtualisation/azure-agent.nix b/nixos/modules/virtualisation/azure-agent.nix index 8903bf0985a2a2..d9f2d54031c78f 100644 --- a/nixos/modules/virtualisation/azure-agent.nix +++ b/nixos/modules/virtualisation/azure-agent.nix @@ -1,18 +1,61 @@ -{ config, lib, pkgs, ... }: +{ + config, + lib, + ... +}: with lib; let - cfg = config.virtualisation.azure.agent; - - provisionedHook = pkgs.writeScript "provisioned-hook" '' - #!${pkgs.runtimeShell} - /run/current-system/systemd/bin/systemctl start provisioned.target - ''; - in { + imports = [ + (mkChangedOptionModule + [ + "virtualisation" + "azure" + "agent" + "enable" + ] + [ + "services" + "waagent" + "enable" + ] + ) + (mkChangedOptionModule + [ + "virtualisation" + "azure" + "agent" + "verboseLogging" + ] + [ + "services" + "waagent" + "settings" + "Logs" + "Verbose" + ] + ) + (mkChangedOptionModule + [ + "virtualisation" + "azure" + "agent" + "mountResourceDisk" + ] + [ + "services" + "waagent" + "settings" + "ResourceDisk" + "Format" + ] + ) + ]; + ###### interface options.virtualisation.azure.agent = { @@ -32,260 +75,24 @@ in ###### implementation - config = lib.mkIf cfg.enable { - assertions = [{ - assertion = config.networking.networkmanager.enable == false; - message = "Windows Azure Linux Agent is not compatible with NetworkManager"; - }]; - - boot.initrd.kernelModules = [ "ata_piix" ]; - networking.firewall.allowedUDPPorts = [ 68 ]; - - - environment.etc."waagent.conf".text = '' - # - # Microsoft Azure Linux Agent Configuration - # - - # Enable extension handling. Do not disable this unless you do not need password reset, - # backup, monitoring, or any extension handling whatsoever. - Extensions.Enabled=y - - # How often (in seconds) to poll for new goal states - Extensions.GoalStatePeriod=6 - - # Which provisioning agent to use. Supported values are "auto" (default), "waagent", - # "cloud-init", or "disabled". - Provisioning.Agent=auto - - # Password authentication for root account will be unavailable. - Provisioning.DeleteRootPassword=n - - # Generate fresh host key pair. - Provisioning.RegenerateSshHostKeyPair=n - - # Supported values are "rsa", "dsa", "ecdsa", "ed25519", and "auto". - # The "auto" option is supported on OpenSSH 5.9 (2011) and later. - Provisioning.SshHostKeyPairType=ed25519 - - # Monitor host name changes and publish changes via DHCP requests. - Provisioning.MonitorHostName=y - - # How often (in seconds) to monitor host name changes. - Provisioning.MonitorHostNamePeriod=30 - - # Decode CustomData from Base64. - Provisioning.DecodeCustomData=n - - # Execute CustomData after provisioning. - Provisioning.ExecuteCustomData=n - - # Algorithm used by crypt when generating password hash. - #Provisioning.PasswordCryptId=6 - - # Length of random salt used when generating password hash. - #Provisioning.PasswordCryptSaltLength=10 - - # Allow reset password of sys user - Provisioning.AllowResetSysUser=n - - # Format if unformatted. If 'n', resource disk will not be mounted. - ResourceDisk.Format=${if cfg.mountResourceDisk then "y" else "n"} - - # File system on the resource disk - # Typically ext3 or ext4. FreeBSD images should use 'ufs2' here. - ResourceDisk.Filesystem=ext4 - - # Mount point for the resource disk - ResourceDisk.MountPoint=/mnt/resource - - # Create and use swapfile on resource disk. - ResourceDisk.EnableSwap=n - - # Size of the swapfile. - ResourceDisk.SwapSizeMB=0 - - # Comma-separated list of mount options. See mount(8) for valid options. - ResourceDisk.MountOptions=None - - # Enable verbose logging (y|n) - Logs.Verbose=${if cfg.verboseLogging then "y" else "n"} - - # Enable Console logging, default is y - # Logs.Console=y - - # Enable periodic log collection, default is n - Logs.Collect=n - - # How frequently to collect logs, default is each hour - Logs.CollectPeriod=3600 - - # Is FIPS enabled - OS.EnableFIPS=n - - # Root device timeout in seconds. - OS.RootDeviceScsiTimeout=300 - - # How often (in seconds) to set the root device timeout. - OS.RootDeviceScsiTimeoutPeriod=30 - - # If "None", the system default version is used. - OS.OpensslPath=${pkgs.openssl_3.bin}/bin/openssl - - # Set the SSH ClientAliveInterval - # OS.SshClientAliveInterval=180 - - # Set the path to SSH keys and configuration files - OS.SshDir=/etc/ssh - - # If set, agent will use proxy server to access internet - #HttpProxy.Host=None - #HttpProxy.Port=None - - # Detect Scvmm environment, default is n - # DetectScvmmEnv=n - - # - # Lib.Dir=/var/lib/waagent - - # - # DVD.MountPoint=/mnt/cdrom/secure - - # - # Pid.File=/var/run/waagent.pid - - # - # Extension.LogDir=/var/log/azure - - # - # Home.Dir=/home - - # Enable RDMA management and set up, should only be used in HPC images - OS.EnableRDMA=n - - # Enable checking RDMA driver version and update - # OS.CheckRdmaDriver=y - - # Enable or disable goal state processing auto-update, default is enabled - AutoUpdate.Enabled=n - - # Determine the update family, this should not be changed - # AutoUpdate.GAFamily=Prod - - # Determine if the overprovisioning feature is enabled. If yes, hold extension - # handling until inVMArtifactsProfile.OnHold is false. - # Default is enabled - EnableOverProvisioning=n - - # Allow fallback to HTTP if HTTPS is unavailable - # Note: Allowing HTTP (vs. HTTPS) may cause security risks - # OS.AllowHTTP=n - - # Add firewall rules to protect access to Azure host node services - OS.EnableFirewall=n - - # How often (in seconds) to check the firewall rules - OS.EnableFirewallPeriod=30 - - # How often (in seconds) to remove the udev rules for persistent network interface - # names (75-persistent-net-generator.rules and /etc/udev/rules.d/70-persistent-net.rules) - OS.RemovePersistentNetRulesPeriod=30 - - # How often (in seconds) to monitor for DHCP client restarts - OS.MonitorDhcpClientRestartPeriod=30 - ''; - - services.udev.packages = [ pkgs.waagent ]; - - # Provide waagent-shipped udev rules in initrd too. - boot.initrd.services.udev.packages = [ pkgs.waagent ]; - # udev rules shell out to chmod, cut and readlink, which are all - # provided by pkgs.coreutils, which is in services.udev.path, but not - # boot.initrd.services.udev.binPackages. - boot.initrd.services.udev.binPackages = [ pkgs.coreutils ]; - - networking.dhcpcd.persistent = true; - - services.logrotate = { - enable = true; - settings."/var/log/waagent.log" = { - compress = true; - frequency = "monthly"; - rotate = 6; - }; - }; - - systemd.targets.provisioned = { - description = "Services Requiring Azure VM provisioning to have finished"; - }; - - systemd.services.consume-hypervisor-entropy = + config = lib.mkIf cfg.enable ( + lib.warn + '' + `virtualisation.azure.agent` provided by `azure-agent.nix` module has been replaced + by `services.waagent` options, and will be removed in a future release. + '' { - description = "Consume entropy in ACPI table provided by Hyper-V"; - - wantedBy = [ "sshd.service" "waagent.service" ]; - before = [ "sshd.service" "waagent.service" ]; - - path = [ pkgs.coreutils ]; - script = - '' - echo "Fetching entropy..." - cat /sys/firmware/acpi/tables/OEM0 > /dev/random - ''; - serviceConfig.Type = "oneshot"; - serviceConfig.RemainAfterExit = true; - serviceConfig.StandardError = "journal+console"; - serviceConfig.StandardOutput = "journal+console"; - }; - - systemd.services.waagent = { - wantedBy = [ "multi-user.target" ]; - after = [ "network-online.target" "sshd.service" ]; - wants = [ "network-online.target" ]; - - path = [ - pkgs.e2fsprogs - pkgs.bash - - pkgs.findutils - pkgs.gnugrep - pkgs.gnused - pkgs.iproute2 - pkgs.iptables - - # for hostname - pkgs.nettools - - pkgs.openssh - pkgs.openssl - pkgs.parted - - # for pidof - pkgs.procps - - # for useradd, usermod - pkgs.shadow - - pkgs.util-linux # for (u)mount, fdisk, sfdisk, mkswap - - # waagent's Microsoft.OSTCExtensions.VMAccessForLinux needs Python 3 - pkgs.python39 - - # waagent's Microsoft.CPlat.Core.RunCommandLinux needs lsof - pkgs.lsof - ]; - description = "Windows Azure Agent Service"; - unitConfig.ConditionPathExists = "/etc/waagent.conf"; - serviceConfig = { - ExecStart = "${pkgs.waagent}/bin/waagent -daemon"; - Type = "simple"; - }; - }; - - # waagent will generate files under /etc/sudoers.d during provisioning - security.sudo.extraConfig = '' - #includedir /etc/sudoers.d - ''; - - }; + services.waagent = { + inherit (cfg) enable; + settings = { + Logs.Verbose = cfg.verboseLogging; + ResourceDisk = lib.mkIf cfg.mountResourceDisk { + Format = true; + FileSystem = "ext4"; + MountPoint = "/mnt/resource"; + }; + }; + }; + } + ); } diff --git a/nixos/modules/virtualisation/waagent.nix b/nixos/modules/virtualisation/waagent.nix new file mode 100644 index 00000000000000..ff48af4e11aaa8 --- /dev/null +++ b/nixos/modules/virtualisation/waagent.nix @@ -0,0 +1,334 @@ +{ + config, + lib, + pkgs, + ... +}: + +with lib; +let + cfg = config.services.waagent; + + # Format for waagent.conf + settingsFormat = { + type = + with types; + let + singleAtom = + (oneOf [ + null + bool + str + int + float + ]) + // { + description = "atom (null, bool, string, int or float)"; + }; + atom = coercedTo singleAtom singleton (nonEmptyListOf singleAtom) // { + description = singleAtom.description + " or a non-empty list of them"; + }; + in + (oneOf [ + atom + (attrsOf atom) + ]) + // { + description = atom.description + " or a attribute set of them"; + }; + generate = + name: value: + let + # Transform non-attribute values + transform = + x: + # Transform bool to "y" or "n" + if (isBool x) then + (if x then "y" else "n") + # Concatenate list items with comma + else if (isList x) then + (concatStringsSep "," (map transform xs)) + else + toString x; + + # Convert to format of waagent.conf + recurse = + path: value: + if builtins.isAttrs value then + pipe value [ + (mapAttrsToList (k: v: recurse (path ++ [ k ]) v)) + concatLists + ] + else + [ + { + name = concatStringsSep "." path; + inherit value; + } + ]; + convert = + attrs: + pipe (recurse [ ] attrs) [ + (filter (kv: kv.value != null)) + (map (kv: "${kv.name}=${transform kv.value}")) + (concatStringsSep "\n") + ]; + in + pkgs.writeText name (convert value); + }; + + settingsType = types.submodule { + freeformType = settingsFormat.type; + options = { + Provisioning = { + Enable = mkOption { + type = types.bool; + default = !config.services.cloud-init.enable; + defaultText = literalExpression "!config.services.cloud-init.enable"; + description = '' + Whether to enable provisioning functionality in the agent. + + If provisioning is disabled, SSH host and user keys in the image are preserved + and configuration in the Azure provisioning API is ignored. + + Set to `false` if cloud-init is used for provisioning tasks. + ''; + }; + + Agent = mkOption { + type = types.enum [ + "auto" + "waagent" + "cloud-init" + "disabled" + ]; + default = "auto"; + description = '' + Which provisioning agent to use. + ''; + }; + }; + + ResourceDisk = { + Format = mkEnableOption '' + If set to `true`, waagent formats and mounts the resource disk that the platform provides, + unless the file system type in `ResourceDisk.FileSystem` is set to `ntfs`. + The agent makes a single Linux partition (ID 83) available on the disk. + This partition isn't formatted if it can be successfully mounted. + + This configuration has no effect if resource disk is managed by cloud-init. + ''; + + FileSystem = mkOption { + type = types.str; + default = "ext4"; + description = '' + The file system type for the resource disk. + If the string is `X`, then `mkfs.X` should be present in the environment. + You can add additional filesystem packages using `services.waagent.extraPackages`. + + This configuration has no effect if resource disk is managed by cloud-init. + ''; + }; + + MountPoint = mkOption { + type = types.str; + default = "/mnt/resource"; + description = '' + This option specifies the path at which the resource disk is mounted. + The resource disk is a temporary disk and might be emptied when the VM is deprovisioned. + + This configuration has no effect if resource disk is managed by cloud-init. + ''; + }; + + EnableSwap = mkEnableOption '' + If enabled, the agent creates a swap file (`/swapfile`) on the resource disk + and adds it to the system swap space. + + This configuration has no effect if resource disk is managed by cloud-init. + ''; + + SwapSizeMB = mkOption { + type = types.int; + default = 0; + description = '' + Specifies the size of the swap file in megabytes. + + This configuration has no effect if resource disk is managed by cloud-init. + ''; + }; + }; + + Logs.Verbose = lib.mkEnableOption '' + If you set this option, log verbosity is boosted. + Waagent logs to `/var/log/waagent.log` and uses the system logrotate functionality to rotate logs. + ''; + + OS = { + EnableRDMA = lib.mkEnableOption '' + If enabled, the agent attempts to install and then load an RDMA kernel driver + that matches the version of the firmware on the underlying hardware. + ''; + + RootDeviceScsiTimeout = lib.mkOption { + type = types.nullOr types.int; + default = 300; + description = '' + Configures the SCSI timeout in seconds on the OS disk and data drives. + If set to `null`, the system defaults are used. + ''; + }; + }; + + HttpProxy = { + Host = lib.mkOption { + type = types.nullOr types.str; + default = null; + description = '' + If you set http proxy, waagent will use is proxy to access the Internet. + ''; + }; + + Port = lib.mkOption { + type = types.nullOr types.int; + default = null; + description = '' + If you set http proxy, waagent will use this proxy to access the Internet. + ''; + }; + }; + + AutoUpdate.Enable = lib.mkEnableOption '' + Enable or disable autoupdate for goal state processing. + ''; + }; + }; +in +{ + options.services.waagent = { + enable = lib.mkEnableOption '' + Whether to enable the Windows Azure Linux Agent. + ''; + + package = lib.mkPackageOption pkgs "waagent" { }; + + extraPackages = lib.mkOption { + default = [ ]; + description = '' + Additional packages to add to the waagent {env}`PATH`. + ''; + example = lib.literalExpression "[ pkgs.powershell ]"; + type = lib.types.listOf lib.types.package; + }; + + settings = lib.mkOption { + type = settingsType; + default = { }; + description = '' + The waagent.conf configuration, see https://learn.microsoft.com/en-us/azure/virtual-machines/extensions/agent-linux for documentation. + ''; + }; + }; + + config = lib.mkIf cfg.enable { + boot.initrd.kernelModules = [ "ata_piix" ]; + networking.firewall.allowedUDPPorts = [ 68 ]; + + services.udev.packages = with pkgs; [ waagent ]; + + boot.initrd.services.udev = with pkgs; { + # Provide waagent-shipped udev rules in initrd too. + packages = [ waagent ]; + # udev rules shell out to chmod, cut and readlink, which are all + # provided by pkgs.coreutils, which is in services.udev.path, but not + # boot.initrd.services.udev.binPackages. + binPackages = [ coreutils ]; + }; + + networking.dhcpcd.persistent = true; + + services.logrotate = { + enable = true; + settings."/var/log/waagent.log" = { + compress = true; + frequency = "monthly"; + rotate = 6; + }; + }; + + # Write settings to /etc/waagent.conf + environment.etc."waagent.conf".source = settingsFormat.generate "waagent.conf" cfg.settings; + + systemd.targets.provisioned = { + description = "Services Requiring Azure VM provisioning to have finished"; + }; + + systemd.services.consume-hypervisor-entropy = { + description = "Consume entropy in ACPI table provided by Hyper-V"; + + wantedBy = [ + "sshd.service" + "waagent.service" + ]; + before = [ + "sshd.service" + "waagent.service" + ]; + + path = [ pkgs.coreutils ]; + script = '' + echo "Fetching entropy..." + cat /sys/firmware/acpi/tables/OEM0 > /dev/random + ''; + serviceConfig.Type = "oneshot"; + serviceConfig.RemainAfterExit = true; + serviceConfig.StandardError = "journal+console"; + serviceConfig.StandardOutput = "journal+console"; + }; + + systemd.services.waagent = { + wantedBy = [ "multi-user.target" ]; + after = [ + "network-online.target" + "sshd.service" + ]; + wants = [ "network-online.target" ]; + + path = with pkgs; [ + e2fsprogs + bash + findutils + gnugrep + gnused + iproute2 + iptables + openssh + openssl + parted + + # for hostname + nettools + # for pidof + procps + # for useradd, usermod + shadow + + util-linux # for (u)mount, fdisk, sfdisk, mkswap + # waagent's Microsoft.CPlat.Core.RunCommandLinux needs lsof + lsof + ]; + description = "Windows Azure Agent Service"; + unitConfig.ConditionPathExists = "/etc/waagent.conf"; + serviceConfig = { + ExecStart = "${lib.getExe cfg.package} -daemon"; + Type = "simple"; + }; + }; + + # waagent will generate files under /etc/sudoers.d during provisioning + security.sudo.extraConfig = '' + #includedir /etc/sudoers.d + ''; + }; +} diff --git a/pkgs/by-name/wa/waagent/package.nix b/pkgs/by-name/wa/waagent/package.nix index cbfbc9e026dac7..6f5246de6811fd 100644 --- a/pkgs/by-name/wa/waagent/package.nix +++ b/pkgs/by-name/wa/waagent/package.nix @@ -4,6 +4,7 @@ lib, python3, bash, + gitUpdater, }: let @@ -63,7 +64,11 @@ python.pkgs.buildPythonApplication rec { dontWrapPythonPrograms = false; - meta = { + passthru.updateScript = gitUpdater { + rev-prefix = "v"; + }; + + meta = with lib; { description = "Microsoft Azure Linux Agent (waagent)"; mainProgram = "waagent"; longDescription = '' @@ -71,6 +76,7 @@ python.pkgs.buildPythonApplication rec { manages Linux provisioning and VM interaction with the Azure Fabric Controller''; homepage = "https://github.com/Azure/WALinuxAgent"; - license = with lib.licenses; [ asl20 ]; + maintainers = with maintainers; [ codgician ]; + license = with licenses; [ asl20 ]; }; }