Brána MS Remote Desktop, HAProxy a heslo hrubá sila

Priatelia, ahoj!

Existuje mnoho spôsobov, ako sa pripojiť z domu k pracovnému priestoru v kancelárii. Jedným z nich je použitie brány Microsoft Remote Desktop Gateway. Toto je RDP cez HTTP. Nechcem sa tu dotknúť nastavenia samotného RDGW, nechcem diskutovať o tom, prečo je to dobré alebo zlé, berme to ako jeden z nástrojov vzdialeného prístupu. Chcem hovoriť o ochrane vášho servera RDGW pred zlým internetom. Keď som nastavil server RDGW, okamžite som sa začal zaujímať o bezpečnosť, najmä ochranu pred hrubou silou hesla. Bol som prekvapený, že som na internete nenašiel žiadne články o tom, ako to urobiť. No, budete to musieť urobiť sami.

Samotný RDGW nemá žiadne ochrany. Áno, môže byť vystavený s holým rozhraním bielej sieti a bude to fungovať skvele. To ale správneho správcu alebo špecialistu na informačnú bezpečnosť zneistí. Navyše vám umožní vyhnúť sa situácii zablokovania účtu, keď si neopatrný zamestnanec zapamätal heslo k firemnému účtu na svojom domácom počítači a následne si heslo zmenil.

Dobrý spôsob, ako chrániť interné zdroje pred vonkajším prostredím, je prostredníctvom rôznych proxy serverov, publikačných systémov a iných WAF. Pripomeňme si, že RDGW je stále http, potom si len žiada pripojiť špecializované riešenie medzi interné servery a internet.

Viem, že existujú skvelé F5, A10, Netscaler (ADC). Ako správca jedného z týchto systémov poviem, že na týchto systémoch je možné nastaviť aj ochranu pred hrubou silou. A áno, tieto systémy vás tiež ochránia pred akoukoľvek syn záplavou.

Nie každá firma si ale môže dovoliť zaobstarať takéto riešenie (a nájsť na takýto systém správcu :), no zároveň sa vie postarať o bezpečnosť!

Je úplne možné nainštalovať bezplatnú verziu HAProxy na bezplatný operačný systém. Testoval som na Debian 10, haproxy verzia 1.8.19 v stabilnom repozitári. Testoval som to aj na verzii 2.0.xx z testovacieho úložiska.

Samotné nastavenie debianu necháme mimo rámca tohto článku. Stručne: na bielom rozhraní zatvorte všetko okrem portu 443, na sivom rozhraní - podľa vašej politiky napríklad zatvorte aj všetko okrem portu 22. Otvárajte iba to, čo je potrebné pre prácu (napríklad VRRP pre plávajúcu IP).

Najprv som nakonfiguroval haproxy v režime premostenia SSL (režim http) a zapol protokolovanie, aby som zistil, čo sa deje vo vnútri RDP. Takpovediac som sa dostal do stredu. Chýba teda cesta /RDWeb špecifikovaná vo „všetkých“ článkoch o nastavení RDGateway. Všetko, čo tam je, je /rpc/rpcproxy.dll a /remoteDesktopGateway/. V tomto prípade sa nepoužívajú štandardné požiadavky GET/POST, ale používa sa ich vlastný typ požiadavky RDG_IN_DATA, RDG_OUT_DATA.

Nie veľa, ale aspoň niečo.

Poďme otestovať.

Spustím mstsc, prejdem na server, v protokoloch uvidím štyri chyby 401 (neoprávnené), potom zadám svoje používateľské meno/heslo a zobrazí sa odpoveď 200.

Vypnem ho, znova spustím a v protokoloch vidím rovnaké štyri chyby 401. Zadám nesprávne prihlasovacie meno/heslo a znova sa mi zobrazia štyri chyby 401. To je to, čo potrebujem. Toho sa chytíme.

Keďže nebolo možné určiť prihlasovaciu adresu URL a okrem toho neviem, ako zachytiť chybu 401 v haproxy, zachytím (v skutočnosti nezachytím, ale spočítam) všetky chyby 4xx. Vhodné aj na riešenie problému.

Podstata ochrany bude spočívať v tom, že budeme počítať počet 4xx chýb (na backende) za jednotku času a ak to prekročí zadaný limit, tak odmietneme (na frontende) všetky ďalšie spojenia z tejto ip na určený čas .

Technicky to nebude ochrana pred heslom hrubou silou, bude to ochrana proti chybám 4xx. Ak napríklad často požadujete neexistujúcu adresu URL (404), ochrana bude tiež fungovať.

Najjednoduchším a najefektívnejším spôsobom je počítať s backendom a hlásiť, ak sa objaví niečo navyše:

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

Nie je to najlepšia možnosť, poďme to skomplikovať. Budeme počítať s backendom a blokovať na frontende.

K útočníkovi sa budeme správať hrubo a prerušíme jeho TCP spojenie.

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

to isté, ale zdvorilo vrátime chybu http 429 (Príliš veľa žiadostí)

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

Skontrolujem: spustím mstsc a začnem náhodne zadávať heslá. Po treťom pokuse ma to do 10 sekúnd vyhodí späť a mstsc vypíše chybu. Ako vidno v protokoloch.

Vysvetlenia. Od majstra haproxy mám ďaleko. Nechápem prečo napr
http-request deny deny_status 429 if { sc_http_err_rate(0) gt 4 }
umožňuje urobiť asi 10 chýb, kým to funguje.

Som zmätený z číslovania počítadiel. Majstri haproxy, budem rád, ak ma doplníte, opravíte, zlepšíte.

V komentároch môžete navrhnúť iné spôsoby ochrany brány RD, bude zaujímavé študovať.

Pokiaľ ide o klienta vzdialenej pracovnej plochy Windows (mstsc), stojí za zmienku, že nepodporuje TLS1.2 (aspoň v systéme Windows 7), takže som musel opustiť TLS1; nepodporuje súčasnú šifru, tak som musel nechať aj tie staré.

Pre tých, ktorí ničomu nerozumejú, len sa učia a už chcú robiť dobre, dám vám celú konfiguráciu.

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

Prečo dva servery na backende? Pretože takto môžete dosiahnuť odolnosť voči chybám. Haproxy vie spraviť aj dve s plávajúcou bielou ip.

Výpočtové zdroje: môžete začať s „dvoma koncertmi, dvoma jadrami, herným počítačom“. Podľa wikipedia toto bude stačiť na rezervu.

odkazy:

Nastavenie brány rdp z HAProxy
Jediný článok, ktorý som našiel, kde sa obťažovali hrubo vynútiť heslo

Zdroj: hab.com

Pridať komentár