Enabling SSL and HTTPS in our application

Enabling SSL and HTTPS in our application

What is SSL

Secure Socket Layer is a protocol that provides secure communication over two network devices. It ensures that the data transmitted between two devices remains private. The most common example would be the client computer (us) connecting to a secure server (for example a bank website).

The server usually has its own private key and public signed certificate. The private key is only known by the server and no one else. Here are the steps to establish SSL communication:

  1. The publicly signed certificate is sent to the client for verification with the Certificate Authority (CA)

  2. Once verified, the client sends a secret message encoded with the public certificate and sends it back to the server

  3. The secret message can be decoded by the private key held by the server and read the contents of the message, thereby creating a new session whereby now only the client and server know this message.

  4. Now both the client and server can use this message to encrypt and decrypt data with the session key

Setting up SSL

We will be setting up SSL and HTTPS on our Gitea application that we set up previously. Previously our Gitea application only used HTTP for communication.

We will be exploring two ways to set up SSL/HTTPS:

  1. Let Gitea handle SSL encryption/decryption by providing the private and public keys to our application

  2. Let our reverse proxy (Nginx) handle SSL encryption/decryption instead and communicate using HTTP between the reverse proxy and Gitea

Option 1

Most of the time applications have the option to enable SSL/HTTPS by specifying the private key and public self-signed certificate. Looking at the Gitea documentation to set up SSL, we need to change a few configurations in the app.ini file.

from gitea HTTPS documentation

  1. Let's generate a private key for the CA

     # gitea GITEA_CUSTOM path
     cd /data/gitea/
    
     mkdir certs && cd ./certs
    
     # generate private key
     openssl genrsa -out giteaCA.key 2048
    
  2. Create a self-signed certificate for the CA

    First, create a csr config to set our required information. The important parts here are the subjectAlternativeName. We will need to add the IP address to the Subject Alternative Name (SAN). This is important later when we want to use Gitea as our Kubernetes image repository.

     [req]
     default_bits = 2048
     prompt = no
     default_md = sha256
     distinguished_name = dn
     req_extensions = req_ext
     default_days = 900
    
     [dn]
     # The Common Name should be the fully qualified domain name (FQDN) of the server.
     # Replace "example.com" with your domain name.
     CN = 192.168.1.4
    
     [req_ext]
     # Subject Alternative Name (SAN) extension
     subjectAltName = @alt_names
    
     [alt_names]
     # Add additional subject alternative names (SANs) here.
     IP = 192.168.1.4
    

    After that we can use this csr to generate our certificate

  3.   openssl req -new -x509 -key giteaCA.key -out giteaCA.crt -config csr -extensions req_ext
    

    `-x509` : output should be an X.509 certificate instead of a certificate signing request

    -days : how long this certificate is valid for

    -key : the private key file used for generating the public certificate

    -out : specifies the name of the newly created public certificate

    -config : specifies the location of our configuration file

    -externsions : specifies that we are using extensions by passing in the var req_ext in our config file

  4. Now we have giteaCA.key and giteaCA.crt

  5. Looking at the Gitea app.ini file here. Our ROOT_URL here should be using HTTPS, PROTOCOL set to HTTPS and also we need to provide the path to our CERT_FILE and KEY_FILE as shown below

     [server]
     ...
     HTTP_PORT = 3000
     ROOT_URL = https://192.168.1.4:3000/
     PROTOCOL = https
     CERT_FILE = ./certs/giteaCA.crt
     KEY_FILE = ./certs/giteaCA.key
    
  6. After editing the app.ini file we can restart our Gitea application

  7. Verify that we can access the application via https://192.168.1.4:3000 , however, it will give us a warning when we are accessing via a browser

    invalid certificate

    This is because the client PC that we are accessing our application failed to validate the public certificate that the server has sent us. To solve this issue, we need to add our giteaCA.crt file to the trusted root certificates. This can be done in different ways on different machines (windows, Mac, Linux, etc...) but won't be covered here for simplicity.

Option 1 is a simple way to set out SSL and HTTPS on the application itself.

Option 2

We will add SSL and HTTPS to our Nginx reverse proxy instead and keep the Gitea application communication using HTTP. Here are a few reasons why we do this:

  1. Sometimes enabling SSL on every application can be a hassle. If we have X number of applications needing to enable SSL, then we probably need to generate X private and public keys for each application. This can be reduced to 1 if we only use SSL up to the reverse proxy.

  2. We might not want our application to handle SSL as it needs to use extra resources (ie CPU and memory) to handle SSL encryption and decryption.

As such, we sometimes let our network gateway, which is our reverse proxy also be the point where SSL is decrypted, and for the internal network communication is often done through http instead.

  1. Let's create our certificate and key again, one-liner to generate it:

     openssl req -new -x509 -days 365 -newkey rsa:2048 -nodes -keyout ca.key -out ca.crt
    
  2. Let's update our nginx.conf to include this certificate and key:

     server {
         listen 443 ssl;
         server_name 192.168.1.4;
         ssl_certificate /certs/ca.crt;
         ssl_certificate_key /certs/ca.key;
    
         location /gitea/ {
    
             rewrite ^ $request_uri;
             rewrite ^/gitea(/.*) $1 break;
    
             proxy_pass http://192.168.1.4:3000$uri;
             proxy_set_header Host $host;
             proxy_set_header X-Real-IP $remote_addr;
             proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
             proxy_set_header X-Forwarded-Proto $scheme;
         }
     }
    

    SSL uses port 443, which we will configure our server to listen to. ssl_certifcate and ssl_certificate_key we will provide a path from our side. We want our client to access Gitea using HTTPS using the domain: https://192.168.1.4/gitea, so we added a /gitea/ location so that it can match our prefix with the URI.

    rewrite ^/gitea(/.*) $1 break; over here will then rewrite our URI from gitea/($1) to ($1), thereby removing the extra gitea/ prefix before calling our proxy_pass directive.

    For example, a client sending a request https://192.168.1.4/gitea/user/login will be modified to http://192.168.1.4/user/login in Nginx before forwarding this request. The URI being rewritten here is /gitea/user/login to /user/login.

  3. If we are running our Nginx on a docker container, remember to enable port 443 under the ports section

     version: "1.0"
     services:
       nginx:
         image: nginx:stable
         container_name: nginx
         volumes:
           - /home/internal/nginx/nginx.conf:/etc/nginx/conf.d/nginx.conf
           - /home/internal/nginx/access.log:/var/log/nginx/access.log
           - /home/internal/nginx/error.log:/var/log/nginx/error.log
           - /home/internal/certs/:/certs
         ports:
           - "80:80"
           # for SSL use only
           - "443:443"
    
  4. Update the Gitea app.ini file to use the correct domain. This should be 192.168.1.4/gitea

     [server]
     ...
     DOMAIN = 192.168.1.4/gitea
     SSH_DOMAIN = 192.168.1.4/gitea
     ROOT_URL = http://192.168.1.4:3000/gitea
    

Start our Nginx application and try to access our Gitea application through HTTPS again and it should work as well.