From c4a2550518e19b0995ef143b44e7dabaa3851437 Mon Sep 17 00:00:00 2001 From: Miroslav Lichvar Date: Tue, 30 Jun 2020 12:27:10 +0200 Subject: [PATCH] conf: add directives to specify interfaces for binding sockets Add binddevice, bindacqdevice, and bindcmddevice directive to specify the interface for binding the NTP server, NTP client, and command socket respectively. --- cmdmon.c | 6 ++++-- conf.c | 38 +++++++++++++++++++++++++++++++++++ conf.h | 3 +++ doc/chrony.conf.adoc | 48 ++++++++++++++++++++++++++++++++++++++------ ntp_io.c | 10 ++++++--- sys_linux.c | 3 ++- 6 files changed, 96 insertions(+), 12 deletions(-) diff --git a/cmdmon.c b/cmdmon.c index d5dbe71..1ade206 100644 --- 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 a5e2d25..2e994dc 100644 --- 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 0406d2d..c35f245 100644 --- 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); diff --git a/doc/chrony.conf.adoc b/doc/chrony.conf.adoc index 5658af0..3d8a681 100644 --- a/doc/chrony.conf.adoc +++ b/doc/chrony.conf.adoc @@ -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 <> and <> 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 <> directive, except that it allows monitoring access (rather than NTP client access) to a particular subnet or diff --git a/ntp_io.c b/ntp_io.c index 9c1b686..1ba5efb 100644 --- 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)); diff --git a/sys_linux.c b/sys_linux.c index 8c41259..7cdd33c 100644 --- a/sys_linux.c +++ b/sys_linux.c @@ -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);