typioca: 2.7.0 -> 2.8.0
[NixPkgs.git] / nixos / modules / virtualisation / digital-ocean-config.nix
blobe004b7880aad0dd93c2ddde4f4f547ffca3809d5
1 { config, pkgs, lib, modulesPath, ... }:
2 with lib;
4   imports = [
5     (modulesPath + "/profiles/qemu-guest.nix")
6     (modulesPath + "/virtualisation/digital-ocean-init.nix")
7   ];
8   options.virtualisation.digitalOcean = with types; {
9     setRootPassword = mkOption {
10       type = bool;
11       default = false;
12       example = true;
13       description = lib.mdDoc "Whether to set the root password from the Digital Ocean metadata";
14     };
15     setSshKeys = mkOption {
16       type = bool;
17       default = true;
18       example = true;
19       description = lib.mdDoc "Whether to fetch ssh keys from Digital Ocean";
20     };
21     seedEntropy = mkOption {
22       type = bool;
23       default = true;
24       example = true;
25       description = lib.mdDoc "Whether to run the kernel RNG entropy seeding script from the Digital Ocean vendor data";
26     };
27   };
28   config =
29     let
30       cfg = config.virtualisation.digitalOcean;
31       hostName = config.networking.hostName;
32       doMetadataFile = "/run/do-metadata/v1.json";
33     in mkMerge [{
34       fileSystems."/" = {
35         device = "/dev/disk/by-label/nixos";
36         autoResize = true;
37         fsType = "ext4";
38       };
39       boot = {
40         growPartition = true;
41         kernelParams = [ "console=ttyS0" "panic=1" "boot.panic_on_fail" ];
42         initrd.kernelModules = [ "virtio_scsi" ];
43         kernelModules = [ "virtio_pci" "virtio_net" ];
44         loader = {
45           grub.device = "/dev/vda";
46           timeout = 0;
47           grub.configurationLimit = 0;
48         };
49       };
50       services.openssh = {
51         enable = mkDefault true;
52         settings.PasswordAuthentication = mkDefault false;
53       };
54       services.do-agent.enable = mkDefault true;
55       networking = {
56         hostName = mkDefault ""; # use Digital Ocean metadata server
57       };
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 = {
62         path = [ pkgs.curl ];
63         description = "Get host metadata provided by Digitalocean";
64         script = ''
65           set -eu
66           DO_DELAY_ATTEMPTS=0
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
70               echo "giving up"
71               exit 1
72             fi
74             echo "metadata unavailable, trying again in 1s..."
75             sleep 1
76           done
77           chmod 600 $RUNTIME_DIRECTORY/v1.json
78           '';
79         environment = {
80           DO_DELAY_ATTEMPTS_MAX = "10";
81         };
82         serviceConfig = {
83           Type = "oneshot";
84           RemainAfterExit = true;
85           RuntimeDirectory = "do-metadata";
86           RuntimeDirectoryPreserve = "yes";
87         };
88         unitConfig = {
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";
93         };
94       };
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" ];
103         script = ''
104           set -eo pipefail
105           ROOT_PASSWORD=$(jq -er '.auth_key' ${doMetadataFile})
106           echo "root:$ROOT_PASSWORD" | chpasswd
107           mkdir -p /etc/do-metadata/set-root-password
108           '';
109         unitConfig = {
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" ];
114         };
115         serviceConfig = {
116           Type = "oneshot";
117         };
118       };
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" ];
127         script = ''
128           set -e
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
133           fi
134         '';
135         unitConfig = {
136           Before = [ "network.target" ];
137           After = [ "digitalocean-metadata.service" ];
138           Wants = [ "digitalocean-metadata.service" ];
139         };
140         serviceConfig = {
141           Type = "oneshot";
142         };
143       };
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" ];
149         path = [ pkgs.jq ];
150         script = ''
151           set -e
152           mkdir -m 0700 -p /root/.ssh
153           jq -er '.public_keys[]' ${doMetadataFile} > /root/.ssh/authorized_keys
154           chmod 600 /root/.ssh/authorized_keys
155         '';
156         serviceConfig = {
157           Type = "oneshot";
158           RemainAfterExit = true;
159         };
160         unitConfig = {
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" ];
165         };
166       };
168       /* Initialize the RNG by running the entropy-seed script from the
169        * Digital Ocean metadata
170        */
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 ];
175         script = ''
176           set -eo pipefail
177           TEMPDIR=$(mktemp -d)
178           jq -er '.vendor_data' ${doMetadataFile} | munpack -tC $TEMPDIR
179           ENTROPY_SEED=$(grep -rl "DigitalOcean Entropy Seed script" $TEMPDIR)
180           ${pkgs.runtimeShell} $ENTROPY_SEED
181           rm -rf $TEMPDIR
182           '';
183         unitConfig = {
184           Before = [ "network.target" ];
185           After = [ "digitalocean-metadata.service" ];
186           Requires = [ "digitalocean-metadata.service" ];
187         };
188         serviceConfig = {
189           Type = "oneshot";
190         };
191       };
193     }
194   ];
195   meta.maintainers = with maintainers; [ arianvp eamsden ];