CURL works, CFHTTP does not

CURL:

curl --request POST \
  'https://iamcredentials.googleapis.com/v1/projects/-/serviceAccounts/task-runner@sandbox1-187518.iam.gserviceaccount.com:generateAccessToken' \
  --header 'Authorization: Bearer [TOKEN_FOR_REQUEST]' \
  --header 'Accept: application/json' \
  --header 'Content-Type: application/json' \
  --data '{"scope":["https://www.googleapis.com/auth/cloud-tasks"]}' \
  --compressed

. . . returns the expected accessToken

CFHTTP:

      cfhttp(url="https://iamcredentials.googleapis.com/v1/projects/-/serviceAccounts/task-runner@sandbox1-187518.iam.gserviceaccount.com:generateAccessToken", 
         method="POST",
         result="resp") {
        cfhttpparam( name='Authorization', type='header', value='Bearer [TOKEN_FOR_REQUEST]') 
        cfhttpparam( name='Accept', type='header', value='application/json');
        cfhttpparam( name='Content-Type', type='header', value='application/json');
        cfhttpparam( type='body', value='{"scope":["https://www.googleapis.com/auth/cloud-tasks"]}');
      }

. . . returns

errordetail	string	Unknown host: metadata.google.internal
filecontent	string	Connection Failure
header	string	
mimetype	string	Unable to determine MIME type of file.
responseheader	Struct
status_code	number	0
status_text	string	Connection Failure
statuscode	string	Connection Failure. Status code unavailable.
text	boolean	true

It seems that Google is doing a redirect in someway that CFHTTP simply cannot handle, but curl can.
I’ve played with the redirect parameter to cfhttp to no effect.

Anyone else experience anything like this? I’ve been prodding at this issue for the last couple of days and I’m not having any luck.

@Brian_Harcourt Please always mention your lucee version, database / ext version, all relevant to your support request

Ah! Of course . . . :blush:

Version Lucee 5.3.10.97
Version Name Gelert
Release date Nov 25, 2022
Remote IP 172.17.0.1
Servlet Container Apache Tomcat/9.0.75
Java 11.0.19 (Eclipse Adoptium) 64bit
Host Name localhost
OS Linux (6.1.0-9-amd64) 64bit
Architecture 64bit
Inspect Templates (CFM/CFC) Never ( Best Performance )
Key case Convert to upper case (CFML Default)
Null Support Complete Support
Local scope mode Classic (CFML Default)

I’m running inside a docker container on my Debian 12 desktop. And my curl test is run inside this container as well.

1 Like

unknown host and and an .internal domain? pretty clear?

run this thru a proxy and compare results

Hmm - running thru a proxy? Can you point to a bit more info?
I also should run this in it’s eventual environment (GCP Cloud Run) it shouldn’t have issues finding metadata.google.internal there.

But since curl resolves this just fine in my test environment, it seems that cfhttp should be able to as well. (although, I realize I’m pretty deep into the weeds with this sitch).

something like fiddler etc, not sure what the options are on debian

you can do the same with curl

Maybe I need to learn more about what a proxy actually is. I think you’re suggesting set up some kind of proxy inside Cloud Run (where ‘internal’ would be resolved) then add that proxy to the cfhttp request. That’s one approach to get this operating on my desktop but I’m going to see if it’ll run correctly inside Cloud Run first. :confused:
But still wondering why curl works just fine from this container (running locally) while cfhttp simply cannot.

In particular, in this case, please indicate the Java version that lucee is using. I suspect your problem is not with Lucee (or cfhttp) but with the underlying Java it runs on. As you may know, cfhttp gets converted into a Java httpclient call. Curl is not java-based. In most cases of such failures, the crux is in the url using https (which I realize you likely can’t change).

Sometimes such https failures are due to certificate issues, having to do with certs implemented within Java, or not. In this case (of such a public url), I doubt that’s it.

The next most common issue is if the tls/SSL versions supported by the server you’re calling differs from what your java version supports. Perhaps you’re on an older Java, and the server you’re calling wants to use newer tls algorithms than the old Java supports. Or it could also be the reverse: since Apr 2021, jvm updates no longer support such https calls to servers that don’t support at least tls 1.2.

These are just guesses, but from experience helping people with such problems. I have blog posts on each:

And if somehow this or thoughts from others here don’t end up helping you resolve thing on your own, I’ll note that I help people solve such problems daily, via remote consulting, often in well less than an hour. You won’t pay for time you don’t find valuable. More at Charlie Arehart's Server Troubleshooting Consulting Services.

Looking forward to hearing how things work out.

3 Likes

Fwiw, I was writing my last post in reply to cfmitrah’s first comment (asking for details), and the few replies in between came while I was writing. Oddly, the site didn’t let me know there were new posts (I thought it did offer such notifications while replying, or I’d have checked first and revised my comment a bit, considering those). I’ll always check, from now on, and just did. :slight_smile:

In any case, I hope it may help Brian or others facing similar challenges.

