MS Remote Desktop Gateway, HAProxy y contraseña de adivinanzas

¡Hola amigos!

Hay muchas formas de conectarse desde el hogar a un lugar de trabajo en una oficina. Uno de ellos es usar Microsoft Remote Desktop Gateway. Esto es RDP sobre HTTP. No quiero tocar la configuración de RDGW en sí, no quiero discutir por qué es bueno o malo, tratémoslo como una de las herramientas de acceso remoto. Quiero hablar sobre la protección de su servidor RDGW del malvado Internet. Cuando configuré el servidor RDGW, inmediatamente me preocupé por la protección, especialmente la protección por contraseña. Me sorprendió no haber encontrado artículos en Internet sobre cómo hacer esto. Bueno, tienes que hacerlo tú mismo.

RDGW en sí no tiene ninguna protección. Sí, es posible exponer una interfaz de riel desnudo en una red blanca y funcionará bien. Pero el administrador correcto o IB'shnik estará inquieto por esto. Además, permitirá evitar la situación de bloquear una cuenta cuando un empleado negligente recordó la contraseña de la cuenta corporativa en la computadora del hogar y luego cambió su contraseña.

Una buena manera de proteger los recursos internos del entorno externo es a través de varios servidores proxy, sistemas de publicación y otros WAF. Recuerde que RDGW es todo el mismo http, luego le pide directamente que pegue una solución especializada entre los servidores internos e Internet.

Sé que hay geniales F5, A10, Netscaler (ADC). Como administrador de uno de estos sistemas, diré que también es posible establecer una protección contra la ruptura en estos sistemas. Y sí, estos sistemas lo protegerán de cualquier inundación de sincronización en el camino.

Pero no todas las empresas pueden darse el lujo de comprar una solución de este tipo (y encontrar al administrador de dicho sistema :), ¡pero puede encargarse de la seguridad!

Es posible instalar la versión gratuita de HAProxy en un sistema operativo gratuito. Probé en Debian 10, en la versión de repositorio estable de haproxy 1.8.19. También verifiqué la versión 2.0.xx del repositorio de pruebas.

La configuración de Debian está más allá del alcance de este artículo. Brevemente: en la interfaz blanca, cierre todo excepto 443 puertos, en la interfaz gris; de acuerdo con su política, por ejemplo, cierre todo excepto 22 puertos. Abra solo lo necesario para el trabajo (VRRP, por ejemplo, para ip flotante).

En primer lugar, configuré el modo de puente de haproxy a SSL (también conocido como modo http) y activé el registro para ver qué hay dentro de RDP. Por así decirlo, trepé por el medio. Por lo tanto, falta la ruta / RDWeb especificada en "todos" los artículos sobre la configuración de RDGateway. Todo lo que hay es /rpc/rpcproxy.dll y / remoteDesktopGateway /. En este caso, no se utilizan solicitudes GET / POST estándar, su propio tipo de solicitud es RDG_IN_DATA, RDG_OUT_DATA.

No mucho, pero al menos algo.

Probemos

Comienzo mstsc, voy al servidor, veo cuatro errores 401 (no autorizados) en los registros, luego ingreso el nombre de usuario / contraseña y veo la respuesta 200. Apago, vuelvo a

comenzar, veo los mismos cuatro errores 401 en los registros. Ingreso el nombre de usuario / contraseña incorrectos y veo cuatro nuevamente errores 401. Lo que necesitas. Esto es lo que atraparemos.

Como no fue posible determinar la URL de inicio de sesión y, además, no sé cómo detectar el error 401 en haproxy, detectaré (no realmente capturaré, pero contaré) todos los errores 4xx. También feliz de resolver el problema.

La esencia de la protección será que contaremos la cantidad de errores 4xx (en el backend) por unidad de tiempo y si excede el límite especificado, rechazaremos (en la interfaz) todas las conexiones adicionales de esta ip durante el tiempo especificado.

Técnicamente, esto no será protección con contraseña, será protección contra errores 4xx. Por ejemplo, si solicita con frecuencia una url inexistente (404), la protección también funcionará.

La forma más fácil y más efectiva es contar y superar el backend, si ha aparecido algo superfluo:

frontend fe_rdp_tsc
    bind *:443 ssl crt /etc/haproxy/cert/desktop.example.com.pem
    mode http
    ...
    default_backend be_rdp_tsc


backend be_rdp_tsc
    ...
    mode http
    ...

    # , , 1000 ,   15 ,  -    10 
    stick-table type string len 128 size 1k expire 15s store http_err_rate(10s)
    # ip
    http-request track-sc0 src
    #  http  429,    10   4 
    http-request deny deny_status 429 if { sc_http_err_rate(0) gt 4 }
	
	...
    server rdgw01 192.168.1.33:443 maxconn 1000 weight 10 ssl check cookie rdgw01
    server rdgw02 192.168.2.33:443 maxconn 1000 weight 10 ssl check cookie rdgw02

No es una buena opción, complica. Contaremos con el backend y lo bloquearemos en la interfaz.

Actuaremos groseramente con el atacante, le desconectaremos la conexión TCP.

frontend fe_rdp_tsc
    bind *:443 ssl crt /etc/haproxy/cert/ertelecom_ru_2020_06_11.pem
    mode http
    ...
    #  ip , 1000 ,   15 ,    
    stick-table type ip size 1k expire 15s store gpc0
    # 
    tcp-request connection track-sc0 src
    # tcp ,    >0
    tcp-request connection reject if { sc0_get_gpc0 gt 0 }
	
    ...
    default_backend be_rdp_tsc


