How do you specify a target hostname for a https request? – sathyasays.com
This page looks best with JavaScript enabled

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

 ·  ☕ 5 min read  ·  ✍️ Sathyajith Bhat · 👀... views

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.

1
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

1
2
3
4
5
6
7
8
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:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22

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 :

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

Adapting this to our needs,

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33

 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:

Share on

Sathyajith Bhat
WRITTEN BY
Sathyajith Bhat
Author, AWS Container Hero and DevOps Specialist

What's on this Page