at 23.11-pre 6.7 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 13 configFile = pkgs.writeText "chrony.conf" '' 14 ${concatMapStringsSep "\n" (server: "server " + server + " " + cfg.serverOption + optionalString (cfg.enableNTS) " nts") cfg.servers} 15 16 ${optionalString 17 (cfg.initstepslew.enabled && (cfg.servers != [])) 18 "initstepslew ${toString cfg.initstepslew.threshold} ${concatStringsSep " " cfg.servers}" 19 } 20 21 driftfile ${driftFile} 22 keyfile ${keyFile} 23 ${optionalString (cfg.enableNTS) "ntsdumpdir ${stateDir}"} 24 25 ${optionalString (!config.time.hardwareClockInLocalTime) "rtconutc"} 26 27 ${cfg.extraConfig} 28 ''; 29 30 chronyFlags = [ "-n" "-m" "-u" "chrony" "-f" "${configFile}" ] ++ cfg.extraFlags; 31in 32{ 33 options = { 34 services.chrony = { 35 enable = mkOption { 36 type = types.bool; 37 default = false; 38 description = lib.mdDoc '' 39 Whether to synchronise your machine's time using chrony. 40 Make sure you disable NTP if you enable this service. 41 ''; 42 }; 43 44 package = mkOption { 45 type = types.package; 46 default = pkgs.chrony; 47 defaultText = literalExpression "pkgs.chrony"; 48 description = lib.mdDoc '' 49 Which chrony package to use. 50 ''; 51 }; 52 53 servers = mkOption { 54 default = config.networking.timeServers; 55 defaultText = literalExpression "config.networking.timeServers"; 56 type = types.listOf types.str; 57 description = lib.mdDoc '' 58 The set of NTP servers from which to synchronise. 59 ''; 60 }; 61 62 serverOption = mkOption { 63 default = "iburst"; 64 type = types.enum [ "iburst" "offline" ]; 65 description = lib.mdDoc '' 66 Set option for server directives. 67 68 Use "iburst" to rapidly poll on startup. Recommended if your machine 69 is consistently online. 70 71 Use "offline" to prevent polling on startup. Recommended if your 72 machine boots offline or is otherwise frequently offline. 73 ''; 74 }; 75 76 enableNTS = mkOption { 77 type = types.bool; 78 default = false; 79 description = lib.mdDoc '' 80 Whether to enable Network Time Security authentication. 81 Make sure it is supported by your selected NTP server(s). 82 ''; 83 }; 84 85 initstepslew = { 86 enabled = mkOption { 87 type = types.bool; 88 default = true; 89 description = lib.mdDoc '' 90 Allow chronyd to make a rapid measurement of the system clock error 91 at boot time, and to correct the system clock by stepping before 92 normal operation begins. 93 ''; 94 }; 95 96 threshold = mkOption { 97 type = types.either types.float types.int; 98 default = 1000; # by default, same threshold as 'ntpd -g' (1000s) 99 description = lib.mdDoc '' 100 The threshold of system clock error (in seconds) above which the 101 clock will be stepped. If the correction required is less than the 102 threshold, a slew is used instead. 103 ''; 104 }; 105 }; 106 107 directory = mkOption { 108 type = types.str; 109 default = "/var/lib/chrony"; 110 description = lib.mdDoc "Directory where chrony state is stored."; 111 }; 112 113 extraConfig = mkOption { 114 type = types.lines; 115 default = ""; 116 description = lib.mdDoc '' 117 Extra configuration directives that should be added to 118 `chrony.conf` 119 ''; 120 }; 121 122 extraFlags = mkOption { 123 default = []; 124 example = [ "-s" ]; 125 type = types.listOf types.str; 126 description = lib.mdDoc "Extra flags passed to the chronyd command."; 127 }; 128 }; 129 }; 130 131 config = mkIf cfg.enable { 132 meta.maintainers = with lib.maintainers; [ thoughtpolice ]; 133 134 environment.systemPackages = [ chronyPkg ]; 135 136 users.groups.chrony.gid = config.ids.gids.chrony; 137 138 users.users.chrony = 139 { uid = config.ids.uids.chrony; 140 group = "chrony"; 141 description = "chrony daemon user"; 142 home = stateDir; 143 }; 144 145 services.timesyncd.enable = mkForce false; 146 147 systemd.services.systemd-timedated.environment = { SYSTEMD_TIMEDATED_NTP_SERVICES = "chronyd.service"; }; 148 149 systemd.tmpfiles.rules = [ 150 "d ${stateDir} 0750 chrony chrony - -" 151 "f ${driftFile} 0640 chrony chrony - -" 152 "f ${keyFile} 0640 chrony chrony - -" 153 ]; 154 155 systemd.services.chronyd = 156 { description = "chrony NTP daemon"; 157 158 wantedBy = [ "multi-user.target" ]; 159 wants = [ "time-sync.target" ]; 160 before = [ "time-sync.target" ]; 161 after = [ "network.target" "nss-lookup.target" ]; 162 conflicts = [ "ntpd.service" "systemd-timesyncd.service" ]; 163 164 path = [ chronyPkg ]; 165 166 unitConfig.ConditionCapability = "CAP_SYS_TIME"; 167 serviceConfig = { 168 Type = "simple"; 169 ExecStart = "${chronyPkg}/bin/chronyd ${builtins.toString chronyFlags}"; 170 171 # Proc filesystem 172 ProcSubset = "pid"; 173 ProtectProc = "invisible"; 174 # Access write directories 175 ReadWritePaths = [ "${stateDir}" ]; 176 UMask = "0027"; 177 # Capabilities 178 CapabilityBoundingSet = [ "CAP_CHOWN" "CAP_DAC_OVERRIDE" "CAP_NET_BIND_SERVICE" "CAP_SETGID" "CAP_SETUID" "CAP_SYS_RESOURCE" "CAP_SYS_TIME" ]; 179 # Device Access 180 DeviceAllow = [ "char-pps rw" "char-ptp rw" "char-rtc rw" ]; 181 DevicePolicy = "closed"; 182 # Security 183 NoNewPrivileges = true; 184 # Sandboxing 185 ProtectSystem = "full"; 186 ProtectHome = true; 187 PrivateTmp = true; 188 PrivateDevices = false; 189 PrivateUsers = false; 190 ProtectHostname = true; 191 ProtectClock = false; 192 ProtectKernelTunables = true; 193 ProtectKernelModules = true; 194 ProtectKernelLogs = true; 195 ProtectControlGroups = true; 196 RestrictAddressFamilies = [ "AF_UNIX" "AF_INET" "AF_INET6" ]; 197 RestrictNamespaces = true; 198 LockPersonality = true; 199 MemoryDenyWriteExecute = true; 200 RestrictRealtime = true; 201 RestrictSUIDSGID = true; 202 RemoveIPC = true; 203 PrivateMounts = true; 204 # System Call Filtering 205 SystemCallArchitectures = "native"; 206 SystemCallFilter = [ "~@cpu-emulation @debug @keyring @mount @obsolete @privileged @resources" "@clock" "@setuid" "capset" "@chown" ]; 207 }; 208 }; 209 }; 210}