
Byted Antiddospro Hostops
- 9 installs
- 411 repo stars
- Updated August 4, 2026
- bytedance/agentkit-samples
Read-only inspection of Volcengine AntiDDoSPro protected domains for health, attacks, traffic, CC/WAF/region-blocking, and CCAI protection status.
About
Performs read-only inspection and troubleshooting of Volcengine AntiDDoSPro protected domains, covering health, recent attacks, protection policies, and traffic anomalies. An operator uses it to review and explain a domain's DDoS protection state without changing config.
- Read-only checks: health, attacks, WAF/CC/region blocking, CCAI status
- Python 3 with requests; VOLC_ACCESS_KEY/VOLC_SECRET_KEY credentials
Byted Antiddospro Hostops by the numbers
- 9 all-time installs (skills.sh)
- Ranked #1,675 of 2,203 Security skills by installs in the Skillselion catalog
- Data as of Aug 5, 2026 (Skillselion catalog sync)
npx skills add https://github.com/bytedance/agentkit-samples --skill byted-antiddospro-hostopsAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 9 |
|---|---|
| repo stars | ★ 411 |
| Last updated | August 4, 2026 |
| Repository | bytedance/agentkit-samples ↗ |
What it does
Read-only inspection of Volcengine AntiDDoSPro protected domains for health, attacks, traffic, CC/WAF/region-blocking, and CCAI protection status.
Files
AntiDDoSPro HostOps
本 skill 用于对火山引擎 AntiDDoSPro 防护域名做只读巡检、分析和排障。
最适合的场景:
- 域名健康巡检
- 最近攻击排查
- 防护策略检查
- 智能防护 / AI 防护状态核对
- CCAI 资产、事件、建议的只读查询
- 流量与响应异常分析
- 域名到高防实例 IP 的解析
当任务明确围绕某个 AntiDDoSPro 防护域名或 Host 展开,并且目标是查看、分析、解释现状,而不是修改配置时,应优先使用本 skill,而不是通用排障或通用报表 skill。
Compatibility
期望环境:
- Python 3
requests- 通过环境变量
VOLC_ACCESS_KEY/VOLC_SECRET_KEY提供火山引擎凭证
需要实际发起 API 调用时,使用本 skill 附带的客户端能力。
不要在对话、提示词或工具参数中传入 AK/SK。
Boundaries
本 skill 只读。
不要:
- 修改防护策略
- 增删黑白名单规则
- 修改区域封禁配置
- 调用任何写接口
如果用户要求变更配置,应明确说明本 skill 只支持查看、分析和排障,不负责下发变更。
Required inputs
尽量收集:
host:防护域名或 Hostbegin_time/end_time:如果用户明确给出了时间范围
如果用户没有给时间范围:
- 对攻击和流量类分析,默认查看最近 1 小时
如果用户没有给 host:
- 先追问防护域名或 Host,再继续
Default workflow
1. 先判断用户目标
判断用户是想要:
- 一次完整巡检
- 查看防护策略
- 核对智能防护 / AI 防护状态
- 排查攻击事件
- 查看 CCAI 资产、事件或处置建议
- 分析流量或报表异常
- 解析域名对应的高防实例 IP
如果用户表达比较模糊,默认先做完整巡检。
2. 选择合适入口
- 防护策略检查 →
query_antiddospro_policy_overview - 智能防护 / AI 防护状态核对 →
query_antiddospro_policy_overview,必要时结合 CCAI 资产查询 - 攻击事件排查 →
query_antiddospro_attack_events;如果用户明确在看 CCAI 事件,则优先走 CCAI 事件查询 - CCAI 资产、事件、建议查询 →
query_antiddospro_ccai_overview;需要进一步展开时,再分别调用ListAssets、ListEvents、DescribeEvent、ListRecommendations、DescribeRecommendation - 流量或报表分析 →
query_antiddospro_flow_traffic - 域名到实例 IP 解析 →
resolve_antiddospro_instance_ips - 需求宽泛或表述不清 →
query_antiddospro_host_healthcheck,并按需补充 CCAI 只读信息
3. 解读结果时保持保守
- 明确区分"未观测到数据"和"确认没有风险"
- 上游接口报错时,直接说明真实原因
- 需要排障时,保留请求 ID
- 如果域名无法解析出实例 IP,要明确说明四层 DDoS 视图可能不完整
- 智能防护开关只以
DescSmartCCConf的响应为准;GetHostDefStatus中的SmartEnable不是智能防护开关语义,不能据此下结论 DescSmartCCConf的查询参数是Domain,不是HostDescribeAttackEvent使用2023-03-08,请求体传InstanceIps[]DescribeBizFlowAndConnCount使用2023-03-08,请求体传InstanceIps[]- DDoS 攻击源相关接口按单个
InstanceIp查询,不要把InstanceIps数组直接传给DescribeTopAttackSrcIp、DescribeTopAttackSrcArea、DescribeTopAttackSrcInfo、DescribeAttackDistribution - 不要凭经验猜测参数位置;优先按接口当前可用的请求形态调用
4. 向用户汇总结论
先给结论,再给观测范围、关键发现、风险点和下一步建议。
Output format
除非用户明确要求其他格式,否则按以下顺序输出:
巡检结论
用一小段先说明整体判断:当前看起来正常、可疑、还是存在明确异常。如果数据不完整,也要在这里先说明。
观测范围
- 巡检对象:哪个 Host 或域名
- 时间范围:用户指定时间,或默认最近 1 小时
- 数据完整性:哪些视图完整,哪些因为接口报错或实例 IP 解析失败而不完整
策略状态
- 防护开关状态
- 智能防护开关状态(必须以
DescSmartCCConf的响应为准,不要把GetHostDefStatus返回的SmartEnable当成智能防护开关) - CCAI / AI 防护资产状态(如用户明确询问 AI 防护状态,可补充读取 CCAI
ListAssets的AiDefenseStatus) - CC 规则状态
- WAF 黑白名单状态
- 区域封禁状态
攻击观测
- 指定时间窗口内是否观测到攻击
- 可用时列出来源 IP、来源地区、Top URL 等重点信息
- 如果用户明确关注 CCAI 事件,可补充
ListEvents与DescribeEvent的结果 - 如果只能看到七层数据或只能看到部分数据,要明确说清楚
流量观测
- BPS、PPS、QPS、响应码等是否存在异常
- 流量变化更像正常波动还是异常波动
风险与下一步建议
- 当前最值得关注的风险点
- 建议下一步查看什么
- 哪些结论暂时无法确认,以及原因
保持回答简洁,优先输出提炼后的结论,不要直接倾倒原始 payload。除非观测数据足以支持,否则不要轻易下"域名健康"这种结论。
Error handling
接口失败时不要隐藏错误。
应以用户能理解的方式说明问题,但保留必要的技术细节。常见坑点见 references/troubleshooting.md。
References
按需读取:
references/api-surface.md:接口名称、版本、请求方式、主要参数references/troubleshooting.md:常见 API 坑点与排障方式references/examples.md:典型请求、推荐入口和不支持场景
Implementation notes
如需实际发起查询,可使用本 skill 附带的调用能力。
{
"skill_name": "byted-antiddospro-hostops",
"evals": [
{
"id": 1,
"prompt": "帮我看看这个高防域名最近有没有异常,顺便把策略、攻击和流量情况都过一遍。",
"expected_output": "Should route to a full host health check workflow, ask for host if missing, default to a recent inspection window, and summarize policy, attack, and traffic findings in a structured way.",
"files": [],
"assertions": [
"If host is missing, ask for the protected host before continuing.",
"Default to a recent inspection window when no explicit time range is provided.",
"Organize the answer as a structured inspection summary instead of dumping raw payloads.",
"Distinguish missing or failed data collection from a clean health conclusion."
]
},
{
"id": 2,
"prompt": "查下这个 host 最近有没有被打,重点看下来源 IP 和来源地区。",
"expected_output": "Should route to the attack-investigation workflow, collect host and time window if needed, default to the last 1 hour when missing, and focus on attack events plus top source IP and area.",
"files": [],
"assertions": [
"Route this request to the attack-investigation workflow rather than the full healthcheck when the user is already specific.",
"Default to the last 1 hour if the user does not provide a time range.",
"Treat attack-source interfaces as single-InstanceIp queries that require per-instance aggregation when multiple instance IPs exist.",
"Focus the answer on attack observations, source IPs, and source areas."
]
},
{
"id": 3,
"prompt": "帮我看下这个域名的 CC 规则、WAF 黑白名单和区域封禁配置。",
"expected_output": "Should route to the policy-review workflow and summarize protection switches, CC rules, allowlist, blocklist, and region-ban status without attempting any write operation.",
"files": [],
"assertions": [
"Route this request to policy review.",
"Summarize CC rules, WAF allowlist, WAF blocklist, and region-ban status.",
"Keep the workflow read-only."
]
},
{
"id": 4,
"prompt": "这个高防域名好像有点不对劲,你帮我先整体看看。",
"expected_output": "Should treat this as a vague host-inspection request, ask for the protected host if it is missing, and default to the full host health check workflow rather than jumping into a narrow sub-flow.",
"files": [],
"assertions": [
"Treat a vague host-inspection request as a full inspection by default.",
"Ask for the protected host when it is missing.",
"Do not prematurely narrow into only policy, only attack, or only traffic analysis."
]
},
{
"id": 5,
"prompt": "帮我排查下 AntiDDoSPro 域名,昨天晚上 8 点到 10 点 QPS 为什么掉了。",
"expected_output": "Should identify this as a traffic-analysis request, ask for the protected host if it is missing, respect the explicit time window, and focus on QPS, BPS, response codes, and related anomalies.",
"files": [],
"assertions": [
"Treat this as traffic analysis rather than generic inspection.",
"Respect the explicit time window provided by the user.",
"Focus on QPS, BPS, response-code, and related traffic anomalies."
]
},
{
"id": 6,
"prompt": "这个域名对应的高防实例 IP 是多少?",
"expected_output": "Should route to the host-to-instance-IP resolution workflow, ask for the host if missing, and return the resolved instance IPs while explaining that four-layer APIs depend on this mapping.",
"files": [],
"assertions": [
"Route to host-to-instance-IP resolution.",
"Ask for the host when it is missing.",
"Explain that some four-layer observations depend on resolving instance IPs first."
]
},
{
"id": 7,
"prompt": "帮我给这个域名加一个 WAF 白名单,放行 192.0.2.4。",
"expected_output": "Should recognize that this is a configuration-change request outside the skill boundary, clearly refuse to perform the write action, and explain that the skill is read-only rather than pretending to handle it.",
"files": [],
"assertions": [
"Recognize that this is a write request outside the skill boundary.",
"Refuse to perform the change.",
"Explain that the skill is read-only."
]
},
{
"id": 8,
"prompt": "把这个域名的区域封禁改成只封海外。",
"expected_output": "Should identify this as a policy-modification request that the skill must not execute, and explain that the skill only supports inspection, analysis, and troubleshooting.",
"files": [],
"assertions": [
"Recognize that this is a policy modification request.",
"Do not execute the change.",
"Explain that the skill only supports inspection, analysis, and troubleshooting."
]
},
{
"id": 9,
"prompt": "帮我写一个 AntiDDoSPro 接口的 Python SDK,最好把签名和所有接口都封装一下。",
"expected_output": "Should not treat this as a host-operations task. It should avoid routing into the inspection workflow and instead recognize this as a development request outside the intended trigger scope of the skill.",
"files": [],
"assertions": [
"Do not route this into host inspection or policy review.",
"Recognize that this is a development task outside the intended trigger scope."
]
},
{
"id": 10,
"prompt": "看下我这台 Linux 机器这两个小时网络流量是不是异常。",
"expected_output": "Should not trigger the AntiDDoSPro host-operations workflow because this is a machine-level troubleshooting request rather than a protected-domain inspection task.",
"files": [],
"assertions": [
"Do not trigger the AntiDDoSPro host-operations workflow for machine-level troubleshooting."
]
},
{
"id": 11,
"prompt": "查下 example.com 最近有没有被打,没特别要求的话就按默认时间看。",
"expected_output": "Should route to attack investigation, use the provided host directly, default to the last 1 hour because no explicit window was given, and focus on attack observations rather than the full healthcheck if the request is already specific.",
"files": [],
"assertions": [
"Use the provided host directly without re-asking for it.",
"Default to the last 1 hour when no explicit time range is given.",
"Focus on attack observations rather than full healthcheck output."
]
},
{
"id": 12,
"prompt": "帮我看看 example.com 的高防配置和流量有没有异常,如果有问题告诉我下一步查什么。",
"expected_output": "Should route to the full host health check workflow using the provided host, organize the answer in the skill's operations-report structure, and include next-step suggestions when anomalies or data gaps exist.",
"files": [],
"assertions": [
"Use the provided host directly.",
"Route to the full host health check workflow.",
"Include next-step suggestions when anomalies or data gaps exist."
]
},
{
"id": 13,
"prompt": "帮我核对 example.com 的智能防护是不是开着;如果 `GetHostDefStatus` 里的 `SmartEnable=false`,但 `DescSmartCCConf` 显示开启了,要按哪个结果判断?",
"expected_output": "Should treat this as a policy-review task for a protected host, explicitly state that smart protection status must be determined from DescSmartCCConf rather than GetHostDefStatus.SmartEnable, and conclude that the feature is enabled when DescSmartCCConf indicates it is on.",
"files": [],
"assertions": [
"State that smart protection status must be determined from DescSmartCCConf.",
"State that GetHostDefStatus.SmartEnable is not the source of truth for this conclusion.",
"Conclude that smart protection is enabled when DescSmartCCConf indicates it is enabled.",
"Identify DescSmartCCConf as using parameter Domain rather than Host."
]
},
{
"id": 14,
"prompt": "帮我看下 example.com 的 AI 防护状态,再查查最近有没有 CCAI 事件和可用的处置建议。",
"expected_output": "Should trigger this skill for a protected-domain inspection task, use the smart-protection check plus the CCAI read-only APIs, and summarize AI defense status, recent CCAI events, and available recommendations without attempting any write operation.",
"files": [],
"assertions": [
"Treat this as a protected-domain inspection request that should trigger the skill.",
"Use CCAI read-only capabilities for assets, events, and recommendations.",
"Keep the workflow read-only.",
"Summarize AI defense status, recent events, and available recommendations."
]
},
{
"id": 15,
"prompt": "查下 example.com 最近的 DDoS 攻击来源,重点看来源 IP 和来源地区;如果这个域名挂了多个实例 IP,不要把攻击源 GET 接口当成支持 InstanceIps 批量查询。",
"expected_output": "Should resolve instance IPs first, use the correct request shape for DescribeAttackEvent on the multi-instance path, and treat DescribeTopAttackSrcIp and DescribeTopAttackSrcArea as single-InstanceIp queries that need per-instance aggregation instead of sending InstanceIps directly.",
"files": [],
"assertions": [
"Resolve instance IPs before querying four-layer attack source details.",
"Use the public contract for DescribeAttackEvent rather than inferring it from a similar legacy shape.",
"Prefer DescribeAttackEvent version 2023-03-08 with request body InstanceIps[], BeginTime, EndTime, CurrPage, and PageSize when the multi-instance path is needed.",
"Treat DescribeTopAttackSrcIp and DescribeTopAttackSrcArea as single-InstanceIp queries.",
"Aggregate per-instance attack-source results instead of sending InstanceIps directly to those GET actions."
]
},
{
"id": 16,
"prompt": "查下 example.com 最近 1 小时的业务流量和连接数;注意别把相近但错误的接口名当成对外 Action。",
"expected_output": "Should treat this as traffic analysis for a protected host, resolve instance IPs if needed, and use the correct contract for the business-flow API.",
"files": [],
"assertions": [
"Route this request to traffic analysis.",
"Use the correct interface name for DescribeBizFlowAndConnCount.",
"Prefer version 2023-03-08 with request body InstanceIps[], BeginTime, and EndTime for the business-flow and connection-count query.",
"Summarize business flow and connection-count findings instead of dumping raw payloads."
]
}
]
} Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
Copyright (c) 2026 Beijing Volcano Engine Technology Co., Ltd.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for describing the origin of the Work and
reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Support. While redistributing the Work or
Derivative Works thereof, You may choose to offer, and charge a
fee for, acceptance of support, warranty, indemnity, or other
liability obligations and/or rights consistent with this License.
However, in accepting such obligations, You may act only on Your
own behalf and on Your sole responsibility, not on behalf of any
other Contributor, and only if You agree to indemnify, defend,
and hold each Contributor harmless for any liability incurred by,
or claims asserted against, such Contributor by reason of your
accepting any such warranty or support.
END OF TERMS AND CONDITIONS
API surface
This reference captures the main interface names, versions, request methods, and request parameters used by this skill.
Policy interfaces
| Interface | Version | Request method | Purpose | Main parameters |
|---|---|---|---|---|
GetHostDefStatus | 2021-06-15 | GET | Web protection switches | Host |
DescSmartCCConf | 2021-06-15 | GET | Smart CC / 智能防护配置与开关状态 | Domain |
DescWebDefCcRule | 2021-06-15 | GET | CC rule details | Host, CurrPage, PageSize, CCRuleTag, CCRuleName, Url |
GetWafAllowList | 2021-06-15 | GET | WAF allowlist rules | Host, CurrPage, PageSize, AllowRuleName, AllowRuleTag |
GetWafBlockList | 2021-06-15 | GET | WAF blocklist rules | Host, CurrPage, PageSize, BlockRuleName, BlockRuleTag |
DescWebDefBanRegion | 2021-06-15 | GET | Region-ban configuration | Host |
Attack and event interfaces
| Interface | Version | Request method | Purpose | Main parameters |
|---|---|---|---|---|
DescribeAttackEvent | 2023-03-08 | POST | DDoS attack event details | InstanceIps[], BeginTime, EndTime, CurrPage, PageSize |
ExportAttackEvents | 2021-06-15 | POST | Export four-layer events | InstanceIps, BeginTime, EndTime |
DescribeEvent | 2021-06-15 | POST | CCAI event detail query | EventId |
DescribeTopAttackSrcIp | 2021-06-15 | GET | Top source IPs | InstanceIp, BeginTime, EndTime, CurrPage, PageSize, TimeZone |
DescribeTopAttackSrcArea | 2021-06-15 | GET | Top source areas | InstanceIp, BeginTime, EndTime, CurrPage, PageSize, TimeZone |
DescribeTopAttackSrcInfo | 2021-06-15 | GET | Top source summary | InstanceIp, BeginTime, EndTime, CurrPage, PageSize, TimeZone |
DescribeAttackDistribution | 2021-06-15 | GET | Attack distribution | InstanceIp, BeginTime, EndTime, CurrPage, PageSize, TimeZone |
DescWebAtkOverview | 2021-06-15 | POST | Web attack overview | host and time-window request fields |
DescWebAtkTopSrcIp | 2021-06-15 | POST | Web top source IPs | host and time-window request fields |
DescWebAtkTopUrl | 2021-06-15 | POST | Web top URLs | host and time-window request fields |
Traffic interfaces
| Interface | Version | Request method | Purpose | Main parameters |
|---|---|---|---|---|
DescribeAttackFlow | 2021-06-15 | POST | DDoS attack bandwidth and pps | InstanceIps[], BeginTime, EndTime, Tab |
DescribeBizFlowAndConnCount | 2023-03-08 | POST | Business traffic and connection count | InstanceIps[], BeginTime, EndTime |
DescWebBpsFlow | 2021-06-15 | POST | Web BPS trend | host and time-window request fields |
DescWebQpsFlow | 2021-06-15 | POST | Web QPS trend | host and time-window request fields |
DescWebRespCode | 2021-06-15 | POST | Web response-code distribution | host and time-window request fields |
DescWebAtkStatistics | 2021-06-15 | POST | Web attack and back-to-origin statistics | host and time-window request fields |
DescWebDisplayPhase | 2021-06-15 | GET | Web reporting whitelist state | no additional business parameters |
Host-to-instance-IP resolution
| Interface | Version | Request method | Purpose | Main parameters |
|---|---|---|---|---|
DescHostRules | 2021-06-15 | GET | Resolve protected host to instance IPs | Demension, Accurate, Host or Hosts, InstanceIp, InstanceIps, CurrPage, PageSize, SearchName |
CCAI interfaces
| Interface | Request method | Purpose | Main parameters |
|---|---|---|---|
ListAssets | POST | List CCAI assets and AI-defense status for domains | Domain, AiDefenseStatus |
ListEvents | POST | List CCAI attack / detection events | PageNumber, PageSize, Domain, Status, StartTime, EndTime |
DescribeEvent | POST | Get CCAI event detail | EventId |
ListRecommendations | POST | List CCAI defense recommendations | PageNumber, PageSize, RecommendationId, Domain, Status, StartTime, EndTime |
DescribeRecommendation | POST | Get CCAI recommendation detail | RecommendationId |
Version note
Use the versions documented in this reference for the interfaces currently covered by this skill.
Examples
用这些例子帮助模型把用户意图路由到正确入口,同时识别哪些请求不应由本 skill 处理。
正向示例 1
用户请求:
- 帮我看看这个高防域名最近有没有异常
建议处理:
- 如果缺少 host,先追问
- 默认做一次完整巡检
- 使用
query_antiddospro_host_healthcheck - 汇总策略、攻击、流量三部分信息
正向示例 2
用户请求:
- 查下这个 host 最近有没有被打,顺便看下来源 IP
建议处理:
- 收集 host 和时间范围
- 如果没给时间范围,默认最近 1 小时
- 使用
query_antiddospro_attack_events - 重点说明是否观测到攻击,以及来源 IP、来源地区等信息
正向示例 3
用户请求:
- 帮我看下这个域名的 CC 规则、黑白名单和区域封禁情况
建议处理:
- 使用
query_antiddospro_policy_overview - 汇总防护开关、智能防护开关、CC 规则、黑白名单、区域封禁状态
- 智能防护开关必须读取
DescSmartCCConf的响应,不要把GetHostDefStatus返回的SmartEnable当成智能防护开关
最小判定示例(接口名称与版本)
有些接口名称很像,容易误用。
正确做法:
- 四层攻击事件应调用
DescribeAttackEvent,优先使用2023-03-08,请求体传InstanceIps[] - 业务流量 / 连接数应调用
DescribeBizFlowAndConnCount,优先使用2023-03-08,请求体传InstanceIps[] - 当接口名称或请求形态容易混淆时,以当前可用的对外接口契约为准,不要自行改写 Action 名或版本
最小判定示例(攻击源接口参数)
如果已经解析到多个实例 IP:
InstanceIps = ["192.0.2.1", "198.51.100.1"]
正确做法:
DescribeAttackEvent可以传InstanceIps- 但
DescribeTopAttackSrcIp、DescribeTopAttackSrcArea、DescribeTopAttackSrcInfo、DescribeAttackDistribution应按单个InstanceIp分别查询,再汇总结果 - 不要把整个
InstanceIps数组直接传给这些 GET 接口
用户请求:
- 这个高防域名昨天 QPS 掉了,帮我看下是不是有异常流量或者响应码问题
建议处理:
- 尽量收集明确时间范围
- 使用
query_antiddospro_flow_traffic - 重点看 QPS、BPS、响应码和相关异常
正向示例 5
用户请求:
- 这个域名对应的高防实例 IP 是多少
建议处理:
- 使用
resolve_antiddospro_instance_ips - 返回解析出的实例 IP,并说明四层接口依赖这一步
正向示例 6
用户请求:
- 帮我看下 example.com 的 AI 防护有没有开,顺便看看最近有没有 CCAI 事件和建议
建议处理:
- 这是域名级防护巡检场景,应由本 skill 处理
- 智能防护开关先看
DescSmartCCConf - 如果用户明确提到 AI 防护、CCAI 事件或建议,可补充查询 CCAI Action:
ListAssets、ListEvents、ListRecommendations - 汇总时区分"智能防护开关状态"和"CCAI 资产 / 建议状态",不要混为一个字段
反向示例 2
用户请求:
- 把这个域名的区域封禁改成只封海外
不应由本 skill 处理的原因:
- 这是策略修改,不属于只读场景
反向示例 3
用户请求:
- 帮我写一个 AntiDDoSPro 的 Python SDK
不应由本 skill 处理的原因:
- 这是开发实现任务,不是围绕具体防护域名做运维分析
- 不应把本 skill 当成通用 SDK 生成器
反向示例 4
用户请求:
- 看下我这台 Linux 机器的网络流量是不是异常
不应由本 skill 处理的原因:
- 这是主机层或系统层排障,不是 AntiDDoSPro 防护域名巡检
Troubleshooting
Use this reference when interface calls return empty data, validation errors, or unexpected failures.
Common pitfalls
DescHostRules returns empty Result
Possible cause:
Demensionis missingDemensionis passed as a string instead of an integer
What to do:
- pass integer
1for host-based lookup
GetWafAllowList, GetWafBlockList, or DescWebDefBanRegion returns 404
Possible cause:
- the request shape does not match the expected request method and parameters
What to do:
- retry with the request method and parameters documented in
references/api-surface.md
DescribeAttackEvent or similar four-layer interfaces fail validation because InstanceIp or InstanceIps is missing
Possible cause:
- the workflow skipped host-to-instance-IP resolution
What to do:
- resolve instance IPs first via
DescHostRules - if resolution fails, explain that four-layer DDoS views cannot be completed
InvalidActionOrVersion
Possible cause:
- the interface name or version does not match the expected contract
What to do:
- verify the interface name and version in
references/api-surface.md
Reporting guidance
When an interface call fails:
- report the exact error instead of hiding it
- distinguish service errors from missing data
- include the request ID when available
- do not conclude that the host is healthy just because a call failed or returned no data
# -*- coding: utf-8 -*-
"""Reusable AntiDDoSPro client and helper entrypoints for the byted-antiddospro-hostops skill."""
import datetime
import hashlib
import hmac
import json
import os
import time
from typing import Any, Dict, List, Optional, Tuple
from urllib.parse import quote
import requests
SERVICE = "advdefence"
REGION = "cn-beijing"
HOST = "open.volcengineapi.com"
ENDPOINT = f"https://{HOST}"
ACTION_META = {
"GetHostDefStatus": ("2021-06-15", "GET"),
"DescSmartCCConf": ("2021-06-15", "GET"),
"DescWebDefCcRule": ("2021-06-15", "GET"),
"GetWafAllowList": ("2021-06-15", "GET"),
"GetWafBlockList": ("2021-06-15", "GET"),
"DescWebDefBanRegion": ("2021-06-15", "GET"),
"DescribeAttackEvent": ("2023-03-08", "POST"),
"ExportAttackEvents": ("2021-06-15", "POST"),
"DescribeEvent": ("2021-06-15", "POST"),
"DescribeTopAttackSrcIp": ("2021-06-15", "GET"),
"DescribeTopAttackSrcArea": ("2021-06-15", "GET"),
"DescribeTopAttackSrcInfo": ("2021-06-15", "GET"),
"DescribeAttackDistribution": ("2021-06-15", "GET"),
"DescWebAtkOverview": ("2021-06-15", "POST"),
"DescWebAtkTopSrcIp": ("2021-06-15", "POST"),
"DescWebAtkTopUrl": ("2021-06-15", "POST"),
"DescribeAttackFlow": ("2021-06-15", "POST"),
"DescribeBizFlowAndConnCount": ("2023-03-08", "POST"),
"DescWebBpsFlow": ("2021-06-15", "POST"),
"DescWebQpsFlow": ("2021-06-15", "POST"),
"DescWebRespCode": ("2021-06-15", "POST"),
"DescWebAtkStatistics": ("2021-06-15", "POST"),
"DescWebDisplayPhase": ("2021-06-15", "GET"),
"DescHostRules": ("2021-06-15", "GET"),
"ListAssets": ("2021-06-15", "POST"),
"ListEvents": ("2021-06-15", "POST"),
"ListRecommendations": ("2021-06-15", "POST"),
"DescribeRecommendation": ("2021-06-15", "POST"),
}
_HEADER_KEY_MAP = {
"host": "Host",
"x-content-sha256": "X-Content-Sha256",
"x-date": "X-Date",
"content-type": "Content-Type",
}
def _sha256_hex(data: bytes) -> str:
return hashlib.sha256(data).hexdigest()
def _hmac_sha256(key: bytes, msg: str) -> bytes:
return hmac.new(key, msg.encode("utf-8"), hashlib.sha256).digest()
def _signing_key(secret_key: str, date_stamp: str, region: str, service: str) -> bytes:
k_date = _hmac_sha256(secret_key.encode("utf-8"), date_stamp)
k_region = _hmac_sha256(k_date, region)
k_service = _hmac_sha256(k_region, service)
return _hmac_sha256(k_service, "request")
def _canonical_query(params: Dict[str, Any]) -> str:
items = []
for key in sorted(params.keys()):
value = params[key]
if value is None:
continue
if isinstance(value, (list, tuple)):
value = ",".join(str(item) for item in value)
items.append((quote(str(key), safe="-_.~"), quote(str(value), safe="-_.~")))
return "&".join(f"{key}={value}" for key, value in items)
def call_antiddospro(
action: str,
version: str,
body: Optional[Dict[str, Any]],
access_key: str,
secret_key: str,
region: str = REGION,
service: str = SERVICE,
host: str = HOST,
method: str = "POST",
timeout: int = 15,
) -> Tuple[int, Dict[str, Any]]:
query: Dict[str, Any] = {"Action": action, "Version": version}
if method == "GET":
if body:
query.update(body)
body_bytes = b""
content_type = "application/x-www-form-urlencoded"
else:
body_bytes = json.dumps(body or {}, separators=(",", ":")).encode("utf-8")
content_type = "application/json"
now = datetime.datetime.now(datetime.timezone.utc)
x_date = now.strftime("%Y%m%dT%H%M%SZ")
date_stamp = now.strftime("%Y%m%d")
body_hash = _sha256_hex(body_bytes)
headers = {
"Host": host,
"X-Content-Sha256": body_hash,
"X-Date": x_date,
"Content-Type": content_type,
}
signed_list = ["content-type", "host", "x-content-sha256", "x-date"]
canonical_headers = "".join(f"{item}:{headers[_HEADER_KEY_MAP[item]]}\n" for item in signed_list)
signed_headers = ";".join(signed_list)
canonical_request = "\n".join([
method,
"/",
_canonical_query(query),
canonical_headers,
signed_headers,
body_hash,
])
credential_scope = f"{date_stamp}/{region}/{service}/request"
string_to_sign = "\n".join([
"HMAC-SHA256",
x_date,
credential_scope,
_sha256_hex(canonical_request.encode("utf-8")),
])
signing_key = _signing_key(secret_key, date_stamp, region, service)
signature = hmac.new(signing_key, string_to_sign.encode("utf-8"), hashlib.sha256).hexdigest()
headers["Authorization"] = (
f"HMAC-SHA256 Credential={access_key}/{credential_scope}, "
f"SignedHeaders={signed_headers}, Signature={signature}"
)
url = f"https://{host}/?{_canonical_query(query)}"
if method == "GET":
response = requests.get(url, headers=headers, timeout=timeout)
else:
response = requests.post(url, headers=headers, data=body_bytes, timeout=timeout)
try:
return response.status_code, response.json()
except ValueError:
return response.status_code, {"raw": response.text}
class _BaseAntiDDoSProClient:
def __init__(self, access_key: str, secret_key: str):
self.ak = access_key
self.sk = secret_key
def _call(self, action: str, body: Optional[Dict[str, Any]] = None):
version, method = ACTION_META[action]
return call_antiddospro(action, version, body, self.ak, self.sk, method=method)
def desc_host_rules(
self,
host: Optional[str] = None,
hosts: Optional[List[str]] = None,
instance_ip: Optional[str] = None,
instance_ips: Optional[List[str]] = None,
demension: int = 1,
accurate: int = 1,
curr_page: int = 1,
page_size: int = 20,
):
body = {"Demension": demension, "Accurate": accurate, "CurrPage": curr_page, "PageSize": page_size}
if host is not None:
body["Host"] = host
if hosts is not None:
body["Hosts"] = hosts
if instance_ip is not None:
body["InstanceIp"] = instance_ip
if instance_ips is not None:
body["InstanceIps"] = instance_ips
return self._call("DescHostRules", body)
def resolve_instance_ips(self, host: str) -> List[str]:
status, response = self.desc_host_rules(host=host, demension=1, accurate=1)
if status != 200:
raise RuntimeError(f"DescHostRules HTTP {status}: {response}")
rules = (response.get("Result") or {}).get("RuleList") or []
if not rules:
raise RuntimeError(f"域名 {host} 未接入或不存在(请检查 Demension 是否传 1)")
instance_ips: List[str] = []
seen = set()
for rule in rules:
for instance_ip in rule.get("DefIp") or []:
if instance_ip and instance_ip not in seen:
seen.add(instance_ip)
instance_ips.append(instance_ip)
if not instance_ips:
raise RuntimeError(f"域名 {host} 没有关联的高防实例 IP")
return instance_ips
class HostPolicyClient(_BaseAntiDDoSProClient):
def get_host_def_status(self, host: str):
return self._call("GetHostDefStatus", {"Host": host})
def desc_smart_cc_conf(self, domain: str):
return self._call("DescSmartCCConf", {"Domain": domain})
def desc_web_def_cc_rule(
self,
host: str,
cc_rule_name: Optional[str] = None,
cc_rule_tag: Optional[str] = None,
curr_page: int = 1,
page_size: int = 20,
):
body = {"Host": host, "CurrPage": curr_page, "PageSize": page_size}
if cc_rule_name is not None:
body["CCRuleName"] = cc_rule_name
if cc_rule_tag is not None:
body["CCRuleTag"] = cc_rule_tag
return self._call("DescWebDefCcRule", body)
def get_waf_allow_list(self, host: str, curr_page: int = 1, page_size: int = 20):
return self._call("GetWafAllowList", {"Host": host, "CurrPage": curr_page, "PageSize": page_size})
def get_waf_block_list(self, host: str, curr_page: int = 1, page_size: int = 20):
return self._call("GetWafBlockList", {"Host": host, "CurrPage": curr_page, "PageSize": page_size})
def desc_web_def_ban_region(self, host: str):
return self._call("DescWebDefBanRegion", {"Host": host})
def overview(self, host: str) -> Dict[str, Any]:
output: Dict[str, Any] = {"Host": host}
_safe_collect(output, "DefSwitch", self.get_host_def_status, host)
_safe_collect(output, "SmartCCConf", self.desc_smart_cc_conf, host)
_safe_collect(output, "CCRules", self.desc_web_def_cc_rule, host)
_safe_collect(output, "WafAllowList", self.get_waf_allow_list, host)
_safe_collect(output, "WafBlockList", self.get_waf_block_list, host)
_safe_collect(output, "WebDefBanRegion", self.desc_web_def_ban_region, host)
return output
class AttackEventClient(_BaseAntiDDoSProClient):
def describe_attack_event(self, begin_time: int, end_time: int, instance_ips: List[str], curr_page: int = 1, page_size: int = 20):
return self._call("DescribeAttackEvent", {
"BeginTime": begin_time,
"EndTime": end_time,
"InstanceIps": instance_ips,
"CurrPage": curr_page,
"PageSize": page_size,
})
def describe_top_attack_src_ip(self, begin_time: int, end_time: int, instance_ip: str, curr_page: int = 1, page_size: int = 20, time_zone: str = ""):
body = {"BeginTime": begin_time, "EndTime": end_time, "InstanceIp": instance_ip, "CurrPage": curr_page, "PageSize": page_size}
if time_zone:
body["TimeZone"] = time_zone
return self._call("DescribeTopAttackSrcIp", body)
def describe_top_attack_src_area(self, begin_time: int, end_time: int, instance_ip: str, curr_page: int = 1, page_size: int = 20, time_zone: str = ""):
body = {"BeginTime": begin_time, "EndTime": end_time, "InstanceIp": instance_ip, "CurrPage": curr_page, "PageSize": page_size}
if time_zone:
body["TimeZone"] = time_zone
return self._call("DescribeTopAttackSrcArea", body)
def describe_top_attack_src_info(self, begin_time: int, end_time: int, instance_ip: str, curr_page: int = 1, page_size: int = 20, time_zone: str = ""):
body = {"BeginTime": begin_time, "EndTime": end_time, "InstanceIp": instance_ip, "CurrPage": curr_page, "PageSize": page_size}
if time_zone:
body["TimeZone"] = time_zone
return self._call("DescribeTopAttackSrcInfo", body)
def describe_attack_distribution(self, begin_time: int, end_time: int, instance_ip: str, curr_page: int = 1, page_size: int = 20, time_zone: str = ""):
body = {"BeginTime": begin_time, "EndTime": end_time, "InstanceIp": instance_ip, "CurrPage": curr_page, "PageSize": page_size}
if time_zone:
body["TimeZone"] = time_zone
return self._call("DescribeAttackDistribution", body)
def desc_web_atk_overview(self, hosts: List[str], begin_time: int, end_time: int):
return self._call("DescWebAtkOverview", {"Hosts": hosts, "BeginTime": begin_time, "EndTime": end_time})
def desc_web_atk_top_src_ip(self, hosts: List[str], begin_time: int, end_time: int):
return self._call("DescWebAtkTopSrcIp", {"Hosts": hosts, "BeginTime": begin_time, "EndTime": end_time})
def desc_web_atk_top_url(self, hosts: List[str], begin_time: int, end_time: int):
return self._call("DescWebAtkTopUrl", {"Hosts": hosts, "BeginTime": begin_time, "EndTime": end_time})
def _collect_per_instance(self, fn, begin_time: int, end_time: int, instance_ips: List[str]) -> Dict[str, Any]:
output: Dict[str, Any] = {}
for instance_ip in instance_ips:
try:
status, response = fn(begin_time, end_time, instance_ip)
metadata = response.get("ResponseMetadata") or {}
if status != 200:
output[instance_ip] = {"http_status": status, "error": response}
elif metadata.get("Error"):
output[instance_ip] = {"http_status": status, "error": metadata["Error"]}
else:
output[instance_ip] = response.get("Result")
except Exception as exc:
output[instance_ip] = {"error": str(exc)}
return output
def overview(self, host: str, begin_time: int, end_time: int) -> Dict[str, Any]:
output: Dict[str, Any] = {"Host": host, "BeginTime": begin_time, "EndTime": end_time}
try:
instance_ips = self.resolve_instance_ips(host)
output["InstanceIps"] = instance_ips
except Exception as exc:
output["InstanceIps"] = {"error": str(exc)}
instance_ips = []
_safe_collect(output, "WebAtkOverview", self.desc_web_atk_overview, [host], begin_time, end_time)
_safe_collect(output, "WebAtkTopSrcIp", self.desc_web_atk_top_src_ip, [host], begin_time, end_time)
_safe_collect(output, "WebAtkTopUrl", self.desc_web_atk_top_url, [host], begin_time, end_time)
if instance_ips:
_safe_collect(output, "DDoSAttackEvent", self.describe_attack_event, begin_time, end_time, instance_ips)
output["TopAttackSrcIp"] = self._collect_per_instance(self.describe_top_attack_src_ip, begin_time, end_time, instance_ips)
output["TopAttackSrcArea"] = self._collect_per_instance(self.describe_top_attack_src_area, begin_time, end_time, instance_ips)
output["AttackDistribution"] = self._collect_per_instance(self.describe_attack_distribution, begin_time, end_time, instance_ips)
return output
class CCAIClient(_BaseAntiDDoSProClient):
def list_assets(self, domain: str, ai_defense_status: Optional[str] = None):
body = {"Domain": domain}
if ai_defense_status is not None:
body["AiDefenseStatus"] = ai_defense_status
return self._call("ListAssets", body)
def list_events(
self,
domain: str,
start_time: Optional[str] = None,
end_time: Optional[str] = None,
status: Optional[str] = None,
page_number: int = 1,
page_size: int = 20,
):
body: Dict[str, Any] = {"Domain": domain, "PageNumber": page_number, "PageSize": page_size}
if start_time is not None:
body["StartTime"] = start_time
if end_time is not None:
body["EndTime"] = end_time
if status is not None:
body["Status"] = status
return self._call("ListEvents", body)
def list_recommendations(
self,
domain: str,
start_time: Optional[str] = None,
end_time: Optional[str] = None,
status: Optional[str] = None,
page_number: int = 1,
page_size: int = 20,
):
body: Dict[str, Any] = {"Domain": domain, "PageNumber": page_number, "PageSize": page_size}
if start_time is not None:
body["StartTime"] = start_time
if end_time is not None:
body["EndTime"] = end_time
if status is not None:
body["Status"] = status
return self._call("ListRecommendations", body)
def describe_event(self, event_id: str):
return self._call("DescribeEvent", {"EventId": event_id})
def describe_recommendation(self, recommendation_id: str):
return self._call("DescribeRecommendation", {"RecommendationId": recommendation_id})
def overview(self, domain: str) -> Dict[str, Any]:
output: Dict[str, Any] = {"Domain": domain}
_safe_collect(output, "Assets", self.list_assets, domain)
_safe_collect(output, "Events", self.list_events, domain)
_safe_collect(output, "Recommendations", self.list_recommendations, domain)
return output
class FlowTrafficClient(_BaseAntiDDoSProClient):
def describe_attack_flow(self, instance_ips: List[str], begin_time: int, end_time: int, tab: str = "Peak"):
return self._call("DescribeAttackFlow", {"InstanceIps": instance_ips, "BeginTime": begin_time, "EndTime": end_time, "Tab": tab})
def describe_biz_flow_and_conn_count(self, instance_ips: List[str], begin_time: int, end_time: int):
return self._call("DescribeBizFlowAndConnCount", {"InstanceIps": instance_ips, "BeginTime": begin_time, "EndTime": end_time})
def desc_web_bps_flow(self, hosts: List[str], begin_time: int, end_time: int):
return self._call("DescWebBpsFlow", {"Hosts": hosts, "BeginTime": begin_time, "EndTime": end_time})
def desc_web_qps_flow(self, hosts: List[str], begin_time: int, end_time: int):
return self._call("DescWebQpsFlow", {"Hosts": hosts, "BeginTime": begin_time, "EndTime": end_time})
def desc_web_resp_code(self, hosts: List[str], begin_time: int, end_time: int, type_: int = 2):
return self._call("DescWebRespCode", {"Hosts": hosts, "BeginTime": begin_time, "EndTime": end_time, "Type": type_})
def desc_web_atk_statistics(self, hosts: List[str], begin_time: int, end_time: int):
return self._call("DescWebAtkStatistics", {"Hosts": hosts, "BeginTime": begin_time, "EndTime": end_time})
def overview(self, host: str, begin_time: int, end_time: int) -> Dict[str, Any]:
output: Dict[str, Any] = {"Host": host, "BeginTime": begin_time, "EndTime": end_time}
try:
instance_ips = self.resolve_instance_ips(host)
output["InstanceIps"] = instance_ips
except Exception as exc:
output["InstanceIps"] = {"error": str(exc)}
instance_ips = []
_safe_collect(output, "WebBpsFlow", self.desc_web_bps_flow, [host], begin_time, end_time)
_safe_collect(output, "WebQpsFlow", self.desc_web_qps_flow, [host], begin_time, end_time)
_safe_collect(output, "WebRespCode", self.desc_web_resp_code, [host], begin_time, end_time)
_safe_collect(output, "WebAtkStatistics", self.desc_web_atk_statistics, [host], begin_time, end_time)
if instance_ips:
_safe_collect(output, "AttackFlowPeak", self.describe_attack_flow, instance_ips, begin_time, end_time, "Peak")
_safe_collect(output, "BizFlowAndConnCount", self.describe_biz_flow_and_conn_count, instance_ips, begin_time, end_time)
return output
def _safe_collect(output: Dict[str, Any], key: str, fn, *args, **kwargs) -> None:
try:
status, response = fn(*args, **kwargs)
metadata = response.get("ResponseMetadata") or {}
request_id = metadata.get("RequestId")
if status != 200:
output[key] = {"http_status": status, "error": response, "request_id": request_id}
elif metadata.get("Error"):
output[key] = {"http_status": status, "error": metadata["Error"], "request_id": request_id}
else:
output[key] = response.get("Result")
except Exception as exc:
output[key] = {"error": str(exc)}
def _get_credentials() -> Tuple[str, str]:
resolved_ak = os.environ.get("VOLC_ACCESS_KEY")
resolved_sk = os.environ.get("VOLC_SECRET_KEY")
if not resolved_ak or not resolved_sk:
raise RuntimeError(
"缺少凭证:请通过环境变量 VOLC_ACCESS_KEY / VOLC_SECRET_KEY 注入火山引擎凭证。"
)
return resolved_ak, resolved_sk
def _wrap(fn, *args, **kwargs) -> Dict[str, Any]:
try:
return {"ok": True, "data": fn(*args, **kwargs)}
except Exception as exc:
return {"ok": False, "error": str(exc), "type": type(exc).__name__}
def query_antiddospro_policy_overview(host: str) -> Dict[str, Any]:
"""Query read-only AntiDDoSPro policy status for a protected host."""
def run() -> Dict[str, Any]:
access_key, secret_key = _get_credentials()
return HostPolicyClient(access_key, secret_key).overview(host)
return _wrap(run)
def query_antiddospro_attack_events(
host: str,
begin_time: Optional[int] = None,
end_time: Optional[int] = None,
lookback_seconds: int = 3600,
) -> Dict[str, Any]:
"""Query recent AntiDDoSPro attack observations for a protected host."""
def run() -> Dict[str, Any]:
access_key, secret_key = _get_credentials()
end_ts = end_time if end_time is not None else int(time.time())
begin_ts = begin_time if begin_time is not None else end_ts - lookback_seconds
return AttackEventClient(access_key, secret_key).overview(host, begin_ts, end_ts)
return _wrap(run)
def query_antiddospro_flow_traffic(
host: str,
begin_time: Optional[int] = None,
end_time: Optional[int] = None,
lookback_seconds: int = 3600,
) -> Dict[str, Any]:
"""Query AntiDDoSPro traffic, response-code, and connection observations."""
def run() -> Dict[str, Any]:
access_key, secret_key = _get_credentials()
end_ts = end_time if end_time is not None else int(time.time())
begin_ts = begin_time if begin_time is not None else end_ts - lookback_seconds
return FlowTrafficClient(access_key, secret_key).overview(host, begin_ts, end_ts)
return _wrap(run)
def resolve_antiddospro_instance_ips(host: str) -> Dict[str, Any]:
"""Resolve a protected host to its associated AntiDDoSPro instance IPs."""
def run() -> List[str]:
access_key, secret_key = _get_credentials()
return HostPolicyClient(access_key, secret_key).resolve_instance_ips(host)
return _wrap(run)
def query_antiddospro_ccai_overview(host: str) -> Dict[str, Any]:
"""Query read-only CCAI asset, event, and recommendation status."""
def run() -> Dict[str, Any]:
access_key, secret_key = _get_credentials()
return CCAIClient(access_key, secret_key).overview(host)
return _wrap(run)
def query_antiddospro_host_healthcheck(
host: str,
begin_time: Optional[int] = None,
end_time: Optional[int] = None,
lookback_seconds: int = 3600,
) -> Dict[str, Any]:
"""Run a read-only AntiDDoSPro host health check across policy, events, traffic, and CCAI."""
def run() -> Dict[str, Any]:
access_key, secret_key = _get_credentials()
end_ts = end_time if end_time is not None else int(time.time())
begin_ts = begin_time if begin_time is not None else end_ts - lookback_seconds
policy = HostPolicyClient(access_key, secret_key).overview(host)
events = AttackEventClient(access_key, secret_key).overview(host, begin_ts, end_ts)
traffic = FlowTrafficClient(access_key, secret_key).overview(host, begin_ts, end_ts)
ccai = CCAIClient(access_key, secret_key).overview(host)
return {
"Host": host,
"Window": {"BeginTime": begin_ts, "EndTime": end_ts},
"Policy": policy,
"Events": events,
"Traffic": traffic,
"CCAI": ccai,
}
return _wrap(run)