keskiviikko 3. heinäkuuta 2024

How to easily protect development environment with certificates and nginx

Say you are developing – or getting yourself familiar – a web service that may have unknown number of vulnerabilities or configuration errors. One could always host a network in an isolated network in your home LAN or even run everything in a local services in your laptop. The problem with those being that you are limited to work within one location or within one computer. It would be nice to test with a mobile browser, for example. There are VPNs that can provide access to closed networks but also they are a bit hassle.

However, there is an easy option to get around it: put a HTTPS reverse proxy requiring client certificates in front of your development server. The idea I got from the presentation “Adversarial Defences: Bear-trapping Linux Servers” by Dan Tentler at Security Fest 2024.

Set up a certificate authority and issue certificates

Running PKI CA is something that requires advanced knowledge but as we are not safeguarding billion euro bank or military secrets, we can relax. We are just trying to make it slightly harder for bad actors to mess with our development system.

Easiest way to build PKI certificate hierarchy is to use easy-rsa that is part of OpenVPN. You can download it from there or with Debian (instructions apply to Ubuntu and other variants too) just install sudo apt install easyrsa.

If you installed it from Debian package, then run make-cadir my_secret_ca where the argument is name of directory you want host the PKI. It should not exists. Note that you can run your CA in your laptop, or at server. With enough tinfoil, you create a live USB stick and run CA from there (with no network connectivity) and keep the stick in a safe when not creating new certs.

Now cd my_secret_ca and you are ready to go. First you need to create the ca. By default it creates 2048 bit RSA keys that are just fine. You can change vars configuration file if you want to have more fancy certificates; see easy-rsa docs for details. So, to create pki you need to have structure and CA.

./easyrsa init-pki
./easyrsa ca

For the second command you can add nopass argument, so you do not need to remember passwords. Remember, that anyone who can get access to this directory, can create new certificates. For most people that is the least of problems if the files are in your home directory in your laptop. Anyway, give some nice name for your CA.

Then just create as many certificates you need. One for laptop, another for mobile phone and third to a friend. We also create p12 packages to be imported in into devices. Again, we can use nopass arguments for build-client-full if we do not want to encrypt the private key. For export-12 we can give an empty password or a more complex one

./easyrsa build-client-full mylaptop nopass
./easyrsa export-p12 mylaptop
./easyrsa build-client-full friend
./easyrsa export-p12 friend
./easyrsa build-client-full mobile
./easyrsa export-p12 mobile

You will find the p12 files from pki/private folder. Should be around 3500 bytes in size. Now just copy the files: one to your laptop, send one as email to a friend and third to Google Drive.

It depends on the system how you install it. In Firefox, you go Settings -> Security -> Certificates and click “Show Certificates”. Then select “Own certificates” and “Import”. Select the mylaptop.p12 file.

In Android you can open the .p12 file in Google Drive and it asks you if you want to use it for application/VPN or WLAN. And you can give nice name for it.

That’s it. Now lets configure the server end. Do not forgot to copy the pki/ca.crt to the server!

Setting up NGINX server and getting certificates

Now back at server, we can install nginx and certbot.

sudo apt install nginx python3-certbot-nginx
sudo certbot --nginx -d bestever.example.com

That will configure nginx to use certificates to your site bestever.example.com the Certbot acquired from Let’s Encrypt. Note that you of course should have that domain name pointed to your testing server.

You can check the setup from /etc/nginx/sites-enabled/default.

We also need to copy our CA root certificate ca.crt to some place where it can be found. In Debian systems, just copy it to /usr/local/share/ca-certificates/ with some reasonable name and update certificate database.

sudo cp ca.crt /usr/local/share/ca-certificates/my-super-ca.crt
sudo update-ca-certificates

It should say “add 1 certificate” or similar. It should be linked from /etc/ssl/certs as my-super-ca.pem. If not, check permissions and actual paths.

Let’s assume we have our experimental web service running at localhost:8000, i,e. it is not reachable outside of system and it is listening on port 8000. You can try to reach it with curl http://localhost:8000 and you should receive some HTML most likely.

There are two points we need to configure in nginx configuration file /etc/nginx/sites-available/default.

So we first need to configure the proxy part by locating server block that has server_name bestever.example.com; directive. Below that there should be a location / block with some try_files statements. We’ll replace it with following:

location / {
    proxy_pass http://localhost:8000;
    include proxy_params;
}

Further down, still within the same server block you will find various ssl letsencrypt directives. Below those (and above }) add following lines:

    ssl_client_certificate /etc/ssl/certs/my-super-ca.pem;
    ssl_verify_client on;

Now all is configured and ready to go. Just sudo systemctl restart nginx and point your browser (with a user certificate) towards the site. The browser should ask if you want to use certificate to authenticate. Just select the one you installed some time ago.

If the system does not work, like if you got some error messages from systemctl command, you can check error messages with sudo journalctl -xeu nginx.service. There maybe a missing semicolon.

Finally, you need to check that certificate is actually required. Use some machine or browser where the user certificate is not installed. You should receive an error like:

400 Bad Request  No required SSL certificate was sent

So, everything seems to be right. Happy developing!


Edit: 4th July. One extra / removed from nginx config.

Ei kommentteja:

Lähetä kommentti