Self-signed certificate, issues and solutions

Recently, I've encounter several situations where I have to deal with self-signed certificate. As this is such a common issue, I've decided to write this article to document my learnings and for my own reference in the future.

This article will start by explaining what is a SSL certificate, and what is a self-signed certificate. From there, I will detail the problems I encountered recently on self-signed certificate, and how I solved them.

What is a (SSL) certificate?

A certificate, is something you used to prove you identity. For example, in most countries around the world, you would usually have something to identify who you are and prove you are who you claimed to be, this could be something like a driver license, and are usually issued by the government.

In the context of internet security, you've got SSL certificate. This is something the browser would use to authenticate the identity of a website. An SSL certificate is most likely issued and signed by a trusted CA or certificate authority.

What is a self-signed certificate?

To put it in short, a self-signed certificate is something that's issued and signed by the same party. In other words, this is like you have issued yourself a driver license, to identify and prove you are you. I'm pretty sure you would have a hard time convincing people to accept that as a proof of your identity.

Benefits and risks associated with self-signed certificate

It is quite obvious that to most people, self-signed certificate would pose a big risk. Because SSL certificate are usually used for HTTPS communication, if a self-signed certificate is obtained from an untrusted source and installed on your computer, the malicious party can pretend to be a any website, and will be able to decrypt the HTTPS traffic from your computer. This is called Man-in-the-middle attack.

However, with increasingly strict requirement on HTTPS in most browsers, it is necessary to trust a self-signed certificate. Here are some common scenarios.

  • In a large enterprise, the intranet traffic is usually not exposed to the public. In this situation, a self-signed certificate is appropriate because it would allow the traffic to be encrypted properly.
  • In a home or SOHO network environment, you may want to use a self-signed certificate on your router, this will stop your browser from complaining about HTTP traffic.
  • In a development environment, you may also want to use a self-signed certificate. This is so that you can utilize the Man-in-the-middle method and sniff some of the HTTPS traffic, making things easier to debug. (This is especially useful when you are doing mobile application development)

Issues and solutions

I would like to go over the issues I have encountered recently on self-signed certificates. In most situations, to solve the problem, you can either bypass or trust. Bypass means blindly trust all self-signed certificate and don't warn me again. Trust means trusting only the certificate in question, which means if you ever have another self-signed certificate, you would have to trust it again. Unless you have a very good reason, do not use the bypass solution.

Using self-signed certificate in JavaScript/NodeJS

If you are a NodeJS developer, you would most likely to have encounter error message like Self-signed certificate in certificate chain. This error is actually from NodeJS https client, if you are using a third party library (like Apollo server client), you will have to dig and find how to pass custom http client to that library.

There are several ways to bypass this warning.

  • At global level: using environment variable, export NODE_TLS_REJECT_UNAUTHORIZED=0
  • At HTTPS client level:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    https.request({
    host: '192.168.1.1',
    port: 443,
    path: '/',
    method: 'GET',
    rejectUnauthorized: false,
    requestCert: true,
    agent: false
    })
Trust certificate in NodeJS
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
const https = require('https');
const fs = require('fs');

// Read the self-signed certificate file
const caCert = fs.readFileSync('path/to/self-signed-certificate.pem');

// Configure the agent with the self-signed certificate
const agent = new https.Agent({
ca: caCert
});

// Make the HTTP request using the agent
const options = {
hostname: 'yourserver.com',
port: 443,
path: '/',
method: 'GET',
agent: agent
};

const req = https.request(options, (res) => {
// Response handling logic
});

req.on('error', (error) => {
console.error(error);
});

req.end();
Using self-signed certificate in Python

To bypass at a client level, add verify=False.

1
2
3
4
import requests
data = {'foo':'bar'}
url = 'https://foo.com/bar'
r = requests.post(url, data=data, verify=False)

To trust the certificate, you will need to put the certificate on that machine and either use the environment variable or pass the certificate in the path

Trust certificate in Python
1
2
3
4
5
6
7
8
9
10
11
12
13
14
import requests
import certifi

# Specify the URL of the server you want to connect to
url = 'https://yourserver.com'

# Path to your self-signed certificate file (PEM format)
cert_file = 'path/to/self-signed-certificate.pem'

# Send an HTTP GET request
response = requests.get(url, cert=(cert_file, cert_file), verify=certifi.where())

# Process the response
print(response.text)

Or export REQUESTS_CA_BUNDLE = /path/to/your/certificate.pem

Using self-signed certificate with Docker/Colima

This is something I encountered recently, our internal Docker registry uses self-signed certificate, this turned out to be quite a challenge. I had to find ways to add the certificate Colima host machine for this to work correctly.
Here's the command I ran:
colima ssh -- sudo sh -c "openssl s_client -showcerts -connect our-internal-url:443 </dev/null 2>/dev/null|openssl x509 -outform PEM > /usr/local/share/ca-certificates/internal.crt && update-ca-certificates && cat /var/run/docker.pid | xargs kill"
This will download the certificate and install in the certificate storage in the colima VM in one go.

References

https://gist.github.com/jkremser/a8143384049b171d4e64c5aeb6da4793

  • Some of the code in this article was generated using ChatGPT.