Skip to content

Commit 5783dc5

Browse files
Merge pull request #473 from step-security/rp/cherry/cdr-rules
feat: custom detection rules
2 parents 5cef435 + 6b74456 commit 5783dc5

10 files changed

Lines changed: 393 additions & 63 deletions

File tree

agent.go

Lines changed: 25 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ func Run(ctx context.Context, configFilePath string, hostDNSServer DNSServer,
6767
return err
6868
}
6969

70-
apiclient := &ApiClient{Client: &http.Client{Timeout: 3 * time.Second}, APIURL: config.APIURL, DisableTelemetry: config.DisableTelemetry, EgressPolicy: config.EgressPolicy, OneTimeKey: config.OneTimeKey}
70+
apiclient := &ApiClient{Client: &http.Client{Timeout: 3 * time.Second}, APIURL: config.APIURL, TelemetryURL: config.TelemetryURL, DisableTelemetry: config.DisableTelemetry, EgressPolicy: config.EgressPolicy, OneTimeKey: config.OneTimeKey}
7171

7272
config.OneTimeKey = ""
7373
// TODO: pass in an iowriter/ use log library
@@ -167,15 +167,18 @@ func Run(ctx context.Context, configFilePath string, hostDNSServer DNSServer,
167167
WriteLog("\n")
168168
WriteLog("updated resolved")
169169

170-
// Change DNS for docker, causes process in containers to use agent's DNS proxy
171-
if err := dnsConfig.SetDockerDNSServer(cmd, dockerDaemonConfigPath, tempDir); err != nil {
172-
WriteLog(fmt.Sprintf("Error setting DNS server for docker %v", err))
173-
RevertChanges(iptables, nflog, cmd, resolvdConfigPath, dockerDaemonConfigPath, dnsConfig, sudo)
174-
return err
175-
}
170+
// we uninstall docker using go routine, handle case where that routine finishes before we come here
171+
if !config.DisableSudoAndContainers {
172+
// Change DNS for docker, causes process in containers to use agent's DNS proxy
173+
if err := dnsConfig.SetDockerDNSServer(cmd, dockerDaemonConfigPath, tempDir); err != nil {
174+
WriteLog(fmt.Sprintf("Error setting DNS server for docker %v", err))
175+
RevertChanges(iptables, nflog, cmd, resolvdConfigPath, dockerDaemonConfigPath, dnsConfig, sudo)
176+
return err
177+
}
176178

177-
WriteLog("\n")
178-
WriteLog("set docker config\n")
179+
WriteLog("\n")
180+
WriteLog("set docker config\n")
181+
}
179182

180183
if config.EgressPolicy == EgressPolicyAudit {
181184
netMonitor := NetworkMonitor{
@@ -233,22 +236,27 @@ func Run(ctx context.Context, configFilePath string, hostDNSServer DNSServer,
233236
APIURL: config.APIURL,
234237
Repo: config.Repo,
235238
CorrelationID: config.CorrelationId,
236-
OneTimeKey: config.OneTimeKey,
239+
OneTimeKey: apiclient.OneTimeKey,
237240
DisableTelemetry: config.DisableTelemetry,
238241
},
242+
EnableCustomDetectionRules: IsCustomDetectionRulesEnabled(),
239243
}
240244

241245
conf.Files = append(conf.Files, getProcFilesOfInterest()...)
242246

243247
conf.Files = append(conf.Files, getFilesOfInterest()...)
244248

245-
mArmour := armour.NewArmour(ctx, conf)
246-
err := mArmour.Attach()
249+
err := InitArmour(ctx, conf)
247250
if err != nil {
248251
WriteLog("Armour attachment failed")
249252
} else {
250-
defer mArmour.Detach()
253+
if GlobalArmour != nil {
254+
defer GlobalArmour.Detach()
255+
}
251256
WriteLog("Armour attached")
257+
if IsCustomDetectionRulesEnabled() {
258+
WriteLog("[armour] Custom detection rules enabled")
259+
}
252260
}
253261
}
254262

@@ -370,10 +378,13 @@ func addImplicitEndpoints(endpoints map[string][]Endpoint, disableTelemetry bool
370378
}
371379
}
372380

373-
stepsecurity := Endpoint{domainName: "agent.api.stepsecurity.io", port: 443} // Should be implicit based on user feedback
381+
stepsecurity := Endpoint{domainName: "agent.api.stepsecurity.io", port: 443} // Should be implicit based on user feedback
382+
stepsecurityTelemetry := Endpoint{domainName: "prod.app-api.stepsecurity.io", port: 443} // Telemetry endpoint for sending DNS and net connections to StepSecurity
383+
374384
if !disableTelemetry {
375385
// allowing only if disable_telemetry is set to false
376386
normalEndpoints[stepsecurity.domainName] = append(normalEndpoints[stepsecurity.domainName], stepsecurity)
387+
normalEndpoints[stepsecurityTelemetry.domainName] = append(normalEndpoints[stepsecurityTelemetry.domainName], stepsecurityTelemetry)
377388
}
378389

379390
return normalEndpoints, wildcardEndpoints

