osquery插件机制简介

栏目: 服务器 · 发布时间: 6年前

内容简介:osquery本身将系统的很多信息都封装成为了表,目前osuqery所有的表在官方文档osquery对于插件的编写也写了一个文档,osquery的插件采用

osquery本身将系统的很多信息都封装成为了表,目前osuqery所有的表在官方文档 schema 都进行了详细地说明,这些表的定义在源代码 specs 中也可以找到。osquery考虑到可能用户使用osquery对系统信息收集有特殊的需求,osquery提供了扩展的功能,供用户完成自定义的表的功能。

osquery对于插件的编写也写了一个文档, Extensions 。需要注意的是插件和osquery是通过 UNIX domain socket 进行通信,这也是下面讲到使用C++编写的插件和使用 Go 编写插件的区别。

插件说明

osquery的插件采用 Thrift API 与osqueryi或者osqueryd通信,一般情况下插件都是采用C++编写,当然也可以采用 PythonGo 或者是任何其他支持Thrift的语言。因为C++是Osquery开发的语言,所以更易与Osquery想结合。

如果采用C++编写插件,需要引入 <osquery/sdk.h> 。在这个SDK中,已经实现了osquery需要的库,包括 boost , thrift , glog , gflags , rocksdb .以下就是一个采用C++编写的简单的插件的例子。

// Note 1: Include the sdk.h helper.
#include <osquery/sdk.h>

using namespace osquery;

// Note 2: Define at least one plugin or table.
class ExampleTablePlugin : public TablePlugin {
 private:
  TableColumns columns() const override {
    return {
      std::make_tuple("example_text", TEXT_TYPE, ColumnOptions::DEFAULT),
      std::make_tuple("example_integer", INTEGER_TYPE, ColumnOptions::DEFAULT),
    };
  }

  QueryData generate(QueryContext& request) override {
    QueryData results;
    Row r;

    r["example_text"] = "example";
    r["example_integer"] = INTEGER(1);
    results.push_back(r);
    return results;
  }
};

// Note 3: Use REGISTER_EXTERNAL to define your plugin or table.
REGISTER_EXTERNAL(ExampleTablePlugin, "table", "example");

int main(int argc, char* argv[]) {
  // Note 4: Start logging, threads, etc.
  osquery::Initializer runner(argc, argv, ToolType::EXTENSION);

  // Note 5: Connect to osqueryi or osqueryd.
  auto status = startExtension("example", "0.0.1");
  if (!status.ok()) {
    LOG(ERROR) << status.getMessage();
    runner.requestShutdown(status.getCode());
  }

  // Finally, shutdown.
  runner.waitForShutdown();
  return 0;
}

按照代码中的注释,我们分析下这些代码的含义。

导入SDK

#include <osquery/sdk.h>

定义表

因为一个扩展可能就是一张表,那么在这个扩展中,我们至少需要定义一张表。此表需要继承 TablePlugin 这个表。分别是 columns()generate(QueryContext& request) 方法。

columns()
generate(QueryContext& request)

注册表

将第2步中的表注册到此扩展中。

REGISTER_EXTERNAL(ExampleTablePlugin, "table", "example");

扩展入口

扩展的入口是 main() 函数。

  1. 初始化扩展, osquery::Initializer runner(argc, argv, ToolType::EXTENSION);
  2. osqueryi 或者是 osqueryd 进行通信, auto status = startExtension("example", "0.0.1");

其实可以发现扩展的表结构的定义与osquery自带的表结构的定义完全是一样的,参见表结构.

Go插件

本文的重点是在于如何使用Go语言编写一个插件。在 osquery-go 有一个简单的例子,代码如下:

my_table_plugin.go

package main

import (
	"context"
	"log"
	"os"

	"github.com/kolide/osquery-go"
	"github.com/kolide/osquery-go/plugin/table"
)

func main() {
	if len(os.Args) != 2 {
		log.Fatalf(`Usage: %s SOCKET_PATH`, os.Args[0])
	}

	server, err := osquery.NewExtensionManagerServer("foobar", os.Args[1])
	if err != nil {
		log.Fatalf("Error creating extension: %s\n", err)
	}

	// Create and register a new table plugin with the server.
	// table.NewPlugin requires the table plugin name,
	// a slice of Columns and a Generate function.
	server.RegisterPlugin(table.NewPlugin("foobar", FoobarColumns(), FoobarGenerate))
	if err := server.Run(); err != nil {
		log.Fatalln(err)
	}
}

// FoobarColumns returns the columns that our table will return.
func FoobarColumns() []table.ColumnDefinition {
	return []table.ColumnDefinition{
		table.TextColumn("foo"),
		table.TextColumn("baz"),
	}
}

// FoobarGenerate will be called whenever the table is queried. It should return
// a full table scan.
func FoobarGenerate(ctx context.Context, queryContext table.QueryContext) ([]map[string]string, error) {
	return []map[string]string{
		{
			"foo": "bar",
			"baz": "baz",
		},
		{
			"foo": "bar",
			"baz": "baz",
		},
	}, nil
}

