Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

USM: make the nodejs monitor to a real protocol #34760

Merged
merged 5 commits into from
Mar 5, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions pkg/network/usm/ebpf_main.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ var (
opensslSpec,
goTLSSpec,
istioSpec,
nodejsSpec,
}
)

Expand Down
19 changes: 5 additions & 14 deletions pkg/network/usm/ebpf_ssl.go
Original file line number Diff line number Diff line change
Expand Up @@ -428,13 +428,12 @@ var opensslSpec = &protocols.ProtocolSpec{
}

type sslProgram struct {
cfg *config.Config
watcher *sharedlibraries.Watcher
nodeJSMonitor *nodeJSMonitor
cfg *config.Config
watcher *sharedlibraries.Watcher
}

func newSSLProgramProtocolFactory(m *manager.Manager, c *config.Config) (protocols.Protocol, error) {
if (!c.EnableNativeTLSMonitoring || !usmconfig.TLSSupported(c)) && !c.EnableNodeJSMonitoring {
if !c.EnableNativeTLSMonitoring || !usmconfig.TLSSupported(c) {
return nil, nil
}

Expand Down Expand Up @@ -468,15 +467,9 @@ func newSSLProgramProtocolFactory(m *manager.Manager, c *config.Config) (protoco
}
}

nodejs, err := newNodeJSMonitor(c, m)
if err != nil {
return nil, fmt.Errorf("error initializing nodejs monitor: %w", err)
}

return &sslProgram{
cfg: c,
watcher: watcher,
nodeJSMonitor: nodejs,
cfg: c,
watcher: watcher,
}, nil
}

