Getting a string from the CDN HTTP request

Hi guys,

I have been trying for a long time to get this to work without much success :frowning:
I hope someone here can point me in the right direction :pray:

A website via CDN, has this string on its header.
This is on the CDN side meaning, the HaProxy is able to identify it.

    set req.http.shared-secret = "PASSWORD";

How can I make HaProxy to identify, and accept the HTTP request if that string is present?

The idea is that HaProxy will only accept the request if that string is identified, deny otherwise. Requested coming outside the CDN will be blocked.

I’ve tried this but it does not work. Only the main page works, If I try to navigate the website I get no access. I am not expert so I don’t know what is exactly wrong.

    acl allow_traffic_from_cdn req.hdr(Host) -m str PASSWORD
    http-request deny if !allow_traffic_from_cdn

Any idea is welcome.

Thank you

Hi,

First of all, your acl is matching the HOST header, so it can’t do what you want.

Also,

is the header key ‘PASSWORD’, or is it the value of the header ?

If it’s the key, i use to filter access to private services with simple filter like (not sur if it’s case sensitive or not):
http-request deny if !{ req.hdr(PASSWORD) -m found }

If it’s the value, you can do something like
http-request deny if !{ req.hdr(<header key>) PASSWORD }

Let us know if you find a way to do what you want :slight_smile:

@rhada, first of all, thank you for the support :slight_smile:
I have tried those without success :confused:
Below, is the only info set on the CDN side

        set req.http.shared-secret = "PASSWORD";

So I believe “shared-secret” is the key header.
I have asked Reddit and got to your second suggestion:

http-request deny if !{ req.hdr(shared-secret) "PASSWORD" }

I only have access to the main page, if I try to access anything else if fails with an ERROR: SERVER ERROR.
I might be wrong but it seems like only the main page sends the key, any child page does not have it so why the error.

When I try:

 http-request deny if !{ req.hdr(PASSWORD) -m found }

Everything stops, even the main page.

You have no idea how long I have been trying to solve this headache and got nowhere.

My plan B is to use the certificate instead, check the CA and validation and that is. It seems easier to setup. I was provided with the link Certificate Authentication so I might it a try.

Thank you again for your time and support.

A few things you need to provide for us to be actually able to help you:

  • the output of haproxy -vv
  • the entire configuration you are currently using
  • the name of the CDN
  • any doubts about the behavior of the CDN, please go ahead and ask them. You are paying for it, they should be able to clarify any doubts that you may have (like what the header actually looks like)

Yes, this is varnish configuration and sets header shared-secret to value PASSWORD.

I don’t see anything wrong with this, this should do exactly what you want.

Provide the EXACT output and clarify if this comes from haproxy or your backend server. When a haproxy deny action executed, you should see a 403 Forbidden instead, that’s why I doubt your problem is actually caused by this.

What happens if you don’t have any deny statement in haproxy at all?

Also look at client, haproxy and server logs.

I disagree strongly, compared to checking a single header in the HTTP request, configuring client certificate validation is certainly an order of magnitude more complicated to setup.

@lukastribus thank you so much for your time and support, and I am sorry for any newbie misunderstanding.

I am sending you the data as requested, this is from my staging env which allows me to play around prior to applying to the production. They mirror each other.

Our CDN is Section.io, founded here in Australia and moved to US.
I will contact them to explain what the header actually looks like.

CentOS 7: haproxy -vv

    HA-Proxy version 1.8.14-52e4d43 2018/09/20
    Copyright 2000-2018 Willy Tarreau <willy@haproxy.org>
    Build options :
      TARGET  = linux2628
      CPU     = generic
      CC      = gcc
      CFLAGS  = -O2 -g -fno-strict-aliasing -Wdeclaration-after-statement -fwrapv -fno-strict-overflow -Wno-unused-label
      OPTIONS = USE_LINUX_TPROXY=1 USE_ZLIB=1 USE_REGPARM=1 USE_OPENSSL=1 USE_PCRE=1

    Default settings :
      maxconn = 2000, bufsize = 16384, maxrewrite = 1024, maxpollevents = 200

    Built with OpenSSL version : OpenSSL 1.0.2k-fips  26 Jan 2017
    Running on OpenSSL version : OpenSSL 1.0.2k-fips  26 Jan 2017
    OpenSSL library supports TLS extensions : yes
    OpenSSL library supports SNI : yes
    OpenSSL library supports : SSLv3 TLSv1.0 TLSv1.1 TLSv1.2
    Built with transparent proxy support using: IP_TRANSPARENT IPV6_TRANSPARENT IP_FREEBIND
    Encrypted password support via crypt(3): yes
    Built with multi-threading support.
    Built with PCRE version : 8.32 2012-11-30
    Running on PCRE version : 8.32 2012-11-30
    PCRE library supports JIT : no (USE_PCRE_JIT not set)
    Built with zlib version : 1.2.7
    Running on zlib version : 1.2.7
    Compression algorithms supported : identity("identity"), deflate("deflate"), raw-deflate("deflate"), gzip("gzip")
    Built with network namespace support.

    Available polling systems :
          epoll : pref=300,  test result OK
           poll : pref=200,  test result OK
         select : pref=150,  test result OK
    Total: 3 (3 usable), will use epoll.

    Available filters :
    	[SPOE] spoe
    	[COMP] compression
    	[TRACE] trace

haproxy.cfg