可以看到使用Go的用法大致和C++的用法类似,但是还是存在不同。在C++中,使用 osquery::Initializer runner(argc, argv, ToolType::EXTENSION); 初始化插件。在GO中使用 server, err := osquery.NewExtensionManagerServer("foobar", os.Args[1]) 初始化插件,但是Go语言编写的需要额外传入一个参数。文章后面通过 select value from osquery_flags where name = 'extensions_socket'; 这个语句得到 UNIX domain socket 。但是我们通过 osqueryd 来加载这个插件时,我们是不会在控制台输入的,那么这就意味着我们需要在Go代码固化 UNIX domain socket 这个值。如此,我们可以手动地指定 UNIX domain socket ,以此解决手需要手动输入的问题。我们将如下:

if len(os.Args) != 2 {
	log.Fatalf(`Usage: %s SOCKET_PATH`, os.Args[0])
}

server, err := osquery.NewExtensionManagerServer("foobar", os.Args[1])
if err != nil {
	log.Fatalf("Error creating extension: %s\n", err)
}

修改为:

server, err := osquery.NewExtensionManagerServer("foobar", "/var/osquery/osquery.em")
if err != nil {
	log.Fatalf("Error creating extension: %s\n", err)
}

与C++编写的插件不同,在这里注册表时,需要自己手动传入表定义的相关方法,即 FoobarColumns()FoobarGenerate .上述的代码的逻辑也非常的清晰,声明了一个名为 foobar 的表,列名分别是 foobaz ,并向表中插入了两条记录,都是 "baz": "baz" .

编译Go插件

go build -o my_table_plugin.ext my_table_plugin.go

编译得到 test.ext

运行Go插件

以普通用户尝试通过如下的方式加载插件

osqueryi --extension /path/to/test.ext

会出现 Error creating extension: waiting for unix socket to be available: /var/osquery/osquery.em: context deadline exceeded 的问题。

出现这个的原因是在于 osqueryi 无法找到 /var/osquery/osquery.em 。出现这个的原因是在于在之前的插件初始化中使用了 osquery.NewExtensionManagerServer("foobar", "/var/osquery/osquery.em") 来初始化,这个错误的意思就是无法找到 /var/osquery/osquery.em 。此时我们需要手动指定 osquery.em 的值。运行如下

osqueryi --extensions_socket=/var/osquery/osquery.em  --extension /path/to/test.ext

结果又出现了 init.cpp:654] Cannot start extension manager: Cannot create extension socket: /var/osquery/osquery.em 的错误。出现这个问题的原因是权限的问题。我们以root权限运行即可。

当我们以root权限运行时,又出现了如下的错误:

Extension binary has unsafe permissions: /path/to/test.ext

这个错误的提示也比较地明确,osquery提示我们运行此插件是不安全的,我们加上 --allow_unsafe=true 表示允许运行安全的插件。那么最终命令的命令就变为了:

osqueryi --allow_unsafe=true --extensions_socket=/var/osquery/osquery.em  --extension /path/to/test.ext

运行之后,就出现了 I0317 10:39:16.987525 15839 interface.cpp:105] Registering extension (foobar, 62760, version=, sdk=) 。这个就表示插件加载成功,表名是 foobar

osquery> select * from foobar;
+-----+-----+
| foo | baz |
+-----+-----+
| bar | baz |
| bar | baz |
+-----+-----+

成功地显示出我们在代码中插入的两条记录。

注意

看起来利用osquery的扩展能够帮助我们完成很多的功能,但是这其中还是有很多需要注意的地方。

osquery插件加载

限于osquery自身插件的设计原理,导致osquery在加载大量的插件时会出现严重的性能问题。比较好的做法是在一个插件中一次性尽可能地多加载一些插件。

osquery插件的权限问题

根据前面的分析可知,osquery作为一个HIDS的agent,在运行时需要以root权限运行,同时如果需要加载插件也必须以root权限运行,这样就会存在潜在的风险。如果插件一直一root权限运行同时还需要读取系统中的文件信息,如果攻击者篡改了此文件的内容,就有可能通过osquery的插件获取root权限,这样就会危及到主机的安全。所以编写插件非常重要的一点就是当插件初始化成功之后,里面要将其从root权限降级为nobody用户。

当然更多的插件的问题需要自己在实际编写过程中才能够进一步地发现,而本文也仅仅是对osquery的插件编写进行了一个简单的介绍。

有关更多osquery的插件,可以去看这个repo, Trail of Bits osquery Extensions 。这个上面有针对不同平台不同功能的插件,如果有兴趣可以去看看

总结

osquery的插件机制能够很方便地为我们提供一个扩展osquery自身功能的方法来完成我们自定义并且osquery目前还没有的功能。一个良好的扩展机制也是作为一个HIDS的agent的必备的特性。当然osquery还有一些其他的特性等待我们进一步挖掘。


以上所述就是小编给大家介绍的《osquery插件机制简介》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Rework

Rework

Jason Fried、David Heinemeier Hansson / Crown Business / 2010-3-9 / USD 22.00

"Jason Fried and David Hansson follow their own advice in REWORK, laying bare the surprising philosophies at the core of 37signals' success and inspiring us to put them into practice. There's no jarg......一起来看看 《Rework》 这本书的介绍吧!

HTML 压缩/解压工具
HTML 压缩/解压工具

在线压缩/解压 HTML 代码

正则表达式在线测试
正则表达式在线测试

正则表达式在线测试

HEX CMYK 转换工具
HEX CMYK 转换工具

HEX CMYK 互转工具