HAProxy 转发 Windows RDP

部署 HAProxy 实现 Windows RDP 的 TCP 转发、会话粘性及健康检查。


一、环境准备

  • 操作系统:Linux(Debian / Ubuntu / CentOS 等)
  • 安装 HAProxy
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
      # Debian / Ubuntu
    sudo apt update
    sudo apt install haproxy

    # CentOS
    sudo yum install haproxy
    ````

    * **后端 Windows 系统**:

    * 启用 RDP(TCP 端口 3389),并确保防火墙已开放。
    * **网络连通性测试**:
    HAProxy 主机能访问后端所有 RDP 服务器的 3389 端口。

    ---

    ## 二、工作原理概述

    * RDP 基于 TCP 协议,不属于 HTTP,因此只能使用 `mode tcp` 模式,否则连接失败 。
    * Microsoft 客户端登录后会附带 RDP cookie(mstshash),HAProxy 可通过该 cookie 实现会话粘滞性分发。
    * 使用 `tcp-request inspect-delay` 等待初始数据包,提取 RDP cookie 进行粘滞判断。
    * 利用 `stick-table` 存储客户端与服务器映射关系,结合 `persist rdp-cookie` 与 `balance leastconn` 实现负载均衡及粘滞性。
    * 通过 `tcp-check connect port 3389` 对后端进行健康检查。

    ---

    ## 三、HAProxy 配置示例

    ```cfg
    global
    log /dev/log local0
    log /dev/log local1 notice
    chroot /var/lib/haproxy
    stats socket /run/haproxy/admin.sock mode 660 level admin
    stats timeout 30s
    user haproxy
    group haproxy
    daemon

    # Default SSL material locations
    ca-base /etc/ssl/certs
    crt-base /etc/ssl/private

    # See: https://ssl-config.mozilla.org/# server=haproxy& server-version=2.0.3&config=intermediate
    ssl-default-bind-ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384
    ssl-default-bind-ciphersuites TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256
    ssl-default-bind-options ssl-min-ver TLSv1.2 no-tls-tickets

    tune.ssl.default-dh-param 2048
    tune.bufsize 16384
    tune.maxaccept 2000
    tune.maxpollevents 200
    tune.pipesize 524288
    tune.ssl.cachesize 1000000
    tune.ssl.maxrecord 32768
    tune.ssl.default-dh-param 2048
    tune.ssl.cachesize 1000000
    tune.ssl.maxrecord 32768
    tune.maxaccept 2000
    tune.maxpollevents 200
    tune.pipesize 524288
    tune.ssl.cachesize 1000000

    defaults
    log global
    mode http
    option httplog
    option dontlognull
    timeout connect 5000
    timeout client 50000
    timeout server 50000
    errorfile 400 /etc/haproxy/errors/400.http
    errorfile 403 /etc/haproxy/errors/403.http
    errorfile 408 /etc/haproxy/errors/408.http
    errorfile 500 /etc/haproxy/errors/500.http
    errorfile 502 /etc/haproxy/errors/502.http
    errorfile 503 /etc/haproxy/errors/503.http
    errorfile 504 /etc/haproxy/errors/504.http


    frontend rdp_frontend

    mode tcp
    timeout client 1h
    log global
    option tcplog
    tcp-request inspect-delay 5s
    tcp-request content accept if { req_ssl_hello_type 0 }
    tcp-request content accept if { req_rdp_cookie_cnt gt 0 }

    bind *:59001-59002
    option tcplog

    use_backend rdp_1 if { dst_port 59001 }
    use_backend rdp_2 if { dst_port 59002 }

    backend rdp_1
    mode tcp
    balance leastconn
    persist rdp-cookie
    timeout connect 4s
    timeout server 1h
    log global
    option tcplog
    option tcp-check
    option tcpka
    tcp-check connect port 3389
    stick-table type string size 10k expire 12h
    stick on rdp_cookie(mstshash) upper
    server rdp1 10.0.0.1:3389 check verify none
    backend rdp_2
    mode tcp
    balance leastconn
    persist rdp-cookie
    timeout connect 4s
    timeout server 1h
    log global
    option tcplog
    option tcp-check
    option tcpka
    tcp-check connect port 3389
    stick-table type string size 10k expire 12h
    stick on rdp_cookie(mstshash) upper
    server rdp2 10.0.0.2:3389 check verify none

🔑 关键解释

  • 使用 mode tcp 转发原始 RDP 流量,避免 HTTP 层干扰。
  • timeout client/server = 1h 适应长时间空闲 RDP 会话。
  • tcp-request inspect-delay 5s 确保客户端发送 RDP cookie 后进行粘滞决策。
  • stick-table + persist rdp-cookie + balance leastconn 实现既负载均衡又粘性粘滞效果。如用户名重复(仅有限字符识别),可能粘附不同用户,建议使用 user@domain 格式登录保证唯一性。
  • 启用 tcp-check connect port 3389 进行后端健康探测,结合 inter/rise/fall 参数可快速响应服务状态变化。

四、部署与校验步骤

  1. 验证配置语法

    1
    haproxy -c -f /etc/haproxy/haproxy.cfg
  2. 启动或重载服务

    1
    2
    systemctl enable haproxy
    systemctl restart haproxy

五、常见问题

问题 / 场景 解决方案与建议
客户端无 cookie 连接将随机分配,可能多次连接同一后端,建议为无用户场景配置默认后端。
用户名重复 cookie 基于用户名前 9 字符生成,避免不同用户重复影响粘性;推荐使用完整邮箱格式。
多 HAProxy 节点 可使用 peers 同步 stick-table,实现多节点间粘滞表共享。
重载不中断会话 使用 reload 而非 restart,老进程接管现有链接,匹配 stick-table 数据迁移。

六、整体流程概览

1
2
3
4
5
6
7
1. 系统安装 HAProxy
2. 编辑 haproxy.cfg,配置 global / defaults / frontend / backend
3. 配置粘滞、健康检查、负载均衡策略
4. 校验配置并重载服务
5. 测试连接、粘性、故障切换行为
6. 查看映射表,调试用户对应关系
7. 根据部署需求调整(用户名策略、多节点 peering 等)