MS Remote Desktop Gateway, HAProxy und Kennwortraten

Hallo Freunde!

Es gibt viele Möglichkeiten, eine Verbindung von zu Hause zu einem Arbeitsplatz in einem Büro herzustellen. Eine davon ist die Verwendung von Microsoft Remote Desktop Gateway. Dies ist RDP über HTTP. Ich möchte nicht auf die Konfiguration von RDGW selbst eingehen, ich möchte nicht diskutieren, warum es gut oder schlecht ist. Lassen Sie uns es als eines der RAS-Tools behandeln. Ich möchte über den Schutz Ihres RDGW-Servers vor dem bösen Internet sprechen. Als ich den RDGW-Server einrichtete, war ich sofort mit dem Schutz beschäftigt, insbesondere mit dem Kennwortschutz. Ich war überrascht, dass ich im Internet keine Artikel dazu gefunden habe. Nun, du musst es selbst tun.

RDGW selbst hat keinen Schutz. Ja, es ist möglich, eine Bare- Rail- Schnittstelle in einem weißen Netz freizulegen, und es funktioniert einwandfrei. Aber der richtige Administrator oder IB'shnik wird davon unruhig sein. Darüber hinaus wird vermieden, dass ein Konto gesperrt wird, wenn sich ein fahrlässiger Mitarbeiter das Kennwort des Unternehmenskontos auf dem Heimcomputer merkt und dann sein Kennwort ändert.

Ein guter Weg, um interne Ressourcen vor der externen Umgebung zu schützen, sind verschiedene Proxys, Veröffentlichungssysteme und andere WAFs. Denken Sie daran, dass RDGW alle dieselbe http ist, und bittet dann direkt darum, eine spezielle Lösung zwischen internen Servern und dem Internet zu verwenden.

Ich weiß, dass es coole F5, A10, Netscaler (ADC) gibt. Als Administrator eines dieser Systeme kann ich auch sagen, dass es möglich ist, auf diesen Systemen einen Schutz gegen Busting einzurichten. Und ja, diese Systeme schützen Sie unterwegs vor jeder Syn-Flut.

Aber nicht jedes Unternehmen kann es sich leisten, eine solche Lösung zu kaufen (und den Administrator eines solchen Systems zu finden :), aber es kann sich um die Sicherheit kümmern!

Es ist möglich, die kostenlose Version von HAProxy auf einem kostenlosen Betriebssystem zu installieren. Ich habe auf Debian 10 in der stabilen Repository-Version von Haproxy 1.8.19 getestet. Ich habe auch Version 2.0.xx aus dem Test-Repository überprüft.

Das Einrichten von Debian selbst geht über den Rahmen dieses Artikels hinaus. Kurz gesagt: Schließen Sie auf der weißen Oberfläche alles außer 443 Ports, auf der grauen Oberfläche - gemäß Ihrer Richtlinie schließen Sie beispielsweise alles außer 22 Ports. Öffnen Sie nur das, was für die Arbeit erforderlich ist (VRRP zum Beispiel für Floating IP).

Zunächst habe ich Haproxy für den SSL-Bridging-Modus (auch bekannt als http-Modus) konfiguriert und die Protokollierung aktiviert, um zu sehen, was in RDP enthalten ist. Sozusagen in die Mitte geklettert. Daher fehlt der in "allen" Artikeln zum Konfigurieren von RDGateway angegebene / RDWeb-Pfad. Alles, was da ist, ist /rpc/rpcproxy.dll und / remoteDesktopGateway /. In diesem Fall werden Standard-GET / POST-Anforderungen nicht verwendet. Der eigene Anforderungstyp lautet RDG_IN_DATA, RDG_OUT_DATA.

Nicht viel, aber zumindest etwas.

Lassen Sie uns testen.

Ich starte mstsc, gehe zum Server, sehe vier 401-Fehler (nicht autorisiert) in den Protokollen, dann gebe ich das Login / Passwort ein und sehe die Antwort 200. Ich

schalte aus, ich starte erneut, ich sehe die gleichen vier 401-Fehler in den Protokollen. Ich gebe den falschen Benutzernamen / das falsche Passwort ein und ich sehe wieder vier Fehler 401. Was Sie brauchen. Das werden wir fangen.

Da es nicht möglich war, die Anmelde-URL zu bestimmen, und ich außerdem nicht weiß, wie ich den 401-Fehler in Haproxy abfangen kann, werde ich alle 4xx-Fehler abfangen (nicht tatsächlich abfangen, sondern zählen). Auch gerne das Problem zu lösen.

Der Kern des Schutzes besteht darin, dass wir die Anzahl der 4xx-Fehler (im Backend) pro Zeiteinheit zählen. Wenn sie den angegebenen Grenzwert überschreiten, lehnen Sie (im Frontend) alle weiteren Verbindungen von dieser IP für die angegebene Zeit ab.

Technisch gesehen ist dies kein Passwortschutz, sondern ein 4xx-Fehlerschutz. Wenn Sie beispielsweise häufig eine nicht vorhandene URL (404) anfordern, funktioniert der Schutz ebenfalls.

Der einfachste und am besten funktionierende Weg ist, im Backend zu zählen und abzuschlagen, wenn etwas Überflüssiges aufgetaucht ist:

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

Keine gute Option, kompliziert. Wir werden auf das Backend zählen und es im Frontend blockieren.

Wir werden unhöflich mit dem Angreifer umgehen und die TCP-Verbindung zu ihm trennen.

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

das gleiche, aber höflich werden wir den Fehler http 429 (Too Many Requests) zurückgeben

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

Überprüfen Sie: Führen Sie mstsc aus und geben Sie die Passwörter nach dem Zufallsprinzip ein. Nach dem dritten Versuch tritt es mich in 10 Sekunden und mstsc gibt einen Fehler aus. Wie aus den Protokollen hervorgeht.

Erklärungen Ich bin weit entfernt von einem Haproxy-Meister. Ich verstehe nicht, warum beispielsweise die
http-Anfrage den Verweigerungsstatus 429 verweigert, wenn Sie mit {sc_http_err_rate (0) gt 4}
ungefähr 10 Fehler machen können, bevor es funktioniert.

Ich bin verwirrt in der Nummerierung der Zähler. Haproxy-Meister, ich würde mich freuen, wenn Sie mich ergänzen, korrigieren, besser machen.

In den Kommentaren können Sie andere Möglichkeiten zum Schutz des RD-Gateways finden. Es wird interessant sein, diese zu studieren.

In Bezug auf den Windows-Remotedesktopclient (mstsc) ist anzumerken, dass er TLS1.2 nicht unterstützt (zumindest in Windows 7), sodass ich TLS1 verlassen musste. unterstützt keine aktuelle Chiffre, daher musste ich auch die alten verlassen.

Für diejenigen, die nichts verstehen, nur lernen und bereits gut machen wollen, werde ich die gesamte Konfiguration geben.

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


Warum zwei Server im Backend? Denn so kann Fehlertoleranz gemacht werden. Haproxy kann auch zwei mit schwebender weißer IP machen.

Computerressourcen: Sie können mit "zwei Gigs, zwei Kernen, einem Gaming-PC" beginnen. Laut Wikipedia wird dies mit einem gewissen Rand ausreichen.

Links:

Konfigurieren des rdp-Gateways über HAProxy Der einzige Artikel, in dem ich Probleme mit dem Knacken von Passwörtern hatte

All Articles