forked from mirrors/nixpkgs
83ea88e03f
AWS's metadata service has two versions. Version 1 allowed plain HTTP requests to get metadata. However, this was frequently abused when a user could trick an AWS-hosted server in to proxying requests to the metadata service. Since the metadata service is frequently used to generate AWS access keys, this is pretty gnarly. Version two is identical except it requires the caller to request a token and provide it on each request. Today, starting a NixOS AMI in EC2 where the metadata service is configured to only allow v2 requests fails: the user's SSH key is not placed, and configuration provided by the user-data is not applied. The server is useless. This patch addresses that. Note the dependency on curl is not a joyful one, and it expand the initrd by 30M. However, see the added comment for more information about why this is needed. Note the idea of using `echo` and `nc` are laughable. Don't do that.
158 lines
5.6 KiB
Nix
158 lines
5.6 KiB
Nix
# 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, ... }:
|
||
|
||
with lib;
|
||
|
||
let
|
||
cfg = config.ec2;
|
||
metadataFetcher = import ./ec2-metadata-fetcher.nix {
|
||
inherit (pkgs) curl;
|
||
targetRoot = "$targetRoot/";
|
||
wgetExtraOptions = "-q";
|
||
};
|
||
in
|
||
|
||
{
|
||
imports = [ ../profiles/headless.nix ./ec2-data.nix ./amazon-init.nix ];
|
||
|
||
config = {
|
||
|
||
assertions = [
|
||
{ assertion = cfg.hvm;
|
||
message = "Paravirtualized EC2 instances are no longer supported.";
|
||
}
|
||
{ assertion = cfg.efi -> cfg.hvm;
|
||
message = "EC2 instances using EFI must be HVM instances.";
|
||
}
|
||
];
|
||
|
||
boot.growPartition = cfg.hvm;
|
||
|
||
fileSystems."/" = {
|
||
device = "/dev/disk/by-label/nixos";
|
||
fsType = "ext4";
|
||
autoResize = true;
|
||
};
|
||
|
||
fileSystems."/boot" = mkIf cfg.efi {
|
||
device = "/dev/disk/by-label/ESP";
|
||
fsType = "vfat";
|
||
};
|
||
|
||
boot.extraModulePackages = [
|
||
config.boot.kernelPackages.ena
|
||
];
|
||
boot.initrd.kernelModules = [ "xen-blkfront" "xen-netfront" ];
|
||
boot.initrd.availableKernelModules = [ "ixgbevf" "ena" "nvme" ];
|
||
boot.kernelParams = mkIf cfg.hvm [ "console=ttyS0" "random.trust_cpu=on" ];
|
||
|
||
# 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" ];
|
||
|
||
# Generate a GRUB menu. Amazon's pv-grub uses this to boot our kernel/initrd.
|
||
boot.loader.grub.version = if cfg.hvm then 2 else 1;
|
||
boot.loader.grub.device = if (cfg.hvm && !cfg.efi) then "/dev/xvda" else "nodev";
|
||
boot.loader.grub.extraPerEntryConfig = mkIf (!cfg.hvm) "root (hd0)";
|
||
boot.loader.grub.efiSupport = cfg.efi;
|
||
boot.loader.grub.efiInstallAsRemovable = cfg.efi;
|
||
boot.loader.timeout = 0;
|
||
|
||
boot.initrd.network.enable = true;
|
||
|
||
# Mount all formatted ephemeral disks and activate all swap devices.
|
||
# We cannot do this with the ‘fileSystems’ and ‘swapDevices’ options
|
||
# because the set of devices is dependent on the instance type
|
||
# (e.g. "m1.small" has one ephemeral filesystem and one swap device,
|
||
# while "m1.large" has two ephemeral filesystems and no swap
|
||
# devices). Also, put /tmp and /var on /disk0, since it has a lot
|
||
# more space than the root device. Similarly, "move" /nix to /disk0
|
||
# by layering a unionfs-fuse mount on top of it so we have a lot more space for
|
||
# Nix operations.
|
||
boot.initrd.postMountCommands =
|
||
''
|
||
${metadataFetcher}
|
||
|
||
diskNr=0
|
||
diskForUnionfs=
|
||
for device in /dev/xvd[abcde]*; do
|
||
if [ "$device" = /dev/xvda -o "$device" = /dev/xvda1 ]; then continue; fi
|
||
fsType=$(blkid -o value -s TYPE "$device" || true)
|
||
if [ "$fsType" = swap ]; then
|
||
echo "activating swap device $device..."
|
||
swapon "$device" || true
|
||
elif [ "$fsType" = ext3 ]; then
|
||
mp="/disk$diskNr"
|
||
diskNr=$((diskNr + 1))
|
||
if mountFS "$device" "$mp" "" ext3; then
|
||
if [ -z "$diskForUnionfs" ]; then diskForUnionfs="$mp"; fi
|
||
fi
|
||
else
|
||
echo "skipping unknown device type $device"
|
||
fi
|
||
done
|
||
|
||
if [ -n "$diskForUnionfs" ]; then
|
||
mkdir -m 755 -p $targetRoot/$diskForUnionfs/root
|
||
|
||
mkdir -m 1777 -p $targetRoot/$diskForUnionfs/root/tmp $targetRoot/tmp
|
||
mount --bind $targetRoot/$diskForUnionfs/root/tmp $targetRoot/tmp
|
||
|
||
if [ "$(cat "$metaDir/ami-manifest-path")" != "(unknown)" ]; then
|
||
mkdir -m 755 -p $targetRoot/$diskForUnionfs/root/var $targetRoot/var
|
||
mount --bind $targetRoot/$diskForUnionfs/root/var $targetRoot/var
|
||
|
||
mkdir -p /unionfs-chroot/ro-nix
|
||
mount --rbind $targetRoot/nix /unionfs-chroot/ro-nix
|
||
|
||
mkdir -m 755 -p $targetRoot/$diskForUnionfs/root/nix
|
||
mkdir -p /unionfs-chroot/rw-nix
|
||
mount --rbind $targetRoot/$diskForUnionfs/root/nix /unionfs-chroot/rw-nix
|
||
|
||
unionfs -o allow_other,cow,nonempty,chroot=/unionfs-chroot,max_files=32768 /rw-nix=RW:/ro-nix=RO $targetRoot/nix
|
||
fi
|
||
fi
|
||
'';
|
||
|
||
boot.initrd.extraUtilsCommands =
|
||
''
|
||
# We need swapon in the initrd.
|
||
copy_bin_and_libs ${pkgs.utillinux}/sbin/swapon
|
||
'';
|
||
|
||
# Don't put old configurations in the GRUB menu. The user has no
|
||
# way to select them anyway.
|
||
boot.loader.grub.configurationLimit = 0;
|
||
|
||
# Allow root logins only using the SSH key that the user specified
|
||
# at instance creation time.
|
||
services.openssh.enable = true;
|
||
services.openssh.permitRootLogin = "prohibit-password";
|
||
|
||
# Creates symlinks for block device names.
|
||
services.udev.packages = [ pkgs.ec2-utils ];
|
||
|
||
# Force getting the hostname from EC2.
|
||
networking.hostName = mkDefault "";
|
||
|
||
# Always include cryptsetup so that Charon can use it.
|
||
environment.systemPackages = [ pkgs.cryptsetup ];
|
||
|
||
boot.initrd.supportedFilesystems = [ "unionfs-fuse" ];
|
||
|
||
# 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;
|
||
};
|
||
}
|