This document provides my solution to the ForgeRock technical writing task.
Introduction
The task can be summarized as:
Document how to setup tools so that you can serve a JSON file over the HTTPS protocol, and prove it works from the command-line.
This is suitably vague that the solutions can range from extremely simple to quite complex. Some possible approaches include:
-
Serving static JSON files via a Glitch web server (HTTPS using the
*.glitch.com
certificate). -
Using a Flask web app locally to serve static JSON over HTTPS (using a self-signed certificate).
-
Installing a Nginx (or other web server), and configuring it to serve static JSON over HTTPS. This also uses a self-signed certificate.
Further clues hint at the third approach, so that is what is described here.
The task also states:
You’re free to choose the format, style, and tone of the documentation.
For this task I chose a hands-on tutorial approach, with limited (but hopefully sufficient) technical discussion.
If further time was available I would:
-
Add a troubleshooting section.
-
Do more research and add more conceptual material on Digital Certificates, Certificate Authorities, and key pairs. The file formats and file extensions used need further clarification.
-
Provide a screencast.
-
Illustrate with an overview diagram.
-
Explain why you would want to serve static JSON files!
-
Try a different syntax highlighter.
-
Add authentication. I did however implement this in a Python/Flask version of the server, hosted on Glitch.
Assumptions
The following assumptions are made:
-
The platform is Mac OS X.
-
Install for local testing only.
-
Curl is already installed.
-
OpenSSL is already installed.
-
Commands are carried out on the command line unless otherwise stated.
-
The user is comfortable using the command line and with editing files.
-
ForgeRock uses American English.
The process
The steps that are described here are summarized as follows:
Tools required
The tools required are:
-
Nginx
-
OpenSSL
-
Curl
Optionally, you can install jq
to pretty-print and validate JSON responses.
1 - Install Nginx
In this step you install Nginx, which functions as the web server.
Nginx can be installed using brew
:
brew update
brew install nginx
You can test for successful installation using:
nginx -v
The Nginx version is displayed as follows:
nginx version: nginx/1.19.3
Nginx is installed to the the location /usr/local/etc/nginx/
. This directory contains the configuration files you will be working with.
You can now start Nginx with:
nginx
Navigate your browser to http://localhost:8080
and you are greeted with an Nginx welcome message.
Note
|
Port 8080 is the default for Nginx using the HTTP protocol. |
You have now successfully installed Nginx.
2 - Verify Nginx can serve static JSON files
In this step you verify that Nginx can serve static JSON files.
Create a sample JSON file in the Nginx data directory /usr/local/var/www
. This is where the files to be served by Nginx are located.
For example, create the following file patient.json
:
{
"test-id": "FF475013-CDD9-4377-AEAB-A65932C698DE",
"retest": false,
"key-worker": true,
"testing-center-id": "swindon-east-1",
"tester-id": "6789-dcba-123",
"test-number": 123,
"test-date-time": "2020-10-08T13:10:20Z",
"patient": {
"id": "1234-abcd",
"firstname": "Sally",
"lastname": "Bright",
"dob": "1974-05-12",
"postcode": "SN4 7RH",
"mobile": "07932-155051"
},
"covid-19-result": "negative"
}
Note
|
This information is fictitious. |
You can now verify Nginx is capable of serving this file:
curl http://localhost:8080/patient.json | jq
In this case the output of Curl is piped to jq
to nicely format and validate the JSON returned. This is optional.
You receive a response identical to the JSON data file you created previously, verifying that the static file has been served.
3 - Configure Nginx to use HTTPS
In this step you configure Nginx to use HTTPS.
Open the Nginx configuration file /usr/local/etc/nginx/nginx.conf
.
Find the following section:
# HTTPS server
#
#server {
# listen 443 ssl;
# server_name localhost;
# ssl_certificate cert.pem;
# ssl_certificate_key cert.key;
# ssl_session_cache shared:SSL:1m;
# ssl_session_timeout 5m;
# ssl_ciphers HIGH:!aNULL:!MD5;
# ssl_prefer_server_ciphers on;
# location / {
# root html;
# index index.html index.htm;
# }
#}
Remove the comments so that the configuration is as follows:
# HTTPS server
#
server {
listen 443 ssl;
server_name localhost;
ssl_certificate cert.pem;
ssl_certificate_key cert.key;
ssl_session_cache shared:SSL:1m;
ssl_session_timeout 5m;
ssl_ciphers HIGH:!aNULL:!MD5;
ssl_prefer_server_ciphers on;
location / {
root html;
index index.html index.htm;
}
}
Restart Nginx with the following:
nginx -s reload
This ensures the new configuration is processed by Nginx.
However, you will receive an error:
nginx: [emerg] cannot load certificate "/usr/local/etc/nginx/cert.pem": BIO_new_file() failed (SSL: error:02001002:system library:fopen:No such file or directory:fopen('/usr/local/etc/nginx/cert.pem','r') error:2006D080:BIO routines:BIO_new_file:no such file)
This is because you need to create a certificate in order to have fully functioning HTTPS support. You see how to do this in the next section.
You have now configured Nginx to use HTTPS.
4 - Create required certificate and private key
In this step you create your key and certificate.
A signed certificate is required to verify the identity of the server. As you are testing locally via localhost
in this tutorial, the names used are localhost.key
and localhost.crt
.
Change into the directory /usr/local/etc/nginx
and create your certificate and key using the following command:
openssl req -x509 -newkey rsa:4096 -nodes -out localhost.crt -keyout localhost.key -days 365
You will be prompted to enter the following information. Sample responses are provided:
Value | Description |
---|---|
Country code |
GB |
State or Province Name |
Wiltshire |
Locality Name |
Swindon |
Organization |
ForgeRock |
Organizational Section Name |
Engineering |
Common Name |
localhost |
Email Address |
A suitable email address |
Note
|
Common Name is important as that is the name of the server whose identity is to be verified. The generated certificate is only valid for the server named |
The options you used to create the certificate and key are summarized in the following table:
Parameter | Description |
---|---|
|
Certificate request |
|
Certificate format |
|
Generate a new key |
|
Key encryption method |
|
No DES algorithm used |
|
Certificate file generated |
|
Private key file |
|
Certificate is valid for 365 days |
Your Nginx configuration now needs to be updated to reflect the newly generated certificate and key:
ssl_certificate localhost.crt;
ssl_certificate_key localhost.key;
To make sure the updated configuration is loaded, restart Nginx with:
nginx -s reload
Note
|
If you receive permission errors you will need to use |
You have now created the certificate that is used to verify the identity of the server.
5 - Request JSON file over HTTPS using Curl
In this step you test whether you can receive a static JSON file over HTTPS.
Attempt to receive the JSON file over HTTPS using the following command:
curl https://localhost/patient.json
Note
|
The protocol specified is |
You receive the following error:
curl: (60) SSL certificate problem: self signed certificate
More details here: https://curl.haxx.se/docs/sslcerts.html
curl performs SSL certificate verification by default, using a "bundle"
of Certificate Authority (CA) public keys (CA certs). If the default
bundle file isn't adequate, you can specify an alternate file
using the --cacert option.
If this HTTPS server uses a certificate signed by a CA represented in
the bundle, the certificate verification probably failed due to a
problem with the certificate (it might be expired, or the name might
not match the domain name in the URL).
If you'd like to turn off curl's verification of the certificate, use
the -k (or --insecure) option.
HTTPS-proxy has similar options --proxy-cacert and --proxy-insecure.
The problem is that a self-signed certificate has been used. This is not as secure as a certificate signed by a well-known Certificate Authority, and is therefore a serious potential security issue. Curl (and your web browser) notice this and prevent access to a potentially malicious server. You can obtain a clearer picture on what is happening by activating the Curl debug mode using the following command:
curl -v https://localhost/patient.json
You will see tracing such as:
* Connected to localhost (127.0.0.1) port 443 (#0)
* ALPN, offering h2
* ALPN, offering http/1.1
* Cipher selection: ALL:!EXPORT:!EXPORT40:!EXPORT56:!aNULL:!LOW:!RC4:@STRENGTH
* successfully set certificate verify locations:
* CAfile: /etc/ssl/cert.pem
CApath: none
* TLSv1.2 (OUT), TLS handshake, Client hello (1):
* TLSv1.2 (IN), TLS handshake, Server hello (2):
* TLSv1.2 (IN), TLS handshake, Certificate (11):
* TLSv1.2 (OUT), TLS alert, Server hello (2):
* SSL certificate problem: self signed certificate
* stopped the pause stream!
* Closing connection 0
In particular, the message SSL certificate problem: self signed certificate
indicates the exact nature of the problem.
Note
|
In a real-world scenario the certificate associated with a server would be signed by a Certificate Authority such as Verisign. Your application, a browser, Curl, or another such client, would perform a certificate check using a database of well-known Certificate Authorities. In the situation where you are testing locally you signed your own certificate, and so are in theory the Certificate Authority, although this is an insecure practice and must not be done for production servers. |
In this tutorial you are testing locally, and there are three possible ways of working around the self-signed certificate issue:
-
Use the
curl -k
orcurl --insecure
option to ignore certificate testing. -
Specify the Certificate Authority on the command line.
-
Add the self-signed certificate to your system’s database of Certificate Authorities. This can be achieved using the command
sudo security add-trusted-cert -d -r trustRoot -k /Library/Keychains/System.keychain /usr/local/etc/nginx/localhost.crt
. The certificate appears in your Mac keychain, and its details can be verified. You will also be able to access Nginx via your web browsers. However, for security reasons, this third option is not recommended.
The second option is preferable for local testing and an example is shown here:
curl -v --cacert /usr/local/etc/nginx/localhost.crt https://localhost/patient.json
The debug output shows clearly what is happening:
...
* Connected to localhost (127.0.0.1) port 443 (#0)
* ALPN, offering h2
* ALPN, offering http/1.1
* Cipher selection: ALL:!EXPORT:!EXPORT40:!EXPORT56:!aNULL:!LOW:!RC4:@STRENGTH
* successfully set certificate verify locations:
* CAfile: /usr/local/etc/nginx/localhost.crt
CApath: none
* TLSv1.2 (OUT), TLS handshake, Client hello (1):
* TLSv1.2 (IN), TLS handshake, Server hello (2):
* TLSv1.2 (IN), TLS handshake, Certificate (11):
* TLSv1.2 (IN), TLS handshake, Server key exchange (12):
* TLSv1.2 (IN), TLS handshake, Server finished (14):
* TLSv1.2 (OUT), TLS handshake, Client key exchange (16):
* TLSv1.2 (OUT), TLS change cipher, Client hello (1):
* TLSv1.2 (OUT), TLS handshake, Finished (20):
* TLSv1.2 (IN), TLS change cipher, Client hello (1):
* TLSv1.2 (IN), TLS handshake, Finished (20):
* SSL connection using TLSv1.2 / ECDHE-RSA-AES256-GCM-SHA384
* ALPN, server accepted to use http/1.1
* Server certificate:
* subject: C=GB; ST=Wiltshire; L=Swindon; O=ForgeRock; OU=Engineering; CN=localhost; emailAddress=sample@domain.com
* start date: Oct 9 14:44:56 2020 GMT
* expire date: Oct 9 14:44:56 2021 GMT
* common name: localhost (matched)
* issuer: C=GB; ST=Wiltshire; L=Swindon; O=ForgeRock; OU=Engineering; CN=localhost; emailAddress=sample@domain.com
* SSL certificate verify ok.
> GET /patient.json HTTP/1.1
> Host: localhost
> User-Agent: curl/7.54.0
> Accept: */*
>
< HTTP/1.1 200 OK
< Server: nginx/1.19.3
< Date: Fri, 09 Oct 2020 15:19:47 GMT
< Content-Type: application/json
< Content-Length: 395
< Last-Modified: Fri, 09 Oct 2020 13:48:42 GMT
< Connection: keep-alive
< ETag: "5f806a3a-18b"
< Accept-Ranges: bytes
...
Note that the certificate is verified and the certificate information is correctly displayed. This is followed by the JSON data (not shown).
This verifies that the JSON file has been served over HTTPS, and the debug output shows that the Transport Layer Service (TLS) protocol has been used, and that certificate verification has taken place.
You have now completed this tutorial.
Conclusion
In this tutorial you have seen how to set up an Nginx web server to serve static JSON files over HTTPS, in an environment suitable for local development. If setting up a production server, the process is similar, but there are additional security concerns that are beyond the scope of this tutorial.
Further information
Colophon
The source for this document is written in AsciiDoc, using Emacs and adoc-mode
, and converted to HTML5 and PDF using AsciiDoctor. The syntax highlighter used was Rouge.
The file is spell-checked, converted, and uploaded to hosting provider Neocities, using a simple script:
#!/usr/bin/env bash
mdspell --en-us README.adoc
asciidoctor README.adoc
asciidoctor-pdf README.adoc
cp README.html test.html
cp README.pdf test.pdf
neocities upload -d forgerock test.html test.pdf
Spellchecking is carried out in interactive mode, using mdspell, with the US dictionary specified. All source files are stored for future reference in a private GitHub repository.