Skip to content

Commit 0ff0007

Browse files
authored
[CSPM] extract sysprobe client into interface and add local version (#43138)
### What does this PR do? The CSPM agent can query the system-probe for some compliance data benchmarks. This PR extracts this capability into an interface, and adds a local version of this feature. This will allow the system-probe version of the CSPM agent to query the data locally instead of querying it through the endpoint. No functional change intended. ### Motivation ### Describe how you validated your changes ### Additional Notes Co-authored-by: paul.cacheux <paul.cacheux@datadoghq.com>
1 parent fd5ae00 commit 0ff0007

4 files changed

Lines changed: 117 additions & 62 deletions

File tree

cmd/security-agent/subcommands/start/command.go

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -137,8 +137,13 @@ func Commands(globalParams *command.GlobalParams) []*cobra.Command {
137137
return status.NewInformationProvider(nil), nil, err
138138
}
139139

140+
var sysProbeClient compliance.SysProbeClient
141+
if cfg := sysprobeconfig.SysProbeObject(); cfg != nil && cfg.SocketAddress != "" {
142+
sysProbeClient = compliance.NewRemoteSysProbeClient(cfg.SocketAddress)
143+
}
144+
140145
// start compliance security agent
141-
complianceAgent, err := compliance.StartCompliance(log, config, sysprobeconfig, hostnameDetected, stopper, statsdClient, wmeta, compression, ipc)
146+
complianceAgent, err := compliance.StartCompliance(log, config, hostnameDetected, stopper, statsdClient, wmeta, compression, ipc, sysProbeClient)
142147
if err != nil {
143148
return status.NewInformationProvider(nil), nil, err
144149
}

pkg/compliance/agent.go

Lines changed: 4 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -11,15 +11,10 @@ package compliance
1111
import (
1212
"context"
1313
"encoding/binary"
14-
"encoding/json"
1514
"expvar"
1615
"fmt"
1716
"hash/fnv"
18-
"io"
1917
"math/rand"
20-
"net/http"
21-
"net/url"
22-
"strconv"
2318
"sync"
2419
"time"
2520

@@ -90,9 +85,9 @@ type AgentOptions struct {
9085
// enabled.
9186
EnabledConfigurationExporters []ConfigurationExporter
9287

93-
// SysProbeClient is the HTTP client to allow the execution of benchmarks
94-
// from system-probe. see: cmd/system-probe/modules/compliance.go
95-
SysProbeClient *http.Client
88+
// SysProbeClient is the possibly remote client to allow the execution of benchmarks
89+
// from system-probe.
90+
SysProbeClient SysProbeClient
9691
}
9792

9893
// ConfigurationExporter is an enum type defining all configuration export
@@ -490,37 +485,11 @@ func (a *Agent) reportDBConfigurationFromSystemProbe(ctx context.Context, contai
490485
return fmt.Errorf("system-probe socket client was not created")
491486
}
492487

493-
qs := make(url.Values)
494-
qs.Add("pid", strconv.FormatInt(int64(pid), 10))
495-
sysProbeComplianceModuleURL := &url.URL{
496-
Scheme: "http",
497-
Host: "unix",
498-
Path: "/compliance/dbconfig",
499-
RawQuery: qs.Encode(),
500-
}
501-
502-
req, err := http.NewRequestWithContext(ctx, http.MethodGet, sysProbeComplianceModuleURL.String(), nil)
503-
if err != nil {
504-
return err
505-
}
506-
507-
resp, err := a.opts.SysProbeClient.Do(req)
488+
resource, err := a.opts.SysProbeClient.FetchDBConfig(ctx, pid)
508489
if err != nil {
509490
return err
510491
}
511-
defer resp.Body.Close()
512-
if resp.StatusCode != http.StatusOK {
513-
return fmt.Errorf("error running cross-container benchmark: %s", resp.Status)
514-
}
515492

516-
var resource *dbconfig.DBResource
517-
body, err := io.ReadAll(resp.Body)
518-
if err != nil {
519-
return err
520-
}
521-
if err := json.Unmarshal(body, &resource); err != nil {
522-
return err
523-
}
524493
if resource != nil {
525494
dbResourceLog := NewResourceLog(a.opts.Hostname+"_"+string(containerID), resource.Type, resource.Config)
526495
dbResourceLog.Container = &CheckContainerMeta{

pkg/compliance/compliance.go

Lines changed: 1 addition & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,7 @@
99
package compliance
1010

1111
import (
12-
"context"
1312
"fmt"
14-
"net"
15-
"net/http"
1613
"os"
1714
"time"
1815

@@ -21,7 +18,6 @@ import (
2118
"github.com/DataDog/datadog-agent/comp/core/config"
2219
ipc "github.com/DataDog/datadog-agent/comp/core/ipc/def"
2320
log "github.com/DataDog/datadog-agent/comp/core/log/def"
24-
"github.com/DataDog/datadog-agent/comp/core/sysprobeconfig"
2521
workloadmeta "github.com/DataDog/datadog-agent/comp/core/workloadmeta/def"
2622
"github.com/DataDog/datadog-agent/comp/dogstatsd/constants"
2723
compression "github.com/DataDog/datadog-agent/comp/serializer/logscompression/def"
@@ -35,13 +31,13 @@ import (
3531
// and checks.
3632
func StartCompliance(log log.Component,
3733
config config.Component,
38-
sysprobeconfig sysprobeconfig.Component,
3934
hostname string,
4035
stopper startstop.Stopper,
4136
statsdClient ddgostatsd.ClientInterface,
4237
wmeta workloadmeta.Component,
4338
compression compression.Component,
4439
ipc ipc.Component,
40+
sysProbeClient SysProbeClient,
4541
) (*Agent, error) {
4642

4743
enabled := config.GetBool("compliance_config.enabled")
@@ -70,11 +66,6 @@ func StartCompliance(log log.Component,
7066
resolverOptions.StatsdClient = statsdClient
7167
}
7268

73-
var sysProbeClient *http.Client
74-
if config := sysprobeconfig.SysProbeObject(); config != nil && config.SocketAddress != "" {
75-
sysProbeClient = newSysProbeClient(config.SocketAddress)
76-
}
77-
7869
enabledConfigurationsExporters := []ConfigurationExporter{
7970
KubernetesExporter,
8071
}
@@ -125,19 +116,3 @@ func sendRunningMetrics(statsdClient ddgostatsd.ClientInterface, moduleName stri
125116

126117
return heartbeat
127118
}
128-
129-
func newSysProbeClient(address string) *http.Client {
130-
return &http.Client{
131-
Timeout: 10 * time.Second,
132-
Transport: &http.Transport{
133-
MaxIdleConns: 2,
134-
IdleConnTimeout: 30 * time.Second,
135-
DialContext: func(_ context.Context, _, _ string) (net.Conn, error) {
136-
return net.Dial("unix", address)
137-
},
138-
TLSHandshakeTimeout: 1 * time.Second,
139-
ResponseHeaderTimeout: 5 * time.Second,
140-
ExpectContinueTimeout: 50 * time.Millisecond,
141-
},
142-
}
143-
}

pkg/compliance/sysprobe.go

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
// Unless explicitly stated otherwise all files in this repository are licensed
2+
// under the Apache License Version 2.0.
3+
// This product includes software developed at Datadog (https://www.datadoghq.com/).
4+
// Copyright 2016-present Datadog, Inc.
5+
6+
// Package compliance implements a specific part of the datadog-agent
7+
// responsible for scanning host and containers and report various
8+
// misconfigurations and compliance issues.
9+
package compliance
10+
11+
import (
12+
"context"
13+
"encoding/json"
14+
"fmt"
15+
"io"
16+
"net"
17+
"net/http"
18+
"net/url"
19+
"strconv"
20+
"time"
21+
22+
"github.com/DataDog/datadog-agent/pkg/compliance/dbconfig"
23+
)
24+
25+
// SysProbeClient is an interface for fetching database configuration from system probe
26+
type SysProbeClient interface {
27+
FetchDBConfig(ctx context.Context, pid int32) (*dbconfig.DBResource, error)
28+
}
29+
30+
var _ SysProbeClient = (*RemoteSysProbeClient)(nil)
31+
var _ SysProbeClient = (*LocalSysProbeClient)(nil)
32+
33+
// RemoteSysProbeClient is a client that fetches database configuration from a remote system probe instance
34+
type RemoteSysProbeClient struct {
35+
client *http.Client
36+
}
37+
38+
// NewRemoteSysProbeClient creates a new remote system probe client with the given Unix socket address
39+
func NewRemoteSysProbeClient(address string) *RemoteSysProbeClient {
40+
httpClient := &http.Client{
41+
Timeout: 10 * time.Second,
42+
Transport: &http.Transport{
43+
MaxIdleConns: 2,
44+
IdleConnTimeout: 30 * time.Second,
45+
DialContext: func(_ context.Context, _, _ string) (net.Conn, error) {
46+
return net.Dial("unix", address)
47+
},
48+
TLSHandshakeTimeout: 1 * time.Second,
49+
ResponseHeaderTimeout: 5 * time.Second,
50+
ExpectContinueTimeout: 50 * time.Millisecond,
51+
},
52+
}
53+
54+
return &RemoteSysProbeClient{
55+
client: httpClient,
56+
}
57+
}
58+
59+
// FetchDBConfig fetches database configuration for the given process ID from the remote system probe
60+
func (c *RemoteSysProbeClient) FetchDBConfig(ctx context.Context, pid int32) (*dbconfig.DBResource, error) {
61+
qs := make(url.Values)
62+
qs.Add("pid", strconv.FormatInt(int64(pid), 10))
63+
sysProbeComplianceModuleURL := &url.URL{
64+
Scheme: "http",
65+
Host: "unix",
66+
Path: "/compliance/dbconfig",
67+
RawQuery: qs.Encode(),
68+
}
69+
70+
req, err := http.NewRequestWithContext(ctx, http.MethodGet, sysProbeComplianceModuleURL.String(), nil)
71+
if err != nil {
72+
return nil, err
73+
}
74+
75+
resp, err := c.client.Do(req)
76+
if err != nil {
77+
return nil, err
78+
}
79+
defer resp.Body.Close()
80+
if resp.StatusCode != http.StatusOK {
81+
return nil, fmt.Errorf("error running cross-container benchmark: %s", resp.Status)
82+
}
83+
84+
var resource *dbconfig.DBResource
85+
body, err := io.ReadAll(resp.Body)
86+
if err != nil {
87+
return nil, err
88+
}
89+
if err := json.Unmarshal(body, &resource); err != nil {
90+
return nil, err
91+
}
92+
93+
return resource, nil
94+
}
95+
96+
// LocalSysProbeClient is a client that fetches database configuration locally without going through system probe
97+
type LocalSysProbeClient struct{}
98+
99+
// FetchDBConfig fetches database configuration for the given process ID locally
100+
func (c *LocalSysProbeClient) FetchDBConfig(ctx context.Context, pid int32) (*dbconfig.DBResource, error) {
101+
res, ok := dbconfig.LoadDBResourceFromPID(ctx, pid)
102+
if !ok {
103+
return nil, fmt.Errorf("DB resource not found for pid=%d", pid)
104+
}
105+
return res, nil
106+
}

0 commit comments

Comments
 (0)