Skip to content

Commit 51abcaf

Browse files
authored
[SIEM][Auditbeat] Fix socket dataset startup when IPv6 is disabled (#13966)
This patch fixes a few problems with the new system/socket dataset when IPv6 has been disabled by booting the kernel with `ipv6.disable=1`. - Detection of IPv6 can fail in an unexpected way causing a startup failure instead of disabling IPv6 support. - One offset guess depended on the ability to create AF_INET6 sockets. - A couple of offset guessing tasks depended on a connect() to a magic address in the range 127/8 or fd00::/8, which can cause a timeout error due to connect() blocking on some systems. Fixes #13953
1 parent a678bc9 commit 51abcaf

File tree

6 files changed

+110
-42
lines changed

6 files changed

+110
-42
lines changed

CHANGELOG.next.asciidoc

+1
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,7 @@ https://github.com/elastic/beats/compare/v7.0.0-alpha2...master[Check the HEAD d
127127
- Add syscalls used by librpm for the system/package dataset to the default Auditbeat seccomp policy. {issue}12578[12578] {pull}12617[12617]
128128
- Process dataset: Do not show non-root warning on Windows. {pull}12740[12740]
129129
- Host dataset: Export Host fields to gob encoder. {pull}12940[12940]
130+
- Socket dataset: Fix start errors when IPv6 is disabled on the kernel. {issue}13953[13953] {pull}13966[13966]
130131

131132
*Filebeat*
132133

x-pack/auditbeat/module/system/socket/guess/guess.go

+3-3
Original file line numberDiff line numberDiff line change
@@ -208,9 +208,9 @@ func guessOnce(guesser Guesser, installer helper.ProbeInstaller, ctx Context) (r
208208
// Need to make sure that the trigger has finished before extracting
209209
// results because there could be data-races between Trigger and Extract.
210210
select {
211-
case result := <-thread.C():
212-
if result.Err != nil {
213-
return nil, errors.Wrap(err, "trigger execution failed")
211+
case r := <-thread.C():
212+
if r.Err != nil {
213+
return nil, errors.Wrap(r.Err, "trigger execution failed")
214214
}
215215
case <-timer.C:
216216
return nil, errors.New("timeout while waiting for trigger to complete")

x-pack/auditbeat/module/system/socket/guess/inetsockaf.go

+11-4
Original file line numberDiff line numberDiff line change
@@ -51,9 +51,10 @@ func init() {
5151
}
5252

5353
type guessInetSockFamily struct {
54-
ctx Context
55-
family int
56-
limit int
54+
ctx Context
55+
family int
56+
limit int
57+
canIPv6 bool
5758
}
5859

5960
// Name of this guess.
@@ -103,6 +104,12 @@ func (g *guessInetSockFamily) Prepare(ctx Context) error {
103104
if g.limit, ok = g.ctx.Vars["INET_SOCK_V6_LIMIT"].(int); !ok {
104105
return errors.New("required variable INET_SOCK_V6_LIMIT not found")
105106
}
107+
// check that this system can create AF_INET6 sockets. Otherwise revert to
108+
// using AF_INET only.
109+
fd, err := unix.Socket(unix.AF_INET6, unix.SOCK_DGRAM, 0)
110+
if g.canIPv6 = err == nil; g.canIPv6 {
111+
unix.Close(fd)
112+
}
106113
return nil
107114
}
108115

@@ -114,7 +121,7 @@ func (g *guessInetSockFamily) Terminate() error {
114121
// Trigger creates and then closes a socket alternating between AF_INET/AF_INET6
115122
// on each run.
116123
func (g *guessInetSockFamily) Trigger() error {
117-
if g.family == unix.AF_INET {
124+
if g.canIPv6 && g.family == unix.AF_INET {
118125
g.family = unix.AF_INET6
119126
} else {
120127
g.family = unix.AF_INET

x-pack/auditbeat/module/system/socket/guess/sockaddrin.go

+37-17
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,10 @@
77
package guess
88

99
import (
10+
"bytes"
1011
"encoding/binary"
11-
"net"
1212

13+
"github.com/pkg/errors"
1314
"golang.org/x/sys/unix"
1415

1516
"github.com/elastic/beats/libbeat/common"
@@ -29,19 +30,15 @@ import (
2930

3031
func init() {
3132
if err := Registry.AddGuess(
32-
&guessSockaddrIn{
33-
address: net.TCPAddr{
34-
IP: net.IPv4(127, 0x12, 0x34, 0x56).To4(),
35-
Port: 0xABCD,
36-
},
37-
}); err != nil {
33+
&guessSockaddrIn{}); err != nil {
3834
panic(err)
3935
}
4036
}
4137

4238
type guessSockaddrIn struct {
43-
ctx Context
44-
address net.TCPAddr
39+
ctx Context
40+
local, remote unix.SockaddrInet4
41+
server, client int
4542
}
4643

4744
// Name of this guess.
@@ -81,25 +78,48 @@ func (g *guessSockaddrIn) Probes() ([]helper.ProbeDef, error) {
8178
}
8279

8380
// Prepare is a no-op.
84-
func (g *guessSockaddrIn) Prepare(ctx Context) error {
81+
func (g *guessSockaddrIn) Prepare(ctx Context) (err error) {
8582
g.ctx = ctx
83+
g.local = unix.SockaddrInet4{
84+
Port: 0,
85+
Addr: randomLocalIP(),
86+
}
87+
g.remote = unix.SockaddrInet4{
88+
Port: 0,
89+
Addr: randomLocalIP(),
90+
}
91+
for bytes.Equal(g.local.Addr[:], g.remote.Addr[:]) {
92+
g.remote.Addr = randomLocalIP()
93+
}
94+
if g.server, g.local, err = createSocket(g.local); err != nil {
95+
return errors.Wrap(err, "error creating server")
96+
}
97+
if g.client, g.remote, err = createSocket(g.remote); err != nil {
98+
return errors.Wrap(err, "error creating client")
99+
}
100+
if err = unix.Listen(g.server, 1); err != nil {
101+
return errors.Wrap(err, "error in listen")
102+
}
86103
return nil
87104
}
88105

89106
// Terminate is a no-op.
90107
func (g *guessSockaddrIn) Terminate() error {
108+
unix.Close(g.client)
109+
unix.Close(g.server)
91110
return nil
92111
}
93112

94113
// Trigger connects a socket to a random local address (127.x.x.x).
95114
func (g *guessSockaddrIn) Trigger() error {
96-
dialer := net.Dialer{
97-
Timeout: g.ctx.Timeout,
115+
if err := unix.Connect(g.client, &g.local); err != nil {
116+
return err
98117
}
99-
conn, err := dialer.Dial("tcp", g.address.String())
100-
if err == nil {
101-
conn.Close()
118+
fd, _, err := unix.Accept(g.server)
119+
if err != nil {
120+
return err
102121
}
122+
unix.Close(fd)
103123
return nil
104124
}
105125

@@ -116,13 +136,13 @@ func (g *guessSockaddrIn) Extract(ev interface{}) (common.MapStr, bool) {
116136
return nil, false
117137
}
118138

119-
binary.BigEndian.PutUint16(needle[:], uint16(g.address.Port))
139+
binary.BigEndian.PutUint16(needle[:], uint16(g.local.Port))
120140
offsetOfPort := indexAligned(arr, needle[:], offsetOfFamily+2, 2)
121141
if offsetOfPort == -1 {
122142
return nil, false
123143
}
124144

125-
offsetOfAddr := indexAligned(arr, []byte(g.address.IP), offsetOfPort+2, 4)
145+
offsetOfAddr := indexAligned(arr, []byte(g.local.Addr[:]), offsetOfPort+2, 4)
126146
if offsetOfAddr == -1 {
127147
return nil, false
128148
}

x-pack/auditbeat/module/system/socket/guess/sockaddrin6.go

+49-17
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,8 @@ package guess
88

99
import (
1010
"encoding/binary"
11-
"net"
1211

12+
"github.com/pkg/errors"
1313
"golang.org/x/sys/unix"
1414

1515
"github.com/elastic/beats/libbeat/common"
@@ -30,19 +30,16 @@ import (
3030

3131
func init() {
3232
if err := Registry.AddGuess(
33-
&guessSockaddrIn6{
34-
address: net.TCPAddr{
35-
IP: []byte{0xFD, 0xE5, 0x7C, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD},
36-
Port: 0xFEEF,
37-
},
38-
}); err != nil {
33+
&guessSockaddrIn6{}); err != nil {
3934
panic(err)
4035
}
4136
}
4237

4338
type guessSockaddrIn6 struct {
44-
ctx Context
45-
address net.TCPAddr
39+
ctx Context
40+
loopback helper.IPv6Loopback
41+
clientAddr, serverAddr unix.SockaddrInet6
42+
client, server int
4643
}
4744

4845
// Name of this guess.
@@ -88,25 +85,60 @@ func (g *guessSockaddrIn6) Probes() ([]helper.ProbeDef, error) {
8885
}
8986

9087
// Prepare is a no-op.
91-
func (g *guessSockaddrIn6) Prepare(ctx Context) error {
88+
func (g *guessSockaddrIn6) Prepare(ctx Context) (err error) {
9289
g.ctx = ctx
90+
g.loopback, err = helper.NewIPv6Loopback()
91+
if err != nil {
92+
return errors.Wrap(err, "detect IPv6 loopback failed")
93+
}
94+
defer func() {
95+
if err != nil {
96+
g.loopback.Cleanup()
97+
}
98+
}()
99+
clientIP, err := g.loopback.AddRandomAddress()
100+
if err != nil {
101+
return errors.Wrap(err, "failed adding first device address")
102+
}
103+
serverIP, err := g.loopback.AddRandomAddress()
104+
if err != nil {
105+
return errors.Wrap(err, "failed adding second device address")
106+
}
107+
copy(g.clientAddr.Addr[:], clientIP)
108+
copy(g.serverAddr.Addr[:], serverIP)
109+
110+
if g.client, g.clientAddr, err = createSocket6WithProto(unix.SOCK_STREAM, g.clientAddr); err != nil {
111+
return errors.Wrap(err, "error creating server")
112+
}
113+
if g.server, g.serverAddr, err = createSocket6WithProto(unix.SOCK_STREAM, g.serverAddr); err != nil {
114+
return errors.Wrap(err, "error creating client")
115+
}
116+
if err = unix.Listen(g.server, 1); err != nil {
117+
return errors.Wrap(err, "error in listen")
118+
}
93119
return nil
94120
}
95121

96122
// Terminate is a no-op.
97123
func (g *guessSockaddrIn6) Terminate() error {
124+
unix.Close(g.client)
125+
unix.Close(g.server)
126+
if err := g.loopback.Cleanup(); err != nil {
127+
return err
128+
}
98129
return nil
99130
}
100131

101132
// Trigger performs a connection attempt on the random address.
102133
func (g *guessSockaddrIn6) Trigger() error {
103-
dialer := net.Dialer{
104-
Timeout: g.ctx.Timeout,
134+
if err := unix.Connect(g.client, &g.serverAddr); err != nil {
135+
return errors.Wrap(err, "connect failed")
105136
}
106-
conn, err := dialer.Dial("tcp", g.address.String())
107-
if err == nil {
108-
conn.Close()
137+
fd, _, err := unix.Accept(g.server)
138+
if err != nil {
139+
return errors.Wrap(err, "accept failed")
109140
}
141+
unix.Close(fd)
110142
return nil
111143
}
112144

@@ -124,13 +156,13 @@ func (g *guessSockaddrIn6) Extract(ev interface{}) (common.MapStr, bool) {
124156
return nil, false
125157
}
126158

127-
binary.BigEndian.PutUint16(needle[:], uint16(g.address.Port))
159+
binary.BigEndian.PutUint16(needle[:], uint16(g.serverAddr.Port))
128160
offsetOfPort := indexAligned(arr, needle[:], offsetOfFamily+2, 2)
129161
if offsetOfPort == -1 {
130162
return nil, false
131163
}
132164

133-
offsetOfAddr := indexAligned(arr, g.address.IP, offsetOfPort+2, 1)
165+
offsetOfAddr := indexAligned(arr, g.serverAddr.Addr[:], offsetOfPort+2, 1)
134166
if offsetOfAddr == -1 {
135167
return nil, false
136168
}

x-pack/auditbeat/module/system/socket/socket_linux.go

+9-1
Original file line numberDiff line numberDiff line change
@@ -239,7 +239,8 @@ func (m *MetricSet) Setup() (err error) {
239239

240240
hasIPv6, err := detectIPv6()
241241
if err != nil {
242-
return errors.Wrap(err, "error detecting IPv6 support")
242+
m.log.Debugf("Error detecting IPv6 support: %v", err)
243+
hasIPv6 = false
243244
}
244245
m.log.Debugf("IPv6 supported: %v", hasIPv6)
245246
if m.config.EnableIPv6 != nil {
@@ -459,6 +460,13 @@ func (m *mountPoint) String() string {
459460
}
460461

461462
func detectIPv6() (bool, error) {
463+
// Check that AF_INET6 is available.
464+
// This fails when the kernel is booted with ipv6.disable=1
465+
fd, err := unix.Socket(unix.AF_INET6, unix.SOCK_DGRAM, 0)
466+
if err != nil {
467+
return false, nil
468+
}
469+
unix.Close(fd)
462470
loopback, err := helper.NewIPv6Loopback()
463471
if err != nil {
464472
return false, err

0 commit comments

Comments
 (0)