MS Remote Desktop Gateway, HAProxy och lösenordsÄterstÀllning

Hej vÀnner!

Det finns mÄnga sÀtt att ansluta hemifrÄn till arbetsplatsen pÄ kontoret. Ett av dem Àr att anvÀnda Microsoft Remote Desktop Gateway. Detta Àr RDP över HTTP. Jag vill inte gÄ in pÄ hur man konfigurerar sjÀlva RDGW, eller diskutera varför det Àr bra eller dÄligt, lÄt oss se pÄ det som ett av verktygen för fjÀrrÄtkomst. Jag vill prata om hur man skyddar din RDGW-server frÄn det onda internet. NÀr jag stÀllde in RDGW-servrarna blev jag genast bekymrad över sÀkerheten, sÀrskilt skyddet mot lösenordsattacker. Jag blev förvÄnad över att jag inte hittade nÄgra artiklar pÄ nÀtet om hur man kan göra detta. NÄvÀl, jag fÄr göra det sjÀlv.

RDGW i sig har inga skydd. Ja, man kan placera det med en öppen grÀnssnitt i det offentliga nÀtet och det kommer att fungera bra. Men en riktig administratör eller sÀkerhetsspecialist kommer att kÀnna sig obekvÀm med det. Dessutom kan detta förhindra situationen dÀr ett konto blockerats nÀr en oansvarig anstÀlld har sparat sitt företagskontolösenord pÄ sin hemmadator och sedan Àndrat sitt eget lösenord.

Ett bra sÀtt att skydda interna resurser frÄn den externa miljön Àr genom olika proxyservrar, publiceringssystem och andra WAF. Kom ihÄg att RDGW trots allt Àr http, sÄ det Àr sjÀlvklart att sÀtta en specialiserad lösning mellan de interna servrarna och internet.

Jag vet att det finns fantastiska F5, A10, Netscaler (ADC). Som administratör av ett av dessa system kan jag sÀga att det ocksÄ Àr möjligt att konfigurera skydd mot lösenordsattacker pÄ dessa system. Och ja, dessa system skyddar er ocksÄ frÄn olika syn-floods.

Men inte varje företag har rÄd att köpa en sÄdan lösning (och hitta en administratör för ett sÄdant system :), men de kan ÀndÄ ta hand om sÀkerheten!

Det Àr fullt möjligt att installera gratisversionen av HAProxy pÄ ett gratis operativsystem. Jag testade pÄ Debian 10, i det stabila arkivet finns version haproxy 1.8.19. Jag testade ocksÄ pÄ versionerna 2.0.xx frÄn testing-arkivet.

Konfigurationen av sjĂ€lva Debian lĂ€mnar vi utanför artikeln. Kort sagt: stĂ€ng allt pĂ„ den offentliga grĂ€nssnittet, förutom port 443, pĂ„ det grĂ„ grĂ€nssnittet - enligt din policy, till exempel stĂ€nga allt utom port 22. Öppna bara det som Ă€r nödvĂ€ndigt för driften (VRRP till exempel, för fluktuerande ip).

Först och frÀmst har jag konfigurerat haproxy i SSL bridging-lÀge (Àven kÀnt som mode http) och aktiverat loggning för att se vad som egentligen hÀnder inne i RDP. Man kan sÀga att jag har satt mig mitt emellan. SÄ, den vÀg som anges i "alla" artiklar om RDGateway, /RDWeb, saknas. Det som finns dÀr Àr bara /rpc/rpcproxy.dll och /remoteDesktopGateway/. Standard GET/POST-förfrÄgningar anvÀnds inte, utan en egen typ av förfrÄgningar: RDG_IN_DATA, RDG_OUT_DATA.

Inte mycket, men Ätminstone nÄgot.

LÄt oss testa.

Jag startar mstsc, gÄr till servern och ser fyra fel 401 (obehörig) i loggarna. Sedan anger jag anvÀndarnamn/lösenord och ser svaret 200.

Jag stÀnger ner, startar om och ser de samma fyra felen 401 i loggarna. Jag anger felaktigt anvÀndarnamn/lösenord och ser Äterigen fyra fel 401. Det Àr nÄgot att bygga vidare pÄ. Detta ska vi fÄnga.

Eftersom jag fortfarande inte lyckats bestÀmma login-URL:en, och dessutom inte vet hur man fÄngar exakt fel 401 i haproxy, kommer jag att fÄnga (egentligen rÀkna) alla 4xx-fel. Det kommer ocksÄ att fungera för att lösa problemet.

Skyddet kommer att bestÄ av att vi rÀknar antalet 4xx-fel (pÄ backend) per tidsenhet och om det överskrider den angivna grÀnsen, kommer vi att avvisa (pÄ frontend) alla vidare anslutningar frÄn den IP-adressen under den angivna tiden.

