iTranslated by AI
Access Control with Client Certificates
*This article is based on partially edited and supplemented notes for my past self, so some parts may be difficult to understand. Please bear with me on that point.
Websites Accessible Only to Users with Certificates
However, there are times when you want to create a site restricted to specific people. In such cases, access restrictions are applied.
Normal websites can be viewed from all over the world as long as you have a web browser.
Basic Authentication
A typical example is access restriction using Basic Authentication. It becomes viewable by entering an ID and password into the browser.

However, authentication using only an ID/password is not highly secure these days, and there is a possibility that it can be broken by mechanical, continuous retries.
IP Address Restriction
Next is IP restriction, which allows access only from designated IP addresses.
IP restriction is still quite commonly used today. However, if you are aiming for complete security, it can sometimes be bypassed through packet spoofing. Additionally, if the source IP address changes irregularly, it takes effort to re-register it every time.
Restriction by Client Certificates
This is a method that allows only users who possess a designated certificate.
By registering a certificate distributed by an administrator in the browser, users can browse the site as long as the certificate is valid. Access without a certificate, or with an invalid certificate, will be displayed as a 400 Bad Request.
A disadvantage is that since certificate files can be duplicated, if they are leaked to someone other than the intended user, there is a possibility that an unexpected third party could browse the site.
Combining this with the previously mentioned restrictions makes it even more robust.
Client certificates are authenticated using certificate files issued by a Certificate Authority on the client-server. Since certificates have an expiration date, they cannot be used once they expire. Additionally, they may be revoked by an administrator even before expiration, in which case they can no longer be used for browsing.

Creating an Environment Restricted by Client Certificates
In this case, the construction will be based on the following configuration.
| Product | |
|---|---|
| Certificate Authority and Certificates | OpenSSL |
| Web Server | nginx |
Creating the Certificate Authority
To issue so-called self-signed certificates, we will create a Certificate Authority (CA) and issue client certificates from it.
Here, the CSR for creating the Certificate Authority was set as follows:
| Item | Value |
|---|---|
| Country Name(C) | JP |
| State or Province Name(ST) | Tokyo |
| Locality Name(L) | Chiyoda |
| Organization Name(O) | anon5r.dev |
| Organization unit or division name(OU) | Development |
| Common Name(CN) | anon5r.dev |
| emailAddress | webmaster@anon5r.dev |
When written as a subject during creation, it looks like this:
C=JP/ST=Tokyo/L=Chiyoda/O=anon5r.dev/OU=Development/CN=anon5r.dev/emailAddress=webmaster@anon5r.dev
subject="/C=JP/ST=Tokyo/L=Chiyoda/O=anon5r.dev/OU=Development/CN=anon5r.dev/emailAddress=webmaster@anon5r.dev"
cd /etc/pki/CA/
openssl req -new -x509 -days $expiry -key ca-priv-anon5r-dev.key -out ca-priv-anon5r-dev.crt -subj $subject
mv ca.key ca-anon5r-dev.key
openssl req -new -x509 -days 3650 -key ca-anon5r-dev.key -out ca-anon5r-dev.crt -subj $subject
openssl ca -name CA_default -gencrl -keyfile ca.key -cert ca.crt -out ca.crl -crldays 730
openssl ca -name CA_default -gencrl -keyfile ca-anon5r-dev.key -cert ca-anon5r-dev.crt -out ca-anon5r-dev.crl -crldays 3650
diff -U0 <(openssl x509 -noout -modulus -in ca-anon5r-dev.crt) <(openssl rsa -noout -modulus -in ca-anon5r-dev.key)
diff -U0 <(openssl x509 -noout -modulus -in ca-anon5r-dev.crt) <(openssl rsa -noout -modulus -in ca-anon5r-dev.key)
Creating Client Certificates
The CSR settings for client issuance are as follows. Change the Common Name and emailAddress for each user being issued.
| Item | Value |
|---|---|
| Country Name(C) | JP |
| State or Province Name(ST) | Tokyo |
| Locality Name(L) | Chiyoda |
| Organization Name(O) | anon5r.dev |
| Organization unit or division name(OU) | Development |
| Common Name(CN) | user |
| emailAddress | user@anon5r.dev |
When written as the subject during creation, it looks like this:
/C=JP/ST=Tokyo/L=Chiyoda/O=anon5r.dev/OU=Development/CN=user/emailAddress=user@anon5r.dev
In this case, the output is placed under the certs directory directly inside /etc/nginx. For management purposes, directories are organized based on each user's email address, and the files are output there.
clientEmail=developer@anon5r.dev
subject="/C=JP/ST=Tokyo/L=Chiyoda/O=anon5r.dev/OU=Development/CN=anon/emailAddress=$clientEmail"
expiry=365 # Valid for 1 year
cd /etc/nginx/certs/client/anon5r.dev/
mkdir -p users/$clientEmail
openssl genrsa -out users/$clientEmail/user.key 4096
openssl req -new -key users/$clientEmail/user.key -out users/$clientEmail/user.csr -subj $subject
openssl x509 -req -days $expiry -in users/$clientEmail/user.csr -CA /etc/nginx/certs/client/anon5r.dev/ca.crt -CAkey /etc/nginx/certs/client/anon5r.dev/ca.key -CAcreateserial -CAserial ../ca.seq -out users/$clientEmail/user.crt
openssl pkcs12 -export -clcerts -in users/$clientEmail/user.crt -inkey users/$clientEmail/user.key -out users/$clientEmail/user.p12
# Restart nginx
sudo systemctl restart nginx.service
Extra
Since the output hierarchy is deep and it's an environment only I use, I created a symbolic link in the home directory so that I can quickly retrieve the issued client certificates.
ln -s /etc/nginx/certs/client/anon5r.dev/users/$clientEmail ~/client-certs
mkdir -p ~/usercerts
cp ~/client-certs/*.(crt|csr|key|p12) ~/usercerts/
Updating Client Certificates
Update the client certificate when it expires.
clientEmail=developer@anon5r.dev
expiry=365 # Valid for 1 year
openssl x509 -req -days $expiry -in users/$clientEmail/developer.csr -CA /etc/nginx/certs/client/anon5r.dev/ca.crt -CAkey /etc/nginx/certs/client/anon5r.dev/ca.key -CAcreateserial -CAserial ../ca.seq -out users/$clientEmail/user.crt
openssl pkcs12 -export -clcerts -in users/$clientEmail/user.crt -inkey users/$clientEmail/user.key -out users/$clientEmail/user.p12
# Restart nginx
sudo systemctl restart nginx.service
Afterword
This article was originally a memo from when I set up my personal development environment. I wanted to make that environment accessible via the internet, but I didn't want it to be open to everyone.
- I want to prepare an environment that only I can use easily!
- Logging in every time is a hassle!
- I want to be able to use it from anywhere, not restricted by IP address!
This was a memo for creating what I thought was an optimal environment based on these selfish desires.
Reflections and Thoughts
While it may not prevent attacks on the web server itself, I think it was a fairly good choice for a personal development environment, as it can be made available to only the necessary people without much trouble and without the worry of being crawled over the internet.
However, there are challenges, such as the effort involved in issuing certificates when they expire, issuing certificates for temporary guests, and the recipient's effort to import the certificate. It is more complicated than the common, simple ID and password management.
In particular, I would like to make the process of issuing and updating client certificates a bit more streamlined.
Discussion