One port to catch HTTP and HTTPS requests and redirect to the HTTPS version


#1

I have configuration that works well when HTTPS is in the URL but of course, when it is HTTP, it fails. The problem is, I must specify the port number in the URL. I am using this as a way to test individual servers. So the website name must remain unchanged to work with the SSL cert but I can assign one port (and an associated frontend and backend) in the haproxy.cfg file to route to the correct server.

The problem happens when for what ever reason, HTTPS is not specified but the magic port number is. Some apps do a redirect (code beyond my control) and because the server is only running on port 80 without SSL and haproxy is doing all the SSL work, the app doesn’t realize it needs to redirect to https://… so it just redirects to the http:// version and that is where things break.

My config entry…

frontend port_801
bind *:801 ssl crt /etc/ssl/private/unified-cert-file.pem
capture request header Host len 32
capture request header User-Agent len 90
default_backend server_1_through_801

backend server_1_through_801
option forwardfor
http-request add-header X-CLIENT-IP %[src]
server server1 192.168.10.101:80 check

Now, the question is, how do I make this work so if I specify http://mysite.com:801 that haproxy will still respond but auto redirect to https://mysite.com:801 without throwing a fit?

Any help would be greatly appreciated.


#2

Use a TCP frontend to differentiate between HTTP and SSL traffic, than recirculate the traffic to proper HTTP or HTTPS frontends.

Something like:

frontend port801_combined
    mode tcp
    bind :801
    tcp-request inspect-delay 2s
    tcp-request content accept if HTTP
    tcp-request content accept if { req.ssl_hello_type 1 }
    use_backend recir_http if HTTP
    default_backend recir_https

backend recir_http
    mode tcp
    server loopback-for-http abns@haproxy-http send-proxy-v2
backend recir_https
    mode tcp
    server loopback-for-https abns@haproxy-https send-proxy-v2

frontend fe-https
    mode http
    bind abns@haproxy-https accept-proxy ssl crt /etc/ssl/private/unified-cert-file.pem
    # whatever you need todo for HTTPS traffic
frontend fe-http
    mode http
    bind abns@haproxy-http accept-proxy
    # whatever you need todo for HTTP traffic

#3

I will try this but I really don’t understand what it is trying to do because I can’t find any details on some of these settings.

Everything in the “frontend port801_combined” makes sense.

I don’t understand what the two backend blocks are doing

I don’t see where the other two frontend blocks even get used

Can you explain this a bit before I go sticking it into my config file?


#4

The frontend port801_combined detects whether the incoming request is SSL/HTTPS or plaintext HTTP.
It forwards the traffic to backend recir_http if the it is plaintext HTTP.
Otherwise it forwards the traffic to backend recir_https.

The backend recir_http sends all the traffic to the frontend fe-http via the socket abns@haproxy-http.

The backend recir_https sends all the traffic to the frontend fe-https via the socket abns@haproxy-https.

Frontend fe-http receives plaintext HTTP traffic (therefor no “ssl” configuration there).

Frontend fe-https receives encrypted HTTPS traffic (therefor your ssl and certificate configuration belongs there).

You can work with the latter 2 frontends to do whatever you want.


#5

I see now. The bindings in the last two frontend blocks do the connection back to the backend blocks. Thanks for explaining that.

I will give it a try shortly and let you know how it goes.


#6

Ultimately, I did some tweaking to include the port number into all of the reference points and because I have several servers that I want to be able to address individually, I needed to replicate this process once for each port 801, 802, 803, etc.

Feels clunky, but since these only exist for testing purposes and cannot be accessed from the world because of our firewall, this works pretty well. If there is a simpler way, would love to know it but this will do the trick for me.

frontend port801_combined
    mode tcp
    bind :801
    tcp-request inspect-delay 2s
    tcp-request content accept if HTTP
    tcp-request content accept if { req.ssl_hello_type 1 }
    use_backend recir_801_http if HTTP
    default_backend recir_801_https
backend recir_801_http
    mode tcp
    server loopback-for-http abns@801-haproxy-http send-proxy-v2
backend recir_801_https
    mode tcp
    server loopback-for-https abns@801-haproxy-https send-proxy-v2
frontend fe_801_https
    mode http
    bind abns@801-haproxy-https accept-proxy ssl crt /etc/ssl/private/unified-cert-file.pem
    capture request header Host len 32
    capture request header User-Agent len 90
    default_backend port_801
frontend fe_801_http
    mode http
    bind abns@801-haproxy-http accept-proxy
    capture request header Host len 32
    capture request header User-Agent len 90
    default_backend port_801
backend port_801
    option  forwardfor
    http-request add-header X-CLIENT-IP %[src]
    redirect scheme https code 301 if !{ ssl_fc }
    server server1 192.168.10.107:80 check

#7

AAAAAHHHHH!!!

Ok, well I thought I was being smart. I copied the code which is working. Replaced every reference to 801 with 802 and HAPROXY won’t start.

I remove the new lines and it works again.

WHY???


#8

Share the error message please?


#9

I was tired and stupid…

I missed one label when switching 801 to 802 and caused a duplicate backend entry.

This morning I was looking it over and spotted it. So putting the port number into the labels and socket name works, baring human error when retyping the port numbers.

Thanks for all the help!