静看光阴荏苒
不管不顾不问不说也不念

Anubis:基于工作量证明 (PoW)的Web防火墙

Anubis介绍(摘自项目页面):

Anubis是一款Web防火墙,它使用一个或多个挑战来衡量您的连接安全性,以保护上游资源免受爬虫机器人的侵害。该项目旨在帮助保护小型互联网免受人工智能公司源源不断的请求冲击。Anubis力求轻量级。

Anubis的推出相当于采取了核打击措施。这将导致您的网站无法被小型爬虫程序访问,并可能影响像 Internet Archive 这样的“优质机器人”。您可以配置机器人策略定义,将它们明确列入允许列表。我们正在努力完善一套精选的“已知优质”机器人,以便在可发现性和正常运行时间之间取得平衡。

大多数情况下,您不需要这样做,使用Cloudflare保护特定源站即可。但是,如果您无法或不愿使用 Cloudflare,Anubis可以满足您的需求。

简单总结:

不是完整的WAF,更像一个CloudFlare的挑战页面,主要用来防御爬虫,应该也能防御一定量的CC攻击。主要采用PoW机制进行挑战(也可以调整为别的挑战方式)

这篇文章记录下Anubis部署与NGINX集成的步骤,其实坑还是有点多的,如果你选择用套接字配置的话。。可能会遇到各种权限问题,以及各种奇葩BUG。。

Debian安装Anubis、NGINX、CertBot:

wget https://github.com/TecharoHQ/anubis/releases/download/v1.25.0/anubis_1.25.0_amd64.deb
apt install ./anubis_1.25.0_amd64.deb
apt update
apt install nginx python3-certbot-nginx

anubis的systemd单元配置文件采用多实例配置,意味着你可以同时运行多个anubis实例,并且官方推荐的是一个anubis实例对应保护一个上游服务。

假设现在要运行一个保护wordpress的anubis实例,复制一份默认的.env文件:

cp /etc/anubis/default.env /etc/anubis/wordpress.env

编辑.env文件:

nano /etc/anubis/wordpress.env

写入如下内容:

BIND=/run/anubis/wordpress/instance.sock
BIND_NETWORK=unix
SOCKET_MODE=0777
DIFFICULTY=5
METRICS_BIND=:9090
SERVE_ROBOTS_TXT=0
POLICY_FNAME=/etc/anubis/wordpress.botPolicies.yaml
TARGET=unix:///run/nginx-wordpress.sock

因为我用了套接字配置,这里遇到了很多坑,我详细说一下吧。。

1. BIND=套接字的路径必须是:

/run/anubis/{path}/instance.sock

不能用官方文档指出的路径:

/run/anubis/instance.sock

否正你会遇到permission denied报错:

"level":"INFO","msg":"failed to bind to unix:/run/anubis/instance.sock: listen unix /run/anubis/instance.sock: bind: permission denied"}

原因是systemd单元配置文件里面有一行这个配置导致的,见此issue

RuntimeDirectory=anubis/%i

2. SOCKET_MODE=权限务必设置为0777或者0666。默认是0770,这意味着其它用户没有权限访问套接字,Debian的NGINX worker进程默认使用www-data用户运行,与anubis运行使用的用户不相同也不在同一个组里面,所以必须为其它用户设置6(读/写)或者7(读/写/执行)权限,否则NGINX会没有权限访问anubis的套接字。

为什么不把www-data加到同一个组里面?或者把NGINX worker改为anubis相同的用户运行?因为systemd单元配置文件里面有一行这个配置:

DynamicUser=yes

这代表anubis运行时使用的是动态用户,你不知道它运行时会使用什么用户/组。而且这样操作也不够优雅。

接下来复制默认的策略文件:

cp /usr/share/doc/anubis/botPolicies.yaml /etc/anubis/wordpress.botPolicies.yaml

我没有用默认的策略文件,因为里面有很多我不需要用到的规则,所以我自己搓了一份配置:

bots:
  # RSS Feed 放行
  - name: allow-feed
    path_regex: ^/feed/?$
    action: ALLOW

  # 静态资源放行,避免反复 challenge
  - name: allow-static
    path_regex: \.(css|js|jpg|jpeg|png|gif|svg|ico|webp|woff|woff2|ttf)$
    action: ALLOW

  # robots.txt 放行
  - name: allow-robots
    path_regex: ^/robots\.txt$
    action: ALLOW

  # favicon 放行
  - name: allow-favicon
    path_regex: ^/favicon\.ico$
    action: ALLOW

  # 放行正常搜索引擎
  - import: (data)/crawlers/_allow-good.yaml

  # 保持互联网正常工作(ACME/健康检查等)
  - import: (data)/common/keep-internet-working.yaml

  # 明显自动化工具直接拒绝
  - name: deny-bad-useragents
    user_agent_regex: ^$|curl|wget|python|httpclient|go-http-client|scrapy|nikto|sqlmap|masscan|zgrab|nmap
    action: DENY
  
  # 兜底 全站挑战 防CC攻击的关键
  - name: fallback
    path_regex: .*
    action: CHALLENGE
    challenge:
      algorithm: fast
      difficulty: 4

在搓规则的时候记住一个定律即可:anubis规则是从上到下依次匹配的。第一个规则不匹配才会进入到下一个规则再次进行匹配,以此类推。

启动anubis:

systemctl enable --now anubis@wordpress.service

配置NGINX站点:

nano /etc/nginx/sites-available/wordpress

