MS Remote Desktop Gateway, HAProxy og password cracking

Hej venner!

Der er mange måder at forbinde hjemmefra til din arbejdsplads på kontoret. En af dem er at bruge Microsoft Remote Desktop Gateway. Dette er RDP over HTTP. Jeg vil ikke her berøre konfigurationen af RDGW i sig selv, eller diskutere hvorfor det er godt eller dårligt; lad os betragte det som et værktøj til fjernadgang. Jeg vil tale om, hvordan du kan beskytte din RDGW-server mod det onde internet. Da jeg satte RDGW-serverne op, sørgede jeg straks for sikkerheden, især beskyttelsen mod brute-force angreb. Jeg blev overrasket over, at jeg ikke kunne finde artikler på internettet om, hvordan man kunne gøre dette. Nå, så må jeg gøre det selv.

RDGW har ikke nogen sikkerhed i sig selv. Ja, du kan eksponere det med en nøgen grænseflade til det offentlige netværk, og det vil fungere fint. Men en rigtig administrator eller sikkerhedsekspert ville være uroligt ved det. Desuden vil det undgå situationen med, at en konto bliver låst, når en uansvarlig medarbejder glemmer sin virksomhedskonto-adgangskode på sin hjemmecomputer, og derefter skifter sin adgangskode.

En god måde at beskytte interne ressourcer mod det eksterne miljø på er ved hjælp af forskellige proxyer, publiceringssystemer og andre WAF-løsninger. Lad os huske, at RDGW trods alt er http, så det ligger ligefor at indsætte en specialiseret løsning mellem interne servere og internettet.

Jeg ved, at der findes fantastiske løsninger som F5, A10, Netscaler(ADC). Som administrator for en af disse systemer kan jeg sige, at det også er muligt at konfigurere beskyttelse mod brute-force angreb på disse systemer. Og ja, disse systemer vil samtidig beskytte dig mod al slags syn flood.

Men ikke hver virksomhed har råd til at købe en sådan løsning (og finde en administrator til systemet :), men sikkerhed kan man godt sørge for!

Det er helt muligt at installere en gratis version af HAProxy på et gratis operativsystem. Jeg har testet det på Debian 10, og i det stabile repository er versionen haproxy 1.8.19. Jeg har også kontrolleret version 2.0.xx fra testing repository.

Lad os lade konfigurationen af selve Debian være uden for denne artikel. Kort sagt: på den offentlige grænseflade skal alt lukkes undtagen port 443; på den private grænseflade - afhængigt af din politik, for eksempel også lukke alt undtagen port 22. Åbn kun det, der er nødvendigt for driften (som VRRP for eksempel, til flydende IP).

Først opsatte jeg HAProxy i SSL bridging mode (det vil sige mode http) og aktiverede logning for at se, hvad der egentlig foregår med RDP. Så at sige, jeg trådte ind i midten. Det, der er angivet i "alle" artikler om opsætning af RDGateway, stien /RDWeb, findes ikke. Alt, hvad der er der, er /rpc/rpcproxy.dll og /remoteDesktopGateway/. Der anvendes ikke standard GET/POST anmodninger, men en særlig type anmodning RDG_IN_DATA, RDG_OUT_DATA.

Lidt, men dog noget.

Lad os teste.

Jeg starter mstsc, går til serveren, og i loggen ser jeg fire fejl 401 (unauthorized), derefter indtaster jeg brugernavn/adgangskode og ser svaret 200.

Jeg slår af, starter igen, og i loggen ser jeg de samme fire fejl 401. Jeg indtaster forkert brugernavn/adgangskode og ser igen fire fejl 401. Det, vi har brug for. Det vil vi fange.

Da jeg ikke kunne bestemme login-URLen, og desuden ikke ved, hvordan man fanger netop fejl 401 i HAProxy, vil jeg fange (eller nærmere tælle) alle 4xx fejl. Det vil også være tilstrækkeligt for at løse opgaven.

Essensen af beskyttelsen vil være, at vi vil tælle antallet af 4xx fejl (på backend) over en tidsenhed, og hvis det overstiger den angivne grænse, så vil vi afvise (på frontend) alle yderligere forbindelser fra denne IP i den angivne tid.

