vuls: init at 0.27.0
[NixPkgs.git] / nixos / modules / tasks / auto-upgrade.nix
blobc995016d85c76e6e2400bfd8418b5156a7c1bd82
1 { config, lib, pkgs, ... }:
2 let cfg = config.system.autoUpgrade;
4 in {
6   options = {
8     system.autoUpgrade = {
10       enable = lib.mkOption {
11         type = lib.types.bool;
12         default = false;
13         description = ''
14           Whether to periodically upgrade NixOS to the latest
15           version. If enabled, a systemd timer will run
16           `nixos-rebuild switch --upgrade` once a
17           day.
18         '';
19       };
21       operation = lib.mkOption {
22         type = lib.types.enum ["switch" "boot"];
23         default = "switch";
24         example = "boot";
25         description = ''
26           Whether to run
27           `nixos-rebuild switch --upgrade` or run
28           `nixos-rebuild boot --upgrade`
29         '';
30       };
32       flake = lib.mkOption {
33         type = lib.types.nullOr lib.types.str;
34         default = null;
35         example = "github:kloenk/nix";
36         description = ''
37           The Flake URI of the NixOS configuration to build.
38           Disables the option {option}`system.autoUpgrade.channel`.
39         '';
40       };
42       channel = lib.mkOption {
43         type = lib.types.nullOr lib.types.str;
44         default = null;
45         example = "https://nixos.org/channels/nixos-14.12-small";
46         description = ''
47           The URI of the NixOS channel to use for automatic
48           upgrades. By default, this is the channel set using
49           {command}`nix-channel` (run `nix-channel --list`
50           to see the current value).
51         '';
52       };
54       flags = lib.mkOption {
55         type = lib.types.listOf lib.types.str;
56         default = [ ];
57         example = [
58           "-I"
59           "stuff=/home/alice/nixos-stuff"
60           "--option"
61           "extra-binary-caches"
62           "http://my-cache.example.org/"
63         ];
64         description = ''
65           Any additional flags passed to {command}`nixos-rebuild`.
67           If you are using flakes and use a local repo you can add
68           {command}`[ "--update-input" "nixpkgs" "--commit-lock-file" ]`
69           to update nixpkgs.
70         '';
71       };
73       dates = lib.mkOption {
74         type = lib.types.str;
75         default = "04:40";
76         example = "daily";
77         description = ''
78           How often or when upgrade occurs. For most desktop and server systems
79           a sufficient upgrade frequency is once a day.
81           The format is described in
82           {manpage}`systemd.time(7)`.
83         '';
84       };
86       allowReboot = lib.mkOption {
87         default = false;
88         type = lib.types.bool;
89         description = ''
90           Reboot the system into the new generation instead of a switch
91           if the new generation uses a different kernel, kernel modules
92           or initrd than the booted system.
93           See {option}`rebootWindow` for configuring the times at which a reboot is allowed.
94         '';
95       };
97       randomizedDelaySec = lib.mkOption {
98         default = "0";
99         type = lib.types.str;
100         example = "45min";
101         description = ''
102           Add a randomized delay before each automatic upgrade.
103           The delay will be chosen between zero and this value.
104           This value must be a time span in the format specified by
105           {manpage}`systemd.time(7)`
106         '';
107       };
109       fixedRandomDelay = lib.mkOption {
110         default = false;
111         type = lib.types.bool;
112         example = true;
113         description = ''
114           Make the randomized delay consistent between runs.
115           This reduces the jitter between automatic upgrades.
116           See {option}`randomizedDelaySec` for configuring the randomized delay.
117         '';
118       };
120       rebootWindow = lib.mkOption {
121         description = ''
122           Define a lower and upper time value (in HH:MM format) which
123           constitute a time window during which reboots are allowed after an upgrade.
124           This option only has an effect when {option}`allowReboot` is enabled.
125           The default value of `null` means that reboots are allowed at any time.
126         '';
127         default = null;
128         example = { lower = "01:00"; upper = "05:00"; };
129         type = with lib.types; nullOr (submodule {
130           options = {
131             lower = lib.mkOption {
132               description = "Lower limit of the reboot window";
133               type = lib.types.strMatching "[[:digit:]]{2}:[[:digit:]]{2}";
134               example = "01:00";
135             };
137             upper = lib.mkOption {
138               description = "Upper limit of the reboot window";
139               type = lib.types.strMatching "[[:digit:]]{2}:[[:digit:]]{2}";
140               example = "05:00";
141             };
142           };
143         });
144       };
146       persistent = lib.mkOption {
147         default = true;
148         type = lib.types.bool;
149         example = false;
150         description = ''
151           Takes a boolean argument. If true, the time when the service
152           unit was last triggered is stored on disk. When the timer is
153           activated, the service unit is triggered immediately if it
154           would have been triggered at least once during the time when
155           the timer was inactive. Such triggering is nonetheless
156           subject to the delay imposed by RandomizedDelaySec=. This is
157           useful to catch up on missed runs of the service when the
158           system was powered down.
159         '';
160       };
162     };
164   };
166   config = lib.mkIf cfg.enable {
168     assertions = [{
169       assertion = !((cfg.channel != null) && (cfg.flake != null));
170       message = ''
171         The options 'system.autoUpgrade.channel' and 'system.autoUpgrade.flake' cannot both be set.
172       '';
173     }];
175     system.autoUpgrade.flags = (if cfg.flake == null then
176         [ "--no-build-output" ] ++ lib.optionals (cfg.channel != null) [
177           "-I"
178           "nixpkgs=${cfg.channel}/nixexprs.tar.xz"
179         ]
180       else
181         [ "--refresh" "--flake ${cfg.flake}" ]);
183     systemd.services.nixos-upgrade = {
184       description = "NixOS Upgrade";
186       restartIfChanged = false;
187       unitConfig.X-StopOnRemoval = false;
189       serviceConfig.Type = "oneshot";
191       environment = config.nix.envVars // {
192         inherit (config.environment.sessionVariables) NIX_PATH;
193         HOME = "/root";
194       } // config.networking.proxy.envVars;
196       path = with pkgs; [
197         coreutils
198         gnutar
199         xz.bin
200         gzip
201         gitMinimal
202         config.nix.package.out
203         config.programs.ssh.package
204       ];
206       script = let
207         nixos-rebuild = "${config.system.build.nixos-rebuild}/bin/nixos-rebuild";
208         date     = "${pkgs.coreutils}/bin/date";
209         readlink = "${pkgs.coreutils}/bin/readlink";
210         shutdown = "${config.systemd.package}/bin/shutdown";
211         upgradeFlag = lib.optional (cfg.channel == null) "--upgrade";
212       in if cfg.allowReboot then ''
213         ${nixos-rebuild} boot ${toString (cfg.flags ++ upgradeFlag)}
214         booted="$(${readlink} /run/booted-system/{initrd,kernel,kernel-modules})"
215         built="$(${readlink} /nix/var/nix/profiles/system/{initrd,kernel,kernel-modules})"
217         ${lib.optionalString (cfg.rebootWindow != null) ''
218           current_time="$(${date} +%H:%M)"
220           lower="${cfg.rebootWindow.lower}"
221           upper="${cfg.rebootWindow.upper}"
223           if [[ "''${lower}" < "''${upper}" ]]; then
224             if [[ "''${current_time}" > "''${lower}" ]] && \
225                [[ "''${current_time}" < "''${upper}" ]]; then
226               do_reboot="true"
227             else
228               do_reboot="false"
229             fi
230           else
231             # lower > upper, so we are crossing midnight (e.g. lower=23h, upper=6h)
232             # we want to reboot if cur > 23h or cur < 6h
233             if [[ "''${current_time}" < "''${upper}" ]] || \
234                [[ "''${current_time}" > "''${lower}" ]]; then
235               do_reboot="true"
236             else
237               do_reboot="false"
238             fi
239           fi
240         ''}
242         if [ "''${booted}" = "''${built}" ]; then
243           ${nixos-rebuild} ${cfg.operation} ${toString cfg.flags}
244         ${lib.optionalString (cfg.rebootWindow != null) ''
245           elif [ "''${do_reboot}" != true ]; then
246             echo "Outside of configured reboot window, skipping."
247         ''}
248         else
249           ${shutdown} -r +1
250         fi
251       '' else ''
252         ${nixos-rebuild} ${cfg.operation} ${toString (cfg.flags ++ upgradeFlag)}
253       '';
255       startAt = cfg.dates;
257       after = [ "network-online.target" ];
258       wants = [ "network-online.target" ];
259     };
261     systemd.timers.nixos-upgrade = {
262       timerConfig = {
263         RandomizedDelaySec = cfg.randomizedDelaySec;
264         FixedRandomDelay = cfg.fixedRandomDelay;
265         Persistent = cfg.persistent;
266       };
267     };
268   };