Chisels
Sysdig 的 chisels 是通过分析sysdig 事件数据流去完成特定动作的小脚本。在Sysdig中,系统事件events 被高效地呈现在了用户空间,所以脚本能够直接被应用到处理事件上。好处如下:
- 能使用普遍被使用的而不是定制的语言。sysdig的chisels 使用 Lua 脚本语言。
- Chisels 能很方便地使用众多的Lua库
- Chisels 在实时系统上工作情况良好,但也能和捕获的文件一起来完成离线的分析工作。
使用
- 列出可所有使用的chisels
$ sysdig -cl
- 输出某个chisel 的详细描述和参数列表,使用 -i 命令行选项
$ sysdig -i spy_ip
- 运行某个chisel 工具,使用 -c 选项
$ sysdig -c topfiles_bytes
- chisel工具需要参数时,直接在后面补充
$ sysdig -c spy_ip 192.168.1.157
- 能够同时运行多个chisel工具
$ sysdig -c stding -c stdout proc.name=cat
- chisels 可以和过滤器filters 结合使用
# 不需要对 /dev 的访问
$ sysdig -c topfiles_bytes "not fd.name contains /dev"
# 只包含 /root 路径的访问
$ sysdig -c topfiles_bytes "fd.name contains /root"
# 进程 vi 访问的文件
$ sysdig -c topfiles_bytes "proc.name=vi"
# 用户 loris 访问的文件
$ sysdig -c topfiles_bytes "user.name=loris"
注意:
使用filters时,我们需要注意它是作为sysdig的还是chisels的参数传入
$ sysdig -i lsof
...
Args:
[filter] filter - A sysdig-like filter expression that allows r
estricting the FD list. E.g. 'proc.name=foo and fd.name contain
s /etc'.
当作为chisels参数传入时,如上面的lsof,当有多个筛选条件,如sysdig -c lsof "'user.name=phil or proc.name=sshd'",这里是需要双重引号的。
当作为sysdig参数传入,当有多个筛选条件,如:sysdig "fd.rip=54.165.81.189 and fd.port=6666"
,这时候是可加双引号的
更多规则使用可参考https://sysdig.com/blog/linux-troubleshooting-cheatsheet/#lsof
开发自定义Chisel
Sysdig chisels 使用Lua 语言编写,如果不熟悉,可以参考Programming in Lu 和Lua reference manual
Chisels 脚本结构
--[[
Lua 脚本如果要想被sysdig 识别,必须要定义以下几个全局变量
description
short_description
category
args:包含参数的列表,可为空
以上这些参数都会被sysdig识别并添加在chisels信息中(使用 sysdig -cl / sysdig -i chisels_name)
--]]
-- Chisel description
description = "counts how many times the specified system call has been called"
short_description = "syscall count"
category = "misc"
-- Chisel argument list
args =
{
{
name = "syscall_name",
description = "the name of the system call to count",
argtype = "string"
},
}
--[[
当需要给chisels传递参数时,用参数信息补充arg 表和增加 on_set_arg()函数。sysdig收集到命令行参数时,接着传入并调用 on_set_arg函数。当有多个参数时,可使用不同的name来区分开来。
--]]
-- Argument notification callback
function on_set_arg(name, val)
syscallname = val
return true
end
--[[
chisel 初始化函数,在收到第一个事件前执行。
以下代码使用了chisel.request_field去提取需要的ftype和fdir字段的地址,方便后面根据这两个字段去筛选事件
--]]
-- Initialization callback
function on_init()
-- Request the fields that we need
-- 以下变量为userdata类型,猜测存储了c/c++中指针类型数据
ftype = chisel.request_field("evt.type")
fdir = chisel.request_field("evt.dir")
-- set the filter
-- 通过chisels使用sysdig引擎来优化筛选事件
chisel.set_filter("evt.type=" .. syscallname .. " and evt.dir = >")
--[[
可以通过print()自定义事件通知,可以使用以下定义通知格式
""相当于"default"
chisel.set_event_formatter("")
--]]
return true
end
count = 0
-- Event parsing callback
-- 每当一个事件到来都会触发该函数的执行
function on_event()
count = count + 1
return true
end
--[[
捕获结束时的动作,一般用于通知操作
结束的情况:
离线捕获中收到最后一个数据包
实时捕获中按下CTRL+C
--]]
-- End of capture callback
function on_capture_end()
print(syscallname .. " has been called " .. count .. " times")
return true
end
Chisels API
Chisels 通过以下三种独立的接口与sysdig 交流
- Callbacks 回调函数的集合
- sysdig library 与主程交互的函数库
- evt library 用来从捕获的事件中提取信息
强制的变量
为了能被识别为Chisels,Lua 脚本中必须包含以下全局变量:
- description: 对该chisel 详细的描述 字符串类型
- short_description: 简略描述,字符串类型
- category: chisel 所属的目录,如 IO,net,security,字符串类型
- args: 描述每一个chisel参数,table表格类型
args =
{
{
name = "name1",
description = "description1",
argtype = "ipv4"
optional = false
},
{
name = "name2",
description = "description2",
argtype = "string"
optional = true
},
}
注意:
- args 中 optional设为"false",sysdig 命令行的chisel 名字后面必须指定这个参数,否则执行失败
- args 中的每一个参数都会生成一个对on_set_arg() 的调用
- args 可为空
- 参数列表中有不止一个参数时,可用 "" 概括。如 sysdig -c chisel_name "arg1 arg2 arg3"
Callbacks
Callbacks 是sysdig 用来通知chisel 事情发生时的方式。除了on_interval() 需要使用sysdig.set_interval_s() 或者 sysdig.set_interval_ns()注册,大多数callbacks 不需要注册。Callbacks 是可选的,不需要则不用加上。
on_set_arg(name, val)
args 表中的每一个参数都会调用该函数。name 是参数设置的名字,val 是用户在命令行给出的值。
参数无效时会返回false并导致sysdig退出。
on_init()
在所有参数的on_set_arg()都调用后但capture被配置前被调用。通常,用来初始化chisel工具。
chisel 初始化失败返回false并导致sysdig退出
on_capture_start()
在capture配置后、on_set_arg被调用后,但在事件的数据包被捕获之前调用。能在需要询问capture状态时的最终初始化chisel步骤中使用
chisel 初始化失败返回false并导致sysdig退出
on_event()
在没有设置set_filter() 或者通过set_filter()筛选后,每个被捕获的事件到达都会激活该函数。这个函数常用来写核心的chisel逻辑,需要保证代码是高效可用的。在这个回调函数中,开发者可以对evt library访问,从而从正在处理的事件中提取信息。
在指定了格式工具(set_event_formatter)的情况下,返回false会让工具忽略该次事件。
on_capture_end(ts_s,ts_ns,delta)
捕获结束时的动作,一般用于通知操作。结束的情况如下:
- 离线捕获中收到最后一个数据包
- 实时捕获中按下CTRL+C
参数含义如下: - ts_s,ts_ns: 最后收到事件的时间戳的秒(以秒的单位表示当前时间)和纳秒部分
- delta:捕获的第一个包到最后一个包的间隔时间,纳秒表示
on_interval(ts_s,ts_ns,delta)
周期性的超时的回调函数。可以用来以一定频率如每秒一次报告信息。使用chisel.set_interval_s() 或者 chisel.set_interval_ns()配置。参数含义如下:
- ts_s, ts_ns:函数被调用的时间,秒+纳秒表示
- delta:被调用的间隔时间
sysdig library
这个库里面的函数可以用来设置全局的sysdig配置
sysdig.end_capture()
这个调用会让sysdig停止接受事件,然后‘makes it initiate the end of capture cleanup’(不太理解),感觉就是准备结束sysdig,开始清理捕获动作的相关环境。
sysdig.get_machine_info()
返回一个带有产生事件的机器的信息的表(table类型),表有以下字段:
- num_cpus: 机器上的CPU核心的编号
- memory_size_bytes:机器RAM的大小
- max_pid:机器允许的最大PID数值
- hostname:机器主机名
注意:这个调用对文件捕获也同样有效,因为机器信息存储在跟踪文件(sysdig -w output.scap)上。这种情况下,返回的机器信息是捕获发生的其中一个机器。
sysdig.get_filter()
返回在命令行传递给sysdig的包含filter的字符串
sysdig.get_evtsource_name()
返回包含事件源名字的字符串。跟踪文件名(如果事件来源于一份跟踪文件)或者""(如果是实时捕获)
sysdig.get_output_format()
返回用户在命令行规定的输出格式,返回值可以是"normal"或者"json"
sysdig.is_print()_container_data
返回Boolean类型,表示是否使用了-pc 或者 -pcontainer 选项。
sysdig.get_container_table(filter)
返回sysdig container table
filter可以选择性地作为输入参数传递,用来限制返回的元素。语法与常规sysdig filters相同,但仅限于fd,proc,user和group filter类型。
返回以下值:
- id:容器id,唯一标识符
- name:容器名字
- images:容器镜像
- type:容器类型(docker,lxc,& libvirt_lxc)
注意:
container.id==host 且/或者 container.name == host 表示是主机而不是容器
sysdig.get_thread_table(filter)
返回sysdig process table。filter使用与get_container_table 相同。
返回值过多,建议参考官网资料。
sysdig.is_tty()
返回是否sysdig运行在有ANSI功能的交互终端,能被chisel用来决定是否有可能在屏幕上显示颜色
sysdig.islive()
返回当前的捕获是否实时的,即事件是来自系统还是个跟踪文件
sysdig.run_sysdig(args)
从目前执行的sysdig退出,然后再启动一个新的,参数由args传递。chisel 能用它来运行另一个不同的捕获或者再一次启动sysdig去完成不同的任务(如 保存特定事件到文件中)
sysdig.set_fatfile_dump_mode(mode)
开启/禁用 fatfile 模式。mode 参数是个Boolean值。
sysdig.set_filter(filter)
在解析事件前,将filter过滤器配置到sysdig引擎中。和在命令行中设置filter的作用是一样的,且会覆盖命令行的参数。
sysdig.set_snaplen(snaplen)
设置I/O类型的系统调用(如 read,write,sendto,recvform)的buffers的字节数。
chisel library
这个库里面的函数与设置chisel环境最相关,经常在初始化的时候被调用,例如 on_init()
chisel.exec(chiselname, arg1, arg2, ...)
这个函数能被用来停止正在调用的chisel的执行,转而去运行一个不同的chisel
chisel.request_field(fld_name)
这个函数用来在事件被捕获的时候提取一个filter字段。fld_name 是将要去提取的sysdig filter 字段(sysdig 教程或者sysdig -l 可看所有可用的字段)。函数返回字段的句柄,该句柄能用在 evt.field() 以在on_event()回调函数中获得字段的值。
chisel.set_filter(filter)
设置sysdig 引擎,在chisel的on_event()调用处理事件前使用给出的filter过滤事件。
注意:
- set_filter 设置的filter 只对当前的chisel有效
- 每个chisel 只能设置一个,调用两次将会导致第一次被覆盖
- 应该尽可能地使用filters。sysdig 引擎做了大量优化,所以尽可能地减少到达chisel 的on_event()前的事件能使chisels更高效
chisel.set_event_formatter(format)
配置事件的格式工具。format 变量是一个包含将要打印的字段列表的字符串,符号使用和命令行选项 -p 一致。
默认情况下,chisel 没有事件格式工具,它们可以使用Lua 的print() 函数任意输出。设置了格式工具,可以利用sysdig 的过滤字段系统(filter fields system)去替你格式化地输出信息。"default"或"", sysdig 添加它的默认格式到chisel。on_event() 返回true 的时候才会打印信息,且chisel 的输出不会被命令行-p 选项覆盖。
chisel.set_interval_s(interval)
为当前chisel 设置一个周期性的调用。使用这个函数,需要chisel 包含on_interval()函数,sysdig 引擎会以interval 秒的间隔循环调用
chisel.set_interval_ns(interval)
和set_interval_s 类似,不过更精细的时间设置
evt library
这个库的函数有关于正在被处理的事件,所以只能被 on_event() 函数调用
evt.get_cpuid()
返回捕获到事件的CPU的编号
evt.field(fld)
从事件中提取特定特定字段的值。参数fld是从上一个requet_field()调用中获得的字段句柄。函数返回字段的值,可能是个数字或者字符串。sysdig -lv 命令可以查看每个导出字段的类型。
注意:
- 如果请求的字段没有被事件导出(例如非I/O事件不会导出fd.name 字段),返回值为 nil (Lua 中表示只有一个值的一种类型)
- evt.rawarg 字段需要注意。它能用来提取任意系统调用的参数,如 evt.rawarg.res,所以它的类型取决于你询问的字段
evt.get_num()
返回递增的事件编号
evt.get_ts()
返回两个分别代表事件原始时间戳的秒和纳秒部分的时间戳
evt.get_type()
以数字形式返回事件类型。能用来事件过滤。事件类型编码可以在driver/ppm_event_events_public.h的ppm_event_type 枚举中查看
参考资料:
https://github.com/draios/sysdig/wiki/Chisels-User-Guide
https://github.com/draios/sysdig/wiki/Writing-a-Sysdig-Chisel%2C-a-Tutorial
https://github.com/draios/sysdig/wiki/Sysdig-Chisel-API-Reference-Manual