]> git.ipfire.org Git - thirdparty/chrony.git/commitdiff
conf: add directives to specify interfaces for binding sockets
authorMiroslav Lichvar <mlichvar@redhat.com>
Tue, 30 Jun 2020 10:27:10 +0000 (12:27 +0200)
committerMiroslav Lichvar <mlichvar@redhat.com>
Wed, 1 Jul 2020 14:19:44 +0000 (16:19 +0200)
Add binddevice, bindacqdevice, and bindcmddevice directive to specify
the interface for binding the NTP server, NTP client, and command socket
respectively.

cmdmon.c
conf.c
conf.h
doc/chrony.conf.adoc
ntp_io.c
sys_linux.c

index d5dbe710608a007969d17c82ce63ae5128b3cce5..1ade206272441ad6671ef88cbcf77ce11aa1b3a3 100644 (file)
--- a/cmdmon.c
+++ b/cmdmon.c
@@ -157,8 +157,8 @@ static void read_from_cmd_socket(int sock_fd, int event, void *anything);
 static int
 open_socket(int family)
 {
+  const char *local_path, *iface;
   IPSockAddr local_addr;
-  const char *local_path;
   int sock_fd, port;
 
   switch (family) {
@@ -173,7 +173,9 @@ open_socket(int family)
         SCK_GetLoopbackIPAddress(family, &local_addr.ip_addr);
       local_addr.port = port;
 
-      sock_fd = SCK_OpenUdpSocket(NULL, &local_addr, NULL, SCK_FLAG_RX_DEST_ADDR);
+      iface = CNF_GetBindCommandInterface();
+
+      sock_fd = SCK_OpenUdpSocket(NULL, &local_addr, iface, SCK_FLAG_RX_DEST_ADDR);
       if (sock_fd < 0) {
         LOG(LOGS_ERR, "Could not open command socket on %s",
             UTI_IPSockAddrToString(&local_addr));
diff --git a/conf.c b/conf.c
index a5e2d25beecfd7db2a8df1a22955adcb89a7e33c..2e994dcbf9a1b6e457ef3b3b09655ea91c502abf 100644 (file)
--- a/conf.c
+++ b/conf.c
@@ -192,6 +192,11 @@ static IPAddr bind_acq_address4, bind_acq_address6;
    the loopback address will be used */
 static IPAddr bind_cmd_address4, bind_cmd_address6;
 
+/* Interface names to bind the NTP server, NTP client, and command socket */
+static char *bind_ntp_iface = NULL;
+static char *bind_acq_iface = NULL;
+static char *bind_cmd_iface = NULL;
+
 /* Path to the Unix domain command socket. */
 static char *bind_cmd_path = NULL;
 
@@ -422,6 +427,9 @@ CNF_Finalise(void)
   Free(keys_file);
   Free(leapsec_tz);
   Free(logdir);
+  Free(bind_ntp_iface);
+  Free(bind_acq_iface);
+  Free(bind_cmd_iface);
   Free(bind_cmd_path);
   Free(ntp_signd_socket);
   Free(pidfile);
@@ -511,10 +519,16 @@ CNF_ParseLine(const char *filename, int number, char *line)
     parse_authselectmode(p);
   } else if (!strcasecmp(command, "bindacqaddress")) {
     parse_bindacqaddress(p);
+  } else if (!strcasecmp(command, "bindacqdevice")) {
+    parse_string(p, &bind_acq_iface);
   } else if (!strcasecmp(command, "bindaddress")) {
     parse_bindaddress(p);
   } else if (!strcasecmp(command, "bindcmdaddress")) {
     parse_bindcmdaddress(p);
+  } else if (!strcasecmp(command, "bindcmddevice")) {
+    parse_string(p, &bind_cmd_iface);
+  } else if (!strcasecmp(command, "binddevice")) {
+    parse_string(p, &bind_ntp_iface);
   } else if (!strcasecmp(command, "broadcast")) {
     parse_broadcast(p);
   } else if (!strcasecmp(command, "clientloglimit")) {
@@ -2228,6 +2242,30 @@ CNF_GetBindAcquisitionAddress(int family, IPAddr *addr)
 
 /* ================================================== */
 
+char *
+CNF_GetBindNtpInterface(void)
+{
+  return bind_ntp_iface;
+}
+
+/* ================================================== */
+
+char *
+CNF_GetBindAcquisitionInterface(void)
+{
+  return bind_acq_iface;
+}
+
+/* ================================================== */
+
+char *
+CNF_GetBindCommandInterface(void)
+{
+  return bind_cmd_iface;
+}
+
+/* ================================================== */
+
 char *
 CNF_GetBindCommandPath(void)
 {
diff --git a/conf.h b/conf.h
index 0406d2db05c0a2c939f08c807e1a80b68081cd45..c35f24501e6249538f3818bff435a0b1edbdd796 100644 (file)
--- a/conf.h
+++ b/conf.h
@@ -79,6 +79,9 @@ extern void CNF_GetFallbackDrifts(int *min, int *max);
 extern void CNF_GetBindAddress(int family, IPAddr *addr);
 extern void CNF_GetBindAcquisitionAddress(int family, IPAddr *addr);
 extern void CNF_GetBindCommandAddress(int family, IPAddr *addr);
+extern char *CNF_GetBindNtpInterface(void);
+extern char *CNF_GetBindAcquisitionInterface(void);
+extern char *CNF_GetBindCommandInterface(void);
 extern char *CNF_GetBindCommandPath(void);
 extern char *CNF_GetNtpSigndSocket(void);
 extern char *CNF_GetPidFile(void);
index 5658af0dc7c2f576217592670ec6ec90964ad490..3d8a681fe31ac1f8ae9db1dd16f8899623877c7d 100644 (file)
@@ -630,7 +630,7 @@ This would change the source port used for client requests to UDP port 1123.
 You could then persuade the firewall administrator to open that port.
 
 [[bindacqaddress]]*bindacqaddress* _address_::
-The *bindacqaddress* directive sets the network interface to which
+The *bindacqaddress* directive specifies a local IP address to which
 *chronyd* will bind its NTP client sockets. The syntax is similar to the
 <<bindaddress,*bindaddress*>> and <<bindcmdaddress,*bindcmdaddress*>>
 directives.
@@ -638,6 +638,19 @@ directives.
 For each of the IPv4 and IPv6 protocols, only one *bindacqaddress* directive
 can be specified.
 
+[[bindacqdevice]]*bindacqdevice* _interface_::
+The *bindacqdevice* directive binds the client sockets to a network device
+specified by the interface name. This can be useful when the local address is
+dynamic, or to enable an NTP source specified with a link-local IPv6 address.
+This directive can specify only one interface and it is supported on Linux
+only.
++
+An example of the directive is:
++
+----
+bindacqdevice eth0
+----
+
 [[dumpdir]]*dumpdir* _directory_::
 To compute the rate of gain or loss of time, *chronyd* has to store a
 measurement history for each of the time sources it uses.
@@ -1336,6 +1349,17 @@ Currently, for each of the IPv4 and IPv6 protocols, only one *bindaddress*
 directive can be specified. Therefore, it is not useful on computers which
 should serve NTP on multiple network interfaces.
 
+[[binddevice]]*binddevice* _interface_::
+The *binddevice* directive binds the NTP server sockets to a network device
+specified by the interface name. This directive can specify only one interface
+and it is supported on Linux only.
++
+An example of the directive is:
++
+----
+binddevice eth0
+----
+
 [[broadcast]]*broadcast* _interval_ _address_ [_port_]::
 The *broadcast* directive is used to declare a broadcast address to which
 chronyd should send packets in the NTP broadcast mode (i.e. make *chronyd* act
@@ -1664,8 +1688,8 @@ smoothtime 50000 0.01
 === Command and monitoring access
 
 [[bindcmdaddress]]*bindcmdaddress* _address_::
-The *bindcmdaddress* directive allows you to specify an IP address of an
-interface on which *chronyd* will listen for monitoring command packets (issued
+The *bindcmdaddress* directive specifies a local IP address to which *chronyd*
+will bind the UDP socket listening for monitoring command packets (issued
 by *chronyc*). On systems other than Linux, the address of the interface needs
 to be already configured when *chronyd* is started.
 +
@@ -1676,9 +1700,10 @@ directory will be created on start if it does not exist. The compiled-in default
 path of the socket is _@CHRONYRUNDIR@/chronyd.sock_. The socket can be
 disabled by setting the path to _/_.
 +
-By default, *chronyd* binds to the loopback interface (with addresses
-_127.0.0.1_ and _::1_). This blocks all access except from localhost. To listen
-for command packets on all interfaces, you can add the lines:
+By default, *chronyd* binds the UDP sockets to the addresses _127.0.0.1_ and
+_::1_ (i.e. the loopback interface). This blocks all access except from
+localhost. To listen for command packets on all interfaces, you can add the
+lines:
 +
 ----
 bindcmdaddress 0.0.0.0
@@ -1696,6 +1721,17 @@ An example that sets the path of the Unix domain command socket is:
 bindcmdaddress /var/run/chrony/chronyd.sock
 ----
 
+[[bindcmddevice]]*bindcmddevice* _interface_::
+The *bindcmddevice* directive binds the UDP command sockets to a network device
+specified by the interface name. This directive can specify only one interface
+and it is supported on Linux only.
++
+An example of the directive is:
++
+----
+bindcmddevice eth0
+----
+
 [[cmdallow]]*cmdallow* [*all*] [_subnet_]::
 This is similar to the <<allow,*allow*>> directive, except that it allows
 monitoring access (rather than NTP client access) to a particular subnet or
index 9c1b686a271d9c59ca498a1997e55cba61e92c3c..1ba5efb0452c978ec1637561b1e1a221b2fbce1e 100644 (file)
--- a/ntp_io.c
+++ b/ntp_io.c
@@ -85,14 +85,18 @@ open_socket(int family, int local_port, int client_only, IPSockAddr *remote_addr
 {
   int sock_fd, sock_flags, events = SCH_FILE_INPUT;
   IPSockAddr local_addr;
+  char *iface;
 
   if (!SCK_IsIpFamilyEnabled(family))
     return INVALID_SOCK_FD;
 
-  if (!client_only)
+  if (!client_only) {
     CNF_GetBindAddress(family, &local_addr.ip_addr);
-  else
+    iface = CNF_GetBindNtpInterface();
+  } else {
     CNF_GetBindAcquisitionAddress(family, &local_addr.ip_addr);
+    iface = CNF_GetBindAcquisitionInterface();
+  }
 
   if (local_addr.ip_addr.family != family)
     SCK_GetAnyLocalIPAddress(family, &local_addr.ip_addr);
@@ -103,7 +107,7 @@ open_socket(int family, int local_port, int client_only, IPSockAddr *remote_addr
   if (!client_only)
     sock_flags |= SCK_FLAG_BROADCAST;
 
-  sock_fd = SCK_OpenUdpSocket(remote_addr, &local_addr, NULL, sock_flags);
+  sock_fd = SCK_OpenUdpSocket(remote_addr, &local_addr, iface, sock_flags);
   if (sock_fd < 0) {
     if (!client_only)
       LOG(LOGS_ERR, "Could not open NTP socket on %s", UTI_IPSockAddrToString(&local_addr));
index 8c41259f0d65ccf2d20003e6e3d60ffc43e97c4a..7cdd33c7dcde088d275705649e7cf213e1af32e6 100644 (file)
@@ -442,7 +442,8 @@ SYS_Linux_DropRoot(uid_t uid, gid_t gid, int clock_control)
      Keep CAP_SYS_TIME if the clock control is enabled. */
   if (snprintf(cap_text, sizeof (cap_text), "%s %s %s",
                CNF_GetNTPPort() ? "cap_net_bind_service=ep" : "",
-               0 ? "cap_net_raw=ep" : "",
+               CNF_GetBindNtpInterface() || CNF_GetBindAcquisitionInterface() ?
+                 "cap_net_raw=ep" : "",
                clock_control ? "cap_sys_time=ep" : "") >= sizeof (cap_text))
     assert(0);