1{
2 config,
3 lib,
4 pkgs,
5 ...
6}:
7
8with lib;
9
10let
11
12 inherit (pkgs) ntp;
13
14 cfg = config.services.ntp;
15
16 configFile = pkgs.writeText "ntp.conf" ''
17 driftfile /var/lib/ntp/ntp.drift
18
19 restrict default ${toString cfg.restrictDefault}
20 restrict -6 default ${toString cfg.restrictDefault}
21 restrict source ${toString cfg.restrictSource}
22
23 restrict 127.0.0.1
24 restrict -6 ::1
25
26 ${toString (map (server: "server " + server + " iburst\n") cfg.servers)}
27
28 ${cfg.extraConfig}
29 '';
30
31 ntpFlags = [
32 "-c"
33 "${configFile}"
34 "-u"
35 "ntp:ntp"
36 ]
37 ++ cfg.extraFlags;
38
39in
40
41{
42
43 ###### interface
44
45 options = {
46
47 services.ntp = {
48
49 enable = mkOption {
50 type = types.bool;
51 default = false;
52 description = ''
53 Whether to synchronise your machine's time using ntpd, as a peer in
54 the NTP network.
55
56 Disables `systemd.timesyncd` if enabled.
57 '';
58 };
59
60 restrictDefault = mkOption {
61 type = types.listOf types.str;
62 description = ''
63 The restriction flags to be set by default.
64
65 The default flags prevent external hosts from using ntpd as a DDoS
66 reflector, setting system time, and querying OS/ntpd version. As
67 recommended in section 6.5.1.1.3, answer "No" of
68 https://support.ntp.org/Support/AccessRestrictions
69 '';
70 default = [
71 "limited"
72 "kod"
73 "nomodify"
74 "notrap"
75 "noquery"
76 "nopeer"
77 ];
78 };
79
80 restrictSource = mkOption {
81 type = types.listOf types.str;
82 description = ''
83 The restriction flags to be set on source.
84
85 The default flags allow peers to be added by ntpd from configured
86 pool(s), but not by other means.
87 '';
88 default = [
89 "limited"
90 "kod"
91 "nomodify"
92 "notrap"
93 "noquery"
94 ];
95 };
96
97 servers = mkOption {
98 default = config.networking.timeServers;
99 defaultText = literalExpression "config.networking.timeServers";
100 type = types.listOf types.str;
101 description = ''
102 The set of NTP servers from which to synchronise.
103 '';
104 };
105
106 extraConfig = mkOption {
107 type = types.lines;
108 default = "";
109 example = ''
110 fudge 127.127.1.0 stratum 10
111 '';
112 description = ''
113 Additional text appended to {file}`ntp.conf`.
114 '';
115 };
116
117 extraFlags = mkOption {
118 type = types.listOf types.str;
119 description = "Extra flags passed to the ntpd command.";
120 example = literalExpression ''[ "--interface=eth0" ]'';
121 default = [ ];
122 };
123
124 };
125
126 };
127
128 ###### implementation
129
130 meta.maintainers = with lib.maintainers; [ thoughtpolice ];
131
132 config = mkIf config.services.ntp.enable {
133 # Make tools such as ntpq available in the system path.
134 environment.systemPackages = [ pkgs.ntp ];
135 services.timesyncd.enable = mkForce false;
136
137 systemd.services.systemd-timedated.environment = {
138 SYSTEMD_TIMEDATED_NTP_SERVICES = "ntpd.service";
139 };
140
141 users.users.ntp = {
142 isSystemUser = true;
143 group = "ntp";
144 description = "NTP daemon user";
145 home = "/var/lib/ntp";
146 createHome = true;
147 };
148 users.groups.ntp = { };
149
150 systemd.services.ntpd = {
151 description = "NTP Daemon";
152
153 wantedBy = [ "multi-user.target" ];
154 wants = [ "time-sync.target" ];
155 before = [ "time-sync.target" ];
156
157 serviceConfig = {
158 ExecStart = "@${ntp}/bin/ntpd ntpd -g ${builtins.toString ntpFlags}";
159 Type = "forking";
160
161 # Hardening options
162 PrivateDevices = true;
163 PrivateIPC = true;
164 PrivateTmp = true;
165 ProtectClock = false;
166 ProtectHome = true;
167
168 ProtectHostname = true;
169 ProtectKernelLogs = true;
170 ProtectKernelModules = true;
171 ProtectKernelTunables = true;
172 ProtectSystem = true;
173
174 RestrictNamespaces = true;
175 RestrictRealtime = true;
176 LockPersonality = true;
177 MemoryDenyWriteExecute = true;
178 AmbientCapabilities = [
179 "CAP_SYS_TIME"
180 ];
181
182 ProtectControlGroups = true;
183 ProtectProc = "invisible";
184 ProcSubset = "pid";
185 RestrictSUIDSGID = true;
186 };
187 };
188
189 };
190
191}