1{ config, lib, pkgs, ... }:
2
3with lib;
4
5let
6 cfg = config.services.cloud-init;
7 path = with pkgs; [
8 cloud-init
9 iproute2
10 nettools
11 openssh
12 shadow
13 util-linux
14 busybox
15 ]
16 ++ optional cfg.btrfs.enable btrfs-progs
17 ++ optional cfg.ext4.enable e2fsprogs
18 ;
19 settingsFormat = pkgs.formats.yaml { };
20 cfgfile = settingsFormat.generate "cloud.cfg" cfg.settings;
21in
22{
23 options = {
24 services.cloud-init = {
25 enable = mkOption {
26 type = types.bool;
27 default = false;
28 description = mdDoc ''
29 Enable the cloud-init service. This services reads
30 configuration metadata in a cloud environment and configures
31 the machine according to this metadata.
32
33 This configuration is not completely compatible with the
34 NixOS way of doing configuration, as configuration done by
35 cloud-init might be overridden by a subsequent nixos-rebuild
36 call. However, some parts of cloud-init fall outside of
37 NixOS's responsibility, like filesystem resizing and ssh
38 public key provisioning, and cloud-init is useful for that
39 parts. Thus, be wary that using cloud-init in NixOS might
40 come as some cost.
41 '';
42 };
43
44 btrfs.enable = mkOption {
45 type = types.bool;
46 default = false;
47 description = mdDoc ''
48 Allow the cloud-init service to operate `btrfs` filesystem.
49 '';
50 };
51
52 ext4.enable = mkOption {
53 type = types.bool;
54 default = true;
55 description = mdDoc ''
56 Allow the cloud-init service to operate `ext4` filesystem.
57 '';
58 };
59
60 network.enable = mkOption {
61 type = types.bool;
62 default = false;
63 description = mdDoc ''
64 Allow the cloud-init service to configure network interfaces
65 through systemd-networkd.
66 '';
67 };
68
69 settings = mkOption {
70 description = mdDoc ''
71 Structured cloud-init configuration.
72 '';
73 type = types.submodule {
74 freeformType = settingsFormat.type;
75 };
76 default = { };
77 };
78
79 config = mkOption {
80 type = types.str;
81 default = "";
82 description = mdDoc ''
83 raw cloud-init configuration.
84
85 Takes precedence over the `settings` option if set.
86 '';
87 };
88
89 };
90
91 };
92
93 config = mkIf cfg.enable {
94 services.cloud-init.settings = {
95 system_info = mkDefault {
96 distro = "nixos";
97 network = {
98 renderers = [ "networkd" ];
99 };
100 };
101
102 users = mkDefault [ "root" ];
103 disable_root = mkDefault false;
104 preserve_hostname = mkDefault false;
105
106 cloud_init_modules = mkDefault [
107 "migrator"
108 "seed_random"
109 "bootcmd"
110 "write-files"
111 "growpart"
112 "resizefs"
113 "update_hostname"
114 "resolv_conf"
115 "ca-certs"
116 "rsyslog"
117 "users-groups"
118 ];
119
120 cloud_config_modules = mkDefault [
121 "disk_setup"
122 "mounts"
123 "ssh-import-id"
124 "set-passwords"
125 "timezone"
126 "disable-ec2-metadata"
127 "runcmd"
128 "ssh"
129 ];
130
131 cloud_final_modules = mkDefault [
132 "rightscale_userdata"
133 "scripts-vendor"
134 "scripts-per-once"
135 "scripts-per-boot"
136 "scripts-per-instance"
137 "scripts-user"
138 "ssh-authkey-fingerprints"
139 "keys-to-console"
140 "phone-home"
141 "final-message"
142 "power-state-change"
143 ];
144 };
145
146 environment.etc."cloud/cloud.cfg" =
147 if cfg.config == "" then
148 { source = cfgfile; }
149 else
150 { text = cfg.config; }
151 ;
152
153 systemd.network.enable = cfg.network.enable;
154
155 systemd.services.cloud-init-local = {
156 description = "Initial cloud-init job (pre-networking)";
157 wantedBy = [ "multi-user.target" ];
158 before = [ "systemd-networkd.service" ];
159 path = path;
160 serviceConfig = {
161 Type = "oneshot";
162 ExecStart = "${pkgs.cloud-init}/bin/cloud-init init --local";
163 RemainAfterExit = "yes";
164 TimeoutSec = "infinity";
165 StandardOutput = "journal+console";
166 };
167 };
168
169 systemd.services.cloud-init = {
170 description = "Initial cloud-init job (metadata service crawler)";
171 wantedBy = [ "multi-user.target" ];
172 wants = [
173 "network-online.target"
174 "cloud-init-local.service"
175 "sshd.service"
176 "sshd-keygen.service"
177 ];
178 after = [ "network-online.target" "cloud-init-local.service" ];
179 before = [ "sshd.service" "sshd-keygen.service" ];
180 requires = [ "network.target" ];
181 path = path;
182 serviceConfig = {
183 Type = "oneshot";
184 ExecStart = "${pkgs.cloud-init}/bin/cloud-init init";
185 RemainAfterExit = "yes";
186 TimeoutSec = "infinity";
187 StandardOutput = "journal+console";
188 };
189 };
190
191 systemd.services.cloud-config = {
192 description = "Apply the settings specified in cloud-config";
193 wantedBy = [ "multi-user.target" ];
194 wants = [ "network-online.target" ];
195 after = [ "network-online.target" "syslog.target" "cloud-config.target" ];
196
197 path = path;
198 serviceConfig = {
199 Type = "oneshot";
200 ExecStart = "${pkgs.cloud-init}/bin/cloud-init modules --mode=config";
201 RemainAfterExit = "yes";
202 TimeoutSec = "infinity";
203 StandardOutput = "journal+console";
204 };
205 };
206
207 systemd.services.cloud-final = {
208 description = "Execute cloud user/final scripts";
209 wantedBy = [ "multi-user.target" ];
210 wants = [ "network-online.target" ];
211 after = [ "network-online.target" "syslog.target" "cloud-config.service" "rc-local.service" ];
212 requires = [ "cloud-config.target" ];
213 path = path;
214 serviceConfig = {
215 Type = "oneshot";
216 ExecStart = "${pkgs.cloud-init}/bin/cloud-init modules --mode=final";
217 RemainAfterExit = "yes";
218 TimeoutSec = "infinity";
219 StandardOutput = "journal+console";
220 };
221 };
222
223 systemd.targets.cloud-config = {
224 description = "Cloud-config availability";
225 requires = [ "cloud-init-local.service" "cloud-init.service" ];
226 };
227 };
228
229 meta.maintainers = [ maintainers.zimbatm ];
230}