0x01 前言

我是用Cachet系统对外展示我系统的一些信息与状态,之前写过一篇文章,通过python脚本更新它的metrics数据,展示效果如下:

以下是相关文章:

近期有位群友想实现Cachet与zabbix之间的互动,获取zabbix中的告警信息并更新Cachet中的系统状态与添加事件记录。

我原本有个小脚本,但实现起来非常繁琐,所以我重新整理逻辑并花一天事件编写一个新的脚本实现以上需求。虽然实现了基本需求,但依旧繁琐且事件记录过于详细,容易泄露敏感数据;另外逻辑也有待优化。

0x02 思路

我利用zabbix的告警提醒功能调用外部的脚本,并向该脚本发送定制的告警内容。然后脚本自动处理告警信息,最后更新Cachet中的数据。

但在Cachet中的部件名称不一定为zabbix中的设备名,另外因为Cachet中的事件记录只支持获取全部或通过事件id进行获取,结合这两个原因,还需要建立一个临时文件,用于存储zabbix hostname、zabbix事件id与Cachet事件id的对应关系。

目前脚本仅支持固定得事件记录内容,而且当zabbix异常恢复后会更新事件记录得内容,但这会将异常信息删掉。在实际应用中这并不是很好的操作,所以计划在下一个版本修改为可自定义的格式并保留告警信息。

因为有可能会出现单一zabbix监控目标出现多个告警情况,所以还得对Cachet临时文件做出增删查的基本功能,这部分异常复杂,下个版本也需要更新。

0x03 部署

首先需要准备配置文件,配置文件名为“cachethq_status_updater_conf.json”,内容如下:

config_main中记录着Cachet的API地址与API key,这部分内容请留意以下文章:

最重要的是host中的字典,字典中的key为zabbix中的hostname,如果在zabbix host设置中为监控点设置了“Visible name”,则key应为“Visible name”的值:

而value是Cachet中部件所对应的的id:

0x03.1 zabbix host groups

然后还需要给zabbix配置告警动作,首先到configuration –> host groups中添加一个名为cachethq_status的主机组:

0x03.2 zabbix Media types

紧接着新建Media types:

相关内容如下:

Options标签中的内容保持默认即可。

0x03.3 zabbix actions

最后到configuration –> actions中添加一个新的action:

在action标签中配置一个动作名称,并在condition中选择主机组等于cachethq_status,选择完成后记住单击“Add”才算完成condition的添加;勾选Enable,然后单击Operations标签:

配置内容如下:

其中”Default operation step duration”请添加尽可能大的数,因为我们不需要重复提醒;”Operation details”中的”Steps”请填写1-1,只通知一次即可;”“选择上面建立的Media type即可。

Operations标签中”Default subject”与”Default message”的内容如下:

请勿更改以上内容与格式。

Recovery operations标签中的配置如下:

“Operation details”中的”“选择上面建立的Media type即可。

相关内容如下:

0x03.4 zabbix 脚本

clone源码之前需要修改zabbix server的配置文件:

AlertScriptsPath的值为一个目录,其中的脚本的所有这需要修改为zabbix的用户与用户组并赋予可执行权限,具体路径请根据实际情况进行选择。

完成上面的配置后即可从我的gitlab中clone源码到本地并将以下文件放置到AlertScriptsPath的目录中:

0x03.5 测试

完成上述操作后需要重启zabbix server,随后即可通过调整zabbix中的阈值进行测试。最简单的测试方式是停止监控点的zabbix-agent,待出现告警后,zabbix server会自动调用脚本,更新Cachet中的数据:

事件记录如下:

重新启动zabbix agent后即可恢复正常。

0x04 源码

第一行需要定义python3的路径,紧接着接受传入数据,然后读取配置文件,最后是定义临时文件路径:

针对传入的内容,转换为字典格式:

传入内容如下:

转换后的内容如下:

以下是创建事件记录并修改部件状态的函数:

以下是修改事件记录并修改部件状态的函数:

创建与修改都需要通过”X-Cachet-Token”这个header传递api token,这两部分的API可以通过以下地址找到相关信息:

接下来是整个脚本逻辑最混乱的一个函数:

以上函数主要实现对临时文件的增删查功能,首先通过try尝试读取临时文件,如果文件不存在则创建文件并填入一个空字典,以免首次运行脚本时报错;如果文件存在则读取并格式化为字典。

接下来的逻辑如下:

  • “act_type”为”r”:查询模式,尝试读取”event_id”的value,如果值不存在的返还False,说明该值不存在,需要建立。我的设想是只有在zabbix出现告警时才会使用查询模式,但后来发现单一监控点可能会出现多个告警,这时候监控点告警恢复时也需要调用该模式。所以增加了event_count值在event_id存在时一起回传;
  • “act_type”为”d”:删除模式,当zabbix异常恢复后,从字典中删除对应的key。我的设想是异常恢复肯定在异常恢复之后,所以相关的event_id肯定在字典中,此时只需要删除即可,不需要其他操作;
  • “act_type”为”w”:写入模式,我的设想是只有在zabbix出现异常时才会写入,所以这里会有两种情况:
    • 监控点从未出现过:此时需要建立一个新的监控点字典并与现有的字典合并;
    • 监控点已出现过:此时只需要增加event_id及其对应的value即可。

最后的pass只是想让逻辑更完整而已,没啥用。

最后是run函数:

这部分内容也是一团糟,只是把功能实现而已。

首先判断传入的内容是发生异常还是异常恢复,然后根据内容的不容组合事件记录的内容,最后分别调用事件记录的创建与更新函数。

这部分有个明显的缺陷:内容过于详细,极其容易造成信息泄露。

0x05 结语

脚本尚处于dev阶段,目前可以通过以下地址找到完整的脚本:

因为这个脚本是被动的脚本,和更新Metrics的脚本有本质上的区别,所以两者不会合并。

这脚本是在半天内临时编写的,相关功能与逻辑尚不完善,开发工作会在接下来的一个月继续,有需要的朋友可持续关注我的gitlab。还可以通过以下地址查看我私有服务的状态: