writing extension tables

While osquery provides many tables you can use to gather data, you may need tables customized for your infrastructure or a table you need to deploy more quickly than waiting for code reviews, merges, and new version. This is where you can use osquery's thrift API and python extension framework.

The code

Below is the example extension from the osquery-python GitHub page annotated with my comments. It outlines all the parts you need to build an extension with python for osquery.

#!/usr/bin/env python
# Make sure osquery-python library is installed
import osquery

@osquery.register_plugin
# You can name the plugin anything you want but it needs
# to inherit from osquery.TablePlugin
class MyTablePlugin(osquery.TablePlugin):
    # This will define table name. In this example you
    # could query it with: select * from foobar
    def name(self):
        return "foobar"

    # This will define your columns. Make sure they
    # are all osquery.STRING type otherwise Thrift
    # may not transfer them properly
    def columns(self):
        return [
            osquery.TableColumn(name="foo", type=osquery.STRING),
            osquery.TableColumn(name="baz", type=osquery.STRING),
        ]

    # This function will be called when the table is queried
    def generate(self, context):
        query_data = []

        for _ in range(2):
            row = {}
            row["foo"] = "bar"
            row["baz"] = "baz"
            query_data.append(row)

        return query_data

# This is the function that's run when you start the extension
# or osquery does. An extensions starts in it's own process
# and uses the name given below.
if __name__ == "__main__":
    osquery.start_extension(name="my_awesome_extension",
                            version="1.0.0",)

Autoload

Once you've written your extension, you'll want it to start with osquery to be available for queries. To do this you specify the extensions_autoload flag.

--extensions_autoload=/path/to/extensions.load

osquery will read this file which contains paths to executables. osquery will execute these paths passing them the osquery thrift socket for communication. In this case your extensions.load file would have one entry; a fully qualified path to the python script above.