apiclient.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ type NetworkConnection struct {
4141
type ApiClient struct {
4242
Client *http.Client
4343
APIURL string
44+
TelemetryURL string
4445
DisableTelemetry bool
4546
EgressPolicy string
4647
OneTimeKey string
@@ -57,7 +58,7 @@ func (apiclient *ApiClient) sendDNSRecord(correlationId, repo, domainName, ipAdd
5758
dnsRecord.ResolvedIPAddress = ipAddress
5859
dnsRecord.TimeStamp = time.Now().UTC()
5960

60-
url := fmt.Sprintf("%s/github/%s/actions/jobs/%s/dns", apiclient.APIURL, repo, correlationId)
61+
url := fmt.Sprintf("%s/github/%s/actions/jobs/%s/dns", apiclient.TelemetryURL, repo, correlationId)
6162

6263
return apiclient.sendApiRequest("POST", url, dnsRecord)
6364
}
@@ -76,7 +77,7 @@ func (apiclient *ApiClient) sendNetConnection(correlationId, repo, ipAddress, po
7677
networkConnection.TimeStamp = timestamp
7778
networkConnection.Tool = tool
7879

79-
url := fmt.Sprintf("%s/github/%s/actions/jobs/%s/networkconnection", apiclient.APIURL, repo, correlationId)
80+
url := fmt.Sprintf("%s/github/%s/actions/jobs/%s/networkconnection", apiclient.TelemetryURL, repo, correlationId)
8081

8182
return apiclient.sendApiRequest("POST", url, networkConnection)
8283
}

armour_manager.go

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
package main
2+
3+
import (
4+
"context"
5+
"fmt"
6+
7+
"github.com/step-security/armour/armour"
8+
)
9+
10+
// NOTE: before usage, make sure to nil check
11+
var GlobalArmour *armour.Armour = nil
12+
13+
func InitArmour(ctx context.Context, conf *armour.Config) error {
14+
15+
GlobalArmour = armour.NewArmour(ctx, conf)
16+
err := GlobalArmour.Init()
17+
if err != nil {
18+
GlobalArmour = nil
19+
return err
20+
}
21+
22+
runnerWorkerPID, err := getRunnerWorkerPID()
23+
if err != nil {
24+
WriteLog(fmt.Sprintf("[armour] Error getting Runner.Worker PID: %v", err))
25+
return nil
26+
}
27+
GlobalArmour.SetRunnerWorkerPID(runnerWorkerPID)
28+
WriteLog(fmt.Sprintf("[armour] Runner.Worker PID: %d", runnerWorkerPID))
29+
30+
return nil
31+
}

common.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,3 +91,7 @@ func getProcMemFiles(pid uint64) []string {
9191

9292
return out
9393
}
94+
95+
func getRunnerWorkerPID() (uint64, error) {
96+
return pidOf("Runner.Worker")
97+
}

config.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ type config struct {
1616
RunId string
1717
WorkingDirectory string
1818
APIURL string
19+
TelemetryURL string
1920
OneTimeKey string
2021
Endpoints map[string][]Endpoint
2122
EgressPolicy string
@@ -37,6 +38,7 @@ type configFile struct {
3738
RunId string `json:"run_id"`
3839
WorkingDirectory string `json:"working_directory"`
3940
APIURL string `json:"api_url"`
41+
TelemetryURL string `json:"telemetry_url"`
4042
OneTimeKey string `json:"one_time_key"`
4143
AllowedEndpoints string `json:"allowed_endpoints"`
4244
EgressPolicy string `json:"egress_policy"`
@@ -65,6 +67,10 @@ func (c *config) init(configFilePath string) error {
6567
c.RunId = configFile.RunId
6668
c.WorkingDirectory = configFile.WorkingDirectory
6769
c.APIURL = configFile.APIURL
70+
c.TelemetryURL = configFile.TelemetryURL
71+
if c.TelemetryURL == "" {
72+
c.TelemetryURL = c.APIURL
73+
}
6874
c.Endpoints = parseEndpoints(configFile.AllowedEndpoints)
6975
c.EgressPolicy = configFile.EgressPolicy
7076
c.DisableTelemetry = configFile.DisableTelemetry

dnsproxy.go

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import (
1111

1212
"github.com/miekg/dns"
1313
"github.com/pkg/errors"
14+
"github.com/step-security/armour/armour"
1415
)
1516

1617
type DNSProxy struct {
@@ -239,6 +240,8 @@ func (proxy *DNSProxy) getIPByDomain(domain string) (string, error) {
239240

240241
go proxy.ApiClient.sendDNSRecord(proxy.CorrelationId, proxy.Repo, domain, answer.Data)
241242

243+
go proxy.submitDNSEvent(answer.Data)
244+
242245
return answer.Data, nil
243246

244247
}
@@ -296,6 +299,23 @@ func (proxy *DNSProxy) processTypeA(q *dns.Question, requestMsg *dns.Msg) (*dns.
296299
return &rr, nil
297300
}
298301

302+
// submitDNSEvent submits a DNS event to the detection manager.
303+
func (proxy *DNSProxy) submitDNSEvent(dest string) {
304+
if !IsCustomDetectionRulesEnabled() {
305+
return
306+
}
307+
if GlobalArmour == nil {
308+
return
309+
}
310+
dm := GlobalArmour.DetectionManager()
311+
if dm == nil {
312+
return
313+
}
314+
dm.SubmitNetwork(&armour.NetworkDetectionEvent{
315+
Dest: dest,
316+
})
317+
}
318+
299319
func startDNSServer(dnsProxy *DNSProxy, server DNSServer, errc chan error) {
300320
dns.HandleFunc(".", func(w dns.ResponseWriter, r *dns.Msg) {
301321
switch r.Opcode {

eventhandler.go

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import (
1515

1616
"github.com/docker/docker/api/types"
1717
"github.com/docker/docker/client"
18+
"github.com/step-security/armour/armour"
1819
)
1920

2021
type EventHandler struct {
@@ -90,6 +91,8 @@ func (eventHandler *EventHandler) handleFileEvent(event *Event) {
9091
}
9192

9293
eventHandler.fileMutex.Unlock()
94+
95+
eventHandler.submitFileEvent(event)
9396
}
9497

9598
func isSourceCodeFile(fileName string) bool {
@@ -123,6 +126,8 @@ func (eventHandler *EventHandler) handleProcessEvent(event *Event) {
123126
} else {
124127
eventHandler.procMutex.Unlock()
125128
}
129+
130+
eventHandler.submitProcessEvent(event)
126131
}
127132

128133
/*
@@ -199,6 +204,8 @@ func (eventHandler *EventHandler) handleNetworkEvent(event *Event) {
199204
}
200205

201206
eventHandler.netMutex.Unlock()
207+
208+
eventHandler.submitNetworkEvent(event)
202209
}
203210

204211
func (eventHandler *EventHandler) HandleEvent(event *Event) {
@@ -440,3 +447,68 @@ func isPrivateIPAddress(ipAddress string) bool {
440447
func isIPv6(ip string) bool {
441448
return strings.Contains(ip, ":")
442449
}
450+
451+
func (eventHandler *EventHandler) submitProcessEvent(event *Event) {
452+
if !IsCustomDetectionRulesEnabled() {
453+
return
454+
}
455+
if GlobalArmour == nil {
456+
return
457+
}
458+
dm := GlobalArmour.DetectionManager()
459+
if dm == nil {
460+
return
461+
}
462+
dm.SubmitProcess(&armour.ProcessDetectionEvent{
463+
Pid: event.Pid,
464+
PPid: event.PPid,
465+
Exe: event.Exe,
466+
Arguments: event.ProcessArguments,
467+
Cwd: event.Path,
468+
Timestamp: event.Timestamp,
469+
})
470+
}
471+
472+
// submitFileEvent submits a file event to the detection manager.
473+
func (eventHandler *EventHandler) submitFileEvent(event *Event) {
474+
if !IsCustomDetectionRulesEnabled() {
475+
return
476+
}
477+
if GlobalArmour == nil {
478+
return
479+
}
480+
dm := GlobalArmour.DetectionManager()
481+
if dm == nil {
482+
return
483+
}
484+
dm.SubmitFile(&armour.FileDetectionEvent{
485+
Syscall: event.Syscall,
486+
FileName: filepath.Base(event.FileName),
487+
Path: event.FileName,
488+
Exe: event.Exe,
489+
Pid: event.Pid,
490+
PPid: event.PPid,
491+
Timestamp: event.Timestamp,
492+
})
493+
}
494+
495+
// submitNetworkEvent submits a network event to the detection manager.
496+
func (eventHandler *EventHandler) submitNetworkEvent(event *Event) {
497+
if GlobalArmour == nil {
498+
return
499+
}
500+
dm := GlobalArmour.DetectionManager()
501+
if dm == nil {
502+
return
503+
}
504+
505+
dm.SubmitNetwork(&armour.NetworkDetectionEvent{
506+
Pid: event.Pid,
507+
PPid: event.PPid,
508+
Exe: event.Exe,
509+
Dest: event.IPAddress,
510+
DestIP: event.IPAddress,
511+
DestPort: event.Port,
512+
Timestamp: event.Timestamp,
513+
})
514+
}

global_feature_flags.go

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,9 @@ const (
1111
)
1212

1313
type GlobalFeatureFlags struct {
14-
AgentType string `json:"agent_type"`
15-
EnableArmour bool `json:"enable_armour"`
14+
AgentType string `json:"agent_type"`
15+
EnableArmour bool `json:"enable_armour"`
16+
EnableCustomDetectionRules bool `json:"enable_custom_detection_rules"`
1617
}
1718

1819
// GlobalFeatureFlagManager manages fetching and caching of global feature flags.
@@ -79,3 +80,8 @@ func IsArmourEnabled() bool {
7980
flags := GetGlobalFeatureFlags()
8081
return flags.EnableArmour
8182
}
83+
84+
func IsCustomDetectionRulesEnabled() bool {
85+
flags := GetGlobalFeatureFlags()
86+
return flags.EnableCustomDetectionRules
87+
}

0 commit comments

Comments
 (0)