-
Notifications
You must be signed in to change notification settings - Fork 103
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
add secure enclave signatures to local server response #1658
base: main
Are you sure you want to change the base?
add secure enclave signatures to local server response #1658
Conversation
|
||
// Msg is the base64 encoded message that contains timestamp, nonce, and | ||
// the response from the endpoint called. | ||
Msg string `json:"msg"` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
not sure if this is the best way to pacakge this data, maybe we should continue to return the endpoint response as it is and add on a HardwareSignatureBox
something like:
type kryptoEcMiddlewareResponse struct {
// Data what the endpoint returned
Data string `json:"data"`
// HardwareSigBox is b64 encoded []byte of msgpack containing
// msg, sig, and key []byte fields
HardwareSigBox string `json:"hardware_sig_box"`
}
type hardwareSigBox struct {
Msg []byte `msgpack:"msg"`
Sig []byte `msgpack:"sig"`
PubKey []byte `msgpack:"pub_key"`
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I made it partway through -- didn't get a chance to look at localserver in detail yet. I'll come back tomorrow. 🙂
"github.com/kolide/launcher/ee/secureenclavesigner" | ||
) | ||
|
||
const secureEnclaveTimestampValiditySeconds = 10 | ||
|
||
// runSecureEnclave performs either a create-key operation using the secure enclave. | ||
// It's available as a separate command because launcher runs as root by default and since it's | ||
// not in a user security context, it can't use the secure enclave directly. However, this command | ||
// can be run in the user context using launchctl. | ||
func runSecureEnclave(args []string) error { | ||
// currently we are just creating key, but plan to add sign command in future |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
// currently we are just creating key, but plan to add sign command in future |
// tag the ends of the data to sign, this is intended to ensure that launcher wont | ||
// sign arbitrary things, any party verifying the signature will need to | ||
// handle these tags | ||
dataToSign := []byte(fmt.Sprintf("kolide:%s:kolide", signRequest.Data)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think maybe I'm missing something (I will revisit once you've got a diagram available) -- why do we add kolide:
+ :kolide
on either side of the data at this point? Shouldn't whatever is calling this command have already put kolide
on either side so that we can validate that here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I created a PR for a doc here https://github.com/kolide/monorepo/pull/166.
We have to do it at this point because there is no way to ensure that the process which is execing launcher in the user context adds the tags.
The inability for a process to confidently detect it's parent process is the primary driver for all this tag hoopla.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
shape of it looks good to me overall! just a couple questions
Timestamp: time.Now().UTC().Unix(), | ||
// add the kolide:%s:kolide tags since the secure enclave cmd | ||
// wont be execed and add them | ||
Data: []byte(fmt.Sprintf("kolide:%s:kolide", string(data))), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Optional -- maybe could add a small helper function in secureenclavesigner
to do the kolide:%s:kolide
formatting? Then cmd/launcher/secure_enclave_darwin.go
could call that when it needs it too (also line 41 in krypto-ec-middleware-response_other.go
), and we'd only have one place to remember/update it.
ctx, span := traces.StartSpan(ctx) | ||
defer span.End() | ||
|
||
if e.hardwareSigner == nil || e.hardwareSigner.Public() == nil { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When/why would the hardware signer be nil? Early on in registration/enrollment before we've generated keys? Or another reason?
"err", err, | ||
) | ||
traces.SetError(span, fmt.Errorf("signing with console user hardware key, %w", err)) | ||
return e.responseWithoutHardwareSig(o, data) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When/why do we choose to return responseWithoutHardwareSig
versus returning nil,err
?
James and I are revisiting this, and we think we need to rethink it to be less awkward. |
This PR updates the local server response to include a hardware signature. On macOS the hardware signer is the secure enclave. The secure enclave can only be used by a process running in the user security context. To accomplish this, launcher uses launchctl to exec itself in the user security context.
One thing we wanted to focus on preventing was using a signed launcher binary to sign arbitrary things. To do this, the launcher secure enclave command will only work when it has been provided validated a local server challenge. Additionally, the command adds
kolide:
&:kolide
to either ends of the data it's signing. As well as a nonce and a timestamp to the signed data.This was not required for windows and linux since the TPM can be used by the root user. However, this has been added to both the linux and TPM signing so that the data structure returned by launcher is the same for all platforms.
Here are some more detailed docs https://github.com/kolide/monorepo/pull/166