top of page
Writer's pictureNOURA ALSHAREEF

Secure Your Apps with HTTPS

In our previous article titled "How HTTPS Works?" we delved into the concepts of HTTPS, TCP, TLS, and SNI. Now, as a developer, you might be contemplating the most effective approach to ensure that your apps utilize HTTPS seamlessly. This raises the question: what strategy should you adopt?


While some developers opt for implementing HTTPS individually for each app, I personally advocate for concluding the handshake at the reverse proxy. The underlying rationale behind this approach is to consolidate the management of server certificates for all your apps in a single location. By doing so, you can maintain a unified server certificate that covers all your applications. This simplifies the management process and guarantees consistent security across your entire system. Let's proceed with configuring our reverse proxy to use HTTPS.



Let's kick things off by creating a straightforward application specifically designed for testing purposes. In a previous article titled Create a Simple Dotnet 8 App in Linux ,we provided a step-by-step guide on quickly setting up a basic Dotnet service in Linux.


For now, you can focus on creating App1 by following the instructions provided in the article. You don't need to create App2 or App3 for this particular case.


Once you've completed the setup of App1, make sure to proceed to the bottom of the article, where you'll find a section titled "Dotnet App as a Linux service"


It's crucial to carefully follow the instructions outlined in that section and thoroughly test the functionality using curl:

It should show you :


Great work!


As observed, the service is currently accessible only via HTTP and not HTTPS. However, the positive aspect is that it can only be accessed locally on the localhost and is not exposed externally. Now, let's delve into the steps that will demonstrate how to enable external access to the service using both HTTP and HTTPS protocols.


Creating an SSL Certificate: A Prerequisite for HTTPS


Now, let's establish a directory where we can store the certificates for our applications. It is customary to place them in the /etc directory.


Therefore, please execute the following command:

mkdir /etc/ssl 

Great! Now, for each application or group of related applications, we need to create a separate directory. Let's assume that your app domain name is "app1.com"

In that case, execute the following commands:

mkdir /etc/ssl/app1
cd /etc/ssl/app1/

Now, let's proceed with generating the server certificate file for "app1". You can follow the steps outlined in this article: Create a Server Certificate.


Once you have completed the certificate generation process, let's ensure that the certificates are present in the directory:

ls /etc/ssl/app1

Configure Nginx for HTTP and HTTPS


After installing Nginx, try to add this server block inside http block :

events {
    worker_connections 1024;
    # Other relevant event-related settings can be specified here
}

http {
    server {
        listen 80;
        server_name www.app1.com;

        location / {
            proxy_pass http://localhost:7001;
        }
    }
}

test :


When you curl http://localhost, you are actually sending a request to Nginx because it is listening on the default port 80. Nginx acts as a reverse proxy in this setup. It receives the communication and then uses the proxy_pass directive to forward the request to the application server, which is running on port 7001 and referred to as app1. The application server handles the request and returns a response, which Nginx then sends back to the client that made the initial request.


To establish secure communication, we need to add SSL configuration inside the http block in the nginx.cnf file. This ensures that Nginx can handle HTTPS requests and establish secure connections. By configuring SSL, we enable encrypted communication between the client and the server, providing an added layer of security for the data transmitted over the network.

server {
    listen 443 ssl;
    server_name www.app1.com;

    ssl_certificate /etc/ssl/app1/app1.pem;
    ssl_certificate_key /etc/ssl/app1/app1Key.pem;

    location / {
        proxy_pass http://localhost:7001;
    }
}

Test the config and reload it

nginx -t
nginx -s reload

Ok, let’s do another curl !

You might see this response :


This error message indicates that curl failed to verify the legitimacy of the server's SSL certificate because it couldn't find the local issuer certificate. It does not trust the CA certificate.


To obtain a more detailed and verbose explanation, you can try executing the command with the -v option


Take a look at the line "CAfile: /etc/ssl/certs/ca-certificates.crt". This indicates that the client only trusts the certificates listed in that file, "CAfile: /etc/ssl/certs/ca-certificates.crt". Since the certificate at /etc/ssl/app1/ca.pem is not included in that list, it is not trusted.

To resolve this issue, you have two solutions: a permanent one and a temporary one. Let's start with the temporary solution.


Make Your CA Cert Trusted Temporarily


To temporarily address this issue, you can specify the path to the ca.pem file using the --cacert option when using the curl command. By providing the path to the certificate authority (CA) file, the client can validate the server's certificate and establish a secure connection.


Try the following command:

curl https://localhost --cacert /etc/ssl/app1/ca.pem -v

However, after making this change, another verification check fails!



As observed, although the CA certificate was successfully passed, another error is displayed.

curl: (60) SSL: no alternative certificate subject name matches target host name 'localhost'
More details here: https://curl.se/docs/sslcerts.html

This error occurs because the certificate's alternative subject name does not match the target host name, which is 'localhost' in this case. The certificate's common name (CN) and alternative subject name (Subject Alternative Name) should match the hostname being accessed. In this example, since the hostname is 'localhost' and the certificate doesn't have that hostname, the verification fails.


Now, one might use: -H "Host: example.com", but this will also not work, and the response will remain the same.

curl https://localhost/ --cacert /etc/ssl/app1/ca.pem -H "Host: www.app1.com"

This is because Host header is not taken into account during the initial steps of the TLS handshake (before server hello). The Host header is an HTTP header that is used in the application layer of the HTTP protocol, not during the TLS handshake. Think a little bit about it, what do we need?


Hint : remember this image from :


You are right! we need SNI field as a part of Client Hello Message !

In general, it is advisable to access the service by using the Fully Qualified Domain Name (FQDN) rather than the IP address. For example, instead of accessing the service at http://127.0.0.1, it is preferred to use http://www.app1.com.

However, it only works if your DNS server has the appropriate FQDN record.


In such cases, you can utilize your host file to manually map the FQDN to the corresponding IP address.

sudo nano /etc/hosts

Then add www.app1.com to the existing line

127.0.0.1 localhost www.app1.com

Let's try curl again

curl https://localhost --cacert /etc/ssl/app1/ca.pem

Now, it's working!

***  App 1 ***

However I don’t want to edit my host file especially for test perpuses, so how to send the SNI value in curl request itself?

To fill the SNI value in the Client Hello message using the curlcommand, you can use the --resolve option . Here's an example

curl --cacert /etc/ssl/app1/ca.pem --resolve www.app1.com:443:127.0.0.1 https://www.app1.com/

Worked too!

***  App 1 ***

Make Your CA Cert Trusted Permanently

In addition to trusting a CA certificate for a specific connection, you may want to always trust it, especially for services integrations. To achieve this, you can add your server certificate to the CAFile list.

cp /etc/ssl/app1/app1.pem /etc/ssl/certs
cat /etc/ssl/certs/app1.pem >> /etc/ssl/certs/ca-certificates.crt

Now try the previous command without --cacert option :

curl --resolve www.app1.com:443:127.0.0.1 https://www.app1.com/ -v

The output :


Awesome!


That's it!


I hope that the information provided has given you a comprehensive understanding of how HTTPS works and how to address common challenges when working with SSL certificates, SNI, and hostname mapping. By grasping these concepts, you will be better equipped to handle secure connections and troubleshoot any issues that may arise. I trust that you found this article informative and enjoyable

27 views0 comments

Recent Posts

See All

Comments


bottom of page