How to Fix TLS 1.3 Issues on Outdated Windows 2012 R2 Servers Using an NGINX Reverse Proxy
In 2023, while consulting for a major credit provider in Brazil, I faced a technical challenge that could be helpful for anyone dealing with outdated infrastructure. The company had a critical on-premises Windows 2012 R2 server environment spread across four locations in the country. These servers formed the backbone of one of their key products, but the setup required constant maintenance and didn’t support the TLS 1.3 protocol, which was essential for securing certain communications.
The problem arose when one of our key vendors, a government agency, announced it would soon disable TLS 1.0, 1.1, and 1.2 on its API, keeping only TLS 1.3. With a 15-day deadline for adaptation, we found that Windows 2012 R2 didn’t natively support TLS 1.3. Upgrading the infrastructure was a complex and costly task, requiring a migration to Windows 2022 or the cloud — something we didn’t have time for.
Reverse Proxy Solution: Using Node.js for Initial Testing
The solution I initially proposed was to implement a reverse proxy that would receive requests in TLS 1.2 and then forward them to the vendor using TLS 1.3. As a proof of concept, I created a Node.js script that leverages the OpenSSL library to handle the handshake with the latest protocol. Here’s a simplified version of that code:
const fs = require('fs');
const http = require('http');
const https = require('https');
const server = http.createServer((req, res) => {
const targetOptions = {
hostname: 'supplier-url-tls-1.3',
port: 443,
path: req.url,
method: req.method,
};
const protocol = targetOptions.port === 443 ? https : http;
const proxyReq = protocol.request(targetOptions, (proxyRes) => {
res.writeHead(proxyRes.statusCode, proxyRes.headers);
proxyRes.pipe(res, { end: true });
});
proxyReq.on('error', (e) => {
res.writeHead(500);
});
req.pipe(proxyReq, { end: true });
});
server.listen(0, () => {
const port = `${server.address().port}`;
fs.writeFile(`${port}.PORT`, port, (err) => {
if (err) {
return console.log(err);
}
console.log(`The port number ${port} has been saved to file ${port}.port`);
});
});
Although functional, this Node.js approach proved inadequate for the company’s 100,000 daily requests. While this isn’t an extremely high volume, a more robust and scalable solution would be necessary.
Final Solution with NGINX: Scalability and Reliability in Reverse Proxy
To ensure the scalability and stability of the solution, I chose to set up NGINX as a reverse proxy on an Ubuntu server. I configured two instances: one in the same datacenter as my client's on-premises server and another on AWS, both with 10 Gbps network, 4 GB RAM, and 4 vCPUs. This setup handled the request volume well and cost around $20 per month per machine. This infrastructure was configured to support TLS 1.3, allowing the company to continue using the legacy environment and effectively resolve compatibility issues.
With NGINX, I created a straightforward reverse proxy configuration that accepted internal requests using TLS 1.2 and communicated with the supplier’s API using TLS 1.3. Below is a basic example of an NGINX configuration for this setup:
server {
listen 80;
listen [::]:80;
listen 443 ssl;
listen [::]:443 ssl;
ssl_protocols TLSv1.1 TLSv1.2 TLSv1.3;
ssl_certificate /path/to/your_certificate.crt;
ssl_certificate_key /path/to/your_certificate.key;
server_name reverse-proxy.clientdomain.com;
location / {
proxy_pass https://supplier-api.com;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
The following line ensures that NGINX will run with TLS version 1.1 and above:
ssl_protocols TLSv1.1 TLSv1.2 TLSv1.3;
This line explicitly specifies the supported TLS versions, allowing NGINX to accept connections using TLS 1.1, 1.2, and 1.3.
However, when connecting to the client, NGINX will use the encryption ciphers available on Ubuntu, ensuring secure communication. This means that the supported ciphers are managed by the underlying operating system's OpenSSL configuration, allowing NGINX to negotiate the strongest cipher available that both parties support. This setup leverages Ubuntu's cipher suite for broader compatibility and enhanced security without needing to specify each cipher manually in the NGINX configuration.
Automation with BATCH file for Rapid Infrastructure Expansion
To streamline the creation of new servers, in case replicating the solution becomes necessary, I created a BATCH script. This script automates the provisioning of a server with NGINX and performs the necessary configurations to function as a TLS 1.3 proxy. Here’s the code:
@echo off
set ipAddress=%1
if "%ipAddress%"=="" (
echo IP address is missing. Please provide an IP address.
pause
exit /b
)
ssh root@%ipAddress% "mkdir -p /etc/letsencrypt/live/client/"
scp -r "F:\certs\client.site\*" root@%ipAddress%:/etc/letsencrypt/live/client.site/
ssh root@%ipAddress% "apt install nginx -y"
scp -r "F:\repos\___config_nginx\nginx-config-th-tls.txt" root@%ipAddress%:/etc/nginx/sites-enabled/default
ssh root@%ipAddress% "systemctl restart nginx"
pause
This automation enabled the team to easily manage proxy servers and respond swiftly when replication of the environment was needed. By simplifying the setup process, we ensured that scaling and deploying additional proxies could be done quickly and consistently, minimizing downtime and supporting high availability.
Final Considerations: A Temporary Solution for Outdated Infrastructures
After implementation, I sent a communication to the client explaining that this was a temporary solution to address compatibility with the TLS 1.3 protocol. For a long-term approach, the recommendation would be to upgrade the servers to Windows 2022 or another modern infrastructure, as support for Windows 2012 R2 was nearing its end.
Want to Learn More About Reverse Proxy with NGINX?
If you also work with outdated infrastructures and need to implement compatibility solutions, check out other articles here on the blog about configuring NGINX as a reverse proxy, along with other potential applications to enhance the security and scalability of legacy systems.
These resources provide insights into best practices, configurations, and advanced techniques that can help modernize and stabilize your environment without a complete overhaul.