Of course! Java!
Thank you Charlie, I’ll take a closer look at the java version in my container build.
I’ll post results for future visitors here.

your java version is the latest java 11

try looking up that host name using some java in cf

Oh - Thanks Zackster. More to play with . . . tho, I’ll note that I cannot ping metadata.google.internal from a shell in the container. So, I don’t think it’s an address resolution issue. Somehow, curl handles whatever is needed. I may try corretto vs openjdk? Or maybe it’s about SSL as one of Charlie’s notes suggests?
I’ll be digging around today and report back here.

@Zackster I think you may know best whether it’s worth playing with higher versions of java (and /or tomcat). I guess I am on the latest version of 11, but dockerhub shows tomcat 9 with jdk17 and jdk21. At the moment - I’m just assuming I should avoid versions above 11 as ‘not ready’. Sound right to you?

java 11 is totally fine and recommended.

does nslookup work?

No - that internal domain really is invisible to me. So, I’m not sure what’s happening but it seems like some quality of the connection that curl makes allows ‘iamcredentials.googleapis.com’ to resolve internal connections without an explicit client redirect but the connection java is making requires some kind of client redirect. And the redirect is to a domain that is not resolvable outside GCP. At least, that’s my current theory.

Status (for future visitors):
I was in error - the message that cfhttp gets is a 404 error. (the ‘unknown host’ error was a failed connect attempt elsewhere in the script). The actual error from cfhttp is

errordetail	string	404 Not Found
filecontent	string	
header	string	HTTP/1.1 404 Not Found Content-Type: text/html Date: Wed, 14 Jun 2023 17:22:10 GMT Server: scaffolding on HTTPServer2 Content-Length: 0 X-XSS-Protection: 0 X-Frame-Options: SAMEORIGIN X-Content-Type-Options: nosniff Alt-Svc: h3=":443"; ma=2592000,h3-29=":443"; ma=2592000
http_version	string	HTTP/1.1
mimetype	string	text/html

But the URL that is NOT FOUND is easily found by curl.

My new theory is that somehow Java is munging the URL such that the domain is hit but the path is messed up. I note that the URL has an @ and also ends with a parameter delineated by a colon (:).

Tho URL encoding those characters does not result in a change in behavior.

can you provider a censored example?

Like this?

       req = new http(url="https://iamcredentials.googleapis.com/v1/projects/-/serviceAccounts/task-runner@sandbox1-187518.iam.gserviceaccount.com:generateAccessToken", 
            method="POST");
        req.addParam( name='Authorization', type='header', value='Bearer [TOKEN_FOR_REQUEST]') 
        req.addParam( name='Accept', type='header', value='application/json');
        req.addParam( name='Content-Type', type='header', value='application/json');
        req.addParam( type='body', value='{"scope":["https://www.googleapis.com/auth/cloud-tasks"]}');
        resp = req.send().getPrefix();
        dump(resp);
1 Like

this is how it gets parsed

@Brian_Harcourt what does curl -v show the url to be sent as?

I’m getting a 404

C:\work\lucee6\loader>curl -v https://iamcredentials.googleapis.com/v1/projects/-/serviceAccounts/task-runner@sandbox1-187518.iam.gserviceaccount.com:generateAccessToken
*   Trying 142.250.74.202:443...
* Connected to iamcredentials.googleapis.com (142.250.74.202) port 443 (#0)
* schannel: disabled automatic use of client certificate
* ALPN: offers http/1.1
* ALPN: server accepted http/1.1
* using HTTP/1.1
> GET /v1/projects/-/serviceAccounts/task-runner@sandbox1-187518.iam.gserviceaccount.com:generateAccessToken HTTP/1.1
> Host: iamcredentials.googleapis.com
> User-Agent: curl/8.0.1
> Accept: */*
>
* schannel: remote party requests renegotiation
* schannel: renegotiating SSL/TLS connection
* schannel: SSL/TLS connection renegotiated
< HTTP/1.1 404 Not Found
< Vary: X-Origin
< Vary: Referer
< Content-Type: application/json; charset=UTF-8
< Date: Wed, 14 Jun 2023 17:53:09 GMT
< Server: scaffolding on HTTPServer2
< Cache-Control: private
< X-XSS-Protection: 0
< X-Frame-Options: SAMEORIGIN
< X-Content-Type-Options: nosniff
< Alt-Svc: h3=":443"; ma=2592000,h3-29=":443"; ma=2592000
< Accept-Ranges: none
< Vary: Origin,Accept-Encoding
< Transfer-Encoding: chunked
<
{
  "error": {
    "code": 404,
    "message": "Requested entity was not found.",
    "status": "NOT_FOUND"
  }
}
* Connection #0 to host iamcredentials.googleapis.com left intact
2 Likes

THANK YOU!!!

As usual, sorta seems like I shoulda thought of this.

Google API needs:
:generateAccessToken

with the default encoding of the url cfhttp was sending:
%3AgenerateAccessToken

I set the cfhttp attribute encodeurl=false and . . .
Bob’s-yer-Uncle.

Thanks all . . .
B.