Configuring NGINX for Mutual TLS
NGINX (pronounced “engine-x”) is a popular webserver with a focus on speed and performance. The server is open source, and the NGINX company provides optional paid support for commercial customers.
A Disclaimer
Do not rely on this blog post to create a secure system. Instead, rely on your InfoSec and IT departments or consultants. Only they understand your specific configuration and its issues.
Install NGINX
To configure NGINX for Mutual TLS and access control with the DocuSign Connect webhook system, start by installing NGINX. Create a virtual host configuration in /etc/nginx/sites-available/default
Update the default configuration to support SSL. You must use an SSL server certificate that chains to a root included in the Microsoft CA list. You can use a free server SSL certificate from the Let’s Encrypt project.
Download a root certificate for the DocuSign Connect certificate. There are two to choose from. In my tests, both worked fine. Some customers have found more success with the 1024 bit certificate.
- 2048 bit root certificate. SHA1 Fingerprint=4E:B6:D5:78:49:9B:1C:CF:5F:58:1E:AD:56:BE:3D:9B:67:44:A5:E5
- 1024 bit root certificate: use the "Root 2" cert from https://www.symantec.com/theme/roots SHA1 Fingerprint=A1:DB:63:93:91:6F:17:E4:18:55:09:40:04:15:C7:02:40:B0:AE:6B
Name the certificate “connect_root.pem”
Update your NGINX primary configuration file to add a custom log format named “ssl_client.” We’ll use it in the server’s configuration:
http {
log_format ssl_client
'$remote_addr - $remote_user [$time_local] '
'"$request" $status $body_bytes_sent '
'"Client fingerprint" $ssl_client_fingerprint '
'"Client DN" $ssl_client_s_dn';
Update your virtual host config to require Mutual TLS. We’re also requiring TLS 1.2:
server {
# SSL configuration
listen 443 ssl;
# Using the custom log format:
access_log /var/log/nginx/listener.log ssl_client;
root /var/www/html;
client_max_body_size 50M; # Needed due to large size of DocuSign
# notifications that include
# source documents
index index.php index.html index.htm index.nginx-debian.html;
server_name listener.worldwidecorp.us;
ssl_certificate /etc/letsencrypt/live/listener.worldwidecorp.us/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/listener.worldwidecorp.us/privkey.pem;
ssl_protocols TLSv1.2;
ssl_client_certificate /home/ubuntu/connect_root.pem;
ssl_verify_client on;
ssl_verify_depth 10;
# location settings are not shown…
Testing
Configure DocuSign to send a notification to your listener. Remember to enable Mutual TLS for the Connect configuration. For more information about configuring Mutual TLS, see this blog post.
Check your custom access log, “listener.log.” You should see the expanded log format that we set in the configuration files:
162.248.184.11 - - [13/Feb/2017:11:44:29 +0000] "POST /info.php HTTP/1.1" 200 61 "Client fingerprint" d2818c5263a98ca63ce0cad359f912736cc3a0ce "Client DN" /C=US/ST=Washington/L=Seattle/O=DocuSign, Inc./OU=Technical Operations/CN=demo.connect.docusign.net
The log shows that our listener (info.php) responded with a 200 status code. It also shows the client’s (DocuSign’s) certificate fingerprint and distinguished name.
Access Control
Authenticating a client via its certificate is a good start. But the key is access control. For this example, we’ll control access by checking the client’s certificate fingerprint. We can obtain the right fingerprint either from a known-good connection attempt (as logged above), or through an independent channel. As an independent check, download the appropriate Connect certificate from the DocuSign Trust website. Then use the openssl command to view the certificate’s fingerprint:$ openssl x509 -in DEMO.connect.docusign.net.cer -noout -fingerprint
Output:
SHA1 Fingerprint=D2:81:8C:52:63:A9:8C:A6:3C:E0:CA:D3:59:F9:12:73:6C:C3:A0:CE
You can see that the d2818c52 at the start of the fingerprint in the log is the same as the openssl command’s output of D2:81:8C:52, only formatted differently.
Next, we’ll add access control to the NGINX config by adding an “if” re-write rule in the server section. Be careful about adding the “if” rule to a location section in the configuration file!
# Add the following before the first location section
# Access control -- the if needs to be outside of the location sections
# Note https://www.nginx.com/resources/wiki/start/topics/depth/ifisevil/
if ($ssl_client_fingerprint != "d2818c5263a98ca63ce0cad359f912736cc3a0ce") {
return 403;
}
Restart the NGINX server and check that a test Connect notification works properly.
Then change the fingerprint value in the config file (e.g. prepend “X” to it), restart, and confirm that now a test Connect notification will be refused.
Problems
Notification messages are too large
If you choose to include envelopes’ documents in the Connect notifications, you may encounter maximum POST or payload size limitations. In the example above, we updated NGINX’ maximum request size. Your stack (Java, Node.JS, php, etc) may have additional maximum request size settings that will also need to be increased.
The DocuSign client cert was not sent or other TLS handshake issues
You can turn on “debug” level logging to see more information in the error_log about the TLS handshake process. To do so, change the error_log setting in the base level NGINX configuration file:
error_log /var/log/nginx/error.log debug;Here are the certificate verification lines from the error log when debug is on. Using the 1024 bit root certificate:
2017/02/13 09:58:56 [debug] 6489#6489: *2 verify:1, error:0, depth:3, subject:"/C=US/O=VeriSign, Inc./OU=Class 3 Public Primary Certification Authority", issuer:"/C=US/O=VeriSign, Inc./OU=Class 3 Public Primary Certification Authority"
2017/02/13 09:58:56 [debug] 6489#6489: *2 verify:1, error:0, depth:2, subject:"/C=US/O=VeriSign, Inc./OU=VeriSign Trust Network/OU=(c) 2006 VeriSign, Inc. - For authorized use only/CN=VeriSign Class 3 Public Primary Certification Authority - G5", issuer:"/C=US/O=VeriSign, Inc./OU=Class 3 Public Primary Certification Authority"
2017/02/13 09:58:56 [debug] 6489#6489: *2 verify:1, error:0, depth:1, subject:"/C=US/O=Symantec Corporation/OU=Symantec Trust Network/CN=Symantec Class 3 Secure Server CA - G4", issuer:"/C=US/O=VeriSign, Inc./OU=VeriSign Trust Network/OU=(c) 2006 VeriSign, Inc. - For authorized use only/CN=VeriSign Class 3 Public Primary Certification Authority - G5"
2017/02/13 09:58:56 [debug] 6489#6489: *2 verify:1, error:0, depth:0, subject:"/C=US/ST=Washington/L=Seattle/O=DocuSign, Inc./OU=Technical Operations/CN=demo.connect.docusign.net", issuer:"/C=US/O=Symantec Corporation/OU=Symantec Trust Network/CN=Symantec Class 3 Secure Server CA - G4"
Using the 2048 bit certificate:
2017/02/13 14:57:27 [debug] 8169#8169: *1 verify:1, error:0, depth:2, subject:"/C=US/O=VeriSign, Inc./OU=VeriSign Trust Network/OU=(c) 2006 VeriSign, Inc. - For authorized use only/CN=VeriSign Class 3 Public Primary Certification Authority - G5", issuer:"/C=US/O=VeriSign, Inc./OU=VeriSign Trust Network/OU=(c) 2006 VeriSign, Inc. - For authorized use only/CN=VeriSign Class 3 Public Primary Certification Authority - G5"
2017/02/13 14:57:27 [debug] 8169#8169: *1 verify:1, error:0, depth:1, subject:"/C=US/O=Symantec Corporation/OU=Symantec Trust Network/CN=Symantec Class 3 Secure Server CA - G4", issuer:"/C=US/O=VeriSign, Inc./OU=VeriSign Trust Network/OU=(c) 2006 VeriSign, Inc. - For authorized use only/CN=VeriSign Class 3 Public Primary Certification Authority - G5"
2017/02/13 14:57:27 [debug] 8169#8169: *1 verify:1, error:0, depth:0, subject:"/C=US/ST=Washington/L=Seattle/O=DocuSign, Inc./OU=Technical Operations/CN=demo.connect.docusign.net", issuer:"/C=US/O=Symantec Corporation/OU=Symantec Trust Network/CN=Symantec Class 3 Secure Server CA - G4"
See you in San Francisco!
Now’s a good time to make plans to join me at the free DocuSign developer conference in San Francisco in May. Details and registration. I’ll be speaking at the conference and I look forward to meeting you.