vuls: init at 0.27.0
[NixPkgs.git] / nixos / modules / virtualisation / digital-ocean-config.nix
blob4ef2b85551c6693ee23963b38f3a86b43379592b
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 = "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 = "Whether to fetch ssh keys from Digital Ocean";
20     };
21     seedEntropy = mkOption {
22       type = bool;
23       default = true;
24       example = true;
25       description = "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."/" = lib.mkDefault {
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.grub.devices = ["/dev/vda"];
45       };
46       services.openssh = {
47         enable = mkDefault true;
48         settings.PasswordAuthentication = mkDefault false;
49       };
50       services.do-agent.enable = mkDefault true;
51       networking = {
52         hostName = mkDefault ""; # use Digital Ocean metadata server
53       };
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 = {
58         path = [ pkgs.curl ];
59         description = "Get host metadata provided by Digitalocean";
60         script = ''
61           set -eu
62           DO_DELAY_ATTEMPTS=0
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
66               echo "giving up"
67               exit 1
68             fi
70             echo "metadata unavailable, trying again in 1s..."
71             sleep 1
72           done
73           chmod 600 $RUNTIME_DIRECTORY/v1.json
74           '';
75         environment = {
76           DO_DELAY_ATTEMPTS_MAX = "10";
77         };
78         serviceConfig = {
79           Type = "oneshot";
80           RemainAfterExit = true;
81           RuntimeDirectory = "do-metadata";
82           RuntimeDirectoryPreserve = "yes";
83         };
84         unitConfig = {
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";
89         };
90       };
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" ];
99         script = ''
100           set -eo pipefail
101           ROOT_PASSWORD=$(jq -er '.auth_key' ${doMetadataFile})
102           echo "root:$ROOT_PASSWORD" | chpasswd
103           mkdir -p /etc/do-metadata/set-root-password
104           '';
105         unitConfig = {
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" ];
110         };
111         serviceConfig = {
112           Type = "oneshot";
113         };
114       };
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" ];
123         script = ''
124           set -e
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
129           fi
130         '';
131         unitConfig = {
132           Before = [ "network.target" ];
133           After = [ "digitalocean-metadata.service" ];
134           Wants = [ "digitalocean-metadata.service" ];
135         };
136         serviceConfig = {
137           Type = "oneshot";
138         };
139       };
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" ];
145         path = [ pkgs.jq ];
146         script = ''
147           set -e
148           mkdir -m 0700 -p /root/.ssh
149           jq -er '.public_keys[]' ${doMetadataFile} > /root/.ssh/authorized_keys
150           chmod 600 /root/.ssh/authorized_keys
151         '';
152         serviceConfig = {
153           Type = "oneshot";
154           RemainAfterExit = true;
155         };
156         unitConfig = {
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" ];
161         };
162       };
164       /* Initialize the RNG by running the entropy-seed script from the
165        * Digital Ocean metadata
166        */
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 ];
171         script = ''
172           set -eo pipefail
173           TEMPDIR=$(mktemp -d)
174           jq -er '.vendor_data' ${doMetadataFile} | munpack -tC $TEMPDIR
175           ENTROPY_SEED=$(grep -rl "DigitalOcean Entropy Seed script" $TEMPDIR)
176           ${pkgs.runtimeShell} $ENTROPY_SEED
177           rm -rf $TEMPDIR
178           '';
179         unitConfig = {
180           Before = [ "network.target" ];
181           After = [ "digitalocean-metadata.service" ];
182           Requires = [ "digitalocean-metadata.service" ];
183         };
184         serviceConfig = {
185           Type = "oneshot";
186         };
187       };
189     }
190   ];
191   meta.maintainers = with maintainers; [ arianvp eamsden ];