☰
Faraday 3.17.0 更新解读:`faraday-manage settings --data` 非交互式配置与报告异步化处理机制
2026/10/15 18:36:22 网站建设 项目流程
  • 网络安全
  • 渗透测试
  • 漏洞扫描

【免费下载链接】faraday

Open-source and AI-powered cybersecurity tools for offensive security, vulnerability management, and autonomous pentesting. Built by hackers in Latin America, used worldwide.

项目地址:https://gitcode.com/gh_mirrors/far/faraday
点击查看免费下载

本文以 Faraday 开源仓库 CHANGELOG/3.17.0/community.md(发布于 2021 年 8 月 10 日,见 CHANGELOG/3.17.0/date.md)为骨架,逐条拆解该版本的三项核心变更:faraday-manage settings新增--data参数、报告文件改为在独立进程中处理、bulk_create请求改为异步执行。读完本文,你将掌握 Faraday 配置的"非交互式批量更新"命令行用法,理解报告导入从"请求内同步解析"到"落盘 + 队列/Celery 异步消费"的演进原理,并能结合源码定位REPORTS_QUEUE、process_report_task、BulkCreateView等关键实现。

版本背景:三项变更解决什么问题

3.17.0 的三条 changelog 可以归纳为一个共同主题——把耗时、易阻塞的操作从请求链路中剥离出去,并把运维操作脚本化:

变更类型解决的问题
ADD--dataparameter tofaraday-manage settings功能新增此前faraday-manage settings update只能交互式逐项输入,无法在脚本、自动化流水线中调用
MOD Process report files in a separate process架构调整报告解析(插件识别、数据清洗)不再占用 Web 请求进程,避免大报告拖垮 API 响应
MOD Makebulk_createrequests asynchronous架构调整bulk 数据写入改为先落盘再异步消费,请求立即返回,避免长事务阻塞

三者相互衔接:bulk_create之所以能异步化,正是因为报告文件处理已经被拆到了独立的进程/任务中——请求只负责"把 JSON 写成临时文件并投递",真正的解析与入库交给后台队列完成。

一、faraday-manage settings --data:把配置更新写进脚本

1.1 命令形态与 JSON 校验

在 faraday/manage.py 中,settings命令定义如下:

@click.command(help="Manage settings") @click.option('-a', '--action', type=click.Choice(['show', 'update', 'list', 'clear'], case_sensitive=False), default='list', show_default=True, help="Action") @click.option('--data', type=str, required=False, callback=manage_settings.settings_format_validation, help="Settings config in json") @click.argument('name', required=False) def settings(action, data, name): app = get_app(register_extensions_flag=False) with app.app_context(): manage_settings.manage(action.lower(), data, name)

--data的取值会先经过 faraday/server/commands/manage_settings.py 中的settings_format_validation校验:

def settings_format_validation(ctx, param, value): if value is not None: try: json_data = json.loads(value) except json.JSONDecodeError as e: raise click.BadParameter("Data must be in json") from e else: return json_data

即:传入的不是合法 JSON 时,CLI 直接报错Data must be in json;合法 JSON 会被解析为 dict 传入manage()。

1.2 四种 action 的行为差异

manage()内部(manage_settings.py)对action的处理:

  • list(默认):列出当前版本所有已注册的 settings 名称。注册来源见 faraday/settings/init.py 的load_settings(),依次加载smtp、dashboard、reports、elk、query_limits五类设置。
  • show <name>:逐 key 打印该设置的当前值。
  • update <name>:核心更新逻辑,见下文。
  • clear <name>:删除该设置在数据库中的记录,恢复默认值(调用Settings.delete_configuration(),见 faraday/settings/base.py)。

注意:3.17.0 之后(如 3.19.0)又补充了clear命令,但show/update/list的基础交互在 3.17.0 已就位,本次--data补上了update的非交互路径。

1.3update的两种模式:交互式 vs--data

以update reports为例,该设置只有custom_plugins_folder一个字段(见 faraday/settings/reports.py):

模式一:交互式(不传--data)

faraday-manage settings -a update reports # 逐个字段 click.prompt 输入,最后要求 confirm

模式二:非交互式(传入--data,3.17.0 新增)

faraday-manage settings -a update reports --data '{"custom_plugins_folder": "/opt/faraday/plugins"}'

manage()中--data生效的关键逻辑:

for key, value in settings.value.items(): if json_data is not None: json_value = json_data.get(key, None) if type(json_value) is not type(value) or json_value is None: click.secho(f"Missing or Invalid value for {key} [{json_value}]", fg="red") sys.exit(1) new_value = json_value else: new_value = click.prompt(f'{key}', default=value) new_settings[key] = new_value

