Skip to content

Commit

Permalink
feat: add maintenance token support for uninstalls
Browse files Browse the repository at this point in the history
  • Loading branch information
redhatrises committed Dec 5, 2024
1 parent da39a12 commit 7d1f535
Show file tree
Hide file tree
Showing 5 changed files with 89 additions and 12 deletions.
1 change: 1 addition & 0 deletions internal/cli/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,7 @@ func rootCmd() *cobra.Command {
falconFlag.StringVar(&fc.ProvisioningToken, "provisioning-token", "",
"The provisioning token to use for installing the sensor. If not provided, the API will attempt to retrieve a token")
falconFlag.StringVar(&fc.Tags, "tags", "", "A comma separated list of tags for sensor grouping")
falconFlag.StringVar(&fc.MaintenanceToken, "maintenance-token", "", "Maintenance token for uninstalling the sensor or configuring sensor settings")

if targetOS != "macos" {
falconFlag.BoolVar(&fc.ProxyDisable, "disable-proxy", false, "Disable the sensor proxy settings")
Expand Down
19 changes: 17 additions & 2 deletions pkg/falcon/falconctl/falconctl.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ import (
"fmt"
"log/slog"
"os/exec"
"runtime"
"slices"

"github.com/crowdstrike/falcon-installer/pkg/utils"
)
Expand All @@ -44,7 +46,12 @@ func cliConfig(osType string) string {

// Set configures the Falcon sensor using the OS-specific command.
func Set(osType string, args []string) error {
return configureSensor(cliConfig(osType), args)
return configureSensor(cliConfig(osType), args, nil)
}

// SetWithMaintenanceToken configures the Falcon sensor using the OS-specific command with a maintenance token for macOS only.
func SetWithMaintenanceTokenMacOS(osType string, args []string, maintenanceToken string) error {
return configureSensor(cliConfig(osType), args, &maintenanceToken)
}

// Get retrieves the Falcon sensor settings using the OS-specific command.
Expand All @@ -53,13 +60,21 @@ func Get(osType string, args []string) (string, error) {
}

// configureSensor configures the Falcon sensor using the OS-specific command.
func configureSensor(falconCtlCmd string, args []string) error {
func configureSensor(falconCtlCmd string, args []string, maintenanceToken *string) error {
slog.Debug("Configuring Falcon sensor", "Command", falconCtlCmd, "Args", args)

if _, err := exec.LookPath(falconCtlCmd); err != nil {
return fmt.Errorf("Could not find falcon command: %s: %v", falconCtlCmd, err)
}

if runtime.GOOS == "darwin" && slices.Contains(args, "--maintenance-token") {
if _, stderr, err := utils.RunCmdWithStdin(falconCtlCmd, args, *maintenanceToken); err != nil {
return fmt.Errorf("Error running falcon command: %v, stderr: %s", err, string(stderr))
}

return nil
}

if _, stderr, err := utils.RunCmd(falconCtlCmd, args); err != nil {
return fmt.Errorf("Error running falcon command: %v, stderr: %s", err, string(stderr))
}
Expand Down
27 changes: 17 additions & 10 deletions pkg/installer/uninstall.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
package installer

import (
"fmt"
"log"
"log/slog"
"os/exec"
Expand All @@ -41,13 +42,13 @@ func Uninstall(fc FalconInstaller) {

// Uninstall the Falcon Sensor
if falconInstalled {
fc.uninstallSensor()
fc.uninstallSensor(fc.SensorConfig.MaintenanceToken)
}
slog.Info("Falcon Sensor has been uninstalled")
}

// installSensor installs the Falcon sensor using the appropriate package manager.
func (fc FalconInstaller) uninstallSensor() {
func (fc FalconInstaller) uninstallSensor(maintenanceToken string) {
c := ""
env := ""
args := []string{} //nolint:staticcheck
Expand Down Expand Up @@ -89,6 +90,10 @@ func (fc FalconInstaller) uninstallSensor() {
uninstallRegex := `^((WindowsSensor|FalconSensor_Windows).*\.)(exe)$`
dir := "C:\\ProgramData\\Package Cache"

if maintenanceToken != "" {
uninstallArgs = append(uninstallArgs, fmt.Sprintf("MAINTENANCE_TOKEN=%s", maintenanceToken))
}

slog.Debug("Finding the Falcon Sensor uninstaller", "Directory", dir, "Regex", uninstallRegex)
uninstaller, err := utils.FindFile(dir, uninstallRegex)
if err != nil {
Expand All @@ -103,14 +108,16 @@ func (fc FalconInstaller) uninstallSensor() {

slog.Debug("Removing Falcon Sensor")
case "macos":
slog.Debug("Unloading the Falcon Sensor")
if err := falconctl.Set(fc.OSType, fc.macosArgHandler("unload")); err != nil {
log.Fatalf("Error configuring Falcon sensor: %v", err)
}

slog.Debug("Uninstalling the Falcon Sensor")
if err := falconctl.Set(fc.OSType, fc.macosArgHandler("uninstall")); err != nil {
log.Fatalf("Error configuring Falcon sensor: %v", err)
if maintenanceToken != "" {
slog.Debug("Uninstalling the Falcon Sensor with maintenance token")
if err := falconctl.SetWithMaintenanceTokenMacOS(fc.OSType, fc.macosArgHandler("uninstall"), maintenanceToken); err != nil {
log.Fatalf("Error configuring Falcon sensor: %v", err)
}
} else {
slog.Debug("Uninstalling the Falcon Sensor")
if err := falconctl.Set(fc.OSType, fc.macosArgHandler("uninstall")); err != nil {
log.Fatalf("Error configuring Falcon sensor: %v", err)
}
}
default:
log.Fatalf("Unable to begin package installation. Unsupported OS: %s", fc.OSType)
Expand Down
36 changes: 36 additions & 0 deletions pkg/utils/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,42 @@ func RunCmd(cmnd string, arg []string) ([]byte, []byte, error) {
return stdout.Bytes(), stderr.Bytes(), err
}

// RunCmdWithStdin runs a command with standard input.
func RunCmdWithStdin(cmnd string, arg []string, stdin string) ([]byte, []byte, error) {
var stdout, stderr bytes.Buffer
c := exec.Command(cmnd, arg...)

if c.Stdout == nil {
c.Stdout = &stdout
}
if c.Stderr == nil {
c.Stderr = &stderr
}

stdinPipe, err := c.StdinPipe()
if err != nil {
return nil, nil, fmt.Errorf("could not get stdin pipe: %w", err)
}

if err := c.Start(); err != nil {
return nil, stderr.Bytes(), fmt.Errorf("could not start command: %w", err)
}

if _, err := stdinPipe.Write([]byte(stdin)); err != nil {
return nil, stderr.Bytes(), fmt.Errorf("could not write to stdin: %w", err)
}

if err := stdinPipe.Close(); err != nil {
return nil, stderr.Bytes(), fmt.Errorf("could not close stdin: %w", err)
}

if err := c.Wait(); err != nil {
return nil, stderr.Bytes(), fmt.Errorf("could not wait for command: %w", err)
}

return stdout.Bytes(), stderr.Bytes(), nil
}

// OpenFileForWriting opens a file for writing, creating the directory if it doesn't exist.
func OpenFileForWriting(dir, filename string) (*os.File, error) {
if strings.Contains(filename, "/") {
Expand Down
18 changes: 18 additions & 0 deletions pkg/utils/utils_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,24 @@ func TestRunCmd(t *testing.T) {
}
}

func TestRunCmdWithStdin(t *testing.T) {
cmd, args, newline := testCmnd()
stdin := "hello world"
expectedOutput := fmt.Sprintf("hello world%s", newline)

stdout, stderr, err := RunCmdWithStdin(cmd, args, stdin)
if err != nil {
t.Errorf("Unexpected error: %v", err)
}

if string(stdout) != expectedOutput {
t.Errorf("Expected input: %q, got: %q", expectedOutput, string(stdout))
}
if string(stderr) != "" {
t.Errorf("Expected output: %q, got: %q", "", string(stderr))
}
}

func TestOpenFileForWriting(t *testing.T) {
filename := "test.txt"
dir := os.TempDir()
Expand Down

0 comments on commit 7d1f535

Please sign in to comment.