Skip to content

Commit

Permalink
Release v0.12.0 (#318)
Browse files Browse the repository at this point in the history
Add support for disabling sudo
Update file monitoring logging
Add subscription check for private repos
Fix bugs related to dns config
  • Loading branch information
varunsh-coder authored Oct 24, 2022
1 parent fdbba03 commit dba4849
Show file tree
Hide file tree
Showing 12 changed files with 265 additions and 120 deletions.
47 changes: 36 additions & 11 deletions agent.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,15 +65,27 @@ func Run(ctx context.Context, configFilePath string, hostDNSServer DNSServer,
return err
}

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

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

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

// if this is a private repo
if config.Private {
isActive := apiclient.getSubscriptionStatus(config.Repo)
if !isActive {
config.EgressPolicy = EgressPolicyAudit
config.DisableSudo = false
apiclient.DisableTelemetry = true
config.DisableFileMonitoring = true
WriteAnnotation("StepSecurity Harden Runner disabled. A subscription is required for private repositories. Please start a free trial at https://stepsecurity.io")
}
}

Cache := InitCache(config.EgressPolicy)

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

dnsConfig := DnsConfig{}

sudo := Sudo{}
var ipAddressEndpoints []ipAddressEndpoint

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

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

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

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

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

go refreshDNSEntries(ctx, iptables, allowedEndpoints, &dnsProxy)
}

if config.DisableSudo {
err := sudo.disableSudo(tempDir)
if err != nil {
WriteAnnotation(fmt.Sprintf("%s Unable to disable sudo %v", StepSecurityAnnotationPrefix, err))
} else {
WriteLog("disabled sudo")
}
}

WriteLog("done")

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

}
Expand Down Expand Up @@ -284,7 +305,7 @@ func addImplicitEndpoints(endpoints map[string][]Endpoint, disableTelemetry bool
}