我的完整配置如下:

upstream anubis-wordpress {
  server unix:/run/anubis/wordpress/instance.sock;
}

server {
  server_name anubis.example.com;
  listen [::]:443 ssl ipv6only=on http2; # managed by Certbot
  listen 443 ssl http2; # managed by Certbot
  ssl_certificate /etc/letsencrypt/live/anubis.example.com/fullchain.pem; # managed by Certbot
  ssl_certificate_key /etc/letsencrypt/live/anubis.example.com/privkey.pem; # managed by Certbot
  include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
  ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot

  location / {
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Http-Version $server_protocol;
    proxy_pass http://anubis-wordpress;
  }
}

server {
  listen unix:/run/nginx-wordpress.sock;
  server_name anubis.example.com;
  root "/var/www/wordpress";
  index index.html index.php;

  # 获取客户端真实IP
  set_real_ip_from unix:;
  real_ip_header X-Real-IP;

  location / {
    try_files $uri $uri/ /index.php?$args;
  }

  location ~ \.php$ {
    include snippets/fastcgi-php.conf;
    fastcgi_pass unix:/var/run/php/php7.4-fpm.sock;
  }
}

server {
  listen 80;
  listen [::]:80;
  server_name anubis.example.com;
    if ($host = anubis.example.com) {
        return 301 https://$host$request_uri;
    } # managed by Certbot
    return 404; # managed by Certbot
}

流量的走向是:用户访问你的站点(80端口)301跳转到(443端口),然后流量进入anubis清洗,清洗后的流量通过unix:/run/nginx-wordpress.sock到达上游(后端服务)

如果你使用certbot管理证书,可以这样配置:

upstream anubis-wordpress {
  server unix:/run/anubis/wordpress/instance.sock;
}

server {
  listen 80;
  listen [::]:80;
  server_name anubis.example.com;

  location / {
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Http-Version $server_protocol;
    proxy_pass http://anubis-wordpress;
  }
}

server {
  listen unix:/run/nginx-wordpress.sock;
  server_name anubis.example.com;
  root "/var/www/wordpress";
  index index.html index.php;

  # 获取客户端真实IP
  set_real_ip_from unix:;
  real_ip_header X-Real-IP;

  location / {
    try_files $uri $uri/ /index.php?$args;
  }

  location ~ \.php$ {
    include snippets/fastcgi-php.conf;
    fastcgi_pass unix:/var/run/php/php7.4-fpm.sock;
  }
}

启用站点:

ln -s /etc/nginx/sites-available/wordpress /etc/nginx/sites-enabled/wordpress

用certbot生成证书:

certbot --nginx

certbot会自动补全上述的完整配置,你就不需要自己去配置443那块的内容了。

这里我又遇到一个问题:由NGINX创建的套接字:unix:/run/nginx-wordpress.sock不会在NGINX重启或者停止后自动删除,这导致NGINX启动的时候失败,报错:

bind() to unix:/run/nginx-wordpress.sock failed (98: Address already in use)
bind() to unix:/run/nginx-wordpress.sock failed (98: Address already in use)
bind() to unix:/run/nginx-wordpress.sock failed (98: Address already in use)

搜了一下发现这是一个11年前的BUG,并且在6年前似乎已经修复了,但是我不知道为什么Debian 11的NGINX 1.18还有这个BUG。。要临时解决的话见此答案

将SIGQUIT“优雅关闭”,改为SIGTERM“快速关闭”即可。编辑NGINX的systemd单元配置文件:

systemctl edit nginx

在两段注释中间写入如下配置,写在其他地方是不会生效的哈,这也是systemd奇葩的地方= =:

[Service]
ExecStop=
ExecStop=-/sbin/start-stop-daemon --quiet --stop --retry TERM/5 --pidfile /run/nginx.pid

重启:

systemctl restart nginx

效果如图,说真的这个二次元吉祥物有点丑= =:

下面简单介绍一下多实例部署,多个实例通过.env文件区分:

cp /etc/anubis/default.env /etc/anubis/yourbackend.env
cp /usr/share/doc/anubis/botPolicies.yaml /etc/anubis/yourbackend.botPolicies.yaml

编辑.env:

nano /etc/anubis/yourbackend.env

配置不同的套接字:

BIND=/run/anubis/yourbackend/instance.sock
BIND_NETWORK=unix
SOCKET_MODE=0777
DIFFICULTY=5
METRICS_BIND=:9090
SERVE_ROBOTS_TXT=0
POLICY_FNAME=/etc/anubis/yourbackend.botPolicies.yaml
TARGET=unix:///run/nginx-yourbackend.sock

启动anubis:

systemctl enable --now anubis@yourbackend.service

NGINX也应该监听不同的套接字:

server {
  listen unix:/run/nginx-yourbackend.sock;
  server_name anubis.example.com;
  root "/var/www/wordpress";
  index index.html index.php;

  # Get the visiting IP from the TLS termination server
  set_real_ip_from unix:;
  real_ip_header X-Real-IP;
  ...
}

参考:

https://anubis.techaro.lol/docs/admin/native-install
https://anubis.techaro.lol/docs/admin/installation#environment-variables
https://anubis.techaro.lol/docs/admin/environments/nginx
https://anubis.techaro.lol/docs/admin/policies

赞(0)
未经允许不得转载:荒岛 » Anubis:基于工作量证明 (PoW)的Web防火墙
分享到: 更多 (0)

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址

分享创造快乐

广告合作资源投稿