osquery-1/tools/tests/test_extensions.py

408 lines
14 KiB
Python
Raw Normal View History

#!/usr/bin/env python3
2015-02-19 23:19:00 +00:00
# Copyright (c) 2014-present, The osquery authors
2015-02-19 23:19:00 +00:00
#
# This source code is licensed as defined by the LICENSE file found in the
# root directory of this source tree.
#
# SPDX-License-Identifier: (Apache-2.0 OR GPL-2.0-only)
2015-02-19 23:19:00 +00:00
import glob
2015-02-19 23:19:00 +00:00
import os
import psutil
import signal
import subprocess
import sys
import time
import threading
import unittest
# osquery-specific testing utils
import test_base
import utils
2015-02-19 23:19:00 +00:00
2015-07-02 19:14:44 +00:00
EXTENSION_TIMEOUT = 10
2015-02-19 23:19:00 +00:00
class ExtensionTests(test_base.ProcessGenerator, unittest.TestCase):
2015-02-19 23:19:00 +00:00
def test_1_daemon_without_extensions(self):
# Start the daemon without thrift, prefer no watchdog because the tests
# kill the daemon very quickly.
2015-03-14 01:18:18 +00:00
daemon = self._run_daemon({
"disable_watchdog": True,
"disable_extensions": True,
})
2015-02-19 23:19:00 +00:00
self.assertTrue(daemon.isAlive())
# Now try to connect to the disabled API
2015-05-27 23:50:57 +00:00
client = test_base.EXClient(daemon.options["extensions_socket"])
2015-02-19 23:19:00 +00:00
self.assertFalse(client.open())
daemon.kill()
@test_base.flaky
2015-02-19 23:19:00 +00:00
def test_2_daemon_api(self):
daemon = self._run_daemon({"disable_watchdog": True})
2015-02-19 23:19:00 +00:00
self.assertTrue(daemon.isAlive())
# Get a python-based thrift client
2015-05-27 23:50:57 +00:00
client = test_base.EXClient(daemon.options["extensions_socket"])
2015-07-02 19:14:44 +00:00
self.assertTrue(client.open(timeout=EXTENSION_TIMEOUT))
2015-02-19 23:19:00 +00:00
em = client.getEM()
# List the number of extensions
print(em.ping())
2015-04-24 08:44:41 +00:00
result = test_base.expect(em.extensions, 0)
2015-02-19 23:19:00 +00:00
self.assertEqual(len(result), 0)
# Try the basic ping API
self.assertEqual(em.ping().code, 0)
# Try a query
response = em.query("select * from time")
self.assertEqual(response.status.code, 0)
self.assertEqual(len(response.response), 1)
self.assertTrue("seconds" in response.response[0].keys())
# Try to get the query columns
response = em.getQueryColumns("select seconds as s from time")
self.assertEqual(response.status.code, 0)
self.assertEqual(len(response.response), 1)
self.assertTrue("s" in response.response[0])
client.close()
daemon.kill()
@test_base.flaky
2015-02-19 23:19:00 +00:00
def test_3_example_extension(self):
daemon = self._run_daemon({"disable_watchdog": True})
2015-02-19 23:19:00 +00:00
self.assertTrue(daemon.isAlive())
2015-04-24 08:44:41 +00:00
2015-02-19 23:19:00 +00:00
# Get a python-based thrift client
2015-05-27 23:50:57 +00:00
client = test_base.EXClient(daemon.options["extensions_socket"])
2015-07-02 19:14:44 +00:00
self.assertTrue(client.open(timeout=EXTENSION_TIMEOUT))
2015-02-19 23:19:00 +00:00
em = client.getEM()
# Make sure there are no extensions registered
2015-04-24 08:44:41 +00:00
result = test_base.expect(em.extensions, 0)
2015-02-19 23:19:00 +00:00
self.assertEqual(len(result), 0)
# Make sure the extension process starts
2015-05-27 23:50:57 +00:00
extension = self._run_extension(
path=daemon.options["extensions_socket"],
2015-07-02 19:14:44 +00:00
timeout=EXTENSION_TIMEOUT,
)
2015-02-19 23:19:00 +00:00
self.assertTrue(extension.isAlive())
# Now that an extension has started, check extension list
2015-04-24 08:44:41 +00:00
result = test_base.expect(em.extensions, 1)
2015-02-19 23:19:00 +00:00
self.assertEqual(len(result), 1)
ex_uuid = result.keys()[0]
ex_data = result[ex_uuid]
self.assertEqual(ex_data.name, "example")
self.assertEqual(ex_data.version, "0.0.1")
self.assertEqual(ex_data.min_sdk_version, "0.0.0")
# Get a python-based thrift client to the extension's service
2015-05-27 23:50:57 +00:00
client2 = test_base.EXClient(daemon.options["extensions_socket"],
uuid=ex_uuid)
2015-07-02 19:14:44 +00:00
self.assertTrue(client2.open(timeout=EXTENSION_TIMEOUT))
2015-02-19 23:19:00 +00:00
ex = client2.getEX()
self.assertEqual(ex.ping().code, 0)
# Make sure the extension can receive a call
em_time = em.call("table", "time", {"action": "columns"})
ex_time = ex.call("table", "time", {"action": "columns"})
print(em_time)
print(ex_time)
2015-02-19 23:19:00 +00:00
self.assertEqual(ex_time.status.code, 0)
self.assertTrue(len(ex_time.response) > 0)
self.assertTrue("name" in ex_time.response[0])
2015-02-19 23:19:00 +00:00
self.assertEqual(ex_time.status.uuid, ex_uuid)
# Make sure the extension includes a custom registry plugin
result = ex.call("table", "example", {"action": "generate"})
print(result)
2015-02-19 23:19:00 +00:00
self.assertEqual(result.status.code, 0)
self.assertEqual(len(result.response), 1)
self.assertTrue("example_text" in result.response[0])
self.assertTrue("example_integer" in result.response[0])
self.assertEqual(result.response[0]["example_text"], "example")
# Make sure the core can route to the extension
result = em.call("table", "example", {"action": "generate"})
print(result)
2015-02-19 23:19:00 +00:00
client2.close()
client.close()
extension.kill()
daemon.kill()
@test_base.flaky
2015-02-19 23:19:00 +00:00
def test_4_extension_dies(self):
daemon = self._run_daemon({
"disable_watchdog": True,
"extensions_interval": "0",
2015-07-02 19:14:44 +00:00
"verbose": True,
})
2015-02-19 23:19:00 +00:00
self.assertTrue(daemon.isAlive())
2015-04-24 08:44:41 +00:00
2015-02-19 23:19:00 +00:00
# Get a python-based thrift client
2015-05-27 23:50:57 +00:00
client = test_base.EXClient(daemon.options["extensions_socket"])
2015-07-02 19:14:44 +00:00
self.assertTrue(client.open(timeout=EXTENSION_TIMEOUT))
2015-02-19 23:19:00 +00:00
em = client.getEM()
# Make sure there are no extensions registered
2015-04-24 08:44:41 +00:00
result = test_base.expect(em.extensions, 0)
2015-02-19 23:19:00 +00:00
self.assertEqual(len(result), 0)
# Make sure the extension process starts
2015-05-27 23:50:57 +00:00
extension = self._run_extension(
path=daemon.options["extensions_socket"],
2015-07-02 19:14:44 +00:00
timeout=EXTENSION_TIMEOUT)
2015-02-19 23:19:00 +00:00
self.assertTrue(extension.isAlive())
# Now that an extension has started, check extension list
2015-04-24 08:44:41 +00:00
result = test_base.expect(em.extensions, 1)
2015-02-19 23:19:00 +00:00
self.assertEqual(len(result), 1)
# Kill the extension
extension.kill()
# Make sure the daemon detects the change
2015-07-02 19:14:44 +00:00
result = test_base.expect(em.extensions, 0, timeout=EXTENSION_TIMEOUT)
2015-02-19 23:19:00 +00:00
self.assertEqual(len(result), 0)
# Make sure the extension restarts
2015-05-27 23:50:57 +00:00
extension = self._run_extension(
path=daemon.options["extensions_socket"],
2015-07-02 19:14:44 +00:00
timeout=EXTENSION_TIMEOUT,
)
2015-02-19 23:19:00 +00:00
self.assertTrue(extension.isAlive())
# With the reset there should be 1 extension again
2015-04-24 08:44:41 +00:00
result = test_base.expect(em.extensions, 1)
2015-02-19 23:19:00 +00:00
self.assertEqual(len(result), 1)
print(em.query("select * from example"))
2015-02-19 23:19:00 +00:00
# Now tear down the daemon
client.close()
daemon.kill()
# The extension should tear down as well
self.assertTrue(extension.isDead(extension.pid))
@test_base.flaky
def test_5_extension_timeout(self):
# Start an extension without a daemon, with a timeout.
2015-07-02 19:14:44 +00:00
extension = self._run_extension(timeout=EXTENSION_TIMEOUT)
self.assertTrue(extension.isAlive())
# Now start a daemon
daemon = self._run_daemon({
"disable_watchdog": True,
"extensions_socket": extension.options["extensions_socket"],
2015-07-02 19:14:44 +00:00
"verbose": True,
})
self.assertTrue(daemon.isAlive())
# Get a python-based thrift client
2015-05-27 23:50:57 +00:00
client = test_base.EXClient(extension.options["extensions_socket"])
test_base.expectTrue(client.try_open)
2015-07-02 19:14:44 +00:00
self.assertTrue(client.open(timeout=EXTENSION_TIMEOUT))
em = client.getEM()
# The waiting extension should have connected to the daemon.
2015-04-24 08:44:41 +00:00
result = test_base.expect(em.extensions, 1)
self.assertEqual(len(result), 1)
client.close()
daemon.kill(True)
extension.kill()
@test_base.flaky
def test_6_extensions_autoload(self):
loader = test_base.Autoloader(
[test_base.ARGS.build + "/osquery/example_extension.ext"])
2015-03-14 01:18:18 +00:00
daemon = self._run_daemon({
"disable_watchdog": True,
2015-07-02 19:14:44 +00:00
"extensions_timeout": EXTENSION_TIMEOUT,
"extensions_autoload": loader.path,
2015-03-14 01:18:18 +00:00
})
self.assertTrue(daemon.isAlive())
# Get a python-based thrift client
2015-05-27 23:50:57 +00:00
client = test_base.EXClient(daemon.options["extensions_socket"])
2015-07-02 19:14:44 +00:00
self.assertTrue(client.open(timeout=EXTENSION_TIMEOUT))
em = client.getEM()
# The waiting extension should have connected to the daemon.
2015-04-24 08:44:41 +00:00
result = test_base.expect(em.extensions, 1)
self.assertEqual(len(result), 1)
client.close()
daemon.kill(True)
@test_base.flaky
def test_6_extensions_directory_autoload(self):
utils.copy_file(test_base.ARGS.build + "/osquery/example_extension.ext",
test_base.CONFIG_DIR)
loader = test_base.Autoloader([test_base.CONFIG_DIR])
daemon = self._run_daemon({
"disable_watchdog": True,
"extensions_timeout": EXTENSION_TIMEOUT,
"extensions_autoload": loader.path,
})
self.assertTrue(daemon.isAlive())
# Get a python-based thrift client
client = test_base.EXClient(daemon.options["extensions_socket"])
self.assertTrue(client.open(timeout=EXTENSION_TIMEOUT))
em = client.getEM()
# The waiting extension should have connected to the daemon.
result = test_base.expect(em.extensions, 1)
self.assertEqual(len(result), 1)
client.close()
daemon.kill(True)
2015-11-08 10:29:49 +00:00
@test_base.flaky
2015-03-14 01:18:18 +00:00
def test_7_extensions_autoload_watchdog(self):
loader = test_base.Autoloader(
[test_base.ARGS.build + "/osquery/example_extension.ext"])
daemon = self._run_daemon({
2015-07-02 19:14:44 +00:00
"extensions_timeout": EXTENSION_TIMEOUT,
"extensions_autoload": loader.path,
})
self.assertTrue(daemon.isAlive())
# Get a python-based thrift client
2015-05-27 23:50:57 +00:00
client = test_base.EXClient(daemon.options["extensions_socket"])
2015-07-02 19:14:44 +00:00
self.assertTrue(client.open(timeout=EXTENSION_TIMEOUT))
2015-03-14 01:18:18 +00:00
em = client.getEM()
# The waiting extension should have connected to the daemon.
2015-04-24 08:44:41 +00:00
result = test_base.expect(em.extensions, 1)
2015-03-14 01:18:18 +00:00
self.assertEqual(len(result), 1)
client.close()
daemon.kill(True)
2015-03-14 01:18:18 +00:00
2015-11-08 10:29:49 +00:00
@test_base.flaky
2015-03-14 01:18:18 +00:00
def test_8_external_config(self):
loader = test_base.Autoloader(
[test_base.ARGS.build + "/osquery/example_extension.ext"])
2015-03-14 01:18:18 +00:00
daemon = self._run_daemon({
"extensions_autoload": loader.path,
2015-07-02 19:14:44 +00:00
"extensions_timeout": EXTENSION_TIMEOUT,
2015-03-14 01:18:18 +00:00
"config_plugin": "example",
})
self.assertTrue(daemon.isAlive())
# Get a python-based thrift client
2015-05-27 23:50:57 +00:00
client = test_base.EXClient(daemon.options["extensions_socket"])
2015-07-02 19:14:44 +00:00
self.assertTrue(client.open(timeout=EXTENSION_TIMEOUT))
em = client.getEM()
# The waiting extension should have connected to the daemon.
# If there are no extensions the daemon may have exited (in error).
2015-04-24 08:44:41 +00:00
result = test_base.expect(em.extensions, 1)
self.assertEqual(len(result), 1)
client.close()
daemon.kill(True)
2015-11-08 10:29:49 +00:00
@test_base.flaky
def test_9_external_config_update(self):
# Start an extension without a daemon, with a timeout.
2015-07-02 19:14:44 +00:00
extension = self._run_extension(timeout=EXTENSION_TIMEOUT)
self.assertTrue(extension.isAlive())
# Now start a daemon
daemon = self._run_daemon({
"disable_watchdog": True,
2015-07-02 19:14:44 +00:00
"extensions_timeout": EXTENSION_TIMEOUT,
"extensions_socket": extension.options["extensions_socket"],
})
self.assertTrue(daemon.isAlive())
# Get a python-based thrift client to the manager and extension.
2015-05-27 23:50:57 +00:00
client = test_base.EXClient(extension.options["extensions_socket"])
test_base.expectTrue(client.try_open)
2015-07-02 19:14:44 +00:00
self.assertTrue(client.open(timeout=EXTENSION_TIMEOUT))
em = client.getEM()
2015-07-02 19:14:44 +00:00
# Need the manager to request the extension's UUID.
2015-04-24 08:44:41 +00:00
result = test_base.expect(em.extensions, 1)
self.assertTrue(result is not None)
ex_uuid = result.keys()[0]
2015-05-27 23:50:57 +00:00
client2 = test_base.EXClient(extension.options["extensions_socket"],
uuid=ex_uuid)
test_base.expectTrue(client2.try_open)
2015-07-02 19:14:44 +00:00
self.assertTrue(client2.open(timeout=EXTENSION_TIMEOUT))
ex = client2.getEX()
# Trigger an async update from the extension.
request = {
"action": "update",
"source": "test",
"data": "{\"options\": {\"config_plugin\": \"update_test\"}}"}
ex.call("config", "example", request)
# The update call in the extension should filter to the core.
options = em.options()
self.assertTrue("config_plugin" in options.keys())
self.assertTrue(options["config_plugin"], "update_test")
# Cleanup thrift connections and subprocesses.
client2.close()
client.close()
extension.kill()
daemon.kill()
@test_base.flaky
def test_91_extensions_settings(self):
loader = test_base.Autoloader(
[test_base.ARGS.build + "/osquery/example_extension.ext"])
daemon = self._run_daemon({
"disable_watchdog": True,
"extensions_timeout": EXTENSION_TIMEOUT,
"extensions_autoload": loader.path,
})
self.assertTrue(daemon.isAlive())
# Get a python-based thrift client for the manager (core).
client = test_base.EXClient(daemon.options["extensions_socket"])
self.assertTrue(client.open(timeout=EXTENSION_TIMEOUT))
em = client.getEM()
# The waiting extension should have connected to the daemon.
# This expect statement will block with a short timeout.
result = test_base.expect(em.extensions, 1)
self.assertEqual(len(result), 1)
# The 'complex_example' table reports several columns.
# Each is a 'test_type', check each expected value.
result = em.query("select * from complex_example")
if len(result.response) == 0:
# There is a brief race between register and registry broadcast
# That fast external client fight when querying tables.
# Other config/logger plugins have wrappers to retry/wait.
time.sleep(0.5)
result = em.query("select * from complex_example")
self.assertEqual(result.response[0]['flag_test'], 'false')
self.assertEqual(result.response[0]['database_test'], '1')
client.close()
daemon.kill(True)
2015-02-19 23:19:00 +00:00
if __name__ == "__main__":
2015-04-24 08:44:41 +00:00
test_base.assertPermissions()
module = test_base.Tester()
2015-02-19 23:19:00 +00:00
# Find and import the thrift-generated python interface
2015-05-27 23:50:57 +00:00
test_base.loadThriftFromBuild(test_base.ARGS.build)
2015-02-19 23:19:00 +00:00
module.run()