Skip to main content
  1. Posts/

How do you specify a target hostname for a https request?

··818 words·4 mins·
DevOps Tips & How-To's Curl Sni Tls Https
Table of Contents

Thumbnail image generated by Stable Diffusion Online with the prompt: a man searching for a gate leading to a server in the middle of a thousand servers.

Why?
#

Hostname spoofing is commonly used when you want to test the reachability of a service behind a reverse proxy or a load balancer. - Typically, this is done when you are testing a new server or a load balancer. For instance, let’s say you have service.example.com pointing to lb.example.com, and you want to test the behavior with a new load balancer called new-lb.example.com. Instead of modifying your DNS to point to the new LB, you can employ hostname spoofing.

Hostname spoofing relies on setting the value of the HTTP Host header to indicate which VirtualHost (Apache) or Server block (nginx) the request should be routed to.

In our example mentioned above, if you want to reach service.example.com via the new load balancer, you can use curl to set the Host header to service.example.com.

curl -H "Host: service.example.com" http://new-lb.example.com

Hostname spoofing with TLS/SNI?
#

Recently, I had to test out a scenario where I was moving traffic from one domain (service.example.com) to another (service.example.net). Both the domains were fronted by the same Load Balancer (lb.example.com - which is an AWS ALB), but the load balancer had a default certificate with the Common Name set to *.example.com, with an additional certificate for *.example.net. My goal was to test out the new additional certificate for *.example.net was being used for the requests and ensure that an end-to-end connection could be established, receiving a response in return.

I started out by host spoofing with curl

curl -H "Host: service.example.net" https://lb.example.com/health/

curl: (60) SSL: no alternative certificate subject name matches target host name 'lb.example.com'
More details here: https://curl.se/docs/sslcerts.html

curl failed to verify the legitimacy of the server and therefore could not
establish a secure connection to it. To learn more about this situation and
how to fix it, please visit the web page mentioned above.

Well, this failed. I thought the Host header would be sufficient, but clearly it wasn’t. I found out that Host header wouldn’t be used for SNI, and that SNI sends the hostname (as provided in the URL) during the TLS Handshake. I tried to search for “curl SNI” and found some posts and Super User/Stack Overflow answers but didn’t clearly explain how to get it to work. However, I came across this blog post by Claudio Kuenzler which talks about using OpenSSL to validate that the SNI certificate works, using the -servername parameter.

Happy to see this, I tried it out:


openssl s_client -connect lb.example.com:443 -servername service.example.net
CONNECTED(00000006)
depth=2 CN = Root CA
verify return:1
depth=1 CN = Intermediate CA
verify return:1
depth=0 CN = *.example.net
verify return:1
---
Certificate chain
 0 s:CN = *.example.net
   <redact>
 1 s:CN = Intermediate CA
   i:CN = Root CA
   <redact>
---
Server certificate
-----BEGIN CERTIFICATE-----
<redact>
-----END CERTIFICATE-----
<snip>

Yay! Positive response. I was able to establish a connection with the new domain passed as the servername, and the correct certificate was used to validate the connection. The author, however, wasn’t able to test out using curl since back then, curl had not yet released version 7.49.0. This version added the --connect-to parameter that makes SNI connections much simpler to request.

The general structure of the command is :

curl -iv --connect-to <target host name>:<target port>:<actual host name>:<actual host port> https://<target host name>

Adapting this to our needs,


 curl -iv --connect-to service.example.net:443:lb.example.com https://service.example.net/health

* Connecting to hostname: lb.example.com
*   Trying 10.x.x.x:443...
* Connected to (nil) (10.x.x.x) port 443 (#0)
* ALPN: offers h2,http/1.1
* (304) (OUT), TLS handshake, Client hello (1):
*  CAfile: /etc/ssl/cert.pem
*  CApath: none
* (304) (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, Change cipher spec (1):
* TLSv1.2 (OUT), TLS handshake, Finished (20):
* TLSv1.2 (IN), TLS change cipher, Change cipher spec (1):
* TLSv1.2 (IN), TLS handshake, Finished (20):
* SSL connection using TLSv1.2 / ECDHE-RSA-AES128-GCM-SHA256
* ALPN: server accepted h2
* Server certificate:
*  subject: CN=*.example.net
*  start date: Jul 14 01:21:06 2023 GMT
*  expire date: Jul 13 01:21:35 2024 GMT
*  subjectAltName: host "service.example.net" matched cert's "*.example.net"
*  SSL certificate verify ok.
* using HTTP/2

<..snip.. >

Health: True
* Connection #0 to host (nil) left intact

References
#

If you’re interested in reading more about TLS, TLS Handshake, SNI and more, I would recommend these articles:

Sathyajith Bhat
Author
Sathyajith Bhat
Author, AWS Container Hero and DevOps Specialist.

Related

How to get fzf working in PowerShell
··462 words·3 mins
DevOps Tips & How-To's Fzf Powershell Opensource
Here’s how you can get the goodness of fuzzy search in your PowerShell terminal with fzf.
Getting Terraform to apply only when a change exists using Make
··1982 words·10 mins
Tips & How-To's Terraform Linux Bash Make
This post explains Terraform’s exit codes in detail and how you can use them to skip a terraform apply when no changes are needed.
Self-hosting FreshRSS (for free) on Fly.io in under 10 minutes
··621 words·3 mins
DevOps Self-Hosting Fly.io
Here’s how you can self-host FreshRSS on Fly.io in under 10 minutes