两个实战要点:

  1. --data必须覆盖该设置的全部字段,缺失任何 key 都会以Missing or Invalid value for <key>报错退出(sys.exit(1)),不会出现"部分更新"的模糊状态;
  2. 值类型必须与现有配置一致(type(json_value) is not type(value)即报错),防止把字符串写进布尔字段等低级事故。

随后执行settings.validate_configuration(new_settings),例如 reports 设置会校验custom_plugins_folder指向的目录必须真实存在(reports.py),校验失败抛InvalidConfigurationError并退出。

1.4 持久化:配置存在哪?

Faraday 3.16.0 起把设置从server.ini迁移到数据库的Configuration表(对应迁移文件 faraday/migrations/versions/b49d8efbd0c2_add_configuration_table.py)。update时通过get_or_create按settings.settings_key(如reports_settings)写入:

saved_config, created = get_or_create(db.session, Configuration, key=settings.settings_key) if created: saved_config.value = settings.update_configuration(new_settings) else: # SQLAlchemy 无法感知 JSON 字段的就地修改,必须 deepcopy 后整体替换 saved_config.value = settings.update_configuration(new_settings, saved_config.value) db.session.commit()

由于 SQLAlchemy 检测不到 JSON 列的就地变更,源码特意用deepcopy生成新对象再赋值——这是阅读 faraday/settings/base.py 时值得注意的实现细节。写入成功后,value属性的lru_cache会被清除,新配置即时生效。

二、报告文件在独立进程中处理

2.1 双模式架构:Celery 开 / 关

Faraday 的报告处理有两个运行路径,切换开关是server.ini中的celery_enabled(默认开启,见 faraday/server/config.py)。在 faraday/server/app.py 中:

if faraday.server.config.faraday_server.celery_enabled: logger.info("Celery is enabled ...") # 校验 celery_broker_url / celery_backend_url,缺失则退出 celery.init_app(app) else: logger.info("Celery not enabled ...") logger.info("Starting reports processor background task ...") socketio.start_background_task(reports_manager_background_task)
  • Celery 模式:报告导入交给pre_process_report_task(faraday/server/tasks.py),由 Celery worker 消费;
  • 非 Celery 模式:由 faraday/server/utils/reports_processor.py 中的REPORTS_QUEUE(进程内queue.Queue)+reports_manager_background_task后台任务消费——这就是 3.17.0 所说的"独立进程处理"。

2.2 非 Celery 模式的消费循环

REPORTS_QUEUE = Queue() INTERVAL = 0.5 def reports_manager_background_task(): while not stop_reports_event.is_set(): try: tpl = REPORTS_QUEUE.get(False, timeout=0.1) # 解包 workspace_name, command_id, file_path, plugin_id, user_id, ... if file_path.is_file(): process_report(workspace_name, command_id, file_path, ...) except Empty: socketio.sleep(INTERVAL)

后台任务以 0.1s 超时轮询队列、0.5s 空闲间隔,逐个取出待处理报告调用process_report()。上传接口(faraday/server/api/modules/upload_reports.py)和 bulk_create 接口都只负责REPORTS_QUEUE.put(...)后立即返回{"message": "ok", "command_id": ...}。

2.3process_report的完整处理链

核心实现在 reports_processor.py:

  1. 进入 Flask app context(get_app().app_context());
  2. 若有plugin_id:用PluginsManager加载指定插件,plugin.processReport(str(file_path))解析原始报告,plugin.get_data()取结构化数据;
  3. 若无plugin_id:直接从 JSON 文件读取已结构化的数据(bulk_create 走此路径);
  4. 处理完成后按delete_report_after_process(默认True)删除临时报告文件;
  5. 调用send_report_data():BulkCreateSchema反序列化 → 可选add_creator注入创建者 → 交给bulk_create()入库。

任一步骤失败都会调用command_status_error(command_id),把对应Command的command字段置为"error",供前端与 API 查询失败原因。

2.4 该变更的工程意义

报告解析曾是请求线程内的同步操作:插件加载、XML/JSON 解析、大量对象入库都会长时间占用 Web 进程。拆到独立后台任务(或 Celery)后,API 只负责"收文件、存临时文件、入队",响应延迟与报告大小解耦;同时ReportsSettings.custom_plugins_folder支持的自定义插件目录(faraday/settings/reports.py)也在独立进程中加载,避免污染请求进程。

三、bulk_create请求异步化

3.1 变更前:请求内同步入库

旧实现中,POST /v3/ws/<workspace>/bulk_create会在请求线程里直接遍历 hosts/services/vulns 逐条建对象,大 payload 会产生长事务、长响应。

3.2 变更后:先落盘、再投递、立即返回

BulkCreateView.post(faraday/server/api/modules/bulk_create.py)现在分四步:

# 1. 校验(含 agent token / execution_id 分支) data = self._parse_data(self._get_schema_instance({}), request) ... # 2. 把请求体原样 JSON 落到临时文件 chars = ascii_uppercase + digits random_prefix = ''.join(choice(chars) for _ in range(30)) json_file = f"{random_prefix}.json" file_path = CONST_FARADAY_HOME_PATH / 'uploaded_reports' / json_file with file_path.open('w') as output: json_dump(json_data, output) # 3. 投递:Celery 或队列二选一 if faraday_server.celery_enabled: process_report(workspace.name, command_id, file_path, None, user_id, ...) logger.info(f"Faraday objects sent to celery in bulk for workspace {workspace}") else: REPORTS_QUEUE.put((workspace.name, command_id, file_path, None, user_id, ...)) logger.info(f"Faraday objects enqueued in bulk for workspace {workspace}") # 4. 立即返回 return jsonify({"message": "Created", "command_id": command_id}), HTTP_CREATED

注意临时文件写入的是CONST_FARADAY_HOME_PATH / 'uploaded_reports'目录(与报告上传共用),随机前缀为 30 位大写字母 + 数字,避免并发请求的文件名冲突。若请求只有credentials而无 hosts,则退化为同步bulk_create(workspace, command, data, True, True)(bulk_create.py)。

3.3 Celery 分支:分片 + group/chord 并行建 host

bulk_create()内部(bulk_create.py)在celery_enabled时按消息大小切分批次:

if faraday_server.celery_enabled: for host in data['hosts']: _host_size = get_host_size(host) # len(json.dumps(host).encode('utf-8')) if (_current_size + _host_size) < faraday_server.max_task_message_size: _hosts_to_create.append(host) _current_size += _host_size else: task = process_report_task.delay(workspace_id, command_dict, _hosts_to_create) ...

每个批次投递一个process_report_task(faraday/server/tasks.py),内部用group+chord把该批 hosts 再次拆成多个create_host_task并行执行,每个 host 独立调用_create_host()入库,全部完成后回调on_success_process_report_task统计结果。批次上限max_task_message_size默认 5MB(DEFAULT_MAX_QUEUE_MESSAGE_SIZE = 5 * 1024 * 1024,见 faraday/server/config.py),可从server.ini调整。任务 id 会回写到Command.tasks(JSONB 列),供命令状态 API 查询进度。

3.4 测试证据

仓库测试同步覆盖了该路径:

  • tests/celery/test_api_bulk_create.py 直接调用bc.bulk_create(...).get(timeout=10)验证 Celery 批处理结果;
  • tests/celery/test_on_success_process_report_task.py 验证批次成功回调与不存在的 command_id 容错;
  • tests/test_api_bulk_create.py 中保留了一条@pytest.mark.skip(reason="Must think a new way to test this with async bulk_create")的用例,恰好印证 3.17.0 将 bulk_create 改为异步后,旧有的同步测试思路已不再适用——这是该变更最直接的源码级佐证。

四、部署与运维注意事项

4.1 启用异步处理所需配置

在server.ini的[faraday]段(默认值见 faraday/server/config.py):

[faraday] celery_enabled = True ; 默认开启;关闭则回退到 REPORTS_QUEUE 后台任务 celery_broker_url = redis://127.0.0.1:6379/0 celery_backend_url = redis://127.0.0.1:6379/0 max_task_message_size = 5242880 ; 单批次 JSON 大小上限,默认 5MB delete_report_after_process = True

celery_enabled = True时必须同时配置 broker/backend(源码在缺失时直接sys.exit()),并运行faraday-server celery-worker;关闭 Celery 时则依赖socketio后台任务消费队列。

4.2faraday-manage settings完整使用示例

# 查看已注册的设置 faraday-manage settings -a list # 查看 reports 设置当前值 faraday-manage settings -a show reports # 非交互式更新(3.17.0 起可用) faraday-manage settings -a update reports \ --data '{"custom_plugins_folder": "/opt/faraday/custom_plugins"}' # 清空自定义配置,恢复默认 faraday-manage settings -a clear reports

结合 faraday/settings/init.py,当前版本可管理的设置为smtp、dashboard、reports、elk、query_limits,均以 JSON 形式持久化于Configuration表。

总结

Faraday 3.17.0 的三条变更构成了一条清晰的性能与运维优化主线:--data让配置管理脱离交互终端、可进 CI/CD;报告处理与bulk_create双双异步化,使 Web 请求与重计算彻底解耦。理解 bulk_create.py、reports_processor.py、tasks.py 三者之间的投递—消费—入库链路,是排查报告导入慢、命令卡在 "In progress" 等问题的关键起点。

  • 网络安全
  • 渗透测试
  • 漏洞扫描

【免费下载链接】faraday

Open-source and AI-powered cybersecurity tools for offensive security, vulnerability management, and autonomous pentesting. Built by hackers in Latin America, used worldwide.

项目地址:https://gitcode.com/gh_mirrors/far/faraday
点击查看免费下载

相关推荐

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询