HAProxy - 2 methods for https routing

Recently, there was a project to connect an https web server existing in a private network through the Internet. It is possible to use a VPN, but I decided to study how to use HAProxy.

Please refer to the previous blog for HAProxy installation and basic usage.

<Configuration I want to implement>


Prerequisite

A web server, a certificate, etc. are required to test the content of this article.

The following 3 articles show how to easily build an https web server using Python Prask. Please refer to the building a https web server for testing.

Be Careful : If you test homepages such as https://www.google.com and https://www.facebook.comredirects occur frequently. So it is difficult to test accurately. If possible, I recommend creating a simple https web service to test it yourself.



<My test configuration>

Simplest configuration

The https works based on the certificate installed in the web server and validates the server certificate in the client web browser.

SSL encrypted packets are transmitted and received between the browser and the web server. However, on a larger scale, these packets are only part of the TCP/IP packets. If HAProxy does not perform encryption and decryption, but only forwards packets, the encryption algorithm has no effect on operation.

In our previous blog, we've seen how to route a normal tcp packet using HAProxy.
You can think of how to use tcp mode in haproxy.cfg configuration first.

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 socket /run/haproxy/admin.sock mode 660 level user 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

        # Default ciphers to use on SSL-enabled listening sockets.
        # For more information, see ciphers(1SSL). This list is from:
        #  https://hynek.me/articles/hardening-your-web-servers-ssl-ciphers/
        # An alternative list with additional directives can be obtained from
        #  https://mozilla.github.io/server-side-tls/ssl-config-generator/?server=haproxy
        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-options no-sslv3

defaults
        log     global
        mode    tcp
        timeout client  50000
        timeout server  50000

frontend cti_1_front
   bind *:8080
   mode tcp
   default_backend cti_1_back



backend cti_1_back
   balance roundrobin
   server server_1 www.spypiggy2.ga:8080 check


</etc/haproxy/haproxy.cfg at www.spypiggy.ga>

As you can see in the cfg file, packets coming through port 8080 of haproxy are treated as normal TCP packets and are configured to be routed to port 8080 of www.spypiggy2.ga.

The spypiggy2.ga runs the https web server in advance using Flask.

from flask import Flask
import ssl
app = Flask(__name__)

@app.route('/')
def index():
    return 'Hello Flask'

@app.route('/info')
def info():
    return 'Info'


if __name__ == "__main__":
    ssl_ctx = ssl.SSLContext(ssl.PROTOCOL_TLS)
    ssl_ctx.load_cert_chain(certfile='/etc/letsencrypt/live/spypiggy2.ga/fullchain.pem', keyfile='/etc/letsencrypt/live/spypiggy2.ga/privkey.pem')
    app.run(host="221.139.49.53", port="8080", ssl_context=ssl_ctx)
<myhttps.py at www.spypiggy2.ga>

Tips: For more information on creating and using ssl certificate used in the above source code, refer to the previous blogs introduced by Prerequisite.


[root@spypiggy2.ga pyflask]# python3 myhttps.py
 * Serving Flask app "myhttps" (lazy loading)
 * Environment: production
   WARNING: This is a development server. Do not use it in a production deployment.
   Use a production WSGI server instead.
 * Debug mode: off
 * Running on https://221.139.49.53:8080/ (Press CTRL+C to quit)

You can see that it is waiting for https connection through port 8080 of 221.139.49.53 IP address.

Now let's test it in the browser of the PC. Enter https://117.52.89.240:8080/ in your PC browser. Perhaps the following warning appears.

Press Advanced at the bottom to proceed.



Press Proceed to 117.52.89.240 (unsafe) at the bottom to proceed. Finally you can see the successful result.

 <PC Browser                                                       www.spypiggy2.ga>

The causes of the above browser warning are as follows.
  • The web browser connected to spypiggy.ga using https, but spypiggy.ga sends all packets to spypiggy2.ga.
  • All authentication related work takes place in spypiggy2.ga.
  • The web browser was authenticated, but the security warning was triggered because the 3rd party certificate was used, not the web server (spypiggy.ga) to which it was accessed.
In the above warning, you can see that it corresponds to "attacker intercepting your connection". However, because this is our intended behavior, we have ignored security warnings. The above warning is inevitable when haproxy, which operates as a reverse proxy, processes packets using tcp.

