diff --git a/agent.go b/agent.go index 79c15dd..a52b9d4 100644 --- a/agent.go +++ b/agent.go @@ -95,6 +95,7 @@ func Run(ctx context.Context, configFilePath string, hostDNSServer DNSServer, if !isActive { config.EgressPolicy = EgressPolicyAudit config.DisableSudo = false + config.DisableSudoAndContainers = false apiclient.DisableTelemetry = true config.DisableFileMonitoring = true WriteAnnotation("StepSecurity Harden Runner is disabled. A subscription is required for private repositories. Please start a free trial at https://www.stepsecurity.io") @@ -133,6 +134,10 @@ func Run(ctx context.Context, configFilePath string, hostDNSServer DNSServer, sudo := Sudo{} var ipAddressEndpoints []ipAddressEndpoint + if config.DisableSudoAndContainers { + go sudo.uninstallDocker() + } + // hydrate dns cache if config.EgressPolicy == EgressPolicyBlock { for domainName, endpoints := range allowedEndpoints { @@ -247,6 +252,15 @@ func Run(ctx context.Context, configFilePath string, hostDNSServer DNSServer, } } + if config.DisableSudoAndContainers { + err := sudo.disableSudoAndContainers(tempDir) + if err != nil { + WriteLog(fmt.Sprintf("%s Unable to disable sudo and docker %v", StepSecurityAnnotationPrefix, err)) + } else { + WriteLog("disabled sudo and docker") + } + } + if config.DisableSudo { err := sudo.disableSudo(tempDir) if err != nil { diff --git a/config.go b/config.go index f927477..d63f1b3 100644 --- a/config.go +++ b/config.go @@ -11,18 +11,19 @@ import ( ) type config struct { - Repo string - CorrelationId string - RunId string - WorkingDirectory string - APIURL string - OneTimeKey string - Endpoints map[string][]Endpoint - EgressPolicy string - DisableTelemetry bool - DisableSudo bool - DisableFileMonitoring bool - Private bool + Repo string + CorrelationId string + RunId string + WorkingDirectory string + APIURL string + OneTimeKey string + Endpoints map[string][]Endpoint + EgressPolicy string + DisableTelemetry bool + DisableSudo bool + DisableSudoAndContainers bool + DisableFileMonitoring bool + Private bool } type Endpoint struct { @@ -31,18 +32,19 @@ 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"` - OneTimeKey string `json:"one_time_key"` - 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"` + Repo string `json:"repo"` + CorrelationId string `json:"correlation_id"` + RunId string `json:"run_id"` + WorkingDirectory string `json:"working_directory"` + APIURL string `json:"api_url"` + OneTimeKey string `json:"one_time_key"` + AllowedEndpoints string `json:"allowed_endpoints"` + EgressPolicy string `json:"egress_policy"` + DisableTelemetry bool `json:"disable_telemetry"` + DisableSudo bool `json:"disable_sudo"` + DisableSudoAndContainers bool `json:"disable_sudo_and_containers"` + DisableFileMonitoring bool `json:"disable_file_monitoring"` + Private bool `json:"private"` } // init reads the config file for the agent and initializes config settings @@ -67,6 +69,7 @@ func (c *config) init(configFilePath string) error { c.EgressPolicy = configFile.EgressPolicy c.DisableTelemetry = configFile.DisableTelemetry c.DisableSudo = configFile.DisableSudo + c.DisableSudoAndContainers = configFile.DisableSudoAndContainers c.DisableFileMonitoring = configFile.DisableFileMonitoring c.Private = configFile.Private c.OneTimeKey = configFile.OneTimeKey diff --git a/sudo.go b/sudo.go index 4be2e63..5a4c226 100644 --- a/sudo.go +++ b/sudo.go @@ -1,9 +1,12 @@ package main import ( + "bufio" "fmt" "os" + "os/exec" "path" + "strings" ) type Sudo struct { @@ -12,6 +15,7 @@ type Sudo struct { const ( sudoersFile = "/etc/sudoers.d/runner" + runnerUser = "runner" ) func (s *Sudo) disableSudo(tempDir string) error { @@ -40,3 +44,91 @@ func (s *Sudo) revertDisableSudo() error { return nil } + +func (s *Sudo) disableSudoAndContainers(tempDir string) error { + + s.removeDockerDirectoriesAndFiles() + + // Remove socket permissions if they exist + s.removeSocketPermissions() + var errstrings []string + + err := s.disableSudo(tempDir) + if err != nil { + WriteLog(fmt.Sprintf("error disabling sudo: %v", err)) + errstrings = append(errstrings, err.Error()) + } + + //flatten errs + if len(errstrings) > 0 { + return fmt.Errorf("error disabling sudo and containers: %s", strings.Join(errstrings, "\n")) + } + return nil +} + +// removeSocketPermissions removes permissions from Docker and containerd sockets if they exist +func (s *Sudo) removeSocketPermissions() { + // Check and remove docker.sock permissions if it exists + if _, err := os.Stat("/var/run/docker.sock"); err == nil { + cmd := exec.Command("sudo", "chmod", "000", "/var/run/docker.sock") + err := cmd.Run() + if err != nil { + WriteLog(fmt.Sprintf("error removing docker.sock permissions: %v", err)) + } + } + + // Check and remove containerd.sock permissions if it exists + if _, err := os.Stat("/run/containerd/containerd.sock"); err == nil { + cmd := exec.Command("sudo", "chmod", "000", "/run/containerd/containerd.sock") + err := cmd.Run() + if err != nil { + WriteLog(fmt.Sprintf("error removing containerd.sock permissions: %v", err)) + } + } +} + +func run(cmd string, args ...string) { + WriteLog(fmt.Sprintf("Running: %s %v", cmd, args)) + c := exec.Command(cmd, args...) + + stdout, _ := c.StdoutPipe() + stderr, _ := c.StderrPipe() + + if err := c.Start(); err != nil { + WriteLog(fmt.Sprintf("Failed to start command: %s", err)) + } + + go func() { + scanner := bufio.NewScanner(stdout) + for scanner.Scan() { + WriteLog(scanner.Text()) + } + }() + + // Stream stderr + go func() { + scanner := bufio.NewScanner(stderr) + for scanner.Scan() { + WriteLog(scanner.Text()) + } + }() + + if err := c.Wait(); err != nil { + WriteLog(fmt.Sprintf("Command failed: %v", err)) + } +} + +func (s *Sudo) uninstallDocker() error { + WriteLog("Uninstalling docker") + run("sudo", "apt-get", "purge", "-y", + "docker-ce", "docker-ce-cli", "containerd.io") + return nil +} + +func (s *Sudo) removeDockerDirectoriesAndFiles() error { + run("sudo", "rm", "-rf", "/var/lib/docker") + run("sudo", "rm", "-rf", "/var/lib/containerd") + run("sudo", "rm", "-f", "/etc/apt/sources.list.d/docker.list") + run("sudo", "rm", "-f", "/etc/apt/keyrings/docker.asc") + return nil +}