Tekniskt sett kommer detta inte att vara ett skydd mot lösenordsattacker, utan ett skydd mot 4xx-fel. Till exempel, om man ofta begÀr en icke-existerande URL (404), kommer skyddet ocksÄ att aktiveras.

Det enklaste och fungerande sÀttet Àr att rÀkna och blockera pÄ backend om nÄgot extra dyker upp:

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

    # skapa en tabell, av string-typ, 1000 element, utgÄr pÄ 15 sekunder, spara antalet fel under de senaste 10 sekunderna
    stick-table type string len 128 size 1k expire 15s store http_err_rate(10s)
    # kom ihÄg IP
    http-request track-sc0 src
    # förbjuda med http-fel 429 om mer Àn 4 fel under de senaste 10 sekunderna
    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

Inte den bÀsta lösningen, vi gör det mer komplicerat. Vi kommer att rÀkna pÄ backend och blockera pÄ frontend.

Vi kommer att hantera angriparen grovt, genom att bryta TCP-anslutningen.

frontend fe_rdp_tsc
    bind *:443 ssl crt /etc/haproxy/cert/ertelecom_ru_2020_06_11.pem
    mode http
    ...
    # skapa en IP-adress-tabell, 1000 element, som gÄr ut efter 15 sekunder, spara frÄn den globala rÀknaren
    stick-table type ip size 1k expire 15s store gpc0
    # hÀmta kÀlla
    tcp-request connection track-sc0 src
    # avvisa TCP-anslutning om den globala rÀknaren >0
    tcp-request connection reject if { sc0_get_gpc0 gt 0 }
	
    ...
    default_backend be_rdp_tsc


backend be_rdp_tsc
    ...
    mode http
    ...
	
    # skapa en IP-adress-tabell, 1000 element, som gÄr ut efter 15 sekunder, spara antal fel under 10 sekunder
    stick-table type ip size 1k expire 15s store http_err_rate(10s)
    # mÄnga fel om antalet fel under 10 sekunder överskrider 8
    acl errors_too_fast sc1_http_err_rate gt 8
    # markera attacken i den globala rÀknaren (öka rÀknaren)
    acl mark_as_abuser sc0_inc_gpc0(fe_rdp_tsc) gt 0
    # nollstÀll den globala rÀknaren
    acl clear_as_abuser sc0_clr_gpc0(fe_rdp_tsc) ge 0
    # hÀmta kÀlla
    tcp-request content track-sc1 src
    # avvisa, markera som attack
    tcp-request content reject if errors_too_fast mark_as_abuser
    # tillÄt, nollstÀll attackflaggan
    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

samma sak, men artigt, vi kommer att returnera fel http 429 (För mÄnga begÀrningar)

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

Jag testar: startar mstsc och börjar oordnat ange lösenord. Efter tredje försöket inom 10 sekunder blir jag utsparkad, och mstsc ger ett fel. Vilket syns i loggarna.

Förklaringar. Jag Àr lÄngt ifrÄn en expert pÄ haproxy. Jag förstÄr inte varför, till exempel
http-request deny deny_status 429 if { sc_http_err_rate(0) gt 4 }
tillÄter omkring 10 fel innan det slÄr till.

Jag förvirrar mig nÀr det gÀller rÀknarnumreringen. MÀstarna av haproxy, jag skulle uppskatta om ni kompletterar mig, korrigerar, eller gör det bÀttre.

Ni kan gÀrna lista andra sÀtt att skydda RD Gateway i kommentarerna, det skulle vara intressant att studera.

GÀllande Windows Remote Desktop Client (mstsc) Àr det vÀrt att notera att den inte stöder TLS1.2 (i alla fall i Windows 7), sÄ jag var tvungen att anvÀnda TLS1; den stöder inte aktuella cipher, sÄ jag var ocksÄ tvungen att anvÀnda Àldre.

För de som inte förstod nÄgot och bara lÀr sig, och redan vill göra det bra, hÀr Àr hela konfigurationen.

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

        # StandardinstÀllningar för SSL-material
        ca-base /etc/ssl/certs
        crt-base /etc/ssl/private

        # Se: 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

Varför tvÄ servrar pÄ backend? För det gör det möjligt att skapa redundans. Du kan ocksÄ ha HAProxy med tvÄ flytande vita IP-adresser.

BerÀkningsresurser: man kan börja med "tvÄ gigabyte, tvÄ kÀrnor, en speldator". Enligt Wikipedia detta kommer att vara tillrÀckligt med marginal.

LĂ€nkar:

Konfiguration av rdp-gateway för HAProxy
Den enda artikeln jag har funnit dÀr man har oroat sig över lösenordsÄterstÀllning.

KĂ€lla: habr.com

Köp pĂ„litlig webbhotell med DDoS-skydd, VPS VDS-servrar đŸ”„ Köp pĂ„litlig webbhotell med DDoS-skydd, VPS VDS-servrar | ProHoster