Skip to content

Commit dba4849

Browse files
Release v0.12.0 (#318)
Add support for disabling sudo Update file monitoring logging Add subscription check for private repos Fix bugs related to dns config
1 parent fdbba03 commit dba4849

12 files changed

+265
-120
lines changed

agent.go

+36-11
Original file line numberDiff line numberDiff line change
@@ -65,15 +65,27 @@ func Run(ctx context.Context, configFilePath string, hostDNSServer DNSServer,
6565
return err
6666
}
6767

68-
apiclient := &ApiClient{Client: &http.Client{}, APIURL: config.APIURL, DisableTelemetry: config.DisableTelemetry, EgressPolicy: config.EgressPolicy}
68+
apiclient := &ApiClient{Client: &http.Client{Timeout: 3 * time.Second}, APIURL: config.APIURL, DisableTelemetry: config.DisableTelemetry, EgressPolicy: config.EgressPolicy}
6969

7070
// TODO: pass in an iowriter/ use log library
71-
WriteLog(fmt.Sprintf("read config \n %v", config))
71+
WriteLog(fmt.Sprintf("read config \n %+v", config))
7272
WriteLog("\n")
7373

7474
WriteLog(fmt.Sprintf("%s %s", StepSecurityLogCorrelationPrefix, config.CorrelationId))
7575
WriteLog("\n")
7676

77+
// if this is a private repo
78+
if config.Private {
79+
isActive := apiclient.getSubscriptionStatus(config.Repo)
80+
if !isActive {
81+
config.EgressPolicy = EgressPolicyAudit
82+
config.DisableSudo = false
83+
apiclient.DisableTelemetry = true
84+
config.DisableFileMonitoring = true
85+
WriteAnnotation("StepSecurity Harden Runner disabled. A subscription is required for private repositories. Please start a free trial at https://stepsecurity.io")
86+
}
87+
}
88+
7789
Cache := InitCache(config.EgressPolicy)
7890

7991
allowedEndpoints := addImplicitEndpoints(config.Endpoints, config.DisableTelemetry)
@@ -95,13 +107,13 @@ func Run(ctx context.Context, configFilePath string, hostDNSServer DNSServer,
95107
// start proc mon
96108
if cmd == nil {
97109
procMon := &ProcessMonitor{CorrelationId: config.CorrelationId, Repo: config.Repo,
98-
ApiClient: apiclient, WorkingDirectory: config.WorkingDirectory, DNSProxy: &dnsProxy}
110+
ApiClient: apiclient, WorkingDirectory: config.WorkingDirectory, DisableFileMonitoring: config.DisableFileMonitoring, DNSProxy: &dnsProxy}
99111
go procMon.MonitorProcesses(errc)
100112
WriteLog("started process monitor")
101113
}
102114

103115
dnsConfig := DnsConfig{}
104-
116+
sudo := Sudo{}
105117
var ipAddressEndpoints []ipAddressEndpoint
106118

107119
// hydrate dns cache
@@ -112,7 +124,7 @@ func Run(ctx context.Context, configFilePath string, hostDNSServer DNSServer,
112124
if err != nil {
113125
WriteLog(fmt.Sprintf("Error resolving allowed domain %v", err))
114126
WriteAnnotation(fmt.Sprintf("%s Reverting agent since allowed endpoint %s could not be resolved", StepSecurityAnnotationPrefix, strings.Trim(domainName, ".")))
115-
RevertChanges(iptables, nflog, cmd, resolvdConfigPath, dockerDaemonConfigPath, dnsConfig)
127+
RevertChanges(iptables, nflog, cmd, resolvdConfigPath, dockerDaemonConfigPath, dnsConfig, sudo)
116128
return err
117129
}
118130
for _, endpoint := range endpoints {
@@ -126,7 +138,7 @@ func Run(ctx context.Context, configFilePath string, hostDNSServer DNSServer,
126138
// Change DNS config on host, causes processes to use agent's DNS proxy
127139
if err := dnsConfig.SetDNSServer(cmd, resolvdConfigPath, tempDir); err != nil {
128140
WriteLog(fmt.Sprintf("Error setting DNS server %v", err))
129-
RevertChanges(iptables, nflog, cmd, resolvdConfigPath, dockerDaemonConfigPath, dnsConfig)
141+
RevertChanges(iptables, nflog, cmd, resolvdConfigPath, dockerDaemonConfigPath, dnsConfig, sudo)
130142
return err
131143
}
132144

@@ -136,7 +148,7 @@ func Run(ctx context.Context, configFilePath string, hostDNSServer DNSServer,
136148
// Change DNS for docker, causes process in containers to use agent's DNS proxy
137149
if err := dnsConfig.SetDockerDNSServer(cmd, dockerDaemonConfigPath, tempDir); err != nil {
138150
WriteLog(fmt.Sprintf("Error setting DNS server for docker %v", err))
139-
RevertChanges(iptables, nflog, cmd, resolvdConfigPath, dockerDaemonConfigPath, dnsConfig)
151+
RevertChanges(iptables, nflog, cmd, resolvdConfigPath, dockerDaemonConfigPath, dnsConfig, sudo)
140152
return err
141153
}
142154

@@ -159,7 +171,7 @@ func Run(ctx context.Context, configFilePath string, hostDNSServer DNSServer,
159171
// Add logging to firewall, including NFLOG rules
160172
if err := AddAuditRules(iptables); err != nil {
161173
WriteLog(fmt.Sprintf("Error adding firewall rules %v", err))
162-
RevertChanges(iptables, nflog, cmd, resolvdConfigPath, dockerDaemonConfigPath, dnsConfig)
174+
RevertChanges(iptables, nflog, cmd, resolvdConfigPath, dockerDaemonConfigPath, dnsConfig, sudo)
163175
return err
164176
}
165177

@@ -182,13 +194,22 @@ func Run(ctx context.Context, configFilePath string, hostDNSServer DNSServer,
182194

183195
if err := addBlockRulesForGitHubHostedRunner(iptables, ipAddressEndpoints); err != nil {
184196
WriteLog(fmt.Sprintf("Error setting firewall for allowed domains %v", err))
185-
RevertChanges(iptables, nflog, cmd, resolvdConfigPath, dockerDaemonConfigPath, dnsConfig)
197+
RevertChanges(iptables, nflog, cmd, resolvdConfigPath, dockerDaemonConfigPath, dnsConfig, sudo)
186198
return err
187199
}
188200

189201
go refreshDNSEntries(ctx, iptables, allowedEndpoints, &dnsProxy)
190202
}
191203

204+
if config.DisableSudo {
205+
err := sudo.disableSudo(tempDir)
206+
if err != nil {
207+
WriteAnnotation(fmt.Sprintf("%s Unable to disable sudo %v", StepSecurityAnnotationPrefix, err))
208+
} else {
209+
WriteLog("disabled sudo")
210+
}
211+
}
212+
192213
WriteLog("done")
193214

194215
// Write the status file
@@ -200,7 +221,7 @@ func Run(ctx context.Context, configFilePath string, hostDNSServer DNSServer,
200221
return nil
201222
case e := <-errc:
202223
WriteLog(fmt.Sprintf("Error in Initialization %v", e))
203-
RevertChanges(iptables, nflog, cmd, resolvdConfigPath, dockerDaemonConfigPath, dnsConfig)
224+
RevertChanges(iptables, nflog, cmd, resolvdConfigPath, dockerDaemonConfigPath, dnsConfig, sudo)
204225
return e
205226

206227
}
@@ -284,7 +305,7 @@ func addImplicitEndpoints(endpoints map[string][]Endpoint, disableTelemetry bool
284305
}
285306

286307
func RevertChanges(iptables *Firewall, nflog AgentNflogger,
287-
cmd Command, resolvdConfigPath, dockerDaemonConfigPath string, dnsConfig DnsConfig) {
308+
cmd Command, resolvdConfigPath, dockerDaemonConfigPath string, dnsConfig DnsConfig, sudo Sudo) {
288309
err := RevertFirewallChanges(iptables)
289310
if err != nil {
290311
WriteLog(fmt.Sprintf("Error in RevertChanges %v", err))
@@ -297,6 +318,10 @@ func RevertChanges(iptables *Firewall, nflog AgentNflogger,
297318
if err != nil {
298319
WriteLog(fmt.Sprintf("Error in reverting docker DNS server changes %v", err))
299320
}
321+
err = sudo.revertDisableSudo()
322+
if err != nil {
323+
WriteLog(fmt.Sprintf("Error in reverting sudo changes %v", err))
324+
}
300325
WriteLog("Reverted changes")
301326
}
302327

agent_test.go

+12
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,9 @@ func TestRun(t *testing.T) {
138138
httpmock.RegisterResponder("GET", "https://dns.google/resolve", // no query params to match all other requests
139139
httpmock.NewStringResponder(200, `{"Status":0,"TC":false,"RD":true,"RA":true,"AD":false,"CD":false,"Question":[{"name":"requesteddomain.com.","type":1}],"Answer":[{"name":"requesteddomain.com.","type":1,"TTL":300,"data":"69.69.69.69"}]}`))
140140

141+
httpmock.RegisterResponder("GET", "https://apiurl/v1/github/owner/repo/actions/subscription",
142+
httpmock.NewStringResponder(403, ""))
143+
141144
tests := []struct {
142145
name string
143146
args args
@@ -185,6 +188,15 @@ func TestRun(t *testing.T) {
185188
hostDNSServer: &mockDNSServer{}, dockerDNSServer: &mockDNSServer{},
186189
iptables: nil, nflog: &MockAgentNflogger{}, cmd: &MockCommand{}, resolvdConfigPath: createTempFileWithContents(""),
187190
dockerDaemonConfigPath: createTempFileWithContents("{}"), ciTestOnly: true}, wantErr: false},
191+
192+
{name: "success disable sudo", args: args{ctxCancelDuration: 35, configFilePath: "./testfiles/agent-disable-sudo.json",
193+
hostDNSServer: &mockDNSServer{}, dockerDNSServer: &mockDNSServer{},
194+
iptables: &Firewall{&MockIPTables{}}, nflog: &MockAgentNflogger{}, cmd: &MockCommand{}, resolvdConfigPath: createTempFileWithContents(""),
195+
dockerDaemonConfigPath: createTempFileWithContents("{}"), ciTestOnly: true}, wantErr: false},
196+
197+
{name: "private repo no subscription", args: args{ctxCancelDuration: 2, configFilePath: "./testfiles/agent-private-repo.json", hostDNSServer: &mockDNSServer{}, dockerDNSServer: &mockDNSServer{},
198+
iptables: &Firewall{&MockIPTables{}}, nflog: &MockAgentNflogger{}, cmd: &MockCommand{}, resolvdConfigPath: createTempFileWithContents(""),
199+
dockerDaemonConfigPath: createTempFileWithContents("{}")}, wantErr: false},
188200
}
189201
_, ciTest := os.LookupEnv("CI")
190202
fmt.Printf("ci-test: %t\n", ciTest)

apiclient.go

+14-15
Original file line numberDiff line numberDiff line change
@@ -80,29 +80,28 @@ func (apiclient *ApiClient) sendNetConnection(correlationId, repo, ipAddress, po
8080

8181
}
8282

83-
func (apiclient *ApiClient) sendFileEvent(correlationId, repo, fileType string, timestamp time.Time, tool Tool) error {
83+
func (apiclient *ApiClient) getSubscriptionStatus(repo string) bool {
8484

85-
if !apiclient.DisableTelemetry || apiclient.EgressPolicy == EgressPolicyAudit {
86-
fileEvent := &FileEvent{}
87-
88-
fileEvent.FileType = fileType
89-
fileEvent.TimeStamp = timestamp
90-
fileEvent.Tool = tool
85+
url := fmt.Sprintf("%s/github/%s/actions/subscription", apiclient.APIURL, repo)
9186

92-
url := fmt.Sprintf("%s/github/%s/actions/jobs/%s/fileevent", apiclient.APIURL, repo, correlationId)
87+
req, err := http.NewRequest("GET", url, nil)
9388

94-
return apiclient.sendApiRequest("POST", url, fileEvent)
89+
if err != nil {
90+
return true
9591
}
96-
return nil
9792

98-
}
93+
resp, err := apiclient.Client.Do(req)
9994

100-
/*func (apiclient *ApiClient) sendArtifact(correlationId, repo string, artifact artifact.Artifact) error {
95+
if err != nil {
96+
return true
97+
}
10198

102-
url := fmt.Sprintf("%s/github/%s/actions/jobs/%s/artifact", apiclient.APIURL, repo, correlationId)
99+
if resp.StatusCode == 403 {
100+
return false
101+
}
103102

104-
return apiclient.sendApiRequest("POST", url, artifact)
105-
}*/
103+
return true
104+
}
106105

107106
func (apiclient *ApiClient) sendApiRequest(method, url string, body interface{}) error {
108107

config.go

+25-16
Original file line numberDiff line numberDiff line change
@@ -11,14 +11,17 @@ import (
1111
)
1212

1313
type config struct {
14-
Repo string
15-
CorrelationId string
16-
RunId string
17-
WorkingDirectory string
18-
APIURL string
19-
Endpoints map[string][]Endpoint
20-
EgressPolicy string
21-
DisableTelemetry bool
14+
Repo string
15+
CorrelationId string
16+
RunId string
17+
WorkingDirectory string
18+
APIURL string
19+
Endpoints map[string][]Endpoint
20+
EgressPolicy string
21+
DisableTelemetry bool
22+
DisableSudo bool
23+
DisableFileMonitoring bool
24+
Private bool
2225
}
2326

2427
type Endpoint struct {
@@ -27,14 +30,17 @@ type Endpoint struct {
2730
}
2831

2932
type configFile struct {
30-
Repo string `json:"repo"`
31-
CorrelationId string `json:"correlation_id"`
32-
RunId string `json:"run_id"`
33-
WorkingDirectory string `json:"working_directory"`
34-
APIURL string `json:"api_url"`
35-
AllowedEndpoints string `json:"allowed_endpoints"`
36-
EgressPolicy string `json:"egress_policy"`
37-
DisableTelemetry bool `json:"disable_telemetry"`
33+
Repo string `json:"repo"`
34+
CorrelationId string `json:"correlation_id"`
35+
RunId string `json:"run_id"`
36+
WorkingDirectory string `json:"working_directory"`
37+
APIURL string `json:"api_url"`
38+
AllowedEndpoints string `json:"allowed_endpoints"`
39+
EgressPolicy string `json:"egress_policy"`
40+
DisableTelemetry bool `json:"disable_telemetry"`
41+
DisableSudo bool `json:"disable_sudo"`
42+
DisableFileMonitoring bool `json:"disable_file_monitoring"`
43+
Private bool `json:"private"`
3844
}
3945

4046
// init reads the config file for the agent and initializes config settings
@@ -58,6 +64,9 @@ func (c *config) init(configFilePath string) error {
5864
c.Endpoints = parseEndpoints(configFile.AllowedEndpoints)
5965
c.EgressPolicy = configFile.EgressPolicy
6066
c.DisableTelemetry = configFile.DisableTelemetry
67+
c.DisableSudo = configFile.DisableSudo
68+
c.DisableFileMonitoring = configFile.DisableFileMonitoring
69+
c.Private = configFile.Private
6170
return nil
6271
}
6372

dnsconfig.go

+26-14
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,9 @@ import (
1313
)
1414

1515
type DnsConfig struct {
16-
ResolveConfigBackUpPath string
17-
DockerConfigBackUpPath string
16+
ResolveConfigBackUpPath string
17+
DockerConfigBackUpPath string
18+
ShouldDeleteDockerConfig bool
1819
}
1920

2021
const (
@@ -129,7 +130,7 @@ func (d *DnsConfig) SetDNSServer(cmd Command, resolvdConfigPath, tempDir string)
129130

130131
err = cmd.Run()
131132
if err != nil {
132-
return fmt.Errorf(fmt.Sprintf("error flushing cache: %v", err))
133+
WriteLog(fmt.Sprintf("error flushing cache: %v", err))
133134
}
134135

135136
return nil
@@ -158,13 +159,18 @@ func copy(src, dst string) error {
158159
}
159160

160161
func (d *DnsConfig) SetDockerDNSServer(cmd Command, configPath, tempDir string) error {
161-
d.DockerConfigBackUpPath = path.Join(tempDir, "daemon.json")
162-
err := copy(configPath, d.DockerConfigBackUpPath)
163-
if err != nil {
164-
return fmt.Errorf(fmt.Sprintf("error backing up docker config: %v", err))
162+
if _, err := os.Stat(configPath); err == nil {
163+
d.DockerConfigBackUpPath = path.Join(tempDir, "daemon.json")
164+
err := copy(configPath, d.DockerConfigBackUpPath)
165+
if err != nil {
166+
return fmt.Errorf(fmt.Sprintf("error backing up docker config: %v", err))
167+
}
168+
} else {
169+
d.ShouldDeleteDockerConfig = true
165170
}
171+
166172
mock := cmd != nil
167-
err = updateDockerConfig(configPath)
173+
err := updateDockerConfig(configPath)
168174
if err != nil {
169175
return fmt.Errorf(fmt.Sprintf("error updating to docker daemon config: %v", err))
170176
}
@@ -192,18 +198,24 @@ func (d *DnsConfig) SetDockerDNSServer(cmd Command, configPath, tempDir string)
192198
}
193199

194200
func (d *DnsConfig) RevertDockerDNSServer(cmd Command, configPath string) error {
195-
if len(d.DockerConfigBackUpPath) > 0 {
196-
197-
err := copy(d.DockerConfigBackUpPath, configPath)
198-
if err != nil {
199-
return fmt.Errorf(fmt.Sprintf("error recovering docker config: %v", err))
201+
if len(d.DockerConfigBackUpPath) > 0 || d.ShouldDeleteDockerConfig {
202+
if len(d.DockerConfigBackUpPath) > 0 {
203+
err := copy(d.DockerConfigBackUpPath, configPath)
204+
if err != nil {
205+
return fmt.Errorf(fmt.Sprintf("error recovering docker config: %v", err))
206+
}
207+
} else if d.ShouldDeleteDockerConfig {
208+
err := os.Remove(configPath)
209+
if err != nil {
210+
return fmt.Errorf(fmt.Sprintf("error deleting docker config: %v", err))
211+
}
200212
}
201213

202214
if cmd == nil {
203215
cmd = exec.Command("/bin/sh", "-c", "sudo systemctl daemon-reload && sudo systemctl restart docker")
204216
}
205217

206-
err = cmd.Run()
218+
err := cmd.Run()
207219
if err != nil {
208220
return fmt.Errorf(fmt.Sprintf("error restarting docker: %v", err))
209221
}

0 commit comments

Comments
 (0)