Load Balance ADFS 3.o


#1

Hi,

I am new to HAProxy, I am trying to setup haproxy 1.8 for ADFS 3.0.

Form based authentication is required to goto :

https://ADFSname/adfs/ls/idpinitiatedsignon.aspx

I get following error :

Service unavailable HTTP error 503 : the service is unavailable

I get the following errors in the log file :
Server1 harpoxy[6324] publicip https~ backend/destinationserver1 0/0/0/1/1 503 513 1/1/0/0/0 0/0 adfsname GET adfs/ls/idpinitiatedsignon.aspx HTTP /1.1

Any advice would be much appreciated.


#2

Hi,

Any chance we can see the config you have already?


#3

The problem is more with the ADFS system than with haproxy. ADFS 3.0 (not sure about previous versions) will only react to requests that contain the correct SNI header. Haproxy however does not send it in the configuration you’ve provided.

More on the background here: https://blogs.technet.microsoft.com/applicationproxyblog/2014/06/19/how-to-support-non-sni-capable-clients-with-web-application-proxy-and-ad-fs-2012-r2/

You have three alternatives:

  1. Switch to TCP backend, this way the whole SNI stuff will be worked around, however SSL will not be terminated on the haproxy
  2. Change the ADFS binding through the commands provided in the blockpost so it does react to ALL requests that come to 443, not only those that contain the appropriate SNI header
  3. Change the haproxy configuration so it does include the the SNI header when requests are sent to the backend. Something like sni str(sni.domain.tld) in the server line will do. Beaware: Health checks are to my knowledge not SNI compatible. Meaning you cannot do http health checks as they miss the sni header. you can switch back to just checking for tcp port 443 being alive.

#4

Here’s how I do it. Note: it suffers the checking problem @ularnis mentioned.

### adfs tcp load balancing ###
defaults
  log global
  mode tcp
  option tcplog
  maxconn 3000
  timeout queue 1m
  timeout connect 10s
  timeout client 1m
  timeout server 1m
  timeout check 10s
  load-server-state-from-file global

### adfs ###
listen adfs
  # bind address should be your public address for the pool
  bind 10.0.0.5:443 ssl crt star.pem
  balance roundrobin
  default-server inter 3s rise 2 fall 3
  option httpchk GET /adfs/ls/IdpInitiatedSignon.aspx HTTP/1.0\r\n
  http-check expect string Sign\ in
  reqadd X-Forwarded-Proto:\ https if { ssl_fc }
  server adfs1 10.0.0.6:443 ssl verify none sni ssl_fc_sni #check check-ssl
  server adfs2 10.0.0.7:443 ssl verify none sni ssl_fc_sni #check check-ssl

#5

Starting with haproxy 1.8 you can specify the SNI value used for health checks:

http://cbonte.github.io/haproxy-dconv/1.8/configuration.html#5.2-check-sni


#6

On HAproxy 1.7 we usually use this external health check for ADFS 3.0:

#!/bin/bash
# Script to check SNI enabled servers are healthy
# $3 contains the IP address of the real server and is passed by the
# calling program (HAProxy)
REAL_SERVER_IP=$3
SNI_HOST="adfs.test.com"
SNI_URI="adfs/ls/idpinitiatedsignon.htm"
CHECK_VALUE="Sign in"
# check if previous instance of health check is running & kill if req'd
PIDFILE="/var/run/sni-check-$SNI_HOST.pid"
if [ -f $PIDFILE ]
then
 kill -9 `cat $PIDFILE` > /dev/null 2>&1
fi
# write the process ID to the PID file
echo "$$" > $PIDFILE
# check that the ADFS login page is accessible
CURL_OUTPUT=$(/usr/bin/curl -k -m 5 --resolve \
$SNI_HOST:443:$REAL_SERVER_IP \
https://$SNI_HOST/$SNI_URI)
if [[ $CURL_OUTPUT == *$CHECK_VALUE* ]]
then
 exit 0
else
 exit 1
fi