Skip to content

ACL 与请求路由

ACL(Access Control List)是 HAProxy 最强大的功能之一,通过条件匹配实现精细的请求路由、访问控制、流量限制等。

ACL 基础语法

haproxy
acl <acl_name> <criterion> [flags] [operator] <value> [...]

常用匹配条件(criterion)

条件说明示例
path请求路径(不含参数)path /api
path_beg路径前缀匹配path_beg /api
path_end路径后缀匹配path_end .jpg
path_reg路径正则匹配path_reg ^/user/\\d+$
url完整 URLurl \\.png$
url_paramURL 参数值url_param(session_id)
hdrHTTP Headerhdr(Host) example.com
hdr_begHeader 开头匹配hdr_beg(Host) api.
hdr_regHeader 正则hdr_reg(User-Agent) .*curl.*
src客户端源 IPsrc 192.168.1.0/24
src_port客户端源端口src_port 12345
dst目标 IPdst 10.0.0.1
methodHTTP 方法method POST
req.verHTTP 版本req.ver eq 1.1
beslie后端服务器名beslie web1
status响应状态码status 200
cookCookie 值cook(JSESSIONID)
wait_for_end等待请求体完全接收-

常用 ACL 示例

按路径前缀路由

haproxy
frontend http_front
    bind *:80

    # 定义 ACL
    acl is_api     path_beg /api
    acl is_admin   path_beg /admin
    acl is_static  path_beg /static /images /css /js

    # 路由
    use_backend api_backend    if is_api
    use_backend admin_backend  if is_admin
    use_backend static_backend if is_static
    default_backend web_backend

按域名路由(虚拟主机)

haproxy
frontend http_front
    bind *:80
    bind *:443 ssl crt /etc/ssl/certs/

    acl is_api      hdr_beg(Host) api.
    acl is_cdn      hdr_beg(Host) cdn.
    acl is_main     hdr(Host) example.com

    use_backend api_backend  if is_api
    use_backend cdn_backend  if is_cdn
    use_backend web_backend  if is_main

按源 IP 访问控制

haproxy
frontend http_front
    bind *:80

    # 阻止特定 IP
    acl blocked_ip src -f /etc/haproxy/blocked.lst
    http-request deny if blocked_ip

    # 只允许内网访问管理后台
    acl is_internal src 192.168.0.0/16 10.0.0.0/8 172.16.0.0/12
    http-request deny if ! is_internal

按 User-Agent 路由

haproxy
acl is_mobile   hdr_reg(User-Agent) (iPhone|Android)
acl is_bot      hdr(User-Agent) -i -f /etc/haproxy/bots.lst
acl is_wechat   hdr(User-Agent) -i -m beg WeChat

按请求方法限制

haproxy
acl is_delete method DELETE
acl is_upload method POST

http-request deny if is_delete
http-request deny if ! is_upload

按 URL 参数

haproxy
# /products?id=123 形式
acl has_id url_param(id) -m found
acl valid_id url_param(id) -m int gt 0

http-request deny if !has_id or !valid_id

条件组合

使用 andor 以及 ! 取反:

haproxy
# 移动端 + API 路径
acl mobile_api path_beg /api and hdr_beg(Host) m.
use_backend api_mobile_backend if mobile_api

# API 路径 但不是 Webhook
acl is_api      path_beg /api
acl is_webhook  path_beg /api/webhook
use_backend api_backend if is_api and ! is_webhook

# IP 白名单 且 非管理员路径
acl whitelist src -f /etc/haproxy/whitelist.lst
acl admin_path path_beg /admin
http-request allow if whitelist
http-request deny if admin_path

http-request / http-response 指令

在请求或响应阶段执行动作:

haproxy
# 请求阶段
http-request allow                           # 允许
http-request deny [reason] [<status code>]    # 拒绝
http-request redirect location <url>          # 重定向
http-request set-header <name> <value>        # 设置 Header
http-request del-header <name>                # 删除 Header
http-request replace-header <name> <regex> <fmt>  # 替换 Header
http-request set-cookie <name> <value>        # 设置 Cookie
http-request add-header <name> <value>        # 添加 Header

# 响应阶段
http-response allow
http-response deny
http-response set-header <name> <value>
http-response set-cookie <name> <value> [direct] [nocache]
http-response replace-value <name> <regex> <fmt>

实用示例

强制 HTTPS 重定向:

haproxy
frontend http_front
    bind *:80
    http-request redirect scheme https if !{ ssl_fc }

添加真实 IP 到 Header:

haproxy
frontend http_front
    bind *:80
    http-request set-header X-Real-IP %[src]
    http-request set-header X-Forwarded-For %[src]
    http-request set-header X-Forwarded-Proto https if { ssl_fc }

限制单 IP 并发连接数:

haproxy
frontend http_front
    bind *:80
    stick-table type ip size 100k expire 30s store conn_rate(100s)
    acl too_many_conns sc1_conn_rate gt 50
    tcp-request connection reject if too_many_conns

User-Agent 黑名单:

haproxy
frontend http_front
    acl bad_bot hdr(User-Agent) -i -m sub -f /etc/haproxy/bad-bots.lst
    http-request deny if bad_bot

ACL 调试

开启 ACL 日志以便调试:

haproxy
defaults
    option httplog                        # 完整 HTTP 日志
    option logasce                        # 客户端看到的是服务器时间
    log stdout local0

frontend http_front
    http-request log-format "ACL: is_api=%is_api src=%[src]"

查看实时日志:

bash
tail -f /var/log/haproxy/haproxy.log | grep ACL

ACL 性能注意

  • ACL 在内存中缓存结果,复杂正则 (hdr_reg) 匹配开销较大
  • 常用 ACL 放前面,减少匹配次数
  • 频繁使用的 ACL 尽量用 path_beg 而不是正则