global
    log	127.0.0.1 local2
    log-send-hostname
    pidfile     /var/run/haproxy.pid   
    chroot /var/lib/haproxy
    maxconn 4000
    user haproxy
    group haproxy
    daemon
 
    stats socket /etc/haproxy/haproxy.sock mode 660 level admin
    stats socket 127.0.0.1:13888 level admin
    stats timeout 15s

    ssl-default-bind-ciphers HIGH:!RC4:!aNULL:!MD5:!DSS
    ssl-default-bind-options no-sslv3
    tune.ssl.default-dh-param 2048
 
     
defaults
    mode                    http
    log                     global
    option                  httplog
    option                  dontlognull
    option http-server-close
    option forwardfor       except 127.0.0.0/8
    option                  redispatch
    retries                 3
    timeout http-request    10s
    timeout queue           1m
    timeout connect         10s
    timeout client          1m
    timeout server          1m
    timeout http-keep-alive 10s
    timeout check           10s
    maxconn                 4000


    frontend HTTPS
    maxconn 1000
	bind 0.0.0.0:443 ssl crt SELF_SIGNED_CERITIFICATE.PEM no-sslv3
	option httplog
	mode  http    	
   	option http-server-close
   	option forwardfor except 127.0.0.0/8
	http-request set-header X-Forwarded-Proto https
	http-request set-header X-Forwarded-Port 443
	capture request header      X-Forwarded-For  len 200
	capture request header      Host             len 100
	capture request header      Referrer         len 64
	capture request header      Content-Length   len 10
	capture request header      User-Agent       len 256
	capture cookie              JSESSIONID       len 43
	log-format %ci:%cp\ [%t]\ %f\ %b/%s\ %Tq/%Tw/%Tc/%Tr/%Tt\ %ST\ %B\ %CC\ %tsc\ %ac/%fc/%bc/%sc/%rc\ %sq/%bq\ "%r"\ %hr\ %sslv


        acl url_images path_beg -i XXXXXX
        acl url_content path_beg -i XXXXXX

	    default_backend HTTPS
        use_backend images if url_images
        use_backend content if url_content


backend images
        mode http
        option httpchk
        server XXXXXX XXXXXX weight 10 check port 80

backend content
        mode http
        option httpchk
        server XXXXXX XXXXXX weight 10 check port 80


backend HTTPS
	mode http
	option httpchk
	balance roundrobin
	stick-table type string len 52 size 1m expire 30m # no peers
	stick on urlp(jsessionid)
	stick on urlp(jsessionid,;)
	stick on cookie(JSESSIONID)
	stick store-response cookie(JSESSIONID)


    server XXXXXX XXXXXX:443 weight 10 check port 80 inter 1s fall 1 rise 3 ssl verify none
    server XXXXXX XXXXXX:443 weight 10 check port 80 inter 1s fall 1 rise 3 ssl verify none
    server XXXXXX XXXXXX:443 weight 10 check port 80 inter 1s fall 1 rise 3 ssl verify none


       userlist STATSUSERS
       group admin users XXXXXX
       user XXXXXX insecure-password XXXXXX
       user XXXXXX insecure-password XXXXXX



listen admin_page
       bind *:8161
       mode http
       stats enable
       stats refresh 10s
       stats uri /monitor
       acl AuthOkay_ReadOnly http_auth(STATSUSERS)
       acl AuthOkay_Admin http_auth_group(STATSUSERS) XXXXXX
       stats http-request auth realm admin_page unless AuthOkay_ReadOnly
       stats admin if AuthOkay_Admin

The error message is as shown below and it comes from the backend servers, and by reading your requests and expertise, I am starting to realise that the problem was never the HaProxy.
“Read feeling stupid right now”
I do not master HaProxy either.

HaProxy log does not show any error.
Without this deny, everything works like a charm without problems.

ERROR: Server Error
The server encountered a temporary error and could not complete your request.
Please try again in 30 seconds.

Thank you

You probably want to dig into why this error occurs on the backend servers then.

Maybe there is some circular dependency here. Are your backend server ever accessing the frontend IP’s of haproxy directly for whatever reason?

I don’t really understand how the deny statement in the haproxy configuration could cause your backend servers to emit this error.

@lukastribus I have contacted the CDN to provide more information.
Things should get clarified after that.

Thank you again for your help.

@lukastribus,

I have contacted our CDN and the issues point back to my env.
That server error is a 502 and as soon as I add the ACL, that happens.

The CDN did confirm tho the header will look like

req.http.shared-secret = "PASSWORD";

Which is the ACL being used so I am really not sure where the problem is :frowning:

Silly question, I have been using

`    shared-secret = "PASSWORD";`

Shouldn’t I be using this instead?

req.http.shared-secret = "PASSWORD";

Thanks
WS

You need to dig into your backend servers, to understand why they show “ERROR: Server Error”.

While the issue seems to be triggered by the deny statement, its is unclear why your application behaves this way, and digging into the application is the only way forward.

I have no idea what you mean. Where are you using this?

@lukastribus I’ve found the problem.

My staging environment used for these tests, is Google Cloud based.
In order to restrict access to it, the staging env haproxy is behind Google IAP or Identify-Aware Proxy which also includes Google Load Balance. Only users with the Google company email can login.

Once the shared secret is set, Google doesn’t know about it so why 502.
I’ll perform a test next week which includes changing the current staging haproxy dns to its public IP address instead of Google, and some firewall rules.

I’m 100% sure it will work :slight_smile:

1 Like