MS Remote Desktop Gateway, HAProxy og passordknekking

Hei, venner!

Det finnes mange måter å koble seg fra hjemmet til arbeidsplassen på kontoret. En av dem er å bruke Microsoft Remote Desktop Gateway. Dette er RDP over HTTP. Jeg ønsker ikke å gå inn på hvordan man konfigurerer RDGW her, eller diskutere hvorfor det er bra eller dårlig, la oss behandle det som et av verktøyene for fjernadgang. Jeg vil snakke om hvordan du kan beskytte RDGW-serveren din mot den onde internettverden. Da jeg satt opp RDGW-serverne, begynte jeg umiddelbart å tenke på sikkerhet, spesielt beskyttelse mot passordknekking. Jeg ble overrasket over at jeg ikke fant artikler på nettet om hvordan dette kan gjøres. Vel, det ser ut til at jeg må gjøre det selv.

Selve RDGW har ingen innebygde beskyttelser. Ja, det kan settes opp med bare grensesnittet i det åpne nettet og vil fungere fint. Men en ordentlig administrator eller sikkerhetsekspert vil ikke føle seg trygg med det. I tillegg kan det forhindre situasjoner med blokkerte kontoer, når en uaktsom ansatt husker passordet til bedriftens konto på sin hjemmedatamaskin og deretter endrer sitt eget passord.

En god måte å beskytte interne ressurser mot eksterne trusler på, er gjennom proxyer, publiseringssystemer og andre WAF-løsninger. Husk at RDGW til syvende og sist er http, så det er naturlig å installere en spesialisert løsning mellom interne servere og internett.

Jeg vet at det finnes avanserte løsninger som F5, A10, Netscaler(ADC). Som administrator for et av disse systemene kan jeg si at det også er mulig å konfigurere beskyttelse mot passordknekking på disse systemene. Og ja, disse systemene vil samtidig beskytte deg mot syn flood-angrep.

Men langt fra alle selskaper har råd til å kjøpe en slik løsning (og finne en administrator for et slikt system :), men sikkerheten kan de ta seg av!

Det er fullt mulig å installere en gratis versjon av HAProxy på et gratis operativsystem. Jeg har testet det på Debian 10, og i det stabile depositories er versjonen haproxy 1.8.19. Jeg har også sjekket versjoner 2.0.xx fra testing repository.

Vi lar konfigurasjonen av selve Debian være utenfor artikkelen. Kort oppsummert: på det åpne grensesnittet, lås alt bortsett fra port 443, og på det grå grensesnittet - i henhold til din policy, for eksempel også låse alt bortsett fra port 22. Bare åpne det som er nødvendig for drift (VRRP for eksempel, for flyttbar IP).

Først konfigurerte jeg haproxy i SSL bridging mode (også kjent som mode http) og aktiverte logging for å se hva som skjer med RDP. Med andre ord, jeg plasserte meg midt i det. Så, stien som er nevnt i «alle» artikler om konfigurasjon av RDGateway, /RDWeb, finnes ikke. Alt som er der, er /rpc/rpcproxy.dll og /remoteDesktopGateway/. Standard GET/POST forespørselen brukes ikke, det brukes en egen forespørselstype RDG_IN_DATA, RDG_OUT_DATA.

Ikke mye, men noe.

La oss teste.

Jeg kjører mstsc, går til serveren, og ser fire 401 (unauthorized) feil i loggene, deretter skriver jeg inn brukernavn/passord og ser svar 200.

Jeg kobler fra, starter på nytt, og i loggene ser jeg de samme fire 401 feilene. Jeg skriver inn feil brukernavn/passord og ser igjen fire 401 feil. Dette er det vi skal overvåke.

Siden jeg ikke klarte å identifisere login URL-en, og jeg heller ikke vet hvordan man kan fange opp 401-feilen i haproxy, vil jeg fange opp (egentlig telle) alle 4xx feilene. Det vil også være tilstrekkelig for å løse oppgaven.

Essensen av beskyttelsen vil være at vi vil telle antall 4xx feil (på backend) over en tidsenhet, og hvis det overskrider en spesifisert grense, vil vi avvise (på frontend) alle videre forbindelser fra denne IP-en i den angitte tidsperioden.