Teknisk set vil dette ikke være en beskyttelse mod brute force angreb, men en beskyttelse mod 4xx fejl. For eksempel, hvis man ofte anmoder om en ikke-eksisterende URL (404), så vil beskyttelsen også aktivere.

Den nemmeste og fungerende måde — er at tælle og blokere på backend, hvis der er noget unødvendigt:

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
    ...

    # Opret en tabel, streng, 1000 elementer, udløber efter 15 sekunder, gem antallet af fejl de sidste 10 sekunder
    stick-table type string len 128 size 1k expire 15s store http_err_rate(10s)
    # Husk IP
    http-request track-sc0 src
    # Forbyd med http fejl 429, hvis der i de sidste 10 sekunder er mere end 4 fejl
    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

Det er ikke den bedste løsning, lad os gøre det mere komplekst. Vi tæller på backend, men blokerer på frontend.

Vi vil håndtere angriberen hårdhændet og afbryde hans TCP-forbindelse.

frontend fe_rdp_tsc
    bind *:443 ssl crt /etc/haproxy/cert/ertelecom_ru_2020_06_11.pem
    mode http
    ...
    # opret en IP-adressetabel med 1000 elementer, udløber efter 15 sekunder, gemmes fra den globale tæller
    stick-table type ip size 1k expire 15s store gpc0
    # hent kilde
    tcp-request connection track-sc0 src
    # afvis TCP-forbindelse, hvis den globale tæller >0
    tcp-request connection reject if { sc0_get_gpc0 gt 0 }
	
    ...
    default_backend be_rdp_tsc


backend be_rdp_tsc
    ...
    mode http
    ...
	
    # opret en IP-adressetabel med 1000 elementer, udløber efter 15 sekunder, gem antallet af fejl over 10 sekunder
    stick-table type ip size 1k expire 15s store http_err_rate(10s)
    # for mange fejl, hvis antallet af fejl over 10 sekunder overstiger 8
    acl errors_too_fast sc1_http_err_rate gt 8
    # marker angrebet i den globale tæller (øge tælleren)
    acl mark_as_abuser sc0_inc_gpc0(fe_rdp_tsc) gt 0
    # nulstil den globale tæller
    acl clear_as_abuser sc0_clr_gpc0(fe_rdp_tsc) ge 0
    # hent kilde
    tcp-request content track-sc1 src
    # afvis, marker som angreb
    tcp-request content reject if errors_too_fast mark_as_abuser
    # tillad, nulstil angrebsmarkøren
    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

det samme, men høfligt, vi vil returnere HTTP-fejl 429 (For mange anmodninger)

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
    ...

Jeg checker: starter mstsc og begynder kaotisk at indtaste adgangskoder. Efter det tredje forsøg inden for 10 sekunder bliver jeg sparket ud, og mstsc viser en fejl. Hvilket også fremgår af loggene.

Forklaringer. Jeg er bestemt ikke en ekspert i haproxy. Jeg forstår ikke, hvorfor for eksempel
http-request deny deny_status 429 if { sc_http_err_rate(0) gt 4 }
giver mulighed for cirka 10 fejl, før det aktiveres.

Jeg bliver forvirret over nummereringen af tællerne. Haproxy-eksperter, jeg vil være glad, hvis I kan hjælpe mig, rette mig, gøre det bedre.

I kommentarerne må I gerne komme med andre måder at beskytte RD Gateway på, det vil være interessant at studere.

Vedrørende den fjernskrivebords-klient Windows (mstsc) skal det bemærkes, at den ikke understøtter TLS1.2 (i det mindste i Windows 7), så jeg måtte lade TLS1 være; den understøtter ikke de aktuelle cifre, så jeg måtte også lade de gamle være.

For those who didn't understand or are just learning and already want to do well, I’ll provide the entire configuration.

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

Why two servers on the backend? Because this allows for fault tolerance. You can also set up HAProxy with two floating public IPs.

Computing resources: you can start with "two gigabytes, two cores, gaming PC." According to Wikipedia this will be sufficient with a margin.

Links:

Configuring RDP gateway from HAProxy
Den eneste artikel, jeg har fundet, der tager sig af at brute-force passwords.

Kilde: habr.com

Køb pålidelig webhosting med DDoS-beskyttelse, VPS VDS-servere 🔥 Køb pålidelig webhosting med DDoS-beskyttelse, VPS VDS-servere | ProHoster