CrowdSec的一大亮点就是有一个CAPI(官方的中央API服务器)专门收集和共享各类威胁情报,其中包括攻击者的IP,这些恶意IP会被记录到“社区黑名单”,CrowdSec会定期从CAPI拉取这份黑名单来保护你的服务器。同时如果你的服务器刚被攻击了,那么CrowdSec也会把刚刚发现的恶意IP提交给CAPI,全球其它数万个部署了CrowdSec的服务器也在实时上报,这样就提供了一套联动的防御体系。平子有人类命运共同体,CrowdSec有网络安全共同体
除此之外,CrowdSec的整体设计架构也挺有意思,有人说它和Fail2Ban很像,都是读取、分析系统日志然后作出决策,但现如今的CrowdSec已经远不止这些能力了,CrowdSec不久前还推出了AppSec组件,它可以将你的CrowdSec安装变成一个功能齐全的WAF(虽然肯定不如长亭的雷池)
本文将详细介绍CrowdSec的安装与使用方法。我的服务器系统是Debian,Web服务器是NGINX。
安装需要用到的软件包:
apt update
apt install curl gnupg apt-transport-https debian-archive-keyring
导入GPG密钥:
mkdir -p /etc/apt/keyrings/
curl -fsSL https://packagecloud.io/crowdsec/crowdsec/gpgkey | gpg --dearmor > /etc/apt/keyrings/crowdsec_crowdsec-archive-keyring.gpg
创建存储库配置文件:
nano /etc/apt/sources.list.d/crowdsec_crowdsec.list
写入如下内容:
deb [signed-by=/etc/apt/keyrings/crowdsec_crowdsec-archive-keyring.gpg] https://packagecloud.io/crowdsec/crowdsec/any any main
deb-src [signed-by=/etc/apt/keyrings/crowdsec_crowdsec-archive-keyring.gpg] https://packagecloud.io/crowdsec/crowdsec/any any main
现在就可以安装CrowdSec了:
apt update
apt install crowdsec
CrowdSec LAPI(Local API)默认监听的端口是8080(127.0.0.1:8080)如果服务器已经有程序占用了8080端口,毕竟8080算是一个比较热门的端口,很多程序都喜欢用这个端口,此时为了避免端口冲突CrowdSec是不会自己启动的,你需要将LAPI的端口改为其它的才能让CrowdSec正常运行,为此我们得修改两个配置文件:
nano /etc/crowdsec/config.yaml
这里我将其改为8088:
api:
server:
log_level: info
listen_uri: 127.0.0.1:8088
还有这个配置文件:
nano /etc/crowdsec/local_api_credentials.yaml
将url改为如下所示内容:
url: http://127.0.0.1:8088
启动并设置开机自启:
systemctl enable --now crowdsec
默认情况下,CrowdSec会尝试检测服务器正在运行的服务(CrowdSec >= 1.7.0),然后安装相应的日志源和集合。例如我的服务器在安装CrowdSec前就有SSH服务、NGINX服务在运行,那么CrowdSec就会自动将这些日志源和集合安装好,可以执行如下命令查询当前已经安装的日志源:
cscli metrics show acquisition
这里就列出了我的SSH和NGINX日志源,这里我还有一个Appsec的日志源,这个我稍后说明:
╭───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
│ Acquisition Metrics │
├─────────────────────────────────────────────────┬────────────┬──────────────┬────────────────┬────────────────────────┬───────────────────┤
│ Source │ Lines read │ Lines parsed │ Lines unparsed │ Lines poured to bucket │ Lines whitelisted │
├─────────────────────────────────────────────────┼────────────┼──────────────┼────────────────┼────────────────────────┼───────────────────┤
│ appsec:appsec │ 221 │ 221 │ - │ 210 │ - │
│ file:/var/log/nginx/access.log │ 2.41k │ 2.40k │ 1 │ 1.09k │ - │
│ file:/var/log/nginx/error.log │ 422 │ 259 │ 163 │ 235 │ - │
│ journalctl:journalctl-_SYSTEMD_UNIT=ssh.service │ 13.90k │ 9.14k │ 4.76k │ 35.87k │ - │
╰─────────────────────────────────────────────────┴────────────┴──────────────┴────────────────┴────────────────────────┴───────────────────╯
此时假设你不打算将NGINX作为Web服务器了,想用Caddy替换NGINX,那么你就需要手动配置一个新的日志源。先去CrowdSec的Hub找到你需要的集合:
找到Caddy的集合URL:
根据集合页面提供的命令安装Caddy集合:
cscli collections install crowdsecurity/caddy
CrowdSec Hub上的每个集合都包含一个采集示例,Caddy也不例外,所以你只需要在acquis.d目录下创建一个日志采集的配置文件:
nano /etc/crowdsec/acquis.d/caddy.yaml
对于Caddy而言,你应该写入如下内容:
filenames:
- /var/log/caddy/*.log
labels:
type: caddy
同时你的Caddyfile配置文件内应该指定日志文件的路径。重启CrowdSec使新的配置生效:
systemctl restart crowdsec
请注意CrowdSec或者说CrowdSec的安全引擎,本身是一个检测引擎(IDS),它只做行为检测,不负责阻止攻击行为。
我们刚才配置的这些内容如果拆分的更细一点,其实就是配置了:log-parsers(日志解析器) + scenarios(攻击场景) + Acquisition(日志采集)
其中日志解析器和攻击场景都是由collections(集合)提供的,集合就是自动打包好了这些内容,方便你一次性部署,你就不需要单独去一个个部署了,如果你没有特殊的需求,我们后续在使用过程中都是直接安装集合,很少单独去安装日志解析器和攻击场景。
CrowdSec在首次部署时会根据当时系统内运行的服务自动安装集合、配置日志采集,但如果后续你的系统内安装了新的服务,你想用CrowdSec保护它,那就需要你手动配置了,刚才我们已经使用Caddy实战演示过一遍完整的流程了。
在较新版本的CrowdSec中还有交互式自动探测与添加模式,如果你不想手动配置,可以尝试使用这个命令:
cscli setup interactive
静默全自动添加(适用于自动化脚本):
cscli setup unattended
使用自动化的方式往往没有手动配置可靠,如果可以请一定手动检查添加的配置是否正确。
CrowdSec基本的工作流程是:先采集某个服务的日志,然后将采集到的日志结构化解析再去和攻击场景匹配,比如一个攻击场景描述的是:只要一个IP在10秒内发起了5次SSH登录验证,且全部登录失败,那就说明这个IP在暴力破解SSH。当一个IP触发了此攻击场景后,CrowdSec的安全引擎会作出一个决策:把这个IP封禁4小时。
请注意这里的决策:“把这个IP封禁4小时”,此时的IP根本没有被真正的封禁,它只是CrowdSec安全引擎给出的一个建议或者说决定,还没有真正的去执行。
谁去执行CrowdSec安全引擎的决策?此时就需要你安装对应的Remediation Components(修复组件)了,官方以前又称为Bouncers(拦截器),这才是真正去执行决策的工具。在你安装并配置好修复组件后,CrowdSec就会从IDS变成一个IPS(入侵防御系统)。
下面我们就来实战配置一个防火墙Bouncer,防火墙Bouncer非常适合保护SSH基础架构服务。先查看当前系统使用的是iptables还是nftables:
iptables -V
如果回显中有显示nf_tables,那么就说明你的系统正在使用nftables:
iptables v1.8.11 (nf_tables)
此时你应该安装的软件包是:
apt install crowdsec-firewall-bouncer-nftables
否则安装:
apt install crowdsec-firewall-bouncer-iptables
查看防火墙bouncers运行状态,确保正常:
systemctl status crowdsec-firewall-bouncer.service
防火墙bouncers几乎是开箱即用的,你在安装好后无须修改任何配置应该就能很好的工作。
让我们更进一步,现在来保护Web服务(NGINX)。我的服务器之前已经安装好NGINX了,所以这里我不需要重新安装NGINX,并且我在安装CrowdSec前就已经安装了NGINX,那么CrowdSec会自动帮我配置好NGINX的集合和日志采集,所以这里我只需要安装NGINX bouncers需要用到的依赖即可:
apt install lua5.1 libnginx-mod-http-lua luarocks gettext-base lua-cjson
如果你没有安装NGINX,则这里需要先安装,仅支持Debian官方存储库的NGINX软件包,如果你是NGINX官方存储库安装的NGINX或者自己编译的NGINX则这里的步骤不适合你:
apt install nginx
然后安装对应的集合:
cscli collections install crowdsecurity/nginx
配置日志采集这里就不重复说明了,这是CrowdSec自动为我配置的,仅供参考:
filenames:
- /var/log/nginx/*.log
labels:
type: nginx
source: file
接下来安装crowdsec-nginx-bouncer:
apt install crowdsec-nginx-bouncer
这里有个坑,可能会遇到这个报错:
lua-cjson 2.1.0.10-1 depends on lua >= 5.1 (5.1-1 provided by VM)
gcc -O2 -fPIC -I/usr/include/lua5.1 -c lua_cjson.c -o lua_cjson.o
sh: 1: gcc: not found
Error: Build error: Failed compiling object lua_cjson.o
原因是系统内缺少编译用的gcc,也可能还缺少其它编译用的依赖包,所以这里安装一下这个全家桶:
apt install build-essential
将之前的crowdsec-nginx-bouncer软件包卸载重装以触发重新编译:
apt purge crowdsec-nginx-bouncer
apt install crowdsec-nginx-bouncer
由于安装了两次crowdsec-nginx-bouncer软件包,在CrowdSec那边可能会出现多个相同的Bouncer,可以通过这个命令查看:
cscli bouncers list
我不知道哪个是不再需要的Bouncer,所以我使用curl往NGINX发送一个请求:
curl 127.0.0.1
稍等片刻再次查看bouncers list应该就能发现Last API pull的时间更新了,没更新的那个就是没用的,可以执行如下命令删除:
cscli bouncers delete crowdsec-nginx-bouncer-1784122720
我还发现一个问题,crowdsec-nginx-bouncer软件包不支持Debian 11,我在另外一台Debian 11服务器内安装会报错。排查了一下发现是该软件包对NGINX版本有硬性限制:
crowdsec-nginx-bouncer
Depends: nginx
nginx-core
nginx-extras
nginx-full
nginx-light
Depends: lua5.1
Depends: libnginx-mod-http-lua
Depends: luarocks
Depends: gettext-base
Breaks: libnginx-mod-http-lua (<< 1.20)
Breaks: nginx (<< 1.20)
此修改见Pull。这意味着如果NGINX版本低于1.20将无法安装,而Debian 11官方存储库内的NGINX版本为1.18。如果你也遇到这个问题,可以改为手动安装:
wget https://github.com/crowdsecurity/cs-nginx-bouncer/releases/download/v1.2.0/crowdsec-nginx-bouncer.tgz
tar xvzf crowdsec-nginx-bouncer.tgz
cd crowdsec-nginx-bouncer-v1.2.0/
./install.sh
经过测试,手动安装的crowdsec-nginx-bouncer可以完美工作在NGINX 1.18,至少目前我没有发现有什么问题,搞不懂官方为什么要放弃对NGINX 1.20之前的版本支持。
请注意手动安装crowdsec-nginx-bouncer也需要先安装之前提到的那些依赖包,尤其是build-essential,如果你在执行install.sh安装脚本后也遇到了编译lua_cjson.o报错,请补全依赖后执行如下命令重新编译lua_cjson.o:
luarocks install lua-cjson 2.1.0.10-1
在默认情况下,CrowdSec安全引擎的决策是封禁(ban),当crowdsec-nginx-bouncer执行这个决策后,用户将无法访问你的网站,这在很多时候太过于武断了,可能会误杀正常访问的真实用户,此时你可以将HTTP场景的决策改为更灵活的验证码挑战(captcha)。配置验证码挑战,主要分为两个部分,既CrowdSec安全引擎要能够支持验证码挑战的决策,同时Bouncer还需要拥有“展示验证码”的能力。
我们先来配置crowdsec-nginx-bouncer,目前支持的验证码有:recaptcha / hcaptcha / turnstile,这里我选择使用turnstile。编辑crowdsec-nginx-bouncer的配置文件:
nano /etc/crowdsec/bouncers/crowdsec-nginx-bouncer.conf
修改如下内容:
CAPTCHA_PROVIDER=turnstile
# Captcha Secret Key
SECRET_KEY=
# Captcha Site key
SITE_KEY=
CAPTCHA_TEMPLATE_PATH=/var/lib/crowdsec/lua/templates/captcha.html
CAPTCHA_EXPIRATION=3600
为了让验证码挑战功能能够正常工作,你还需要编辑这个NGINX配置文件添加一个DNS resolver:
nano /etc/nginx/conf.d/crowdsec_nginx.conf
写入如下内容:
resolver 1.1.1.1 ipv6=off;
同时请确保该配置文件内有这一行内容,如果没有请自己加上:
lua_ssl_trusted_certificate /etc/ssl/certs/ca-certificates.crt;
重启NGINX服务使新配置生效:
systemctl restart nginx
接下来配置CrowdSec安全引擎,编辑profiles.yaml决策配置文件:
nano /etc/crowdsec/profiles.yaml
在文件的顶部加入如下内容:
name: captcha_remediation
filters:
- Alert.Remediation == true && Alert.GetScope() == "Ip" && Alert.GetScenario() contains "http"
## Any scenario with http in its name will trigger a captcha challenge
decisions:
- type: captcha
duration: 4h
on_success: break
---
修改后的文件内容应该长这样:
name: captcha_remediation
filters:
- Alert.Remediation == true && Alert.GetScope() == "Ip" && Alert.GetScenario() contains "http"
## Any scenario with http in its name will trigger a captcha challenge
decisions:
- type: captcha
duration: 4h
on_success: break
---
name: default_ip_remediation
#debug: true
filters:
- Alert.Remediation == true && Alert.GetScope() == "Ip"
decisions:
- type: ban
duration: 4h
#duration_expr: Sprintf('%dh', (GetDecisionsCount(Alert.GetValue()) + 1) * 4)
# notifications:
# - slack_default # Set the webhook in /etc/crowdsec/notifications/slack.yaml before enabling this.
# - splunk_default # Set the splunk url and token in /etc/crowdsec/notifications/splunk.yaml before enabling this.
# - http_default # Set the required http parameters in /etc/crowdsec/notifications/http.yaml before enabling this.
# - email_default # Set the required email parameters in /etc/crowdsec/notifications/email.yaml before enabling this.
on_success: break
---
name: default_range_remediation
#debug: true
filters:
- Alert.Remediation == true && Alert.GetScope() == "Range"
decisions:
- type: ban
duration: 4h
#duration_expr: Sprintf('%dh', (GetDecisionsCount(Alert.GetValue()) + 1) * 4)
# notifications:
# - slack_default # Set the webhook in /etc/crowdsec/notifications/slack.yaml before enabling this.
# - splunk_default # Set the splunk url and token in /etc/crowdsec/notifications/splunk.yaml before enabling this.
# - http_default # Set the required http parameters in /etc/crowdsec/notifications/http.yaml before enabling this.
# - email_default # Set the required email parameters in /etc/crowdsec/notifications/email.yaml before enabling this.
on_success: break
重启CrowdSec使新的决策配置生效:
systemctl restart crowdsec
然后我们模拟真实攻击场景进行测试,看看验证码挑战是否能够正常工作,往NGINX日志里面写一些假的访问日志:
for i in {1..15}; do
echo "1.2.3.4 - - [22/Jul/2026:12:00:00 +0000] \"GET /random-test-$i.html HTTP/1.1\" 404 150 \"-\" \"Mozilla/5.0\"" | tee -a /var/log/nginx/access.log
done
将1.2.3.4换成你的代理IP或者蜂窝移动网络IP,然后查看决策列表:
cscli decisions list
正常的话应该有一条如图所示的记录:
此时你通过代理IP访问网站也应该弹出验证码挑战页面。如果你只是想单纯测试验证码挑战页面能否正常加载,可以使用此命令进行快速测试:
cscli decisions add -i 1.2.3.4 -t captcha
请注意此命令会完全绕过整个检测管线,所以无法用来测试profiles.yaml的逻辑。要真正验证profiles.yaml的配置是否正常,关键在于:必须触发一次真实的“告警(Alert)”
如果要测试完整的逻辑,建议使用往NGINX写假日志的方式。测试完成后别忘了删除决策:
cscli decisions delete -i 1.2.3.4
还记得最开始日志源里面有一个appsec:appsec吗?现在让我们来深入了解这个AppSec是什么。
AppSec全称:CrowdSec WAF - AppSec Component,你可以简单理解为配置好AppSec后,你安装的CrowdSec将变成一个功能齐全的WAF。
这和之前基于日志分析的模式有本质区别,在没有WAF的时候CrowdSec的防御其实叫“事后惩罚”,假设一个攻击者向你的WordPress网站发送一个SQL注入请求:
1.恶意的SQL注入请求直接成功到达了你的WordPress网站并执行了
2.NGINX把这次请求记录到了日志文件access.log
3.后台的CrowdSec进程读取日志,发现日志里包含SQL注入的攻击的请求
4.场景触发(Scenario):“哦!这个IP刚才对我们进行了SQL注入”
5.CrowdSec做出决策:ban掉这个IP
6.等到这个攻击者下次再来请求时,crowdsec-nginx-bouncer才会去封禁它
很明显这种模式防不住“一发入魂”的攻击:如果攻击者通过第一个SQL注入请求就把你的数据库拖走了,或者通过第一个XSS请求就盗取了管理员的Cookie。虽然CrowdSec在1秒后读取日志发现了异样并拉黑了它,但伤害已经造成了。而当你启用WAF后,请求还没到达网页时就被拦截了,直接返回403。
安装AppSec规则集(集合):
cscli collections install \
crowdsecurity/appsec-virtual-patching \
crowdsecurity/appsec-generic-rules
创建日志采集配置文件:
nano /etc/crowdsec/acquis.d/appsec.yaml
写入如下内容:
appsec_configs:
- crowdsecurity/appsec-default
labels:
type: appsec
listen_addr: 127.0.0.1:7422
source: appsec
name: myAppSecComponent
重启CrowdSec使新的配置生效:
systemctl restart crowdsec
编辑crowdsec-nginx-bouncer的配置文件:
nano /etc/crowdsec/bouncers/crowdsec-nginx-bouncer.conf
将crowdsec-nginx-bouncer接入AppSec:
APPSEC_URL=http://127.0.0.1:7422
APPSEC_FAILURE_ACTION=passthrough
APPSEC_CONNECT_TIMEOUT=
APPSEC_SEND_TIMEOUT=
APPSEC_PROCESS_TIMEOUT=
ALWAYS_SEND_TO_APPSEC=true
APPSEC_DROP_UNREADABLE_BODY=false
SSL_VERIFY=true
1.修改APPSEC_URL指向127.0.0.1:7422
2.启用ALWAYS_SEND_TO_APPSEC始终将所有请求都发送给AppSec检查。
重启NGINX使配置生效:
systemctl restart nginx
现在你已经拥有了基本的防护,为什么说是基本?因为刚才安装的两个集合内的规则仅能防护特定的已知CVE漏洞和一些常见的网络攻击,对于其它未知的攻击和漏洞基本无能为力。官方这么做的目的其实更多的是为了减少误报和方便运维(快速上手)。
如果你需要更高级别的防护,就需要启用CRS规则了。启用CRS规则有利有弊,利就是防护范围更广,全面覆盖各种通用且未知的SQL注入、XSS、RCE远程代码执行、路径穿越等Web攻击,也有一定的0Day防护能力,即便漏洞今天刚曝光,如果这个漏洞涉及SQL注入/XSS,CRS规则依然能直接阻断。但弊也很明显,CRS规则非常严格,正常用户发含特殊字符的文章/代码段时,可能会被误判,需要后期不断调优设置白名单。
CRS在CrowdSec WAF中又分为带外CRS和带内CRS,只有带内CRS才是真正的WAF模式,它的处理动作是:直接阻止请求,如果某个IP地址被阻止3次请求后还会被封禁。而带外CRS是:可疑请求不会立即被阻止,只有屡犯者才会被封禁。我个人是建议直接用带内CRS。
[不推荐]如果你使用带外CRS,则安装:
cscli collections install crowdsecurity/appsec-crs
如果你使用带内CRS,则安装:
cscli collections install crowdsecurity/appsec-crs-inband
然后编辑AppSec日志采集配置文件:
nano /etc/crowdsec/acquis.d/appsec.yaml
带外CRS配置:
appsec_configs:
- crowdsecurity/appsec-default
- crowdsecurity/crs
labels:
type: appsec
listen_addr: 127.0.0.1:7422
source: appsec
name: myAppSecComponent
带内CRS配置:
appsec_configs:
- crowdsecurity/appsec-default
- crowdsecurity/crs-inband
labels:
type: appsec
listen_addr: 127.0.0.1:7422
source: appsec
name: myAppSecComponent
我使用带内CRS来保护我的WordPress,但遇到了非常多的误报,此时可以安装官方提供的插件,来一定程度缓解:
cscli collections install crowdsecurity/appsec-crs-exclusion-plugin-wordpress
很多时候还是会出现误报甚至把自己给拦在门外,导致无法登录服务器的尴尬场面,为避免这种情况发生,我们可以配置白名单。CrowdSec中有三种白名单类型:
- Parser (at enrich stage)
- Postoverflow
- AllowLists
咱这里先介绍一下最简单最暴力的方法:AllowLists,这是一种IP / CIDR级别的白名单。
创建AllowList列表:
cscli allowlists create my_trusted_ip -d "我的代理节点IP"
添加IP或网段:
cscli allowlists add my_trusted_ip 8.9.6.4
cscli allowlists add my_trusted_ip 10.0.0.0/24 -d "内网网段"
查看列表内的IP:
cscli allowlist inspect my_trusted_ip
由于文章篇幅过长,还有很多内容没写,这篇文章先暂时写到这里,未完待续。
荒岛




















