1 { config, pkgs, lib, modulesPath, ... }:
5 (modulesPath + "/profiles/qemu-guest.nix")
6 (modulesPath + "/virtualisation/digital-ocean-init.nix")
8 options.virtualisation.digitalOcean = with types; {
9 setRootPassword = mkOption {
13 description = "Whether to set the root password from the Digital Ocean metadata";
15 setSshKeys = mkOption {
19 description = "Whether to fetch ssh keys from Digital Ocean";
21 seedEntropy = mkOption {
25 description = "Whether to run the kernel RNG entropy seeding script from the Digital Ocean vendor data";
30 cfg = config.virtualisation.digitalOcean;
31 hostName = config.networking.hostName;
32 doMetadataFile = "/run/do-metadata/v1.json";
34 fileSystems."/" = lib.mkDefault {
35 device = "/dev/disk/by-label/nixos";
41 kernelParams = [ "console=ttyS0" "panic=1" "boot.panic_on_fail" ];
42 initrd.kernelModules = [ "virtio_scsi" ];
43 kernelModules = [ "virtio_pci" "virtio_net" ];
44 loader.grub.devices = ["/dev/vda"];
47 enable = mkDefault true;
48 settings.PasswordAuthentication = mkDefault false;
50 services.do-agent.enable = mkDefault true;
52 hostName = mkDefault ""; # use Digital Ocean metadata server
55 /* Check for and wait for the metadata server to become reachable.
56 * This serves as a dependency for all the other metadata services. */
57 systemd.services.digitalocean-metadata = {
59 description = "Get host metadata provided by Digitalocean";
63 while ! curl -fsSL -o $RUNTIME_DIRECTORY/v1.json http://169.254.169.254/metadata/v1.json; do
64 DO_DELAY_ATTEMPTS=$((DO_DELAY_ATTEMPTS + 1))
65 if (( $DO_DELAY_ATTEMPTS >= $DO_DELAY_ATTEMPTS_MAX )); then
70 echo "metadata unavailable, trying again in 1s..."
73 chmod 600 $RUNTIME_DIRECTORY/v1.json
76 DO_DELAY_ATTEMPTS_MAX = "10";
80 RemainAfterExit = true;
81 RuntimeDirectory = "do-metadata";
82 RuntimeDirectoryPreserve = "yes";
85 ConditionPathExists = "!${doMetadataFile}";
86 After = [ "network-pre.target" ] ++
87 optional config.networking.dhcpcd.enable "dhcpcd.service" ++
88 optional config.systemd.network.enable "systemd-networkd.service";
92 /* Fetch the root password from the digital ocean metadata.
93 * There is no specific route for this, so we use jq to get
94 * it from the One Big JSON metadata blob */
95 systemd.services.digitalocean-set-root-password = mkIf cfg.setRootPassword {
96 path = [ pkgs.shadow pkgs.jq ];
97 description = "Set root password provided by Digitalocean";
98 wantedBy = [ "multi-user.target" ];
101 ROOT_PASSWORD=$(jq -er '.auth_key' ${doMetadataFile})
102 echo "root:$ROOT_PASSWORD" | chpasswd
103 mkdir -p /etc/do-metadata/set-root-password
106 ConditionPathExists = "!/etc/do-metadata/set-root-password";
107 Before = optional config.services.openssh.enable "sshd.service";
108 After = [ "digitalocean-metadata.service" ];
109 Requires = [ "digitalocean-metadata.service" ];
116 /* Set the hostname from Digital Ocean, unless the user configured it in
117 * the NixOS configuration. The cached metadata file isn't used here
118 * because the hostname is a mutable part of the droplet. */
119 systemd.services.digitalocean-set-hostname = mkIf (hostName == "") {
120 path = [ pkgs.curl pkgs.nettools ];
121 description = "Set hostname provided by Digitalocean";
122 wantedBy = [ "network.target" ];
125 DIGITALOCEAN_HOSTNAME=$(curl -fsSL http://169.254.169.254/metadata/v1/hostname)
126 hostname "$DIGITALOCEAN_HOSTNAME"
127 if [[ ! -e /etc/hostname || -w /etc/hostname ]]; then
128 printf "%s\n" "$DIGITALOCEAN_HOSTNAME" > /etc/hostname
132 Before = [ "network.target" ];
133 After = [ "digitalocean-metadata.service" ];
134 Wants = [ "digitalocean-metadata.service" ];
141 /* Fetch the ssh keys for root from Digital Ocean */
142 systemd.services.digitalocean-ssh-keys = mkIf cfg.setSshKeys {
143 description = "Set root ssh keys provided by Digital Ocean";
144 wantedBy = [ "multi-user.target" ];
148 mkdir -m 0700 -p /root/.ssh
149 jq -er '.public_keys[]' ${doMetadataFile} > /root/.ssh/authorized_keys
150 chmod 600 /root/.ssh/authorized_keys
154 RemainAfterExit = true;
157 ConditionPathExists = "!/root/.ssh/authorized_keys";
158 Before = optional config.services.openssh.enable "sshd.service";
159 After = [ "digitalocean-metadata.service" ];
160 Requires = [ "digitalocean-metadata.service" ];
164 /* Initialize the RNG by running the entropy-seed script from the
165 * Digital Ocean metadata
167 systemd.services.digitalocean-entropy-seed = mkIf cfg.seedEntropy {
168 description = "Run the kernel RNG entropy seeding script from the Digital Ocean vendor data";
169 wantedBy = [ "network.target" ];
170 path = [ pkgs.jq pkgs.mpack ];
174 jq -er '.vendor_data' ${doMetadataFile} | munpack -tC $TEMPDIR
175 ENTROPY_SEED=$(grep -rl "DigitalOcean Entropy Seed script" $TEMPDIR)
176 ${pkgs.runtimeShell} $ENTROPY_SEED
180 Before = [ "network.target" ];
181 After = [ "digitalocean-metadata.service" ];
182 Requires = [ "digitalocean-metadata.service" ];
191 meta.maintainers = with maintainers; [ arianvp eamsden ];