func RevertChanges(iptables *Firewall, nflog AgentNflogger,
cmd Command, resolvdConfigPath, dockerDaemonConfigPath string, dnsConfig DnsConfig) {
cmd Command, resolvdConfigPath, dockerDaemonConfigPath string, dnsConfig DnsConfig, sudo Sudo) {
err := RevertFirewallChanges(iptables)
if err != nil {
WriteLog(fmt.Sprintf("Error in RevertChanges %v", err))
Expand All @@ -297,6 +318,10 @@ func RevertChanges(iptables *Firewall, nflog AgentNflogger,
if err != nil {
WriteLog(fmt.Sprintf("Error in reverting docker DNS server changes %v", err))
}
err = sudo.revertDisableSudo()
if err != nil {
WriteLog(fmt.Sprintf("Error in reverting sudo changes %v", err))
}
WriteLog("Reverted changes")
}

Expand Down
12 changes: 12 additions & 0 deletions agent_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,9 @@ func TestRun(t *testing.T) {
httpmock.RegisterResponder("GET", "https://dns.google/resolve", // no query params to match all other requests
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"}]}`))

httpmock.RegisterResponder("GET", "https://apiurl/v1/github/owner/repo/actions/subscription",
httpmock.NewStringResponder(403, ""))

tests := []struct {
name string
args args
Expand Down Expand Up @@ -185,6 +188,15 @@ func TestRun(t *testing.T) {
hostDNSServer: &mockDNSServer{}, dockerDNSServer: &mockDNSServer{},
iptables: nil, nflog: &MockAgentNflogger{}, cmd: &MockCommand{}, resolvdConfigPath: createTempFileWithContents(""),
dockerDaemonConfigPath: createTempFileWithContents("{}"), ciTestOnly: true}, wantErr: false},

{name: "success disable sudo", args: args{ctxCancelDuration: 35, configFilePath: "./testfiles/agent-disable-sudo.json",
hostDNSServer: &mockDNSServer{}, dockerDNSServer: &mockDNSServer{},
iptables: &Firewall{&MockIPTables{}}, nflog: &MockAgentNflogger{}, cmd: &MockCommand{}, resolvdConfigPath: createTempFileWithContents(""),
dockerDaemonConfigPath: createTempFileWithContents("{}"), ciTestOnly: true}, wantErr: false},

{name: "private repo no subscription", args: args{ctxCancelDuration: 2, configFilePath: "./testfiles/agent-private-repo.json", hostDNSServer: &mockDNSServer{}, dockerDNSServer: &mockDNSServer{},
iptables: &Firewall{&MockIPTables{}}, nflog: &MockAgentNflogger{}, cmd: &MockCommand{}, resolvdConfigPath: createTempFileWithContents(""),
dockerDaemonConfigPath: createTempFileWithContents("{}")}, wantErr: false},
}
_, ciTest := os.LookupEnv("CI")
fmt.Printf("ci-test: %t\n", ciTest)
Expand Down
29 changes: 14 additions & 15 deletions apiclient.go
Original file line number Diff line number Diff line change
Expand Up @@ -80,29 +80,28 @@ func (apiclient *ApiClient) sendNetConnection(correlationId, repo, ipAddress, po

}

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

if !apiclient.DisableTelemetry || apiclient.EgressPolicy == EgressPolicyAudit {
fileEvent := &FileEvent{}

fileEvent.FileType = fileType
fileEvent.TimeStamp = timestamp
fileEvent.Tool = tool
url := fmt.Sprintf("%s/github/%s/actions/subscription", apiclient.APIURL, repo)

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

return apiclient.sendApiRequest("POST", url, fileEvent)
if err != nil {
return true
}
return nil

}
resp, err := apiclient.Client.Do(req)

/*func (apiclient *ApiClient) sendArtifact(correlationId, repo string, artifact artifact.Artifact) error {
if err != nil {
return true
}

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

return apiclient.sendApiRequest("POST", url, artifact)
}*/
return true
}

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

Expand Down
41 changes: 25 additions & 16 deletions config.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,17 @@ import (
)

type config struct {
Repo string
CorrelationId string
RunId string
WorkingDirectory string
APIURL string
Endpoints map[string][]Endpoint
EgressPolicy string
DisableTelemetry bool
Repo string
CorrelationId string
RunId string
WorkingDirectory string
APIURL string
Endpoints map[string][]Endpoint
EgressPolicy string
DisableTelemetry bool
DisableSudo bool
DisableFileMonitoring bool
Private bool
}

type Endpoint struct {
Expand All @@ -27,14 +30,17 @@ type Endpoint struct {
}

type configFile struct {
Repo string `json:"repo"`
CorrelationId string `json:"correlation_id"`
RunId string `json:"run_id"`
WorkingDirectory string `json:"working_directory"`
APIURL string `json:"api_url"`
AllowedEndpoints string `json:"allowed_endpoints"`
EgressPolicy string `json:"egress_policy"`
DisableTelemetry bool `json:"disable_telemetry"`
Repo string `json:"repo"`
CorrelationId string `json:"correlation_id"`
RunId string `json:"run_id"`
WorkingDirectory string `json:"working_directory"`
APIURL string `json:"api_url"`
AllowedEndpoints string `json:"allowed_endpoints"`
EgressPolicy string `json:"egress_policy"`
DisableTelemetry bool `json:"disable_telemetry"`
DisableSudo bool `json:"disable_sudo"`
DisableFileMonitoring bool `json:"disable_file_monitoring"`
Private bool `json:"private"`
}

// init reads the config file for the agent and initializes config settings
Expand All @@ -58,6 +64,9 @@ func (c *config) init(configFilePath string) error {
c.Endpoints = parseEndpoints(configFile.AllowedEndpoints)
c.EgressPolicy = configFile.EgressPolicy
c.DisableTelemetry = configFile.DisableTelemetry
c.DisableSudo = configFile.DisableSudo
c.DisableFileMonitoring = configFile.DisableFileMonitoring
c.Private = configFile.Private
return nil
}

Expand Down
40 changes: 26 additions & 14 deletions dnsconfig.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,9 @@ import (
)

type DnsConfig struct {
ResolveConfigBackUpPath string
DockerConfigBackUpPath string
ResolveConfigBackUpPath string
DockerConfigBackUpPath string
ShouldDeleteDockerConfig bool
}

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

err = cmd.Run()
if err != nil {
return fmt.Errorf(fmt.Sprintf("error flushing cache: %v", err))
WriteLog(fmt.Sprintf("error flushing cache: %v", err))
}

return nil
Expand Down Expand Up @@ -158,13 +159,18 @@ func copy(src, dst string) error {
}

func (d *DnsConfig) SetDockerDNSServer(cmd Command, configPath, tempDir string) error {
d.DockerConfigBackUpPath = path.Join(tempDir, "daemon.json")
err := copy(configPath, d.DockerConfigBackUpPath)
if err != nil {
return fmt.Errorf(fmt.Sprintf("error backing up docker config: %v", err))
if _, err := os.Stat(configPath); err == nil {
d.DockerConfigBackUpPath = path.Join(tempDir, "daemon.json")
err := copy(configPath, d.DockerConfigBackUpPath)
if err != nil {
return fmt.Errorf(fmt.Sprintf("error backing up docker config: %v", err))
}
} else {
d.ShouldDeleteDockerConfig = true
}

mock := cmd != nil
err = updateDockerConfig(configPath)
err := updateDockerConfig(configPath)
if err != nil {
return fmt.Errorf(fmt.Sprintf("error updating to docker daemon config: %v", err))
}
Expand Down Expand Up @@ -192,18 +198,24 @@ func (d *DnsConfig) SetDockerDNSServer(cmd Command, configPath, tempDir string)
}

func (d *DnsConfig) RevertDockerDNSServer(cmd Command, configPath string) error {
if len(d.DockerConfigBackUpPath) > 0 {

err := copy(d.DockerConfigBackUpPath, configPath)
if err != nil {
return fmt.Errorf(fmt.Sprintf("error recovering docker config: %v", err))
if len(d.DockerConfigBackUpPath) > 0 || d.ShouldDeleteDockerConfig {
if len(d.DockerConfigBackUpPath) > 0 {
err := copy(d.DockerConfigBackUpPath, configPath)
if err != nil {
return fmt.Errorf(fmt.Sprintf("error recovering docker config: %v", err))
}
} else if d.ShouldDeleteDockerConfig {
err := os.Remove(configPath)
if err != nil {
return fmt.Errorf(fmt.Sprintf("error deleting docker config: %v", err))
}
}

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

err = cmd.Run()
err := cmd.Run()
if err != nil {
return fmt.Errorf(fmt.Sprintf("error restarting docker: %v", err))
}
Expand Down
Loading

0 comments on commit dba4849

Please sign in to comment.