Hej venner!
Der er mange måder at forbinde hjemmefra til din arbejdsplads på kontoret. En af dem er at bruge Microsoft Remote Desktop Gateway. Dette er RDP over HTTP. Jeg vil ikke her berøre konfigurationen af RDGW i sig selv, eller diskutere hvorfor det er godt eller dårligt; lad os betragte det som et værktøj til fjernadgang. Jeg vil tale om, hvordan du kan beskytte din RDGW-server mod det onde internet. Da jeg satte RDGW-serverne op, sørgede jeg straks for sikkerheden, især beskyttelsen mod brute-force angreb. Jeg blev overrasket over, at jeg ikke kunne finde artikler på internettet om, hvordan man kunne gøre dette. Nå, så må jeg gøre det selv.
RDGW har ikke nogen sikkerhed i sig selv. Ja, du kan eksponere det med en nøgen grænseflade til det offentlige netværk, og det vil fungere fint. Men en rigtig administrator eller sikkerhedsekspert ville være uroligt ved det. Desuden vil det undgå situationen med, at en konto bliver låst, når en uansvarlig medarbejder glemmer sin virksomhedskonto-adgangskode på sin hjemmecomputer, og derefter skifter sin adgangskode.
En god måde at beskytte interne ressourcer mod det eksterne miljø på er ved hjælp af forskellige proxyer, publiceringssystemer og andre WAF-løsninger. Lad os huske, at RDGW trods alt er http, så det ligger ligefor at indsætte en specialiseret løsning mellem interne servere og internettet.
Jeg ved, at der findes fantastiske løsninger som F5, A10, Netscaler(ADC). Som administrator for en af disse systemer kan jeg sige, at det også er muligt at konfigurere beskyttelse mod brute-force angreb på disse systemer. Og ja, disse systemer vil samtidig beskytte dig mod al slags syn flood.
Men ikke hver virksomhed har råd til at købe en sådan løsning (og finde en administrator til systemet :), men sikkerhed kan man godt sørge for!
Det er helt muligt at installere en gratis version af HAProxy på et gratis operativsystem. Jeg har testet det på Debian 10, og i det stabile repository er versionen haproxy 1.8.19. Jeg har også kontrolleret version 2.0.xx fra testing repository.
Lad os lade konfigurationen af selve Debian være uden for denne artikel. Kort sagt: på den offentlige grænseflade skal alt lukkes undtagen port 443; på den private grænseflade - afhængigt af din politik, for eksempel også lukke alt undtagen port 22. Åbn kun det, der er nødvendigt for driften (som VRRP for eksempel, til flydende IP).
Først opsatte jeg HAProxy i SSL bridging mode (det vil sige mode http) og aktiverede logning for at se, hvad der egentlig foregår med RDP. Så at sige, jeg trådte ind i midten. Det, der er angivet i "alle" artikler om opsætning af RDGateway, stien /RDWeb, findes ikke. Alt, hvad der er der, er /rpc/rpcproxy.dll og /remoteDesktopGateway/. Der anvendes ikke standard GET/POST anmodninger, men en særlig type anmodning RDG_IN_DATA, RDG_OUT_DATA.
Lidt, men dog noget.
Lad os teste.
Jeg starter mstsc, går til serveren, og i loggen ser jeg fire fejl 401 (unauthorized), derefter indtaster jeg brugernavn/adgangskode og ser svaret 200.
Jeg slår af, starter igen, og i loggen ser jeg de samme fire fejl 401. Jeg indtaster forkert brugernavn/adgangskode og ser igen fire fejl 401. Det, vi har brug for. Det vil vi fange.
Da jeg ikke kunne bestemme login-URLen, og desuden ikke ved, hvordan man fanger netop fejl 401 i HAProxy, vil jeg fange (eller nærmere tælle) alle 4xx fejl. Det vil også være tilstrækkeligt for at løse opgaven.
Essensen af beskyttelsen vil være, at vi vil tælle antallet af 4xx fejl (på backend) over en tidsenhed, og hvis det overstiger den angivne grænse, så vil vi afvise (på frontend) alle yderligere forbindelser fra denne IP i den angivne tid.
Teknisk set vil dette ikke være en beskyttelse mod brute force angreb, men en beskyttelse mod 4xx fejl. For eksempel, hvis man ofte anmoder om en ikke-eksisterende URL (404), så vil beskyttelsen også aktivere.
Den nemmeste og fungerende måde — er at tælle og blokere på backend, hvis der er noget unødvendigt:
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
...
# Opret en tabel, streng, 1000 elementer, udløber efter 15 sekunder, gem antallet af fejl de sidste 10 sekunder
stick-table type string len 128 size 1k expire 15s store http_err_rate(10s)
# Husk IP
http-request track-sc0 src
# Forbyd med http fejl 429, hvis der i de sidste 10 sekunder er mere end 4 fejl
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
Det er ikke den bedste løsning, lad os gøre det mere komplekst. Vi tæller på backend, men blokerer på frontend.
Vi vil håndtere angriberen hårdhændet og afbryde hans TCP-forbindelse.
frontend fe_rdp_tsc
bind *:443 ssl crt /etc/haproxy/cert/ertelecom_ru_2020_06_11.pem
mode http
...
# opret en IP-adressetabel med 1000 elementer, udløber efter 15 sekunder, gemmes fra den globale tæller
stick-table type ip size 1k expire 15s store gpc0
# hent kilde
tcp-request connection track-sc0 src
# afvis TCP-forbindelse, hvis den globale tæller >0
tcp-request connection reject if { sc0_get_gpc0 gt 0 }
...
default_backend be_rdp_tsc
backend be_rdp_tsc
...
mode http
...
# opret en IP-adressetabel med 1000 elementer, udløber efter 15 sekunder, gem antallet af fejl over 10 sekunder
stick-table type ip size 1k expire 15s store http_err_rate(10s)
# for mange fejl, hvis antallet af fejl over 10 sekunder overstiger 8
acl errors_too_fast sc1_http_err_rate gt 8
# marker angrebet i den globale tæller (øge tælleren)
acl mark_as_abuser sc0_inc_gpc0(fe_rdp_tsc) gt 0
# nulstil den globale tæller
acl clear_as_abuser sc0_clr_gpc0(fe_rdp_tsc) ge 0
# hent kilde
tcp-request content track-sc1 src
# afvis, marker som angreb
tcp-request content reject if errors_too_fast mark_as_abuser
# tillad, nulstil angrebsmarkøren
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 høfligt, vi vil returnere HTTP-fejl 429 (For mange anmodninger)
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 checker: starter mstsc og begynder kaotisk at indtaste adgangskoder. Efter det tredje forsøg inden for 10 sekunder bliver jeg sparket ud, og mstsc viser en fejl. Hvilket også fremgår af loggene.
Forklaringer. Jeg er bestemt ikke en ekspert i haproxy. Jeg forstår ikke, hvorfor for eksempel
http-request deny deny_status 429 if { sc_http_err_rate(0) gt 4 }
giver mulighed for cirka 10 fejl, før det aktiveres.
Jeg bliver forvirret over nummereringen af tællerne. Haproxy-eksperter, jeg vil være glad, hvis I kan hjælpe mig, rette mig, gøre det bedre.
I kommentarerne må I gerne komme med andre måder at beskytte RD Gateway på, det vil være interessant at studere.
Vedrørende den fjernskrivebords-klient Windows (mstsc) skal det bemærkes, at den ikke understøtter TLS1.2 (i det mindste i Windows 7), så jeg måtte lade TLS1 være; den understøtter ikke de aktuelle cifre, så jeg måtte også lade de gamle være.
For those who didn't understand or are just learning and already want to do well, I’ll 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 two servers on the backend? Because this allows for fault tolerance. You can also set up HAProxy with two floating public IPs.
Computing resources: you can start with "two gigabytes, two cores, gaming PC." According to this will be sufficient with a margin.
Links:
Kilde: habr.com
