111

Let's say a website is load-balanced between several servers. I want to run a command to test whether it's working, such as curl DOMAIN.TLD. So, to isolate each IP address, I specify the IP manually. But many websites may be hosted on the server, so I still provide a host header, like this: curl IP_ADDRESS -H 'Host: DOMAIN.TLD'. In my understanding, these two commands create the exact same HTTP request. The only difference is that in the latter one I take out the DNS lookup part from cURL and do this manually (please correct me if I'm wrong).

All well so far. But now I want to do the same for an HTTPS url. Again, I could test it like this curl https://DOMAIN.TLD. But I want to specify the IP manually, so I run curl https://IP_ADDRESS -H 'Host: DOMAIN.TLD'. Now I get a cURL error:

curl: (51) SSL: certificate subject name 'DOMAIN.TLD' does not match target host name 'IP_ADDRESS'.

I can of course get around this by telling cURL not to care about the certificate (the "-k" option) but it's not ideal.

Is there a way to isolate the IP address being connected to from the host being certified by SSL?

Ladadadada
  • 25,847
  • 7
  • 57
  • 90
Martin
  • 3,315
  • 4
  • 19
  • 16
  • Is your load balancer the SSL termination point, or is it the individual instances? – rikola Jul 18 '17 at 11:28
  • Can someone explain why the Host header doesn't select the correct VHost and certificate on the back-end for HTTPS, but this alone for HTTP works OK. Does client pass information to the back-end (using HTTPS protocol) whether IP was used or domain? – NeverEndingQueue Apr 30 '19 at 11:34
  • 2
    @NeverEndingQueue To understand this you need to know the relationship between TLS and HTTP. Server needs to show the certificate before it have access to `Host` header, so `--resolve` is the right way to pin an IP address. – Franklin Yu Jun 24 '19 at 03:51

4 Answers4

174

Think I found a solution going through the cURL manual:

curl https://DOMAIN.EXAMPLE --resolve 'DOMAIN.EXAMPLE:443:192.0.2.17'

Added in [curl] 7.21.3. Removal support added in 7.42.0.

from CURLOPT_RESOLVE explained

Paul
  • 2,755
  • 6
  • 24
  • 35
Martin
  • 3,315
  • 4
  • 19
  • 16
  • 1
    --resolve doe not exist in curl... – JustAGuy Sep 10 '14 at 13:07
  • 8
    The curl on my machine (7.27.0) has --resolve. – Theron Luhn Jan 16 '15 at 17:41
  • You can see it work using `curl -v` and you can see that curl adds the resolve option to the DNS cache temporariy, and adds uses the IP address you specified. – CMCDragonkai Oct 19 '15 at 05:13
  • CentOS 6.8 curl: option --resolve: is unknown curl: try 'curl --help' or 'curl --manual' for more information root@server3 [~]# curl -V curl 7.19.7 (x86_64-redhat-linux-gnu) libcurl/7.19.7 NSS/3.21 Basic ECC zlib/1.2.3 libidn/1.18 libssh2/1.4.2 Protocols: tftp ftp telnet dict ldap ldaps http file https ftps scp sftp Features: GSS-Negotiate IDN IPv6 Largefile NTLM SSL libz – Jose Nobile Nov 18 '16 at 05:23
  • Note that if you use a hostname for `IP_ADDRESS`, it will silently ignore the option. – Xiong Chiamiov Jan 30 '18 at 19:12
  • this worked great. thanks! – Sankalp Feb 25 '20 at 11:23
  • 1
    This doesn't work. It adds a record to the resolver's cache, but it seems that there is no guarantee that it will be used. If I use `-v`, I can see it adding it to the cache, but then it goes and does a lookup and receives one of the addresses that's from normal DNS, so it ends up not using the IP I want. – Synchro Sep 16 '20 at 09:08
18

Here is the man entry for the currently most upvoted answer since they only included a link to the programmatic component:

--resolve <host:port:address>

    Provide  a  custom  address  for  a specific host and port pair. Using
    this, you can make the curl requests(s) use a specified address and 
    prevent the otherwise normally resolved address to be used. Consider 
    it a sort of /etc/hosts alternative provided on the command line. 
    The port number should be the number used for the specific protocol 
    the host will be used for. It means you need several entries if you 
    want to provide address for the same host but different ports.

    The provided address set by this option will be used even if -4, 
    --ipv4 or -6, --ipv6 is set to make curl use another IP version.

    This option can be used many times to add many host names to resolve.

    Added in 7.21.3.

But due to the limitation given ("It means you need several entries if you want to provide address for the same host but different ports."), I would consider another, newer option that can translate both at the same time:

--connect-to <HOST1:PORT1:HOST2:PORT2>

    For  a request to the given HOST:PORT pair, connect to 
    CONNECT-TO-HOST:CONNECT-TO-PORT instead. This option is suitable 
    to direct requests at a specific server, e.g. at a specific cluster 
    node in a cluster of servers. This option is only used to establish 
    the network connection. It does NOT affect the hostname/port that 
    is used for TLS/SSL (e.g. SNI, certificate verification) or for 
    the application protocols. "host" and "port" may be the empty string, 
    meaning "any host/port". "connect-to-host" and "connect-to-port" 
    may also be the empty string, meaning "use the request's original host/port".

    This option can be used many times to add many connect rules.

    See also --resolve and -H, --header. 
    Added in 7.49.0.

Example using --connect-to:

curl https://domain.example --connect-to domain.example:443:203.0.113.81:443
Paul
  • 2,755
  • 6
  • 24
  • 35
peedee
  • 381
  • 4
  • 12
  • curl: option --connect-to: is unknown – Nathan B May 21 '22 at 18:40
  • 1
    @NathanB If it isn't in the man page, you might have an older version. But, I have to say, that would be quite an old version because even Ubuntu 18.04 has it by default. – Paul Jul 14 '22 at 15:31
13

You can change in /etc/hosts to make the server think that the domain is located at a certain IP.

This is the syntax:

192.168.10.20 www.domain.tld

This will make cURL use the IP-address you want without the SSL-certificate to break.

miono
  • 536
  • 2
  • 6
  • 6
    That works, but I'm looking at some method which doesn't require modifying the OS as a whole – Martin Oct 31 '12 at 09:35
  • I honestly don't think there's another way. You must get cURL to access the correct domain-name for the certificate to work, and the way to map a certain domain to an IP is either by DNS or by the hosts-file. – miono Oct 31 '12 at 10:19
4

If you're running curl in Windows, use double quotes

curl https://DOMAIN.TLD --resolve "DOMAIN.TLD:443:IP_ADDRESS"

curl DOMAIN.TLD --resolve "DOMAIN.TLD:80:IP_ADDRESS"

You can skip the scheme/protocol up front, but you can't skip the port number in the --resolve string.