summaryrefslogtreecommitdiffstats
path: root/nixos/modules/virtualisation/amazon-image.nix
blob: a66687e3b36e2aecdd2d9a7df4a70d5905c0a5a5 (plain)
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
# Configuration for Amazon EC2 instances. (Note that this file is a
# misnomer - it should be "amazon-config.nix" or so, not
# "amazon-image.nix", since it's used not only to build images but
# also to reconfigure instances. However, we can't rename it because
# existing "configuration.nix" files on EC2 instances refer to it.)

{
  config,
  lib,
  pkgs,
  ...
}:

let
  inherit (lib) mkDefault mkIf;
  cfg = config.ec2;
in

{
  imports = [
    ../profiles/headless.nix
    # Note: While we do use the headless profile, we also explicitly
    # turn on the serial console on ttyS0 below. This is because
    # AWS does support accessing the serial console:
    # https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/configure-access-to-serial-console.html
    ./ec2-data.nix
    ./amazon-init.nix
  ];

  config = {

    assertions = [ ];

    boot.growPartition = true;

    fileSystems."/" = mkIf (!cfg.zfs.enable) (
      lib.mkDefault {
        device = "/dev/disk/by-label/nixos";
        fsType = "ext4";
        autoResize = true;
      }
    );

    fileSystems."/boot" = mkIf (cfg.efi || cfg.zfs.enable) (
      lib.mkDefault {
        # The ZFS image uses a partition labeled ESP whether or not we're
        # booting with EFI.
        device = "/dev/disk/by-label/ESP";
        fsType = "vfat";
      }
    );

    services.zfs.expandOnBoot = mkIf cfg.zfs.enable "all";

    boot.zfs.devNodes = mkIf cfg.zfs.enable "/dev/";

    boot.extraModulePackages = [
      config.boot.kernelPackages.ena
    ];
    boot.initrd.availableKernelModules = [ "nvme" ];
    boot.kernelParams =
      let
        # Amazon recommends setting this to the highest possible value for a good EBS
        # experience, which prior to 4.15 was 255.
        # https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/nvme-ebs-volumes.html#timeout-nvme-ebs-volumes
        nvmeTimeout =
          if lib.versionAtLeast config.boot.kernelPackages.kernel.version "4.15" then "4294967295" else "255";
      in
      [
        "console=ttyS0,115200n8"
        "random.trust_cpu=on"
        "nvme_core.io_timeout=${nvmeTimeout}"
      ];

    # Prevent the nouveau kernel module from being loaded, as it
    # interferes with the nvidia/nvidia-uvm modules needed for CUDA.
    # Also blacklist xen_fbfront to prevent a 30 second delay during
    # boot.
    boot.blacklistedKernelModules = [
      "nouveau"
      "xen_fbfront"
    ];

    boot.loader.grub.device = if cfg.efi then "nodev" else "/dev/xvda";
    boot.loader.grub.efiSupport = cfg.efi;
    boot.loader.grub.efiInstallAsRemovable = cfg.efi;
    boot.loader.timeout = 1;
    boot.loader.grub.extraConfig = ''
      serial --unit=0 --speed=115200 --word=8 --parity=no --stop=1
      terminal_output console serial
      terminal_input console serial
    '';

    systemd.services.fetch-ec2-metadata = {
      wantedBy = [ "multi-user.target" ];
      wants = [ "network-online.target" ];
      after = [ "network-online.target" ];
      path = with pkgs; [
        bzip2
        curl
        file
        gzip
        lzip
        mktemp
        xz
        zstd
      ];
      script = builtins.readFile ./ec2-metadata-fetcher.sh;
      serviceConfig.Type = "oneshot";
      serviceConfig.StandardOutput = "journal+console";
    };

    # Amazon-issued AMIs include the SSM Agent by default, so we do the same.
    # https://docs.aws.amazon.com/systems-manager/latest/userguide/ami-preinstalled-agent.html
    services.amazon-ssm-agent.enable = true;

    # Allow root logins only using the SSH key that the user specified
    # at instance creation time.
    services.openssh.enable = true;
    services.openssh.settings.PermitRootLogin = "prohibit-password";

    # Enable the serial console on ttyS0
    systemd.services."serial-getty@ttyS0".enable = true;

    # Creates symlinks for block device names.
    services.udev.packages = [ pkgs.amazon-ec2-utils ];

    # Force getting the hostname from EC2.
    networking.hostName = mkDefault "";

    # Always include cryptsetup so that Charon can use it.
    environment.systemPackages = [ pkgs.cryptsetup ];

    # EC2 has its own NTP server provided by the hypervisor
    networking.timeServers = [ "169.254.169.123" ];

    # udisks has become too bloated to have in a headless system
    # (e.g. it depends on GTK).
    services.udisks2.enable = false;
  };
  meta.maintainers = with lib.maintainers; [ arianvp ];
}