about summary refs log tree commit diff
path: root/nixos/modules/virtualisation/azure-agent.nix
blob: 5b3b7080ea686ef7ec523f8922e73111037b7d56 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
{ config, lib, pkgs, ... }:

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

{

  ###### interface

  options.virtualisation.azure.agent = {
    enable = mkOption {
      default = false;
      description = "Whether to enable the Windows Azure Linux Agent.";
    };
    verboseLogging = mkOption {
      default = false;
      description = "Whether to enable verbose logging.";
    };
    mountResourceDisk = mkOption {
      default = true;
      description = "Whether the agent should format (ext4) and mount the resource disk to /mnt/resource.";
    };
  };

  ###### implementation

  config = lib.mkIf cfg.enable {
    assertions = [{
      assertion = pkgs.stdenv.hostPlatform.isx86;
      message = "Azure not currently supported on ${pkgs.stdenv.hostPlatform.system}";
    }
      {
        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 =
      {
        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
    '';

  };
}