backend be_rdp_tsc
    ...
    mode http
    ...
	
    #  ip , 1000 ,   15 ,  -   10 
    stick-table type ip size 1k expire 15s store http_err_rate(10s)
    # ,  -   10   8
    acl errors_too_fast sc1_http_err_rate gt 8
    #     ( )
    acl mark_as_abuser sc0_inc_gpc0(fe_rdp_tsc) gt 0
    #  
    acl clear_as_abuser sc0_clr_gpc0(fe_rdp_tsc) ge 0
    # 
    tcp-request content track-sc1 src
    #, ,  
    tcp-request content reject if errors_too_fast mark_as_abuser
    #,   
    tcp-request content accept if !errors_too_fast clear_as_abuser
	
    ...
    server rdgw01 192.168.1.33:443 maxconn 1000 weight 10 ssl check cookie rdgw01
    server rdgw02 192.168.2.33:443 maxconn 1000 weight 10 ssl check cookie rdgw02

lo mismo, pero cortésmente, devolveremos el error http 429 (Demasiadas solicitudes)

frontend fe_rdp_tsc
    ...
    stick-table type ip size 1k expire 15s store gpc0
    http-request track-sc0 src
    http-request deny deny_status 429 if { sc0_get_gpc0 gt 0 }
    ...
    default_backend be_rdp_tsc

backend be_rdp_tsc
    ...
    stick-table type ip size 1k expire 15s store http_err_rate(10s)
    acl errors_too_fast sc1_http_err_rate gt 8
    acl mark_as_abuser sc0_inc_gpc0(fe_rdp_tsc) gt 0
    acl clear_as_abuser sc0_clr_gpc0(fe_rdp_tsc) ge 0
    http-request track-sc1 src
    http-request allow if !errors_too_fast clear_as_abuser
    http-request deny deny_status 429 if errors_too_fast mark_as_abuser
    ...

Verifique: ejecute mstsc y comience a ingresar contraseñas al azar. Después del tercer intento, me patea en 10 segundos y mstsc da un error. Como se puede ver en los registros.

Explicaciones Estoy lejos de ser un maestro de haproxy. No entiendo por qué, por ejemplo,
http-request deny deny_status 429 if {sc_http_err_rate (0) gt 4} le
permite cometer unos 10 errores antes de que funcione.

Estoy confundido en la numeración de los contadores. Maestros de Haproxy, me alegraré si me complementan, me corrigen, lo hacen mejor.

En los comentarios, puede lanzar otras formas de proteger RD Gateway, será interesante estudiar.

Con respecto al Cliente de escritorio remoto de Windows (mstsc), vale la pena señalar que no es compatible con TLS1.2 (al menos en Windows 7), por lo que tuve que abandonar TLS1; no es compatible con el cifrado actual, por lo que también tuve que dejar los antiguos.

Para aquellos que no entienden nada, solo aprenden y ya quieren hacerlo bien, les daré toda la configuración.

haproxy.conf
global
        log /dev/log    local0
        log /dev/log    local1 notice
        chroot /var/lib/haproxy
        stats socket /run/haproxy/admin.sock mode 660 level admin expose-fd listeners
        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-ciphers ECDH+AESGCM:DH+AESGCM:ECDH+AES256:DH+AES256:ECDH+AES128:DH+AES:RSA+AESGCM:RSA+AES:!aNULL:!MD5:!DSS
        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
        ssl-default-bind-options no-sslv3
        ssl-server-verify none


defaults
        log     global
        mode    http
        option  httplog
        option  dontlognull
        timeout connect 5000
        timeout client  15m
        timeout server  15m
        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 fe_rdp_tsc
    bind *:443 ssl crt /etc/haproxy/cert/dektop.example.com.pem
    mode http
    capture request header Host len 32
    log global
    option httplog
    timeout client 300s
    maxconn 1000

    stick-table type ip size 1k expire 15s store gpc0
    tcp-request connection track-sc0 src
    tcp-request connection reject if { sc0_get_gpc0 gt 0 }

    acl rdweb_domain hdr(host) -i beg dektop.example.com
    http-request deny deny_status 400 if !rdweb_domain
    default_backend be_rdp_tsc


backend be_rdp_tsc
    balance source
    mode http
    log global

    stick-table type ip size 1k expire 15s store http_err_rate(10s)
    acl errors_too_fast sc1_http_err_rate gt 8
    acl mark_as_abuser sc0_inc_gpc0(fe_rdp_tsc) gt 0
    acl clear_as_abuser sc0_clr_gpc0(fe_rdp_tsc) ge 0
    tcp-request content track-sc1 src
    tcp-request content reject if errors_too_fast mark_as_abuser
    tcp-request content accept if !errors_too_fast clear_as_abuser

    option forwardfor
    http-request add-header X-CLIENT-IP %[src]

    option httpchk GET /
    cookie RDPWEB insert nocache
    default-server inter 3s    rise 2  fall 3
    server rdgw01 192.168.1.33:443 maxconn 1000 weight 10 ssl check cookie rdgw01
    server rdgw02 192.168.2.33:443 maxconn 1000 weight 10 ssl check cookie rdgw02


frontend fe_stats
    mode http
    bind *:8080
    acl ip_allow_admin src 192.168.66.66
    stats enable
    stats uri /stats
    stats refresh 30s
    #stats admin if LOCALHOST
    stats admin if ip_allow_admin


¿Por qué dos servidores en el backend? Porque así es como se puede hacer la tolerancia a fallas. Haproxy también puede hacer dos con ip blanca flotante.

Recursos informáticos: puede comenzar con "dos conciertos, dos núcleos, una PC para juegos". Según Wikipedia, esto será suficiente con un margen.

Enlaces:

Configuración de rdp-gateway desde HAProxy El único artículo que encontré donde me molestaron con el descifrado de contraseñas

All Articles