Skip to content

Commit

Permalink
add tracing, create key in new
Browse files Browse the repository at this point in the history
  • Loading branch information
James-Pickett committed Mar 8, 2024
1 parent 4ee293d commit f1ebb6f
Show file tree
Hide file tree
Showing 7 changed files with 125 additions and 69 deletions.
8 changes: 6 additions & 2 deletions ee/agent/keys.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"github.com/kolide/launcher/ee/agent/keys"
"github.com/kolide/launcher/ee/agent/types"
"github.com/kolide/launcher/pkg/backoff"
"github.com/kolide/launcher/pkg/traces"
)

type keyInt interface {
Expand All @@ -28,7 +29,10 @@ func LocalDbKeys() keyInt {
return localDbKeys
}

func SetupKeys(slogger *slog.Logger, store types.GetterSetterDeleter) error {
func SetupKeys(ctx context.Context, slogger *slog.Logger, store types.GetterSetterDeleter) error {
ctx, span := traces.StartSpan(ctx)
defer span.End()

slogger = slogger.With("component", "agentkeys")

var err error
Expand All @@ -40,7 +44,7 @@ func SetupKeys(slogger *slog.Logger, store types.GetterSetterDeleter) error {
}

err = backoff.WaitFor(func() error {
hwKeys, err := setupHardwareKeys(slogger, store)
hwKeys, err := setupHardwareKeys(ctx, slogger, store)
if err != nil {
return err
}
Expand Down
17 changes: 8 additions & 9 deletions ee/agent/keys_darwin.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,25 +4,24 @@
package agent

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

"github.com/kolide/launcher/ee/agent/types"
"github.com/kolide/launcher/ee/secureenclavesigner"
"github.com/kolide/launcher/pkg/traces"
)

func setupHardwareKeys(slogger *slog.Logger, store types.GetterSetterDeleter) (keyInt, error) {
ses, err := secureenclavesigner.New(slogger, store)
func setupHardwareKeys(ctx context.Context, slogger *slog.Logger, store types.GetterSetterDeleter) (keyInt, error) {
ctx, span := traces.StartSpan(ctx)
defer span.End()

ses, err := secureenclavesigner.New(ctx, slogger, store)
if err != nil {
traces.SetError(span, fmt.Errorf("creating secureenclave signer: %w", err))
return nil, fmt.Errorf("creating secureenclave signer: %w", err)
}

// this is kind of weird, but we need to call public to ensure the key is generated
// it's done this way to do satisfying signer interface which doesn't return an error
if ses.Public() == nil {
return nil, errors.New("public key was not be created")
}

return ses, nil
}
14 changes: 12 additions & 2 deletions ee/agent/keys_tpm.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,13 @@ import (

"github.com/kolide/krypto/pkg/tpm"
"github.com/kolide/launcher/ee/agent/types"
"github.com/kolide/launcher/pkg/traces"
)

// nolint:unused
func setupHardwareKeys(slogger *slog.Logger, store types.GetterSetterDeleter) (keyInt, error) {
func setupHardwareKeys(ctx context.Context, slogger *slog.Logger, store types.GetterSetterDeleter) (keyInt, error) {
ctx, span := traces.StartSpan(ctx)

Check failure on line 17 in ee/agent/keys_tpm.go

View workflow job for this annotation

GitHub Actions / lint (windows-latest)

ineffectual assignment to ctx (ineffassign)

Check failure on line 17 in ee/agent/keys_tpm.go

View workflow job for this annotation

GitHub Actions / lint (windows-latest)

ineffectual assignment to ctx (ineffassign)

Check failure on line 17 in ee/agent/keys_tpm.go

View workflow job for this annotation

GitHub Actions / lint (ubuntu-latest)

ineffectual assignment to ctx (ineffassign)

Check failure on line 17 in ee/agent/keys_tpm.go

View workflow job for this annotation

GitHub Actions / lint (ubuntu-latest)

ineffectual assignment to ctx (ineffassign)

Check warning

Code scanning / CodeQL

Useless assignment to local variable Warning

This definition of ctx is never used.
defer span.End()

priData, pubData, err := fetchKeyData(store)
if err != nil {
return nil, err
Expand All @@ -28,17 +31,24 @@ func setupHardwareKeys(slogger *slog.Logger, store types.GetterSetterDeleter) (k
priData, pubData, err = tpm.CreateKey()
if err != nil {
clearKeyData(slogger, store)
traces.SetError(span, fmt.Errorf("creating key: %w", err))
return nil, fmt.Errorf("creating key: %w", err)
}

span.AddEvent("new_key_created")

if err := storeKeyData(store, priData, pubData); err != nil {
clearKeyData(slogger, store)
traces.SetError(span, fmt.Errorf("storing key: %w", err))
return nil, fmt.Errorf("storing key: %w", err)
}

span.AddEvent("new_key_stored")
}

k, err := tpm.New(priData, pubData)
if err != nil {
traces.SetError(span, fmt.Errorf("creating tpm signer: from new key: %w", err))
return nil, fmt.Errorf("creating tpm signer: from new key: %w", err)
}

Expand Down
3 changes: 2 additions & 1 deletion ee/debug/shipper/shipper_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package shipper

import (
"context"
"encoding/json"
"fmt"
"io"
Expand Down Expand Up @@ -56,7 +57,7 @@ func TestShip(t *testing.T) { //nolint:paralleltest
name: "happy path with signing keys and enroll secret",
mockKnapsack: func(t *testing.T) *typesMocks.Knapsack {
configStore := inmemory.NewStore()
agent.SetupKeys(multislogger.NewNopLogger(), configStore)
agent.SetupKeys(context.TODO(), multislogger.NewNopLogger(), configStore)

k := typesMocks.NewKnapsack(t)
k.On("EnrollSecret").Return("enroll_secret_value")
Expand Down
142 changes: 92 additions & 50 deletions ee/secureenclavesigner/secureenclavesigner_darwin.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"crypto"
"crypto/ecdsa"
"encoding/json"
"errors"
"fmt"
"io"
"log/slog"
Expand All @@ -20,6 +21,7 @@ import (
"github.com/kolide/launcher/ee/agent/types"
"github.com/kolide/launcher/ee/allowedcmd"
"github.com/kolide/launcher/ee/consoleuser"
"github.com/kolide/launcher/pkg/traces"
)

const (
Expand All @@ -37,7 +39,10 @@ type secureEnclaveSigner struct {
mux *sync.Mutex
}

func New(slogger *slog.Logger, store types.GetterSetterDeleter, opts ...opt) (*secureEnclaveSigner, error) {
func New(ctx context.Context, slogger *slog.Logger, store types.GetterSetterDeleter, opts ...opt) (*secureEnclaveSigner, error) {
ctx, span := traces.StartSpan(ctx)
defer span.End()

ses := &secureEnclaveSigner{
uidPubKeyMap: make(map[string]*ecdsa.PublicKey),
store: store,
Expand All @@ -47,18 +52,21 @@ func New(slogger *slog.Logger, store types.GetterSetterDeleter, opts ...opt) (*s

data, err := store.Get([]byte(PublicEccDataKey))
if err != nil {
return nil, fmt.Errorf("getting public ecc data: %w", err)
traces.SetError(span, fmt.Errorf("getting public ecc data from store: %w", err))
return nil, fmt.Errorf("getting public ecc data from store: %w", err)
}

if data != nil {
if err := json.Unmarshal(data, ses); err != nil {
traces.SetError(span, fmt.Errorf("unmarshaling secure enclave signer: %w", err))
ses.slogger.Log(context.TODO(), slog.LevelError,
"unable to unmarshal secure enclave signer, data may be corrupt, wiping",
"err", err,
)

if err := store.Delete([]byte(PublicEccDataKey)); err != nil {
return nil, fmt.Errorf("deleting public ecc data: %w", err)
traces.SetError(span, fmt.Errorf("deleting corrupt public ecc data: %w", err))
return nil, fmt.Errorf("deleting corrupt public ecc data: %w", err)
}
}
}
Expand All @@ -70,69 +78,41 @@ func New(slogger *slog.Logger, store types.GetterSetterDeleter, opts ...opt) (*s
if ses.pathToLauncherBinary == "" {
p, err := os.Executable()
if err != nil {
traces.SetError(span, fmt.Errorf("getting path to launcher binary: %w", err))
return nil, fmt.Errorf("getting path to launcher binary: %w", err)
}

ses.pathToLauncherBinary = p
}

return ses, nil
}

// Public returns the public key of the current console user
// creating and peristing a new one if needed
func (ses *secureEnclaveSigner) Public() crypto.PublicKey {
ses.mux.Lock()
defer ses.mux.Unlock()

c, err := firstConsoleUser()
if err != nil {
// get current console user key to make sure it's available
if _, err := ses.currentConsoleUserKey(ctx); err != nil {
traces.SetError(span, fmt.Errorf("getting current console user key: %w", err))
ses.slogger.Log(context.TODO(), slog.LevelError,
"getting first console user",
"getting current console user key",
"err", err,
)

return nil
}

if v, ok := ses.uidPubKeyMap[c.Uid]; ok {
return v
// intentionally not returning error here, because this runs on start up
// and maybe the console user or secure enclave is not available yet
}

key, err := ses.createKey(context.TODO(), c)
if err != nil {
ses.slogger.Log(context.TODO(), slog.LevelError,
"creating key",
"err", err,
)

return nil
}

ses.uidPubKeyMap[c.Uid] = key
return ses, nil
}

// pesist the new key
json, err := json.Marshal(ses)
// Public returns the public key of the current console user
// creating and peristing a new one if needed
func (ses *secureEnclaveSigner) Public() crypto.PublicKey {
k, err := ses.currentConsoleUserKey(context.TODO())
if err != nil {
ses.slogger.Log(context.TODO(), slog.LevelError,
"marshaling secure enclave signer",
"getting public key",
"err", err,
)

delete(ses.uidPubKeyMap, c.Uid)
return nil
}

if err := ses.store.Set([]byte(PublicEccDataKey), json); err != nil {
ses.slogger.Log(context.TODO(), slog.LevelError,
"persisting secure enclave signer",
"err", err,
)
delete(ses.uidPubKeyMap, c.Uid)
return nil
}

return key
return k
}

func (ses *secureEnclaveSigner) Type() string {
Expand All @@ -148,6 +128,44 @@ type keyData struct {
PubKey string `json:"pub_key"`
}

func (ses *secureEnclaveSigner) currentConsoleUserKey(ctx context.Context) (*ecdsa.PublicKey, error) {
ctx, span := traces.StartSpan(ctx)
defer span.End()

ses.mux.Lock()
defer ses.mux.Unlock()

cu, err := firstConsoleUser(ctx)
if err != nil {
traces.SetError(span, fmt.Errorf("getting first console user: %w", err))
return nil, fmt.Errorf("getting first console user: %w", err)
}

key, ok := ses.uidPubKeyMap[cu.Uid]
if ok {
span.AddEvent("found_existing_key_for_console_user")
return key, nil
}

key, err = ses.createKey(ctx, cu)
if err != nil {
traces.SetError(span, fmt.Errorf("creating key: %w", err))
return nil, fmt.Errorf("creating key: %w", err)
}

span.AddEvent("created_new_key_for_console_user")

ses.uidPubKeyMap[cu.Uid] = key
if err := ses.save(); err != nil {
delete(ses.uidPubKeyMap, cu.Uid)
traces.SetError(span, fmt.Errorf("saving secure enclave signer: %w", err))
return nil, fmt.Errorf("saving secure enclave signer: %w", err)
}

span.AddEvent("saved_key_for_console_user")
return key, nil
}

func (ses *secureEnclaveSigner) MarshalJSON() ([]byte, error) {
var keyDatas []keyData

Expand Down Expand Up @@ -190,6 +208,9 @@ func (ses *secureEnclaveSigner) UnmarshalJSON(data []byte) error {
}

func (ses *secureEnclaveSigner) createKey(ctx context.Context, u *user.User) (*ecdsa.PublicKey, error) {
ctx, span := traces.StartSpan(ctx)
defer span.End()

cmd, err := allowedcmd.Launchctl(
ctx,
"asuser",
Expand All @@ -204,19 +225,22 @@ func (ses *secureEnclaveSigner) createKey(ctx context.Context, u *user.User) (*e
)

if err != nil {
traces.SetError(span, fmt.Errorf("creating command to create key: %w", err))
return nil, fmt.Errorf("creating command to create key: %w", err)
}

// skip updates since we have full path of binary
cmd.Env = append(cmd.Environ(), fmt.Sprintf("%s=%s", "LAUNCHER_SKIP_UPDATES", "true"))
out, err := cmd.CombinedOutput()
if err != nil {
traces.SetError(span, fmt.Errorf("executing launcher binary to create key: %w: %s", err, string(out)))
return nil, fmt.Errorf("executing launcher binary to create key: %w: %s", err, string(out))
}

pubKey, err := echelper.PublicB64DerToEcdsaKey([]byte(lastLine(out)))
if err != nil {
return nil, fmt.Errorf("marshalling public key to der: %w", err)
traces.SetError(span, fmt.Errorf("converting public key to ecdsa: %w", err))
return nil, fmt.Errorf("converting public key to ecdsa: %w", err)
}

return pubKey, nil
Expand All @@ -239,15 +263,33 @@ func lastLine(out []byte) string {
return lastLine
}

func firstConsoleUser() (*user.User, error) {
c, err := consoleuser.CurrentUsers(context.TODO())
func firstConsoleUser(ctx context.Context) (*user.User, error) {
ctx, span := traces.StartSpan(ctx)
defer span.End()

c, err := consoleuser.CurrentUsers(ctx)
if err != nil {
traces.SetError(span, fmt.Errorf("getting current users: %w", err))
return nil, fmt.Errorf("getting current users: %w", err)
}

if len(c) == 0 {
return nil, fmt.Errorf("no console users found")
traces.SetError(span, errors.New("no console users found"))
return nil, errors.New("no console users found")
}

return c[0], nil
}

func (ses *secureEnclaveSigner) save() error {
json, err := json.Marshal(ses)
if err != nil {
return fmt.Errorf("marshaling secure enclave signer: %w", err)
}

if err := ses.store.Set([]byte(PublicEccDataKey), json); err != nil {
return fmt.Errorf("setting public ecc data: %w", err)
}

return nil
}
Loading

0 comments on commit f1ebb6f

Please sign in to comment.