fix(inputs.chrony): Use DGRAM for the unix socket (#15552)

This commit is contained in:
Frank Villaro-Dixon 2024-08-09 17:51:15 +02:00 committed by GitHub
parent 72ca42b743
commit 9df480039a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 87 additions and 9 deletions

View File

@ -40,8 +40,23 @@ See the [CONFIGURATION.md][CONFIGURATION.md] for more details.
## sources -- extended information about peers
## sourcestats -- statistics on peers
# metrics = ["tracking"]
## Socket group & permissions
## If the user requests collecting metrics via unix socket, then it is created
## with the following group and permissions.
# socket_group = "chrony"
# socket_perms = "0660"
```
## Local socket permissions
To use the unix socket, telegraf must be able to talk to it. Please ensure that
the telegraf user is a member of the `chrony` group or telegraf won't be able to
use the socket!
The unix socket is needed in order to use the `serverstats` metrics. All other
metrics can be gathered using the udp connection.
## Metrics
- chrony

View File

@ -9,11 +9,15 @@ import (
"fmt"
"net"
"net/url"
"os"
"os/user"
"path"
"strconv"
"syscall"
"time"
fbchrony "github.com/facebook/time/ntp/chrony"
"github.com/google/uuid"
"github.com/influxdata/telegraf"
"github.com/influxdata/telegraf/config"
@ -24,21 +28,61 @@ import (
var sampleConfig string
type Chrony struct {
Server string `toml:"server"`
Timeout config.Duration `toml:"timeout"`
DNSLookup bool `toml:"dns_lookup"`
Metrics []string `toml:"metrics"`
Log telegraf.Logger `toml:"-"`
Server string `toml:"server"`
Timeout config.Duration `toml:"timeout"`
DNSLookup bool `toml:"dns_lookup"`
SocketGroup string `toml:"socket_group"`
SocketPerms string `toml:"socket_perms"`
Metrics []string `toml:"metrics"`
Log telegraf.Logger `toml:"-"`
conn net.Conn
client *fbchrony.Client
source string
local string
}
func (*Chrony) SampleConfig() string {
return sampleConfig
}
// dialUnix opens an unixgram connection with chrony
func (c *Chrony) dialUnix(address string) (*net.UnixConn, error) {
dir := path.Dir(address)
c.local = path.Join(dir, fmt.Sprintf("chrony-telegraf-%s.sock", uuid.New().String()))
conn, err := net.DialUnix("unixgram",
&net.UnixAddr{Name: c.local, Net: "unixgram"},
&net.UnixAddr{Name: address, Net: "unixgram"},
)
if err != nil {
return nil, err
}
filemode, err := strconv.ParseUint(c.SocketPerms, 8, 32)
if err != nil {
return nil, fmt.Errorf("parsing file mode %q failed: %w", c.SocketPerms, err)
}
if err := os.Chmod(c.local, os.FileMode(filemode)); err != nil {
return nil, fmt.Errorf("changing file mode of %q failed: %w", c.local, err)
}
group, err := user.LookupGroup(c.SocketGroup)
if err != nil {
return nil, fmt.Errorf("looking up group %q failed: %w", c.SocketGroup, err)
}
gid, err := strconv.Atoi(group.Gid)
if err != nil {
return nil, fmt.Errorf("parsing group ID %q failed: %w", group.Gid, err)
}
if err := os.Chown(c.local, os.Getuid(), gid); err != nil {
return nil, fmt.Errorf("changing group of %q failed: %w", c.local, err)
}
return conn, nil
}
func (c *Chrony) Init() error {
// Use the configured server, if none set, we try to guess it in Start()
if c.Server != "" {
@ -48,7 +92,7 @@ func (c *Chrony) Init() error {
return fmt.Errorf("parsing server address failed: %w", err)
}
switch u.Scheme {
case "unix":
case "unixgram":
// Keep the server unmodified
case "udp":
// Check if we do have a port and add the default port if we don't
@ -79,6 +123,14 @@ func (c *Chrony) Init() error {
}
}
if c.SocketGroup == "" {
c.SocketGroup = "chrony"
}
if c.SocketPerms == "" {
c.SocketPerms = "0660"
}
return nil
}
@ -90,8 +142,8 @@ func (c *Chrony) Start(_ telegraf.Accumulator) error {
return fmt.Errorf("parsing server address failed: %w", err)
}
switch u.Scheme {
case "unix":
conn, err := net.DialTimeout("unix", u.Path, time.Duration(c.Timeout))
case "unixgram":
conn, err := c.dialUnix(u.Path)
if err != nil {
return fmt.Errorf("dialing %q failed: %w", c.Server, err)
}
@ -107,7 +159,7 @@ func (c *Chrony) Start(_ telegraf.Accumulator) error {
}
} else {
// If no server is given, reproduce chronyc's behavior
if conn, err := net.DialTimeout("unix", "/run/chrony/chronyd.sock", time.Duration(c.Timeout)); err == nil {
if conn, err := c.dialUnix("/run/chrony/chronyd.sock"); err == nil {
c.Server = "unix:///run/chrony/chronyd.sock"
c.conn = conn
} else if conn, err := net.DialTimeout("udp", "127.0.0.1:323", time.Duration(c.Timeout)); err == nil {
@ -136,6 +188,11 @@ func (c *Chrony) Stop() {
c.Log.Errorf("Closing connection to %q failed: %v", c.Server, err)
}
}
if c.local != "" {
if err := os.Remove(c.local); err != nil {
c.Log.Errorf("Removing temporary socket %q failed: %v", c.local, err)
}
}
}
func (c *Chrony) Gather(acc telegraf.Accumulator) error {

View File

@ -21,3 +21,9 @@
## sources -- extended information about peers
## sourcestats -- statistics on peers
# metrics = ["tracking"]
## Socket group & permissions
## If the user requests collecting metrics via unix socket, then it is created
## with the following group and permissions.
# socket_group = "chrony"
# socket_perms = "0660"