Salut, prieteni!
Există multiple metode de conectare de acasă la locul de muncă din birou. Una dintre ele este utilizarea Microsoft Remote Desktop Gateway. Acesta este RDP peste HTTP. Nu vreau să discut despre configurarea RDGW, nici să analizez de ce este bun sau rău, să-l considerăm uneltele de acces la distanță. Vreau să vorbesc despre protecția serverului dumneavoastră RDGW împotriva internetului rău. Când am configurat serverele RDGW, m-am preocupat imediat de protecție, în special de protecția împotriva atacurilor de tip bruteforce. Am fost surprins că nu am găsit articole pe internet despre cum se poate face acest lucru. Ei bine, va trebui să facem singuri.
RDGW, în sine, nu are nici o protecție. Da, poate fi expus cu o interfață golită în rețeaua publică și va funcționa perfect. Dar unui administrator corect sau unui specialist în securitate îi va fi greu cu acest lucru. De asemenea, va evita situația de blocare a contului atunci când un angajat neglijent a memorat parola contului de companie pe computerul de acasă și apoi a schimbat parola.
O bună metodă de protejare a resurselor interne împotriva mediului extern sunt diversele proxy-uri, sisteme de publicare și alte WAF. Să ne amintim că RDGW este, totuși, HTTP, deci se sugerează să utilizăm o soluție specializată între serverele interne și internet.
Știu că există soluții avansate precum F5, A10, Netscaler (ADC). Ca administrator al uneia dintre aceste sisteme, pot spune că este, de asemenea, posibil să configurezi protecția împotriva atacurilor de tip bruteforce pe aceste sisteme. Și da, aceste sisteme vor proteja în plus împotriva oricăror inundații de tip SYN.
Dar nu orice companie își poate permite să achiziționeze o astfel de soluție (sau să găsească un administrator pentru un astfel de sistem :), dar poate avea grijă de securitate!
Este posibil să instalați o versiune gratuită HAProxy pe un sistem de operare gratuit. Am testat pe Debian 10, în repositoriul stabil având versiunea haproxy 1.8.19. De asemenea, am verificat versiunile 2.0.xx din repositorul testing.
Configurarea Debian-ului în sine o lăsăm în afara acestui articol. Pe scurt: în interfața publică, blocați totul cu excepția portului 443; în interfața gri – conform politicii dumneavoastră, de exemplu, blocați totul cu excepția portului 22. Deschideți doar ceea ce este necesar pentru funcționare (cum ar fi VRRP pentru IP-ul flotant).
În primul rând, am configurat haproxy în modul SSL bridging (cunoscut și ca modul http) și am activat logging-ul pentru a observa ce se întâmplă în interiorul RDP. Adică, am intervenit în mijloc. Astfel, calea specificată în „toate” articolele despre configurarea RDGateway, /RDWeb, lipsește. Tot ce există acolo sunt /rpc/rpcproxy.dll și /remoteDesktopGateway/. În plus, nu sunt folosite cereri standard GET/POST, ci un tip propriu de cerere RDG_IN_DATA, RDG_OUT_DATA.
Nu mult, dar măcar ceva.
Hai să testăm.
Deschid mstsc, merg pe server, în loguri văd patru erori 401 (neautorizat), apoi introduc utilizator/parolă și primesc răspuns 200.
Dezactivez, repornesc, în loguri văd aceleași patru erori 401. Introduc utilizator/parolă greșită și văd din nou patru erori 401. Exact ce este necesar. Asta vom monitoriza.
Deoarece nu am reușit să determin URL-ul de login și, în plus, nu știu cum să monitorizez în haproxy exact eroarea 401, voi monitoriza (de fapt, voi număra) toate erorile 4xx. Asta este acceptabil pentru a rezolva problema.
Ideea protecției va consta în numărarea erorilor 4xx (pe backend) într-o unitate de timp și, dacă acestea depășesc limita specificată, se vor respinge (pe frontend) toate conexiunile ulterioare de la acel IP pentru o perioadă specificată.
Tehnic, aceasta nu va fi o protecție împotriva atacurilor de tip brute-force, ci o protecție împotriva erorilor 4xx. De exemplu, dacă se solicită frecvent un URL inexistent (404), protecția va funcționa și aici.
Cel mai simplu și funcțional mod de a face asta este să contorizăm pe backend și să respingem dacă apar neobișnuite:
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
...
#creați o tabelă, tip string, 1000 de elemente, expiră după 15 secunde, salvați numărul de erori din ultimele 10 secunde
stick-table type string len 128 size 1k expire 15s store http_err_rate(10s)
#salvați ip-ul
http-request track-sc0 src
#interziceți cu eroare http 429, dacă în ultimele 10 secunde sunt mai mult de 4 erori
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
Nu este cea mai bună opțiune, să complicăm. Voi contoriza pe backend și voi bloca pe frontend.
Vom trata atacatorul cu brutalitate, ne vom desface conexiunea TCP.
frontend fe_rdp_tsc
bind *:443 ssl crt /etc/haproxy/cert/ertelecom_ru_2020_06_11.pem
mode http
...
#creează o tabelă de adrese IP, 1000 de elemente, va expira după 15 secunde, păstrează din contorul global
stick-table type ip size 1k expire 15s store gpc0
#ia sursa
tcp-request connection track-sc0 src
#respinge conexiunea tcp dacă contorul global >0
tcp-request connection reject if { sc0_get_gpc0 gt 0 }
...
default_backend be_rdp_tsc
backend be_rdp_tsc
...
mode http
...
#creează o tabelă de adrese IP, 1000 de elemente, va expira după 15 secunde, păstrează numărul de erori timp de 10 secunde
stick-table type ip size 1k expire 15s store http_err_rate(10s)
#multe erori dacă numărul de erori în 10 secunde depășește 8
acl errors_too_fast sc1_http_err_rate gt 8
#marchează atacul în contorul global (crește contorul)
acl mark_as_abuser sc0_inc_gpc0(fe_rdp_tsc) gt 0
#resetează contorul global
acl clear_as_abuser sc0_clr_gpc0(fe_rdp_tsc) ge 0
#ia sursa
tcp-request content track-sc1 src
#respinge, marchează ca atac
tcp-request content reject if errors_too_fast mark_as_abuser
#permite, resetează semnul atacului
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
aceleași lucruri, dar politicos, vom returna eroarea http 429 (Prea multe cereri)
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
...
Verific: pornesc mstsc și încep să tastez parolele în mod aleatoriu. După a treia încercare în 10 secunde sunt respins, iar mstsc arată o eroare. Ce se vede în jurnale.
Explicații. Nu sunt un expert în haproxy. Nu înțeleg de ce, de exemplu
http-request deny deny_status 429 if { sc_http_err_rate(0) gt 4 }
permite să facă aproximativ 10 erori înainte de a se activa.
Mă pierd în numerotarea contorilor. Experții haproxy, aș fi bucuros dacă m-ați completa, corecta, îmbunătăți.
În comentarii, puteți sugera alte metode de protecție a RD Gateway, ar fi interesant de studiat.
Referitor la clientul desktop de la distanță Windows (mstsc), merită menționat că acesta nu suportă TLS1.2 (cel puțin în Windows 7), așa că a trebuit să las TLS1; nu suportă cipherele actuale, așa că a trebuit să păstrez vechile.
Pentru cei care nu au înțeles nimic și abia începe să învețe, voi prezenta întreaga configurație.
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
# Locațiile implicite pentru materialele SSL
ca-base /etc/ssl/certs
crt-base /etc/ssl/private
# Vezi: 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
De ce două servere pe backend? Pentru că astfel se poate asigura redundanța. Puteți configura HAProxy cu două IP-uri publice.
Resursele de calcul: puteți începe cu „două gigabytes, două nuclee, PC de gaming”. Conform acesta va fi suficient cu un surplus.
Linkuri:
Sursa: habr.com
