Skip to content

Commit

Permalink
remote uninstall / disable (kolide#1393)
Browse files Browse the repository at this point in the history
  • Loading branch information
James-Pickett authored Jan 24, 2024
1 parent a63c759 commit f9891a9
Show file tree
Hide file tree
Showing 8 changed files with 276 additions and 11 deletions.
4 changes: 3 additions & 1 deletion cmd/launcher/launcher.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ import (
"github.com/kolide/launcher/ee/control/consumers/flareconsumer"
"github.com/kolide/launcher/ee/control/consumers/keyvalueconsumer"
"github.com/kolide/launcher/ee/control/consumers/notificationconsumer"
"github.com/kolide/launcher/ee/control/consumers/uninstallconsumer"
"github.com/kolide/launcher/ee/debug/checkups"
desktopRunner "github.com/kolide/launcher/ee/desktop/runner"
"github.com/kolide/launcher/ee/localserver"
Expand Down Expand Up @@ -371,7 +372,8 @@ func runLauncher(ctx context.Context, cancel func(), multiSlogger, systemMultiSl

// register accelerate control consumer
actionsQueue.RegisterActor(acceleratecontrolconsumer.AccelerateControlSubsystem, acceleratecontrolconsumer.New(k))

// register uninstall consumer
actionsQueue.RegisterActor(uninstallconsumer.UninstallSubsystem, uninstallconsumer.New(k))
// register flare consumer
actionsQueue.RegisterActor(flareconsumer.FlareSubsystem, flareconsumer.New(k))

Expand Down
17 changes: 7 additions & 10 deletions ee/agent/reset.go
Original file line number Diff line number Diff line change
Expand Up @@ -340,16 +340,13 @@ func getLocalPubKey(k types.Knapsack) ([]byte, error) { // nolint:unused
return pubKeyBytes, nil
}

// wipeDatabase iterates over all stores in the database, deleting all keys from
// WipeDatabase iterates over all stores in the database, deleting all keys from
// each one.
func wipeDatabase(ctx context.Context, k types.Knapsack) error { // nolint:unused
return errors.New("currently not supported")
/*
for storeName, store := range k.Stores() {
if err := store.DeleteAll(); err != nil {
return fmt.Errorf("deleting keys in store %s: %w", storeName, err)
}
func WipeDatabase(ctx context.Context, k types.Knapsack) error {
for storeName, store := range k.Stores() {
if err := store.DeleteAll(); err != nil {
return fmt.Errorf("deleting keys in store %s: %w", storeName, err)
}
return nil
*/
}
return nil
}
29 changes: 29 additions & 0 deletions ee/control/consumers/uninstallconsumer/uninstallconsumer.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package uninstallconsumer

import (
"context"
"io"

"github.com/kolide/launcher/ee/agent/types"
"github.com/kolide/launcher/ee/uninstall"
)

const (
// Identifier for this consumer.
UninstallSubsystem = "uninstall"
)

type UninstallConsumer struct {
knapsack types.Knapsack
}

func New(knapsack types.Knapsack) *UninstallConsumer {
return &UninstallConsumer{
knapsack: knapsack,
}
}

func (c *UninstallConsumer) Do(data io.Reader) error {
uninstall.Uninstall(context.TODO(), c.knapsack, true)
return nil
}
57 changes: 57 additions & 0 deletions ee/uninstall/uninstall.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package uninstall

import (
"context"
"errors"
"log/slog"
"os"

"github.com/kolide/launcher/ee/agent"
"github.com/kolide/launcher/ee/agent/types"
)

// Uninstall just removes the enroll secret file and wipes the database.
// Logs errors, but does not return them, because we want to try each step independently.
// If exitOnCompletion is true, it will also disable launcher autostart and exit.
func Uninstall(ctx context.Context, k types.Knapsack, exitOnCompletion bool) {
slogger := k.Slogger().With("component", "uninstall")

if err := removeEnrollSecretFile(k); err != nil {
slogger.Log(ctx, slog.LevelError,
"removing enroll secret file",
"err", err,
)
}

if err := agent.WipeDatabase(ctx, k); err != nil {
slogger.Log(ctx, slog.LevelError,
"wiping database",
"err", err,
)
}

if !exitOnCompletion {
return
}

if err := disableAutoStart(ctx); err != nil {
k.Slogger().Log(ctx, slog.LevelError,
"disabling auto start",
"err", err,
)
}

os.Exit(0)
}

func removeEnrollSecretFile(knapsack types.Knapsack) error {
if knapsack.EnrollSecretPath() == "" {
return errors.New("no enroll secret path set")
}

if err := os.Remove(knapsack.EnrollSecretPath()); err != nil {
return err
}

return nil
}
27 changes: 27 additions & 0 deletions ee/uninstall/uninstall_darwin.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package uninstall

import (
"context"
"fmt"
"time"

"github.com/kolide/launcher/ee/allowedcmd"
)

func disableAutoStart(ctx context.Context) error {
launchDaemonPList := "/Library/LaunchDaemons/com.kolide-k2.launcher.plist"
launchCtlArgs := []string{"unload", launchDaemonPList}

launchctlCtx, cancel := context.WithTimeout(ctx, 30*time.Second)
defer cancel()
cmd, err := allowedcmd.Launchctl(launchctlCtx, launchCtlArgs...)
if err != nil {
return fmt.Errorf("could create launchctl cmd: %w", err)
}

if out, err := cmd.Output(); err != nil {
return fmt.Errorf("error occurred while unloading launcher daemon, launchctl output %s: err: %w", out, err)
}

return nil
}
23 changes: 23 additions & 0 deletions ee/uninstall/uninstall_linux.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package uninstall

import (
"context"
"fmt"

"github.com/kolide/launcher/ee/allowedcmd"
)

func disableAutoStart(ctx context.Context) error {
// the --now flag will disable and stop the service
cmd, err := allowedcmd.Systemctl(ctx, "disable", "--now", "launcher.kolide-k2.service")
if err != nil {
return fmt.Errorf("creating systemctl cmd: %w", err)
}

out, err := cmd.CombinedOutput()
if err != nil {
return fmt.Errorf("disabling auto start: %w: %s", err, out)
}

return nil
}
96 changes: 96 additions & 0 deletions ee/uninstall/uninstall_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
package uninstall

import (
"context"
"fmt"
"os"
"path/filepath"
"testing"

"github.com/go-kit/kit/log"
"github.com/kolide/kit/ulid"
"github.com/kolide/launcher/ee/agent/storage"
storageci "github.com/kolide/launcher/ee/agent/storage/ci"
"github.com/kolide/launcher/ee/agent/types"
"github.com/kolide/launcher/ee/agent/types/mocks"
"github.com/kolide/launcher/pkg/log/multislogger"
"github.com/stretchr/testify/require"
)

func TestUninstall(t *testing.T) {
t.Parallel()

tests := []struct {
name string
}{
{
name: "happy path",
},
}
for _, tt := range tests {
tt := tt
t.Run(tt.name, func(t *testing.T) {
t.Parallel()

// create a enroll secret to delete
enrollSecretPath := filepath.Join(t.TempDir(), "enroll_secret")
f, err := os.Create(enrollSecretPath)
require.NoError(t, err)
require.NoError(t, f.Close())

// sanity check that the file exist
_, err = os.Stat(enrollSecretPath)
require.NoError(t, err)

// create 3 stores with 3 items each
stores := map[storage.Store]types.KVStore{}
for i := 0; i < 3; i++ {
store, err := storageci.NewStore(t, log.NewNopLogger(), ulid.New())
require.NoError(t, err)

for j := 0; j < 3; j++ {
require.NoError(t, store.Set([]byte(fmt.Sprint(j)), []byte(fmt.Sprint(j))))
}

require.NoError(t, err)
stores[storage.Store(fmt.Sprint(i))] = store
}

// sanity check that we have 3 stores with 3 items each
itemsExpected := 9
itemsFound := 0
for _, store := range stores {
store.ForEach(
func(k, v []byte) error {
itemsFound++
return nil
},
)
}
require.Equal(t, itemsExpected, itemsFound)

k := mocks.NewKnapsack(t)
k.On("Stores").Return(stores)
k.On("EnrollSecretPath").Return(enrollSecretPath)
k.On("Slogger").Return(multislogger.New().Logger)

Uninstall(context.TODO(), k, false)

// check that file was deleted
_, err = os.Stat(enrollSecretPath)
require.True(t, os.IsNotExist(err))

// check that all stores are empty
itemsFound = 0
for _, store := range stores {
store.ForEach(
func(k, v []byte) error {
itemsFound++
return nil
},
)
}
require.Equal(t, 0, itemsFound)
})
}
}
34 changes: 34 additions & 0 deletions ee/uninstall/uninstall_windows.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package uninstall

import (
"context"
"fmt"

"golang.org/x/sys/windows/svc/mgr"
)

func disableAutoStart(ctx context.Context) error {
svcMgr, err := mgr.Connect()
if err != nil {
return fmt.Errorf("connecting to windows service manager: %w", err)
}
defer svcMgr.Disconnect()

launcherSvc, err := svcMgr.OpenService("LauncherKolideK2Svc")
if err != nil {
return fmt.Errorf("opening launcher service: %w", err)
}
defer launcherSvc.Close()

cfg, err := launcherSvc.Config()
if err != nil {
return fmt.Errorf("getting launcher service config: %w", err)
}

cfg.StartType = mgr.StartManual
if err := launcherSvc.UpdateConfig(cfg); err != nil {
return fmt.Errorf("updating launcher service config: %w", err)
}

return nil
}

0 comments on commit f9891a9

Please sign in to comment.