Skip to content

Commit

Permalink
Updates from powersj:
Browse files Browse the repository at this point in the history
* Introduce two config options for the chrony group and permissions
of the socket. Use these to set the permissions and group ownership of
the temporary socket.
* Clean up the temporary socket on close

TODO: do we need to keep "unix" as a supported scheme
TODO: do we need to make the local socket opt-in or only when requesting
  server stats to avoid regressions
  • Loading branch information
powersj committed Aug 1, 2024
1 parent 2fcfc3e commit 46fc4df
Show file tree
Hide file tree
Showing 3 changed files with 58 additions and 13 deletions.
6 changes: 6 additions & 0 deletions plugins/inputs/chrony/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,12 @@ 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"
```

## Rights
Expand Down
59 changes: 46 additions & 13 deletions plugins/inputs/chrony/chrony.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"net"
"net/url"
"os"
"os/user"
"path"
"strconv"
"syscall"
Expand All @@ -26,37 +27,56 @@ 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 dialUnix(address string) (*net.UnixConn, error) {
func (c *Chrony) dialUnix(address string) (*net.UnixConn, error) {
dir := path.Dir(address)
local := path.Join(dir, fmt.Sprintf("chrony-telegraf-%s.sock", uuid.New().String()))
// XXX TODO: remove the file at the end
c.local = path.Join(dir, fmt.Sprintf("chrony-telegraf-%s.sock", uuid.New().String()))
conn, err := net.DialUnix("unixgram",
&net.UnixAddr{Name: local, Net: "unixgram"},
&net.UnixAddr{Name: c.local, Net: "unixgram"},
&net.UnixAddr{Name: address, Net: "unixgram"},
)

if err != nil {
return nil, err
}

if err := os.Chmod(local, 0666); 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
Expand Down Expand Up @@ -102,6 +122,14 @@ func (c *Chrony) Init() error {
}
}

if c.SocketGroup == "" {
c.SocketGroup = "chrony"
}

if c.SocketPerms == "" {
c.SocketPerms = "0660"
}

return nil
}

Expand All @@ -114,7 +142,7 @@ func (c *Chrony) Start(_ telegraf.Accumulator) error {
}
switch u.Scheme {
case "unixgram":
conn, err := dialUnix(u.Path)
conn, err := c.dialUnix(u.Path)
if err != nil {
return fmt.Errorf("dialing %q failed: %w", c.Server, err)
}
Expand All @@ -130,7 +158,7 @@ func (c *Chrony) Start(_ telegraf.Accumulator) error {
}
} else {
// If no server is given, reproduce chronyc's behavior
if conn, err := dialUnix("/run/chrony/chronyd.sock"); 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 {
Expand Down Expand Up @@ -159,6 +187,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 {
Expand Down
6 changes: 6 additions & 0 deletions plugins/inputs/chrony/sample.conf
Original file line number Diff line number Diff line change
Expand Up @@ -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"

0 comments on commit 46fc4df

Please sign in to comment.