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 = lib.mdDoc "Whether to set the root password from the Digital Ocean metadata";
15 setSshKeys = mkOption {
19 description = lib.mdDoc "Whether to fetch ssh keys from Digital Ocean";
21 seedEntropy = mkOption {
25 description = lib.mdDoc "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";
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" ];
45 grub.device = "/dev/vda";
47 grub.configurationLimit = 0;
51 enable = mkDefault true;
52 settings.PasswordAuthentication = mkDefault false;
54 services.do-agent.enable = mkDefault true;
56 hostName = mkDefault ""; # use Digital Ocean metadata server
59 /* Check for and wait for the metadata server to become reachable.
60 * This serves as a dependency for all the other metadata services. */
61 systemd.services.digitalocean-metadata = {
63 description = "Get host metadata provided by Digitalocean";
67 while ! curl -fsSL -o $RUNTIME_DIRECTORY/v1.json http://169.254.169.254/metadata/v1.json; do
68 DO_DELAY_ATTEMPTS=$((DO_DELAY_ATTEMPTS + 1))
69 if (( $DO_DELAY_ATTEMPTS >= $DO_DELAY_ATTEMPTS_MAX )); then
74 echo "metadata unavailable, trying again in 1s..."
77 chmod 600 $RUNTIME_DIRECTORY/v1.json
80 DO_DELAY_ATTEMPTS_MAX = "10";
84 RemainAfterExit = true;
85 RuntimeDirectory = "do-metadata";
86 RuntimeDirectoryPreserve = "yes";
89 ConditionPathExists = "!${doMetadataFile}";
90 After = [ "network-pre.target" ] ++
91 optional config.networking.dhcpcd.enable "dhcpcd.service" ++
92 optional config.systemd.network.enable "systemd-networkd.service";
96 /* Fetch the root password from the digital ocean metadata.
97 * There is no specific route for this, so we use jq to get
98 * it from the One Big JSON metadata blob */
99 systemd.services.digitalocean-set-root-password = mkIf cfg.setRootPassword {
100 path = [ pkgs.shadow pkgs.jq ];
101 description = "Set root password provided by Digitalocean";
102 wantedBy = [ "multi-user.target" ];
105 ROOT_PASSWORD=$(jq -er '.auth_key' ${doMetadataFile})
106 echo "root:$ROOT_PASSWORD" | chpasswd
107 mkdir -p /etc/do-metadata/set-root-password
110 ConditionPathExists = "!/etc/do-metadata/set-root-password";
111 Before = optional config.services.openssh.enable "sshd.service";
112 After = [ "digitalocean-metadata.service" ];
113 Requires = [ "digitalocean-metadata.service" ];
120 /* Set the hostname from Digital Ocean, unless the user configured it in
121 * the NixOS configuration. The cached metadata file isn't used here
122 * because the hostname is a mutable part of the droplet. */
123 systemd.services.digitalocean-set-hostname = mkIf (hostName == "") {
124 path = [ pkgs.curl pkgs.nettools ];
125 description = "Set hostname provided by Digitalocean";
126 wantedBy = [ "network.target" ];
129 DIGITALOCEAN_HOSTNAME=$(curl -fsSL http://169.254.169.254/metadata/v1/hostname)
130 hostname "$DIGITALOCEAN_HOSTNAME"
131 if [[ ! -e /etc/hostname || -w /etc/hostname ]]; then
132 printf "%s\n" "$DIGITALOCEAN_HOSTNAME" > /etc/hostname
136 Before = [ "network.target" ];
137 After = [ "digitalocean-metadata.service" ];
138 Wants = [ "digitalocean-metadata.service" ];
145 /* Fetch the ssh keys for root from Digital Ocean */
146 systemd.services.digitalocean-ssh-keys = mkIf cfg.setSshKeys {
147 description = "Set root ssh keys provided by Digital Ocean";
148 wantedBy = [ "multi-user.target" ];
152 mkdir -m 0700 -p /root/.ssh
153 jq -er '.public_keys[]' ${doMetadataFile} > /root/.ssh/authorized_keys
154 chmod 600 /root/.ssh/authorized_keys
158 RemainAfterExit = true;
161 ConditionPathExists = "!/root/.ssh/authorized_keys";
162 Before = optional config.services.openssh.enable "sshd.service";
163 After = [ "digitalocean-metadata.service" ];
164 Requires = [ "digitalocean-metadata.service" ];
168 /* Initialize the RNG by running the entropy-seed script from the
169 * Digital Ocean metadata
171 systemd.services.digitalocean-entropy-seed = mkIf cfg.seedEntropy {
172 description = "Run the kernel RNG entropy seeding script from the Digital Ocean vendor data";
173 wantedBy = [ "network.target" ];
174 path = [ pkgs.jq pkgs.mpack ];
178 jq -er '.vendor_data' ${doMetadataFile} | munpack -tC $TEMPDIR
179 ENTROPY_SEED=$(grep -rl "DigitalOcean Entropy Seed script" $TEMPDIR)
180 ${pkgs.runtimeShell} $ENTROPY_SEED
184 Before = [ "network.target" ];
185 After = [ "digitalocean-metadata.service" ];
186 Requires = [ "digitalocean-metadata.service" ];
195 meta.maintainers = with maintainers; [ arianvp eamsden ];