MS Remote Desktop Gateway, HAProxy и брутфорс атака

Приятели, здравейте!

Съществуват много начини за свързване от дома до работното място в офиса. Един от тях е да използвате Microsoft Remote Desktop Gateway. Това е RDP върху HTTP. Не искам да се впускам в настройването на самия RDGW, нито да обсъждам защо е добър или лош; нека го разглеждаме просто като инструмент за дистанционен достъп. Искам да говоря за защитата на вашия RDGW сървър от злонамерени интернет заплахи. Когато настроих RDGW сървърите, веднага се загрижих за защитата, особено за защитата от опити за отгатване на паролата. Удивих се, че не намерих статии в интернет за това как може да се направи. Е, ще трябва да го направя сам.

Самият RDGW няма никаква защита. Да, може да бъде изложен с гол интерфейс в публичната мрежа и ще работи прекрасно. Но на добрия администратор или специалист по информационна безопасност това няма да му е спокойно. Освен това, ще избегне ситуацията, в която акаунтът е блокиран, когато непосветен служител запомни паролата на корпоративния си акаунт на домашния компютър и след това я промени.

Добър начин за защита на вътрешните ресурси от външната среда са различни проксита, системи за публикация и други WAF. Нека помним, че RDGW е все пак http, така че направо се налага да внедрим специализирано решение между вътрешните сървъри и интернет.

Знам, че съществуват страхотни решения като F5, A10, Netscaler (ADC). Като администратор на една от тези системи ще кажа, че е възможно да се настрои защита от опити за отгатване и на тези системи. И да, тези системи ще ви защитят и от всякакъв син флууд.

Но не всяка компания може да си позволи да закупи подобно решение (особено да намери администратор на такава система :), но можете да се погрижите за сигурността!

Възможно е да инсталирате безплатна версия на HAProxy на безплатна операционна система. Тествах на Debian 10, в стабилния репо версия haproxy 1.8.19. Също така проверих и версия 2.0.xx от тестовото репо.

Настройките на самия Debian оставяме извън статията. Накратко: на публичния интерфейс затворете всичко, освен порта 443, а на сивия интерфейс — според вашата политика, например затворете всичко, освен порта 22. Отворете само това, което е необходимо за работа (например VRRP за плаващ IP).

Първо настроих haproxy в режим на SSL bridging (т.е. mode http) и включих логовете, за да видя какво точно се случва в RDP. Казано иначе, влязох в средата. Така че, пътят, посочен в "всички" статии за настройка на RDGateway, /RDWeb, липсва. Всичко, което там има, е /rpc/rpcproxy.dll и /remoteDesktopGateway/. При това не се използват стандартни GET/POST заявки, а се използва собствен тип заявка RDG_IN_DATA, RDG_OUT_DATA.

Не много, но поне нещо.

Нека тестваме.

Стартирам mstsc, отивам на сървера, в логовете виждам четири грешки 401 (unauthorized), после въвеждам логин/парола и виждам отговор 200.

Изключвам, стартирам отново, в логовете виждам същите четири грешки 401. Въвеждам грешен логин/парола и отново виждам четири грешки 401. Това е необходимо. Именно това ще ловим.

Тъй като не успях да определя login URL, а и не знам как в haproxy да хвана точно грешката 401, ще ловя (всъщност няма да ловя, а ще броя) всички грешки 4xx. Това също е приемливо за решаване на задачата.

Същността на защитата ще се състои в това, че ще броим броя на грешките 4xx (на backend) за определен период от време и ако той надвиши зададения предел, ще отказваме (на frontend) всички последващи връзки от този IP за определено време.

Технически, това няма да бъде защита от опити за пробив на паролата, а защита от грешки 4xx. Например, ако често се запитва не съществуващ URL (404), защитата също ще сработи.

Най-простият и работещ начин — е на backend да броим и блокираме, ако има нещо излишно:

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

Не най-добрият вариант, нека усложним. Ще броим на backend, а ще блокираме на frontend.

С атакуващия ще постъпим грубо, ще му прекъснем 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

същото, но учтиво, ще връщаме грешка http 429 (Твърде много заявки)

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

Проверявам: стартирам mstsc и започвам да въвеждам пароли без ред. След третия опит за 10 секунди ме отхвърлят, а mstsc издава грешка. Както е видно в логовете.

Обяснения. Аз не съм майстор на haproxy. Не разбирам, защо, например
http-request deny deny_status 429 if { sc_http_err_rate(0) gt 4 }
позволява да се направят около 10 грешки, преди да сработи.

Обърквам се в нумерацията на броячите. Майстори на haproxy, ще се радвам, ако ме допълните, поправите или направите по-добре.

В коментарите можете да предложите други методи за защита на RD Gateway, ще бъде интересно да проучим.

По отношение на клиента за отдалечен работен плот на Windows (mstsc), трябва да се отбележи, че той не поддържа TLS1.2 (във всяко отношение в Windows 7), затова трябваше да остана на TLS1; не поддържа актуални шифри, така че също така трябваше да остана на старите.

За тези, които не разбират и само учат, и вече искат да го направят добре, ще предоставя целия конфиг.

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

        # Локации на основните SSL материали
        ca-base /etc/ssl/certs
        crt-base /etc/ssl/private

        # Виж: 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

Защо два сървъра в бекенд? Защото така може да се направи отказоустойчивост. HAProxy също може да направи два с плаващ бял IP.

Изчислителни ресурси: може да започнете с „две гига, две ядра, игрови компютър“. Според Wikipedia това ще бъде достатъчно с запас.

Връзки:

Настройка на rdp-gateway от HAProxy
Единствената статия, която намерих, където се занимават с груба сила при паролата

Източник: habr.com

Купете надежден хостинг за сайтове със защита от DDoS, VPS и VDS сървъри 🔥 Купете надежден хостинг за сайтове със защита от DDoS, VPS и VDS сървъри | ProHoster