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