{"version":"1.0","provider_name":"Gerald Schneider","provider_url":"https:\/\/schneidr.de","author_name":"Gerald Schneider","author_url":"https:\/\/schneidr.de\/author\/gerald\/","title":"HAProxy f\u00fcr Active Directory LDAP","html":"<!-- wp:image {\"id\":2640,\"align\":\"right\"} -->\n<div class=\"wp-block-image\"><figure class=\"alignright\"><img src=\"https:\/\/schneidr.de\/wp-content\/uploads\/2018\/11\/HAProxyCommunityEdition_60px.png\" alt=\"\" class=\"wp-image-2640\"\/><\/figure><\/div>\n<!-- \/wp:image -->\n\n<!-- wp:paragraph -->\n<p>Prinzipiell ist Active Directory auf Redundanz ausgelegt, mehrere Domain Controller sorgen daf\u00fcr dass immer einer verf\u00fcgbar ist. AD nutzt hierf\u00fcr allerdings SRV-DNS-Records, die von Clients die per LDAP auf das Active Directory zugreifen in der Regel nicht beachtet werden. Das hat den Effekt, dass Anwendungen (wenn sie \u00fcberhaupt die M\u00f6glichkeit bieten mehrere LDAP-Server anzugeben) \u00f6fter mal stocken wenn der DC der als erstes eingetragen ist nicht erreichbar ist. In der Regel warten sie dann auf einen Timeout bis sie den n\u00e4chsten DC probieren. Und das bei jeder Abfrage.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:more -->\n<!--more-->\n<!-- \/wp:more -->\n\n<!-- wp:paragraph -->\n<p>Um das zu vermeiden habe ich jetzt zwei Ubuntu-VMs aufgesetzt (16.04, nicht 18.04, siehe den <a href=\"#nachtrag-2019-04-14\">Nachtrag am Ende<\/a>) auf denen jeweils ein HAProxy l\u00e4uft, der alle DCs als Backend eingetragen hat. HAProxy verteilt die Zugriffe auf die vorhandenen DCs, sollte einer ausfallen merkt HAProxy das und leitet die Anfragen an einen anderen DC bis der ausgefallene DC wieder da ist. Zwei VMs, da dadurch nat\u00fcrlich der Proxy der Single Point of Failure wird.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>Ich habe also:<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:list -->\n<ul><li>dcproxy1, 10.11.12.2<\/li><li>dcproxy2, 10.11.12.3<\/li><\/ul>\n<!-- \/wp:list -->\n\n<!-- wp:paragraph -->\n<p>Dazu kommt eine virtuelle IP-Adresse, die von keepalived immer auf einer der beiden VMs gehalten wird.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>Auf beiden VMs die ben\u00f6tigten Dienste installieren:<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:code -->\n<pre class=\"wp-block-code\"><code>sudo apt install keepalived haproxy<\/code><\/pre>\n<!-- \/wp:code -->\n\n<!-- wp:paragraph -->\n<p>Ein SSL-Zertifikat erzeugen und auf beide VMs kopieren. Ich habe daf\u00fcr \/etc\/ssl\/ gew\u00e4hlt. Hinweis: HAProxy ben\u00f6tigt Zertifikate und Key zusammen in einem bundle file.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:code -->\n<pre class=\"wp-block-code\"><code>sudo cat dcproxy.example.com_cert_with_chain.pem dcproxy.example.com_key.pem |sudo tee dcproxy.example.com_bundle.pem<\/code><\/pre>\n<!-- \/wp:code -->\n\n<!-- wp:paragraph -->\n<p>Die Konfiguration f\u00fcr HAProxy ist auf beiden VMs identisch. So sieht meine \/etc\/haproxy\/haproxy.cfg aus:<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:code -->\n<pre class=\"wp-block-code\"><code>global\n        log \/dev\/log    local0\n        log \/dev\/log    local1 notice\n        chroot \/var\/lib\/haproxy\n        stats socket \/run\/haproxy\/admin.sock mode 660 level admin expose-fd listeners\n        stats timeout 30s\n        user haproxy\n        group haproxy\n        daemon\n\n        # Default SSL material locations\n        ca-base \/etc\/ssl\/certs\n        crt-base \/etc\/ssl\/private\n\n        # Default ciphers to use on SSL-enabled listening sockets.\n        # For more information, see ciphers(1SSL). This list is from:\n        #  https:\/\/hynek.me\/articles\/hardening-your-web-servers-ssl-ciphers\/\n        # An alternative list with additional directives can be obtained from\n        #  https:\/\/mozilla.github.io\/server-side-tls\/ssl-config-generator\/?server=haproxy\n        ssl-default-bind-ciphers ECDH+AESGCM:DH+AESGCM:ECDH+AES256:DH+AES256:ECDH+AES128:DH+AES:RSA+AESGCM:RSA+AES:!aNULL:!MD5:!DSS\n        ssl-default-bind-options no-sslv3\n        tune.ssl.default-dh-param 2048\ndefaults\n        log     global\n        mode    http\n        option  httplog\n        option  dontlognull\n        timeout connect 5000\n        timeout client  50000\n        timeout server  50000\n        errorfile 400 \/etc\/haproxy\/errors\/400.http\n        errorfile 403 \/etc\/haproxy\/errors\/403.http\n        errorfile 408 \/etc\/haproxy\/errors\/408.http\n        errorfile 500 \/etc\/haproxy\/errors\/500.http\n        errorfile 502 \/etc\/haproxy\/errors\/502.http\n        errorfile 503 \/etc\/haproxy\/errors\/503.http\n        errorfile 504 \/etc\/haproxy\/errors\/504.http\n\nfrontend ldap_service_front\n  mode                  tcp\n  log                   global\n  bind                  :389\n  description           LDAP Service\n  option                tcplog\n  option                logasap\n  option                socket-stats\n  option                tcpka\n  timeout client        5s\n  default_backend       ldap_service_back\nbackend ldap_service_back\n  server                ldap-1 dc01.ad.example.com:389 check fall 1 rise 1 inter 2s\n  server                ldap-2 dc02.ad.example.com:389 check fall 1 rise 1 inter 2s\n  server                ldap-3 dc03.ad.example.com:389 check fall 1 rise 1 inter 2s\n  mode                  tcp\n  balance               source\n  timeout server        2s\n  timeout connect       1s\n  option                tcpka\n  # https:\/\/www.mail-archive.com\/haproxy@formilux.org\/msg17371.html\n  option                tcp-check\n  tcp-check             connect port 389\n  tcp-check             send-binary 300c0201            # LDAP bind request \"&lt;ROOT>\" simple\n  tcp-check             send-binary 01                  # message ID\n  tcp-check             send-binary 6007                # protocol Op\n  tcp-check             send-binary 0201                # bind request\n  tcp-check             send-binary 03                  # LDAP v3\n  tcp-check             send-binary 04008000            # name, simple authentication\n  tcp-check             expect binary 0a0100            # bind response + result code: success\n  tcp-check             send-binary 30050201034200      # unbind request\n\n# LDAPS\nfrontend ldapS_service_front\n  mode                  tcp\n  log                   global\n  bind                  :636 ssl crt \/etc\/ssl\/dcproxy.example.com_bundle.pem\n  description           LDAPS Service\n  option                tcplog\n  option                logasap\n  option                socket-stats\n  option                tcpka\n  timeout client        5s\n  default_backend       ldaps_service_back\nbackend ldaps_service_back\n  server                ldapS-1 dc01.example.com:636 check fall 1 rise 1 inter 2s verify none check check-ssl ssl\n  server                ldapS-2 dc02.example.com:636 check fall 1 rise 1 inter 2s verify none check check-ssl ssl\n  server                ldapS-3 dc03.example.com:636 check fall 1 rise 1 inter 2s verify none check check-ssl ssl\n  mode                  tcp\n  balance               source\n  timeout server        2s\n  timeout connect       1s\n  option                tcpka\n  option                tcp-check\n  tcp-check             connect port 636 ssl\n  tcp-check             send-binary 300c0201            # LDAP bind request \"&lt;ROOT>\" simple\n  tcp-check             send-binary 01                  # message ID\n  tcp-check             send-binary 6007                # protocol Op\n  tcp-check             send-binary 0201                # bind request\n  tcp-check             send-binary 03                  # LDAP v3\n  tcp-check             send-binary 04008000            # name, simple authentication\n  tcp-check             expect binary 0a0100            # bind response + result code: success\n  tcp-check             send-binary 30050201034200      # unbind request<\/code><\/pre>\n<!-- \/wp:code -->\n\n<!-- wp:paragraph -->\n<p>Als n\u00e4chstes kommt die Konfiguration von keepalived. Die \/etc\/keepalived\/keepalived.conf auf VM1:<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:code -->\n<pre class=\"wp-block-code\"><code>vrrp_instance VI_1 {\n    state MASTER\n    interface ens160\n    virtual_router_id 101\n    priority 101\n    advert_int 1\n    authentication {\n        auth_type PASS\n        auth_pass SECRET\n    }\n    virtual_ipaddress {\n        10.11.12.1\n    }\n}<\/code><\/pre>\n<!-- \/wp:code -->\n\n<!-- wp:paragraph -->\n<p>Die Konfiguration auf VM2 ist nahezu identisch:<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:code -->\n<pre class=\"wp-block-code\"><code>vrrp_instance VI_1 {\n    state MASTER\n    interface ens160\n    virtual_router_id 101\n    priority 100\n    advert_int 1\n    authentication {\n        auth_type PASS\n        auth_pass SECRET\n    }\n    virtual_ipaddress {\n        10.11.12.1\n    }\n}<\/code><\/pre>\n<!-- \/wp:code -->\n\n<!-- wp:paragraph -->\n<p>Der einzige Unterschied ist die priority.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>Das war es schon. Einmal die Dienste neu starten, dann sollte alles funktionieren:<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:code -->\n<pre class=\"wp-block-code\"><code>sudo systemctl restart haproxy\nsudo systemctl restart keepalived<\/code><\/pre>\n<!-- \/wp:code -->\n\n<!-- wp:paragraph -->\n<p>Die VM mit der h\u00f6heren ID bekommt die virtuelle IP. Sollte die VM ausfallen dauert es nach meinen Tests unter 5 Sekunden bis die andere VM die IP bekommt.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>Auf den Clients wird jetzt dcproxy.example.com als LDAP-Server eingetragen, was auf die floating IP zeigt. Damit ist immer einer der beiden HAProxys erreichbar.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>Im vCenter habe ich noch eine DRS-Regel eingerichtet die daf\u00fcr sorgt dass die beiden VMs nie auf dem gleichen Host laufen, idealerweise eine in jedem Rechnerraum.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:heading -->\n<h2 id=\"nachtrag-2019-04-14\">Nachtrag 14.04.2019<\/h2>\n<!-- \/wp:heading -->\n\n<!-- wp:paragraph -->\n<p>Nach einiger Zeit ist ein Problem aufgetreten, und zwar haben die VMs die virtuelle IP-Adresse verloren, aber keiner der beiden keepalived-Instanzen war der Meinung er m\u00fcsste sie wiederherstellen. War auch kein Wunder, die beiden konnten ja Problemlos miteinander reden und haben kein Problem festgestellt. Nach etwas Recherche habe ich dann herausgefunden dass das Problem aufgetreten ist, wenn ein Systemupdate eingespielt wurde, das den systemd neu gestartet hat. Anscheinend hat dann die Netzwerk-Komponente von systemd die gespeicherte Netzwerkkonfiguration geladen, in der die virtuelle IP-Adresse nicht vorkam, und sie damit entfernt.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>Das Problem ist ein <a href=\"https:\/\/github.com\/acassen\/keepalived\/issues\/836\">bekannter Bug im systemd der bei Ubuntu 18.04<\/a> mitgeliefert wird. Er ist zwar behoben, aber die Version in der es behoben ist ist bei Ubuntu 18.04 noch nicht dabei.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>Ich habe verschiedenste Checks versucht, um den IP-Verlust mit Sicherheit zu erkennen, damit keepalived sie wieder einrichten kann, aber zu guter Letzt habe ich die VMs mit Ubuntu 16.04 neu aufgesetzt. Seitdem ist das Problem nicht wieder aufgetreten.<\/p>\n<!-- \/wp:paragraph -->","type":"rich"}