at 24.11-pre 8.9 kB view raw
1{ config, lib, pkgs, ... }: 2 3with lib; 4 5let 6 cfg = config.services.chrony; 7 chronyPkg = cfg.package; 8 9 stateDir = cfg.directory; 10 driftFile = "${stateDir}/chrony.drift"; 11 keyFile = "${stateDir}/chrony.keys"; 12 rtcFile = "${stateDir}/chrony.rtc"; 13 14 configFile = pkgs.writeText "chrony.conf" '' 15 ${concatMapStringsSep "\n" (server: "server " + server + " " + cfg.serverOption + optionalString (cfg.enableNTS) " nts") cfg.servers} 16 17 ${optionalString 18 (cfg.initstepslew.enabled && (cfg.servers != [])) 19 "initstepslew ${toString cfg.initstepslew.threshold} ${concatStringsSep " " cfg.servers}" 20 } 21 22 driftfile ${driftFile} 23 keyfile ${keyFile} 24 ${optionalString (cfg.enableRTCTrimming) "rtcfile ${rtcFile}"} 25 ${optionalString (cfg.enableNTS) "ntsdumpdir ${stateDir}"} 26 27 ${optionalString (cfg.enableRTCTrimming) "rtcautotrim ${builtins.toString cfg.autotrimThreshold}"} 28 ${optionalString (!config.time.hardwareClockInLocalTime) "rtconutc"} 29 30 ${cfg.extraConfig} 31 ''; 32 33 chronyFlags = 34 [ "-n" "-u" "chrony" "-f" "${configFile}" ] 35 ++ optional cfg.enableMemoryLocking "-m" 36 ++ cfg.extraFlags; 37in 38{ 39 options = { 40 services.chrony = { 41 enable = mkOption { 42 type = types.bool; 43 default = false; 44 description = '' 45 Whether to synchronise your machine's time using chrony. 46 Make sure you disable NTP if you enable this service. 47 ''; 48 }; 49 50 package = mkPackageOption pkgs "chrony" { }; 51 52 servers = mkOption { 53 default = config.networking.timeServers; 54 defaultText = literalExpression "config.networking.timeServers"; 55 type = types.listOf types.str; 56 description = '' 57 The set of NTP servers from which to synchronise. 58 ''; 59 }; 60 61 serverOption = mkOption { 62 default = "iburst"; 63 type = types.enum [ "iburst" "offline" ]; 64 description = '' 65 Set option for server directives. 66 67 Use "iburst" to rapidly poll on startup. Recommended if your machine 68 is consistently online. 69 70 Use "offline" to prevent polling on startup. Recommended if your 71 machine boots offline or is otherwise frequently offline. 72 ''; 73 }; 74 75 enableMemoryLocking = mkOption { 76 type = types.bool; 77 default = config.environment.memoryAllocator.provider != "graphene-hardened"; 78 defaultText = ''config.environment.memoryAllocator.provider != "graphene-hardened"''; 79 description = '' 80 Whether to add the `-m` flag to lock memory. 81 ''; 82 }; 83 84 enableRTCTrimming = mkOption { 85 type = types.bool; 86 default = true; 87 description = '' 88 Enable tracking of the RTC offset to the system clock and automatic trimming. 89 See also [](#opt-services.chrony.autotrimThreshold) 90 91 ::: {.note} 92 This is not compatible with the `rtcsync` directive, which naively syncs the RTC time every 11 minutes. 93 94 Tracking the RTC drift will allow more precise timekeeping, 95 especially on intermittently running devices, where the RTC is very relevant. 96 ::: 97 ''; 98 }; 99 100 autotrimThreshold = mkOption { 101 type = types.ints.positive; 102 default = 30; 103 example = 10; 104 description = '' 105 Maximum estimated error threshold for the `rtcautotrim` command. 106 When reached, the RTC will be trimmed. 107 Only used when [](#opt-services.chrony.enableRTCTrimming) is enabled. 108 ''; 109 }; 110 111 enableNTS = mkOption { 112 type = types.bool; 113 default = false; 114 description = '' 115 Whether to enable Network Time Security authentication. 116 Make sure it is supported by your selected NTP server(s). 117 ''; 118 }; 119 120 initstepslew = { 121 enabled = mkOption { 122 type = types.bool; 123 default = true; 124 description = '' 125 Allow chronyd to make a rapid measurement of the system clock error 126 at boot time, and to correct the system clock by stepping before 127 normal operation begins. 128 ''; 129 }; 130 131 threshold = mkOption { 132 type = types.either types.float types.int; 133 default = 1000; # by default, same threshold as 'ntpd -g' (1000s) 134 description = '' 135 The threshold of system clock error (in seconds) above which the 136 clock will be stepped. If the correction required is less than the 137 threshold, a slew is used instead. 138 ''; 139 }; 140 }; 141 142 directory = mkOption { 143 type = types.str; 144 default = "/var/lib/chrony"; 145 description = "Directory where chrony state is stored."; 146 }; 147 148 extraConfig = mkOption { 149 type = types.lines; 150 default = ""; 151 description = '' 152 Extra configuration directives that should be added to 153 `chrony.conf` 154 ''; 155 }; 156 157 extraFlags = mkOption { 158 default = [ ]; 159 example = [ "-s" ]; 160 type = types.listOf types.str; 161 description = "Extra flags passed to the chronyd command."; 162 }; 163 }; 164 }; 165 166 config = mkIf cfg.enable { 167 meta.maintainers = with lib.maintainers; [ thoughtpolice vifino ]; 168 169 environment.systemPackages = [ chronyPkg ]; 170 171 users.groups.chrony.gid = config.ids.gids.chrony; 172 173 users.users.chrony = 174 { 175 uid = config.ids.uids.chrony; 176 group = "chrony"; 177 description = "chrony daemon user"; 178 home = stateDir; 179 }; 180 181 services.timesyncd.enable = mkForce false; 182 183 # If chrony controls and tracks the RTC, writing it externally causes clock error. 184 systemd.services.save-hwclock = lib.mkIf cfg.enableRTCTrimming { 185 enable = lib.mkForce false; 186 }; 187 188 systemd.services.systemd-timedated.environment = { SYSTEMD_TIMEDATED_NTP_SERVICES = "chronyd.service"; }; 189 190 systemd.tmpfiles.rules = [ 191 "d ${stateDir} 0750 chrony chrony - -" 192 "f ${driftFile} 0640 chrony chrony - -" 193 "f ${keyFile} 0640 chrony chrony - -" 194 ] ++ lib.optionals cfg.enableRTCTrimming [ 195 "f ${rtcFile} 0640 chrony chrony - -" 196 ]; 197 198 systemd.services.chronyd = 199 { 200 description = "chrony NTP daemon"; 201 202 wantedBy = [ "multi-user.target" ]; 203 wants = [ "time-sync.target" ]; 204 before = [ "time-sync.target" ]; 205 after = [ "network.target" "nss-lookup.target" ]; 206 conflicts = [ "ntpd.service" "systemd-timesyncd.service" ]; 207 208 path = [ chronyPkg ]; 209 210 unitConfig.ConditionCapability = "CAP_SYS_TIME"; 211 serviceConfig = { 212 Type = "simple"; 213 ExecStart = "${chronyPkg}/bin/chronyd ${builtins.toString chronyFlags}"; 214 215 # Proc filesystem 216 ProcSubset = "pid"; 217 ProtectProc = "invisible"; 218 # Access write directories 219 ReadWritePaths = [ "${stateDir}" ]; 220 UMask = "0027"; 221 # Capabilities 222 CapabilityBoundingSet = [ "CAP_CHOWN" "CAP_DAC_OVERRIDE" "CAP_NET_BIND_SERVICE" "CAP_SETGID" "CAP_SETUID" "CAP_SYS_RESOURCE" "CAP_SYS_TIME" ]; 223 # Device Access 224 DeviceAllow = [ "char-pps rw" "char-ptp rw" "char-rtc rw" ]; 225 DevicePolicy = "closed"; 226 # Security 227 NoNewPrivileges = true; 228 # Sandboxing 229 ProtectSystem = "full"; 230 ProtectHome = true; 231 PrivateTmp = true; 232 PrivateDevices = false; 233 PrivateUsers = false; 234 ProtectHostname = true; 235 ProtectClock = false; 236 ProtectKernelTunables = true; 237 ProtectKernelModules = true; 238 ProtectKernelLogs = true; 239 ProtectControlGroups = true; 240 RestrictAddressFamilies = [ "AF_UNIX" "AF_INET" "AF_INET6" ]; 241 RestrictNamespaces = true; 242 LockPersonality = true; 243 MemoryDenyWriteExecute = true; 244 RestrictRealtime = true; 245 RestrictSUIDSGID = true; 246 RemoveIPC = true; 247 PrivateMounts = true; 248 # System Call Filtering 249 SystemCallArchitectures = "native"; 250 SystemCallFilter = [ "~@cpu-emulation @debug @keyring @mount @obsolete @privileged @resources" "@clock" "@setuid" "capset" "@chown" ]; 251 }; 252 }; 253 254 assertions = [ 255 { 256 assertion = !(cfg.enableRTCTrimming && builtins.any (line: (builtins.match "^ *rtcsync" line) != null) (lib.strings.splitString "\n" cfg.extraConfig)); 257 message = '' 258 The chrony module now configures `rtcfile` and `rtcautotrim` for you. 259 These options conflict with `rtcsync` and cause chrony to crash. 260 Unless you are very sure the former isn't what you want, please remove 261 `rtcsync` from `services.chrony.extraConfig`. 262 Alternatively, disable this behaviour by `services.chrony.enableRTCTrimming = false;` 263 ''; 264 } 265 ]; 266 }; 267}