Teknisk sett vil dette ikke være en beskyttelse mot passordprøver, men en beskyttelse mot 4xx feil. For eksempel, hvis man ofte ber om en ikke-eksisterende URL (404), vil beskyttelsen også aktivere.

Den enkleste og mest fungerende metoden er å telle opp og avvise på backend, hvis det skulle dukke opp noe overflødig:

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

    #opprett en tabell, teksttype, 1000 elementer, utløper etter 15 sek, lagre antall feil de siste 10 sek
    stick-table type string len 128 size 1k expire 15s store http_err_rate(10s)
    #huske ip
    http-request track-sc0 src
    #forby med http-feil 429, hvis det er mer enn 4 feil de siste 10 sek
    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

Ikke den beste løsningen, la oss gjøre det mer komplisert. Vi vil telle på backend, mens vi blokkerer på frontend.

Vi vil håndtere angriperen brutalt ved å kutte TCP-tilkoblingen hans.

frontend fe_rdp_tsc
    bind *:443 ssl crt /etc/haproxy/cert/ertelecom_ru_2020_06_11.pem
    mode http
    ...
    #laga IP-adresser, 1000 elementer, utløper etter 15 sekunder, lagre fra den globale telleren
    stick-table type ip size 1k expire 15s store gpc0
    #få kilde
    tcp-request connection track-sc0 src
    #avvise tcp-tilkoblingen hvis den globale telleren >0
    tcp-request connection reject if { sc0_get_gpc0 gt 0 }
	
    ...
    default_backend be_rdp_tsc


backend be_rdp_tsc
    ...
    mode http
    ...
	
    #laga IP-adresser, 1000 elementer, utløper etter 15 sekunder, lagre antall feil de siste 10 sek
    stick-table type ip size 1k expire 15s store http_err_rate(10s)
    #mange feil, hvis antall feil de siste 10 sek oversteget 8
    acl errors_too_fast sc1_http_err_rate gt 8
    #markere angrep i den globale telleren (øke telleren)
    acl mark_as_abuser sc0_inc_gpc0(fe_rdp_tsc) gt 0
    #nullstille den globale telleren
    acl clear_as_abuser sc0_clr_gpc0(fe_rdp_tsc) ge 0
    #få kilde
    tcp-request content track-sc1 src
    #avvise, merke som angrep
    tcp-request content reject if errors_too_fast mark_as_abuser
    #tillate, tilbakestille angrepsflagget
    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 på en høflig måte, vi vil returnere HTTP-feil 429 (For mange forespørsler)

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 sjekker: kjører mstsc og begynner å skrive inn passord tilfeldig. Etter det tredje forsøket på 10 sekunder blir jeg kastet ut, og mstsc gir en feil. Dette vises i loggene.

Forklaringer. Jeg er på ingen måte en mester i haproxy. Jeg forstår ikke hvorfor, for eksempel
http-request deny deny_status 429 if { sc_http_err_rate(0) gt 4 }
tillater omtrent 10 feil før det aktiveres.

Jeg blir forvirret av nummereringen av tellerne. Haprosy-mestre, jeg vil sette pris på om dere kan supplere meg, rette meg, eller gjøre det bedre.

I kommentarene kan du nevne andre måter å beskytte RD Gateway på, det vil være interessant å studere.

Når det gjelder Windows Remote Desktop Client (mstsc), er det verdt å merke seg at den ikke støtter TLS1.2 (i hvert fall i Windows 7), så jeg måtte bruke TLS1; den støtter ikke aktuelle cipher, så jeg måtte også beholde de gamle.

For those who haven’t understood anything yet and want to do it well, I will 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 have two servers in the backend? Because this allows for fault tolerance. You can also set up HAProxy with two floating white IPs.

Computing resources: You can start with "two gigs, two cores, gaming PC." According to Wikipedia this will be sufficient with some margin.

Lenker:

Configuring the RDP gateway with HAProxy
Den eneste artikkelen jeg fant, som tar hensyn til passordgjennomgang.

Kilde: habr.com

Kjøp pålitelig hosting for nettsteder med DDoS-beskyttelse, VPS VDS-servere 🔥 Kjøp pålitelig hosting for nettsteder med DDoS-beskyttelse, VPS VDS-servere | ProHoster