1{ lib, config, pkgs, ... }:
2
3with lib;
4
5let
6 templateSubmodule = { ... }: {
7 options = {
8 enable = mkEnableOption "this template";
9
10 target = mkOption {
11 description = "Path in the container";
12 type = types.path;
13 };
14 template = mkOption {
15 description = ".tpl file for rendering the target";
16 type = types.path;
17 };
18 when = mkOption {
19 description = "Events which trigger a rewrite (create, copy)";
20 type = types.listOf (types.str);
21 };
22 properties = mkOption {
23 description = "Additional properties";
24 type = types.attrs;
25 default = {};
26 };
27 };
28 };
29
30 toYAML = name: data: pkgs.writeText name (generators.toYAML {} data);
31
32 cfg = config.virtualisation.lxc;
33 templates = if cfg.templates != {} then let
34 list = mapAttrsToList (name: value: { inherit name; } // value)
35 (filterAttrs (name: value: value.enable) cfg.templates);
36 in
37 {
38 files = map (tpl: {
39 source = tpl.template;
40 target = "/templates/${tpl.name}.tpl";
41 }) list;
42 properties = listToAttrs (map (tpl: nameValuePair tpl.target {
43 when = tpl.when;
44 template = "${tpl.name}.tpl";
45 properties = tpl.properties;
46 }) list);
47 }
48 else { files = []; properties = {}; };
49
50in
51{
52 imports = [
53 ../installer/cd-dvd/channel.nix
54 ../profiles/minimal.nix
55 ../profiles/clone-config.nix
56 ];
57
58 options = {
59 virtualisation.lxc = {
60 templates = mkOption {
61 description = "Templates for LXD";
62 type = types.attrsOf (types.submodule (templateSubmodule));
63 default = {};
64 example = literalExpression ''
65 {
66 # create /etc/hostname on container creation
67 "hostname" = {
68 enable = true;
69 target = "/etc/hostname";
70 template = builtins.writeFile "hostname.tpl" "{{ container.name }}";
71 when = [ "create" ];
72 };
73 # create /etc/nixos/hostname.nix with a configuration for keeping the hostname applied
74 "hostname-nix" = {
75 enable = true;
76 target = "/etc/nixos/hostname.nix";
77 template = builtins.writeFile "hostname-nix.tpl" "{ ... }: { networking.hostName = "{{ container.name }}"; }";
78 # copy keeps the file updated when the container is changed
79 when = [ "create" "copy" ];
80 };
81 # copy allow the user to specify a custom configuration.nix
82 "configuration-nix" = {
83 enable = true;
84 target = "/etc/nixos/configuration.nix";
85 template = builtins.writeFile "configuration-nix" "{{ config_get(\"user.user-data\", properties.default) }}";
86 when = [ "create" ];
87 };
88 };
89 '';
90 };
91 };
92 };
93
94 config = {
95 boot.isContainer = true;
96 boot.postBootCommands =
97 ''
98 # After booting, register the contents of the Nix store in the Nix
99 # database.
100 if [ -f /nix-path-registration ]; then
101 ${config.nix.package.out}/bin/nix-store --load-db < /nix-path-registration &&
102 rm /nix-path-registration
103 fi
104
105 # nixos-rebuild also requires a "system" profile
106 ${config.nix.package.out}/bin/nix-env -p /nix/var/nix/profiles/system --set /run/current-system
107 '';
108
109 system.build.metadata = pkgs.callPackage ../../lib/make-system-tarball.nix {
110 contents = [
111 {
112 source = toYAML "metadata.yaml" {
113 architecture = builtins.elemAt (builtins.match "^([a-z0-9_]+).+" (toString pkgs.system)) 0;
114 creation_date = 1;
115 properties = {
116 description = "NixOS ${config.system.nixos.codeName} ${config.system.nixos.label} ${pkgs.system}";
117 os = "nixos";
118 release = "${config.system.nixos.codeName}";
119 };
120 templates = templates.properties;
121 };
122 target = "/metadata.yaml";
123 }
124 ] ++ templates.files;
125 };
126
127 # TODO: build rootfs as squashfs for faster unpack
128 system.build.tarball = pkgs.callPackage ../../lib/make-system-tarball.nix {
129 extraArgs = "--owner=0";
130
131 storeContents = [
132 {
133 object = config.system.build.toplevel;
134 symlink = "none";
135 }
136 ];
137
138 contents = [
139 {
140 source = config.system.build.toplevel + "/init";
141 target = "/sbin/init";
142 }
143 ];
144
145 extraCommands = "mkdir -p proc sys dev";
146 };
147
148 # Add the overrides from lxd distrobuilder
149 systemd.extraConfig = ''
150 [Service]
151 ProtectProc=default
152 ProtectControlGroups=no
153 ProtectKernelTunables=no
154 '';
155
156 # Allow the user to login as root without password.
157 users.users.root.initialHashedPassword = mkOverride 150 "";
158
159 system.activationScripts.installInitScript = mkForce ''
160 ln -fs $systemConfig/init /sbin/init
161 '';
162
163 # Some more help text.
164 services.getty.helpLine =
165 ''
166
167 Log in as "root" with an empty password.
168 '';
169
170 # Containers should be light-weight, so start sshd on demand.
171 services.openssh.enable = mkDefault true;
172 services.openssh.startWhenNeeded = mkDefault true;
173 };
174}