More accurate configuration

To resolve the above warning, when the client tries to connect to haproxy which has its own certificate, the authentication process through certificate exchange must be performed directly.
This time, let's install a certificate in haproxy and set it to go through the https authentication process by directly exchanging the certificate with the client.

Certificate Installation

Please refer to the link of Prerequisite for information on certificate creation. We will start by copying the generated certificate. We created a certificate on the letsencrypt site, and the certificate is stored in the /etc/letsencrypt/live/spypiggy.ga/ directory.

 mkdir -p /etc/haproxy/ssl
 cp /etc/letsencrypt/live/spypiggy.ga/*.pem /etc/haproxy/ssl/


We have to make the certificate and key into a single pem file for use in haproxy.

cat /etc/haproxy/ssl/privkey.pem > /etc/haproxy/ssl/unified.pem
cat /etc/haproxy/ssl/fullchain.pem >> /etc/haproxy/ssl/unified.pem


Then, modify haproxy.cfg file as follows.

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 socket /run/haproxy/admin.sock mode 660 level user 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

        # Default ciphers to use on SSL-enabled listening sockets.
        # For more information, see ciphers(1SSL). This list is from:
        #  https://hynek.me/articles/hardening-your-web-servers-ssl-ciphers/
        # An alternative list with additional directives can be obtained from
        #  https://mozilla.github.io/server-side-tls/ssl-config-generator/?server=haproxy
        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-options no-sslv3

defaults
        log     global
        mode    tcp
        timeout client  50000
        timeout server  50000

frontend cti_1_front
   #bind *:8080
   #mode tcp
   bind *:8080 ssl crt /etc/haproxy/ssl/unified.pem

   default_backend cti_1_back



backend cti_1_back
   balance roundrobin
   server server_1 www.spypiggy2.ga:8080 check maxconn 500 ssl verify none

There are two important parts.

The first is the bind line at the frontend. The ssl parameter enables SSL termination for this listener. The crt parameter identifies the location of the PEM-formatted SSL certificate. This certificate should contain both the public certificate and private key. And we did this a while ago. The result file is unified.pem.

The second important part is using the ssl parameter and verify none when routing at the backend.
As with the frontend, you need to add the ssl parameter to enable ssl and make a request to https. Also, ignore verification of the spypiggy2.ga server, and trust and connect unconditionally, so add verify none.

Now restart haproxy and test again. Only work with haproxy, no need to touch the rest of the system.

systemctl restart haproxy

This time, you can see that the https connection is established without any error.

 <PC Browser with no warning!>


If you don't trust spypiggy2.ga, you should change your settings to get the correct authentication credentials between haproxy and spypiggy2.ga.
In this case, because the haproxy is a client, you must use the ca certificate like a web browser on a PC.

If you remove verify none from the backend part of the haproxy.cfg file and add ca-file, the authentication process of the server connecting through the backend is performed.

If you are a CentOS user, you can use the ca certificate in the following directory.

backend cti_1_back
   balance roundrobin
   server server_1 www.spypiggy2.ga:8080 ssl check ca-file /etc/ssl/certs/ca-bundle.crt


If you are an Ubuntu user, you can use the ca certificate in the following directory.

backend cti_1_back
   balance roundrobin
   server server_1 www.spypiggy2.ga:8080 ssl check ca-file /etc/ssl/certs/ca-certificates.crt


Wrapping up

We have seen how to handle https in haproxy in 2 ways.
The first tcp mode has the advantage of being very simple to set up, but has the disadvantage of generating a security warning in the browser.

The mode using the second ssl parameter makes https connection with the client browser using haproxy's own certificate, so it is connected without a security warning. However, it is difficult to install and there is a burden to install a certificate. In the test, a free certificate was used, but the free certificate has a short usage period. Paid certificates with a long period of use have the disadvantage of monetary burden.

Make a good comparison of the pros and cons and choose the appropriate method for your environment.









댓글

이 블로그의 인기 게시물

Connecting to SQL Server on Raspberry Pi

Making VoIP Phone Using Raspberry Pi

MQTT - Mosquitto MQTT Broker setup on the Ubuntu 20.04