diff --git a/nixos/modules/virtualisation/google-compute-config.nix b/nixos/modules/virtualisation/google-compute-config.nix new file mode 100644 index 000000000000..9e6be93b6d98 --- /dev/null +++ b/nixos/modules/virtualisation/google-compute-config.nix @@ -0,0 +1,5 @@ +{ config, pkgs, modulesPath, ... }: + +{ + imports = [ "${modulesPath}/virtualisation/google-compute-image.nix" ]; +} diff --git a/nixos/modules/virtualisation/google-compute-image.nix b/nixos/modules/virtualisation/google-compute-image.nix new file mode 100644 index 000000000000..edf10c0e5048 --- /dev/null +++ b/nixos/modules/virtualisation/google-compute-image.nix @@ -0,0 +1,158 @@ +{ config, pkgs, ... }: + +with pkgs.lib; + +{ + imports = [ ../profiles/headless.nix ../profiles/qemu-guest.nix ]; + + system.build.googleComputeImage = + pkgs.vmTools.runInLinuxVM ( + pkgs.runCommand "google-compute-image" + { preVM = + '' + mkdir $out + diskImage=$out/disk.raw + truncate $diskImage --size 10G + mv closure xchg/ + ''; + + postVM = + '' + PATH=$PATH:${pkgs.gnutar}/bin:${pkgs.gzip}/bin + pushd $out + tar -Szcf disk.raw.tar.gz disk.raw + rm $out/disk.raw + popd + ''; + + buildInputs = [ pkgs.utillinux pkgs.perl ]; + exportReferencesGraph = + [ "closure" config.system.build.toplevel ]; + } + '' + # Create partition table + ${pkgs.parted}/sbin/parted /dev/vda mklabel msdos + ${pkgs.parted}/sbin/parted /dev/vda mkpart primary ext4 1 10G + ${pkgs.parted}/sbin/parted /dev/vda print + . /sys/class/block/vda1/uevent + mknod /dev/vda1 b $MAJOR $MINOR + + # Create an empty filesystem and mount it. + ${pkgs.e2fsprogs}/sbin/mkfs.ext4 -L nixos /dev/vda1 + ${pkgs.e2fsprogs}/sbin/tune2fs -c 0 -i 0 /dev/vda1 + + mkdir /mnt + mount /dev/vda1 /mnt + + # The initrd expects these directories to exist. + mkdir /mnt/dev /mnt/proc /mnt/sys + + mount --bind /proc /mnt/proc + mount --bind /dev /mnt/dev + mount --bind /sys /mnt/sys + + # Copy all paths in the closure to the filesystem. + storePaths=$(perl ${pkgs.pathsFromGraph} /tmp/xchg/closure) + + mkdir -p /mnt/nix/store + echo "copying everything (will take a while)..." + cp -prd $storePaths /mnt/nix/store/ + + # Register the paths in the Nix database. + printRegistration=1 perl ${pkgs.pathsFromGraph} /tmp/xchg/closure | \ + chroot /mnt ${config.nix.package}/bin/nix-store --load-db + + # Create the system profile to allow nixos-rebuild to work. + chroot /mnt ${config.nix.package}/bin/nix-env \ + -p /nix/var/nix/profiles/system --set ${config.system.build.toplevel} + + # `nixos-rebuild' requires an /etc/NIXOS. + mkdir -p /mnt/etc + touch /mnt/etc/NIXOS + + # `switch-to-configuration' requires a /bin/sh + mkdir -p /mnt/bin + ln -s ${config.system.build.binsh}/bin/sh /mnt/bin/sh + + # Install a configuration.nix. + mkdir -p /mnt/etc/nixos /mnt/boot/grub + cp ${./google-compute-config.nix} /mnt/etc/nixos/configuration.nix + + # Generate the GRUB menu. + ln -s vda /dev/sda + chroot /mnt ${config.system.build.toplevel}/bin/switch-to-configuration boot + + umount /mnt/proc /mnt/dev /mnt/sys + umount /mnt + '' + ); + + fileSystems."/".label = "nixos"; + + boot.kernelParams = [ "console=ttyS0" "panic=1" "boot.panic_on_fail" ]; + boot.initrd.kernelModules = [ "virtio_scsi" ]; + + # Generate a GRUB menu. Amazon's pv-grub uses this to boot our kernel/initrd. + boot.loader.grub.device = "/dev/sda"; + boot.loader.grub.timeout = 0; + + # 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 = "without-password"; + + # Force getting the hostname from Google Compute. + networking.hostName = mkDefault ""; + + # Always include cryptsetup so that NixOps can use it. + environment.systemPackages = [ pkgs.cryptsetup ]; + + # Prevent logging in as root without a password. This doesn't really matter, + # since the only PAM services that allow logging in with a null + # password are local ones that are inaccessible on Google Compute machines. + security.initialRootPassword = "!"; + + # Configure default metadata hostnames + networking.extraHosts = '' + 169.254.169.254 metadata.google.internal metadata + ''; + + users.extraUsers.root.openssh.authorizedKeys.keys = ["ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCt9GBbtWUTDacovm2O6qDw/SsmUmwYBu3k+6kx8/Xv7IJK5uqxhNTBc0MfADBV6EMZVDkaw+rDeRvcDuEzz2r/CP/x1vVDiwWYjyU2uOZEHFKh15W/1qC9fwBIikcbgm+8R33kmpFr9AChP4KWR+4T3jf0AYknZ3pbZ5GPCTWmWeOR4PVhtusDt0/cfWrENrA0mFo37xohn5lFsbrJ5/rMuhWglTuHIQFGwECqbYPvyxDLZxAfqg1Wc1AJAW8xRuJafvPyuxsj+qJu+MfKpAcxSoXDtldPxCFF6e/4j0Ey/kz435fJS/AjSJwmeurOQ/vB+tm2Db1wLWc1669S46Kl rbvermaa@propowerrrr"]; + + # fetch authorized keys for root user + systemd.services.fetch-root-authorized-keys = + { description = "Fetch authorized_keys for root user."; + + wantedBy = [ "multi-user.target" ]; + before = [ "sshd.service" ]; + after = [ "network.target" ]; + + path = [ pkgs.curl ]; + script = + '' + # Don't download the SSH key if it has already been downloaded + if ! [ -e /root/.ssh/authorized_keys ]; then + echo "obtaining SSH key..." + mkdir -p /root/.ssh + curl -o /root/authorized-keys-metadata http://metadata/0.1/meta-data/authorized-keys + if [ $? -eq 0 -a -e /root/authorized-keys-metadata ]; then + cat /root/authorized-keys-metadata | cut -d: -f2- > /root/key.pub + if ! grep -q -f /root/key.pub /root/.ssh/authorized_keys; then + cat /root/key.pub >> /root/.ssh/authorized_keys + echo "new key added to authorized_keys" + fi + chmod 600 /root/.ssh/authorized_keys + rm -f /root/key.pub /root/authorized-keys-metadata + fi + fi + ''; + serviceConfig.Type = "oneshot"; + serviceConfig.RemainAfterExit = true; + }; + + +}