Skip to content
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

Migration from shelling out to osquery to using Thrift #34

Merged
merged 17 commits into from
Jul 31, 2018
Merged

Conversation

rmcvey
Copy link
Contributor

@rmcvey rmcvey commented Jun 8, 2018

This represents a very large change in the way the application works and I have some concerns about rolling it out.

TLDR; version of the changes:

OLD make shell calls to osqueryi based on what data was requested in the API (e.g.

query { device { version } }

runs:

child_process.spawn(`osqueryi --json "select version from os_version;"`)

NEW start one child process when the app launches:

# nodisable_extensions flag opens a thrift socket
osqueryi --nodisable_extensions

Use thrift protocol to send all queries.

Problems

  • Timing: I'm struggling to determine when the process has successfully started and am currently using an extremely naive approach setTimeout :vomitinmouth: :biohazard_sign: to make the thrift socket connection. I'm occasionally seeing this error: OSQUERY ERROR Error: Thrift: Fri Jun 8 12:13:27 2018 TSocket::open() connect() <Host: Port: 0>Connection refused, it doesn't seem to break anything on Mac but I have seen issues on Windows.
  • Dev vs. Prod: There were a few adjustments I had to make + runtime flags added to get this to work in production builds e.g. https://github.com/Netflix-Skunkworks/stethoscope-app/blob/thrift/sources/osquery_thrift.js#L44-L45

Actions

There are some serious performance issues with master (especially on Windows) and this branch fixes those issues. I would really appreciate any and all feedback on app performance/code quality/approach/etc.

@rmcvey rmcvey added the enhancement New feature or request label Jun 8, 2018
@rmcvey rmcvey self-assigned this Jun 8, 2018
@@ -9,6 +12,11 @@ const osqueryPlatforms = {
linux: '../bin/osqueryi_linux'
}

const socketPath = {
darwin: `/Users/${process.env.USER}/.osquery`,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should/could this be in appData instead?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It could and probably should, I went with using the default socket locations that osquery picks. I think I just need to run some tests to ensure that the provided socket path is reliably used.

})

setTimeout(() => {
this.connection = ThriftClient.getInstance({ path: socket })
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we run some kind of super fast "heartbeat" query at this stage, and retry on failure? Then we're at least polling to see if it's up instead of trying to guess a magic number that will work.

I suppose normal plugins don't have this problem because the load order is reversed: osquery launches them.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Definitely, that would be a much more ideal setup. I tried something like that at first, but I haven't found any real documentation on the NodeJS API produced by thrift. This page exists but all of the links are broken 👎 . I ran this command to generate the thrift lib and have figured out some of the API by reviewing the source:

thrift -r --gen js:node /path/to/osquery/osquery.thrift

I'll dive deeper into the thrift libraries and see what I can do in terms of polling and catching errors.

this.connection.end()
}
log.error(`osquery execution error: ${err}`)
reject(new Error(`Unable to spawn osquery: ${err}`))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this the only error case? Is it possible to crash osqueryi with a bad query, in which case we should try to bring it back up?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

From my tests so far, this is the only error case. The bad queries I've tested catch and report syntax errors and don't kill the osqueryi process.

osquery> select $%^&*( from bobby_tables;
Error: unrecognized token: "$"
osquery> i can haz segfault?;
Error: near "i": syntax error
osquery>

I think it does make sense to cover ourselves should osquery crash for an unexpected reason

}

constructor(opts = { path: '/Users/' + process.env.USER + '/.osquery/shell.em'}) {
const path = opts.path || '/Users/' + process.env.USER + '/.osquery/shell.em'
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't have to do this since you've got the default in the arguments.

Also, same comment as above: should this be in appData?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch, this is a remnant from an earlier setup where I wasn't manually setting the socket path. Will remove this

@rmcvey
Copy link
Contributor Author

rmcvey commented Jul 25, 2018

Created #38 to track this issue

@rmcvey rmcvey merged commit cf1cfc3 into master Jul 31, 2018
@rmcvey rmcvey deleted the thrift branch July 31, 2018 21:39
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants