备注:本博客编写时,系统本身已经完成,本博客为补档内容。

书接上回

前面咱们搞完了搜索系统,里面涉及到了企业版中的 seafevents。我们知道,seafevents 就是用于处理各种定时任务的,其中就包括了病毒扫描。然后我们还在 seafevents 的包里面发现了病毒扫描的相关代码,那么病毒扫描这个需求应该是比较简单的。

查找资料

在网上查询资料后,我们发现一份针对 ClamAV 开源杀毒软件的教程:

在这里插入图片描述

解决前端问题

这个有了前面的经验以后就非常简单了。我们找到管理界面对应的代码文件:frontend/src/pages/sys-admin/side-panel.js,并找到对应片段:

{isPro && otherPermission &&
  <li className="nav-item">
    <Link
      className={`nav-link ellipsis ${this.getActiveClass('virus-files')}`}
      to={siteRoot + 'sys/virus-files/all/'}
      onClick={() => this.props.tabItemClick('virus-files')}
    >
      <span className="sf2-icon-security" aria-hidden="true"></span>
      <span className="nav-text">{gettext('Virus Scan')}</span>
    </Link>
  </li>
}

去掉 isPro 限制就好。

然后我们需要修改与病毒扫描记录相关的三个接口(定义在 urls.py 中):

# admin::virus-files
url(r'^api/v2.1/admin/virus-files/$', AdminVirusFilesView.as_view(), name='api-v2.1-admin-virus-files'),
url(r'^api/v2.1/admin/virus-files/(?P<virus_id>\d+)/$', AdminVirusFileView.as_view(), name='api-v2.1-admin-virus-file'),
url(r'^api/v2.1/admin/virus-files/batch/$', AdminVirusFilesBatchView.as_view(), name='api-v2.1-admin-virus-files-batch'),

进去以后,发现每个接口前面都有一个 IsProVersion 限制:

permission_classes = (IsAdminUser, IsProVersion)

删掉,否则的话会爆 403 错误(别问我怎么知道的)。

数据库问题

数据库的话,前面我们找到一个 SQL 文件,里面就包含了我们需要的两个表格的结构定义:VirusFileVirusScanRecord。不过,VirusFile 的定义有问题,需要改成如下:

CREATE TABLE `VirusFile` (
  `vid` int(11) NOT NULL AUTO_INCREMENT,
  `repo_id` varchar(36) NOT NULL,
  `commit_id` varchar(40) NOT NULL,
  `file_path` text NOT NULL,
  `has_deleted` tinyint(1) NOT NULL,
  `has_ignored` tinyint(1) NOT NULL,
  PRIMARY KEY (`vid`),
  KEY `ix_VirusFile_repo_id` (`repo_id`),
  CONSTRAINT `CONSTRAINT_1` CHECK ((`has_deleted` in (0,1)) AND (`has_ignored` in (0,1)))
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

Again,这个是实验后得到的结果。不知道为什么,明明在 seahub 和 seafevents 的代码操作中都是按照 has_deletedhas_ignored 来设计的,但是在 SQL 文件中却是 has_handled

ClamAV 问题

我们的系统打算使用 ClamAV 作为后端的查杀引擎,根据文档,用 clamscan 命令就行。但是在我进行安装的时候出现了各种问题。

首先就是病毒数据库问题,这个真的太阴间了,校园网的速度不快不慢,刚好会在它下载完成的时候被判超时,然后重连,然后再超时。

更阴间的是,它的网络在一定时间内有下载限制,我就被判恶意下载导致 5 小时内无法下载。

更更阴间的是,我打算找手动下载方式,但是。。。跟!本!没!有!!!只能用官方的 freshclam 来下载!!!

更更更阴间的是,当我完成了 5 小时惩罚后,我再次尝试,它。。。立刻又封了我 5 小时!!!!!!!!!!啊啊啊啊啊啊啊啊啊啊!!!!!!

更更更更阴间的是,在同一个网络内用其他设备进行病毒库更新,它触发了 cloudflare 的限制!!!这跟被 5 小时惩罚没有任何区别!!!!!!

这一套阴间操作把我整的服服帖帖的,但是这还没完。

当我历尽千辛万苦终于完成病毒库更新以后,我尝试把程序开起来。但是!总是有文件失败!!!导致整个库的更新都无法完成!这个后面再说!但是现在我要搞清楚为什么查杀病毒还能失败的???

经过对 Virus Scan 部分代码的研究,我确定了它的流程:

  1. 列出文件库当前版本的所有文件
  2. 列出上一次查杀版本的所有文件
  3. 找出需要查杀的新文件
  4. 调用 seafile 存储系统接口,将文件从存储块写出到临时文件
  5. 调用 ClamAV 命令对临时文件进行查杀
  6. 根据 ClamAV 的返回值判断是否为病毒文件
  7. 将查杀到的病毒文件记录插入到 VirusFile 数据表中
  8. 将本次杀毒记录和对应的文件库版本更新到 VirusScanRecord 数据表中

那么问题就在于,调用 ClamAV 命令对临时文件进行查杀这一步失败了,一开始我们并不知道为什么错了。于是我加了 ClamAV 系统的返回值输出:

logger.debug(u'File %s virus scan by %s: Failed, error code is %d.', fpath.encode('utf-8'), self.settings.scan_cmd, ret)

然后输出,结果是:

[05/30/2021 20:46:21] [DEBUG] File b'/hhh/testin.docx' virus scan by clamscan: Failed, error code is -9.

返回值 -9。然后我到网上去找资料。。。我找了外网各种资料,然后仔细看了官方给的每一份文档,完!全!没!有!任何!资料!!!!!!大家只知道返回值 0 是无病毒,1 是有病毒,其他的都是异常,但是为什么异常完全没有说,文档里对程序的返回值完!全!没!有!任!何!描!述!!!!!!这全靠猜啊!

然后我决定手动运行一下 clamscan,我看看它到底输出什么。由前面我们知道,他是从 SeafileFS 中读出文件内容,然后扔到一个临时文件中。那么,我们添加一个 debug 输出,输出 temp 文件的路径:

logger.debug("Created temp file '%s' for file '%s'", tpath, file_path)

运行了 pro.py virus_scan 命令后,我们看到他创建了三个临时文件:

[05/30/2021 20:47:21] [DEBUG] Created temp file '/tmp/tmpr96dmaex' for file '/a.txt'
[05/30/2021 20:47:21] [DEBUG] Created temp file '/tmp/tmpry8j6qze' for file '/test.docx'
[05/30/2021 20:47:21] [DEBUG] Created temp file '/tmp/tmpo8s6xxvj' for file '/NewFile.txt'

然后我们就选择 '/a.txt 啦,把 /tmp/tmpr96dmaex 复制到一个其他地方,然后运行 clamscan tmpr96dmaex

root@ad7699681c76:~# clamscan tmpr96dmaex
Killed

我破防了。这又是跟 Elastic Search 一模一样的问题。

大哥啊!人家 Elastic Search 要内存多一点就算了,我能理解,但你扫描个 15 KB 大小的文本文件,你。。。你要多少内存啊???????而且 20 MB 的文件你都扫描成功了,为什么?为什么就这个不行??????导致我甚至一度认为是从 SeafileFS 读取内容这步出了错。。。

于是我用 scp 命令将文件传到了生产服务器上,再次运行相同的命令,相同的文件,终于,成功了。

调试过程

还记得前面说数据库要改吗?你知道我是怎么知道的吗?

我在了解到 ClamAV 的问题以后,调整了一个思路:本地调试先不调用 ClamAV 了,直接按照文件名判断。我设定了一个叫 wcry.exe 的文件名,只要包含这个名字的路径都按病毒计算。然后运行时爆了错:

[05/30/2021 22:44:23] [WARNING] Failed to add virus records to db: (pymysql.err.OperationalError) (1054, "Unknown column 'has_deleted' in 'field list'")
[SQL: INSERT INTO `VirusFile` (repo_id, commit_id, file_path, has_deleted, has_ignored) VALUES (%(repo_id)s, %(commit_id)s, %(file_path)s, %(has_deleted)s, %(has_ignored)s)]
[parameters: {'repo_id': 'e0ebda1f-0fc2-4ed9-872b-f9a7d0b75ed6', 'commit_id': 'a1f6d48f79b2020b6e1942d197fb3f5ad8ae4951', 'file_path': '/23333/wcry.exe', 'has_deleted': 0, 'has_ignored': 0}]

没有 has_deleted 键。当我手动打开数据库,并查看表格结构以后,我发现:

MariaDB [seahub]> desc VirusFile;
+------------+-------------+------+-----+---------+----------------+
| Field      | Type        | Null | Key | Default | Extra          |
+------------+-------------+------+-----+---------+----------------+
| vid        | int(11)     | NO   | PRI | NULL    | auto_increment |
| repo_id    | varchar(36) | NO   | MUL | NULL    |                |
| commit_id  | varchar(40) | NO   |     | NULL    |                |
| file_path   | text        | NO   |     | NULL    |                |
| has_handle | tinyint(1)  | NO   |     | NULL    |                |
+------------+-------------+------+-----+---------+----------------+
5 rows in set (0.02 sec)

所以 Seafile 你们的版本控制。。。

最开始我以为这个 has_handle 是新版,然后我把代码里的数据操作都改成了 has_handle ,然后 seahub 的前端界面又炸了。。。

然后我又非常苦逼的把先前改的东西改了回去,然后 drop 掉了旧的数据库,换上了新的。

终于,在历经千难万险以后,终于看到了:

在这里插入图片描述

啊,这个原本是可以选择删除还是忽略的,我这个是已经选择了删除。总之,东西肯定是没问题啦!!!

不过我发现有一些汉化没做好,然后顺手改了一下:

在这里插入图片描述

强迫症满足了,病毒扫描系统开发完毕。

Logo

腾讯云面向开发者汇聚海量精品云计算使用和开发经验,营造开放的云计算技术生态圈。

更多推荐