Expand Down Expand Up @@ -507,7 +500,6 @@ func (o *sslProgram) ConfigureOptions(options *manager.Options) {
// PreStart is called before the start of the provided eBPF manager.
func (o *sslProgram) PreStart() error {
o.watcher.Start()
o.nodeJSMonitor.Start()
return nil
}

Expand All @@ -519,7 +511,6 @@ func (o *sslProgram) PostStart() error {
// Stop stops the program.
func (o *sslProgram) Stop() {
o.watcher.Stop()
o.nodeJSMonitor.Stop()
}

// DumpMaps dumps the content of the map represented by mapName & currentMap, if it used by the eBPF program, to output.
Expand Down
132 changes: 121 additions & 11 deletions pkg/network/usm/nodejs.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,21 @@ package usm

import (
"fmt"
"io"
"strings"

"github.com/cilium/ebpf"

manager "github.com/DataDog/ebpf-manager"

"github.com/DataDog/datadog-agent/pkg/ebpf/uprobes"
"github.com/DataDog/datadog-agent/pkg/network/config"
"github.com/DataDog/datadog-agent/pkg/network/protocols"
"github.com/DataDog/datadog-agent/pkg/network/usm/buildmode"
usmconfig "github.com/DataDog/datadog-agent/pkg/network/usm/config"
"github.com/DataDog/datadog-agent/pkg/network/usm/consts"
"github.com/DataDog/datadog-agent/pkg/process/monitor"
"github.com/DataDog/datadog-agent/pkg/util/kernel"
"github.com/DataDog/datadog-agent/pkg/util/log"
)

const (
Expand Down Expand Up @@ -106,15 +111,96 @@ var (
}
)

var nodejsSpec = &protocols.ProtocolSpec{
Factory: newNodeJSMonitor,
Maps: sharedLibrariesMaps,
Probes: []*manager.Probe{
{
ProbeIdentificationPair: manager.ProbeIdentificationPair{
EBPFFuncName: "kprobe__tcp_sendmsg",
},
},
{
ProbeIdentificationPair: manager.ProbeIdentificationPair{
EBPFFuncName: sslDoHandshakeProbe,
},
},
{
ProbeIdentificationPair: manager.ProbeIdentificationPair{
EBPFFuncName: sslDoHandshakeRetprobe,
},
},
{
ProbeIdentificationPair: manager.ProbeIdentificationPair{
EBPFFuncName: sslSetBioProbe,
},
},
{
ProbeIdentificationPair: manager.ProbeIdentificationPair{
EBPFFuncName: sslSetFDProbe,
},
},
{
ProbeIdentificationPair: manager.ProbeIdentificationPair{
EBPFFuncName: bioNewSocketProbe,
},
},
{
ProbeIdentificationPair: manager.ProbeIdentificationPair{
EBPFFuncName: bioNewSocketRetprobe,
},
},
{
ProbeIdentificationPair: manager.ProbeIdentificationPair{
EBPFFuncName: sslReadProbe,
},
},
{
ProbeIdentificationPair: manager.ProbeIdentificationPair{
EBPFFuncName: nodejsSslReadRetprobe,
},
},
{
ProbeIdentificationPair: manager.ProbeIdentificationPair{
EBPFFuncName: nodejsSslReadExRetprobe,
},
},
{
ProbeIdentificationPair: manager.ProbeIdentificationPair{
EBPFFuncName: sslWriteProbe,
},
},
{
ProbeIdentificationPair: manager.ProbeIdentificationPair{
EBPFFuncName: nodejsSslWriteRetprobe,
},
},
{
ProbeIdentificationPair: manager.ProbeIdentificationPair{
EBPFFuncName: nodejsSslWriteExRetprobe,
},
},
{
ProbeIdentificationPair: manager.ProbeIdentificationPair{
EBPFFuncName: sslShutdownProbe,
},
},
},
}

// nodeJSMonitor essentially scans for Node processes and attaches SSL uprobes
// to them.
type nodeJSMonitor struct {
cfg *config.Config
attacher *uprobes.UprobeAttacher
processMonitor *monitor.ProcessMonitor
}

func newNodeJSMonitor(c *config.Config, mgr *manager.Manager) (*nodeJSMonitor, error) {
if !c.EnableNodeJSMonitoring {
// Ensuring nodeJSMonitor implements the protocols.Protocol interface.
var _ protocols.Protocol = (*nodeJSMonitor)(nil)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's this for? please add a comment

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Verifying we're always implementing the protocol interface


func newNodeJSMonitor(mgr *manager.Manager, c *config.Config) (protocols.Protocol, error) {
if !c.EnableNodeJSMonitoring || !usmconfig.TLSSupported(c) {
return nil, nil
}

Expand All @@ -137,22 +223,28 @@ func newNodeJSMonitor(c *config.Config, mgr *manager.Manager) (*nodeJSMonitor, e
}

return &nodeJSMonitor{
cfg: c,
attacher: attacher,
processMonitor: procMon,
}, nil
}

// Start the nodeJSMonitor
func (m *nodeJSMonitor) Start() {
if m == nil {
return
}
// ConfigureOptions changes map attributes to the given options.
func (m *nodeJSMonitor) ConfigureOptions(options *manager.Options) {
sharedLibrariesConfigureOptions(options, m.cfg)
}

// PreStart is called before the start of the provided eBPF manager.
func (m *nodeJSMonitor) PreStart() error {
if err := m.attacher.Start(); err != nil {
log.Errorf("cannot start nodeJS attacher: %s", err)
} else {
log.Info("Node JS TLS monitoring enabled")
return fmt.Errorf("cannot start nodeJS attacher: %w", err)
}
return nil
}

// PostStart is called after the start of the provided eBPF manager.
func (*nodeJSMonitor) PostStart() error {
return nil
}

// Stop the nodeJSMonitor.
Expand All @@ -173,3 +265,21 @@ func isNodeJSBinary(_ string, procInfo *uprobes.ProcInfo) bool {
}
return strings.Contains(exe, nodeJSPath)
}

// DumpMaps is a no-op.
func (*nodeJSMonitor) DumpMaps(io.Writer, string, *ebpf.Map) {}

// Name return the program's name.
func (*nodeJSMonitor) Name() string {
return nodeJsAttacherName
}

// GetStats is a no-op.
func (*nodeJSMonitor) GetStats() (*protocols.ProtocolStats, func()) {
return nil, nil
}

// IsBuildModeSupported returns always true, as tls module is supported by all modes.
func (*nodeJSMonitor) IsBuildModeSupported(buildmode.Type) bool {
return true
}