Background
I have a Private API Gateway attached to a VPC Endpoint which I am NOT using private DNS for. Therefore the url of the form https://${aws_api_gateway_rest_api.this.id}-${aws_vpc_endpoint.this.id}.execute-api.${var.region}.amazonaws.com/${var.stage}/${var.path} is publicly resolvable per Invoke Private API via VPC Endpoint with Route 53 Alias. This is an endpoint used by other internal clients at my org who are coming from within our private, multi-cloud network.
I can successfully invoke this URL no problem from my private network with the following:
curl -v -X POST https://${aws_api_gateway_rest_api.this.id}-${aws_vpc_endpoint.this.id}.execute-api.${var.region}.amazonaws.com/${var.stage}/${var.path}.
The Necessary Design
This URL will change whenever the api gateway id or vpc endpoint id changes. I need a static url I can provide to internal clients that will never change.
Attempted Solution
I create a route53 CNAME record named endpoint.sub.domain.com in a public hosted zone called sub.domain.com and pointed it at ${aws_api_gateway_rest_api.this.id}-${aws_vpc_endpoint.this.id}.execute-api.${var.region}.amazonaws.com to serve as a static "proxy" to always point to the underlying publicly resolvable DNS record for the Private API Gateway.
What happened
I went to curl this just like the previous one and received:
curl -v -X POST https://endoint.sub.domain.com/<stage>/<path>
but had TLS issues:
Server certificate:
* subject: CN=*.execute-api.us-east-1.amazonaws.com
* start date: Sep 19 00:00:00 2022 GMT
* expire date: Sep 16 23:59:59 2023 GMT
* subjectAltName does not match endpoint.sub.domain.com
* SSL: no alternative certificate subject name matches target host name 'endpoint.sub.domain.com'
* Closing connection 0
* TLSv1.2 (OUT), TLS alert, close notify (256):
curl: (60) SSL: no alternative certificate subject name matches target host name 'airport-mock-endpoint.npd.nortonalto.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.
Tried the same with --insecure just to see:
curl --insecure -v -X POST https://endoint.sub.domain.com/<stage>/<path>
And received {"message”:”Forbidden”}.
Tried the same, this time specifying the api gateway id:
curl --insecure -v -X POST https://endoint.sub.domain.com/<stage>/<path> -H "x-apigw-api-id:5xxxxxxf"
And received:
{"message":"Missing Authentication Token"}
Tried the same, this time specifying the target of the CNAME record (the original url) as the Host:
curl --insecure -v -X POST https://endoint.sub.domain.com/<stage>/<path> -H "Host: {api-gateway-id}-{vpc-endpoint-id}.execute-api.us-east-2.amazonaws.com"
And received:
Success!
The Problem
Clients must be able to call this API without passing in the Host header AND with the same secure TLS that the original endpoint provides. How can this be accomplished?
Possible solutions
1. Have a server 302 redirect requests
Don't know if this would solve all the problems
NOT elegant as I want to accomplish this purely from DNS
2. Configure something involving Cloud Front?
3. Some Route 53 / API Gateway DNS configuration?
4. Enable private DNS in my VPC
Would solve the problem but would introduce the problem of not being able to call public api gateways from this network (as private DNS would "swallow" these queries)
Requires more network complexity I wanted to avoid by using a public DNS
Related
I'm getting a 403 forbidden response when using fetch from a serverless Cloudflare Worker to my own dotnetcore api hosted on AWS EC2 instance. Both GET and POST. example worker code (also tested with init headers like user agent, accept, etc but same result):
fetch('http://54.xxx.xxx.xxx/test')
However that basic fetch to that api ip url returns 200 from local javascript and a simple hosted webpage. As well as postman and curl.
Also the Cloudflare worker can fetch other apis without issue.
fetch('http://jsonplaceholder.typicode.com/posts')
In the end I had to use the AWS DNS url instead.
fetch('http://ec2-54-xxx-xxx-xxx.us-west-1.compute.amazonaws.com/test')
This AWS elasticbeanstalk setup is as basic as possible. Single t3a.nano instance with default security group. I didn't see any documentation regarding the usage of IP vs DNS urls but they should resolve to the same IP. Also I don't see any options to deal with DNS issues on cloudflare side.
Nor any similar issues on stackoverflow.
So after a lot of pain, I'm just documenting the solution here.
Under the amazon instance summary you can find both "Public IPv4 address" and "Public IPv4 DNS".
From the Cloudflare worker the fetch with public dns works
fetch('http://ec2-54-xxx-xxx-xxx.us-west-1.compute.amazonaws.com/test')
and fetch with public ip returns status 403 with statusText "Forbidden"
fetch('http://54.xxx.xxx.xxx/test')
Cloudflare Workers can make outbound HTTP requests, but only to domain names. It is not possible for a Worker to make a fetch request to an IP address.
I can't confirm the exact behavior you observed (your post was 9 months ago, and a number of things have changed with Cloudflare Workers since then), in the last month or so I've observed that calling fetch() on an IP address results in the worker seeing "Error 1003 Access Denied" as the fetch response.
There isn't much info, but here's what's available about Error 1003:
Error 1003 Access Denied: Direct IP Access Not Allowed
Common cause – A client or browser directly accesses a Cloudflare IP address.
Resolution – Browse to the website domain name in your URL instead of the Cloudflare IP address.
As you found, if you use a DNS name instead, fetch works fine.
I have a proxy+ resource configured like this,
NLB is internal, so using VPC Link, but when I hit the API gateway stage url, I am getting 404. Below are the logs,
(some-request-id) Sending request to http://masked.elb.us-east-1.amazonaws.com/microservice/v2/api-docs
Received response. Status: 404, Integration latency: 44 ms
But when I copy paste the same NLB URL from the log in the browser, I am getting json response back with HTTP 200.
What is that I am missing here?
This 404 is being returned from the application on your load balancer so it is definitely connecting.
I can see from your request the hostname you're specifying is an ELB name, is the application listening on this host name? Some web server services such as Apache or Nginx will hit the first vhost if they do not match one within another vhost which may not hit your application.
The domain name you specify in API Gateway should be the one it will connect to on the host, the VPC Link stores the information of which load balancer this link is for. So if your API has a VHOST for https://api.example.com you would specify https://api.example.com/{proxy}.
From your host you should be able to see within the access logs (and error logs) which host/path it is trying to load from.
It turns out that, I was pointing to wrong VPC Link. Once I pointed to correct VPC Link it started working.
Key here is that even though API Gateway logs tells me that, it is hitting http://masked.elb.us-east-1.amazonaws.com/microservice/v2/api-docs, it doesn't actually hit this URL. Instead it hits the NLB which VPC Link is attached to.
I confirmed this by changing the domain name in the Endpoint URL to,
http://domainwhichdoesnotexist.com/microservice/v2/api-docs
And in logs I see this,
Thu Jul 30 09:28:09 UTC 2020 : Sending request to http://domainwhichdoesnotexist.com/microservice/api/api-docs
Thu Jul 30 09:28:09 UTC 2020 : Received response. Status: 200, Integration latency: 72 ms
the concept is simple.
Create a Cname switch that points to blue / green deployment channels via AWS API Gateway. The API has two stages for blue and green mapped back into an environment variable which in turns maps back into the Lambda alias associated with it's own dedicated version. Therefore there are now two separate channels for conducting deployments into. All of this works fine.
The issue arises when a Cname is created in Route53 to point to either of the API Gateways blue or green custom domains. The SSL cert is held in AWS Certificate Manager.
When we call the blue endpoint via the Cname we get an SSL error
curl -Il -H "Host:blue-api.example.com" -H "x-api-key:xxxxxxxxx"
-X GET https://cname-api.example.com/questions/health
curl: (35) SSL peer handshake failed, the server most likely requires a client certificate to connect
Whereas when we call the custom domain directly it works
curl -Il -H "Host:blue-api.example.com" -H "x-api-key:xxxxxxxxx"
-X GET https://blue-api.example.com/questions/health
HTTP/1.1 200 OK
Any pointers or suggestions would be much appreciated?
Response to first comment
Thanks Michael - yes we've exported the the SSL Cert's from eu-west-1 into the AWS Cert Manager for us-east-1, as we are running an edge optimised custom domain name. The API Gateway generated Cloudfront is hosted in us-east-1 along with the costom domain and the cname, but the root domain is hosted in eu-west-1. This may be the issue?
We are trying some further tests around enabling the following headers and will report back -
ResponseParameters:
method.response.header.Access-Control-Allow-Headers: true
method.response.header.Access-Control-Allow-Methods: true
method.response.header.Access-Control-Allow-Origin: true
I realise that we can use one custom domain, and carve up the channels using stages mapped to paths instead, but the aforementioned is the preferred solution.
We also have a ticket open with AWS as this has also flummoxed them, and has been escalated to their Internal service team.
:)
So after many weeks for too'ing and fro'ing with Amazon Support we got a solution working, but perhaps not the most cost effective as the early solution mentioned.
Workflow -
1.Create API GW, where each stage has it's own custom domain.
E.G. blue-stage > blue-api.example.com and green-stage > green-api.example.com
2.Then create a Cname in the same AWS region as where the API GW has been created.
E.G. cname-api.example.com
3.Create a Web Edge Cloudfront instance with the same DNS entry (duplicate) used for the Cname (cname-api.example.com) and point this Cloudfront instance to an arbitrary S3 bucket.
Now make the request
curl -Il -H "Host:blue-api.example.com" -H "x-api-key:xxxxxxxxx"
-X GET https://cname-api.example.com/questions/health
HTTP/1.1 200 OK
Yes this is overkill for what we need to achive and also not the most cost effective nor are we doing KISS (Keep It Simple Stupid). So yeah - it would be great to hear back on how others are building similar infrastructure!
I have created one API using API Gateway. The API is working fine if I call it from localhost or any server outside AWS network(Server is not instantiated from AWS) but when I am trying to call the same API from any server which is within AWS network, it does not work.
I have created custom domain.
When we are calling the API using custom domain name, below error is thrown:
SSL: certificate subject name (*.execute-api.ap-south-1.amazonaws.com) does not match target host name 'custom-domain-name'
but if I use the API 's origin url(getting from the stage), then it throw:
{"message":"Forbidden"}
Here is the code I am using:
curl --header "Content-Type: application/json" --header "x-api-key: ViR6gYpw046xxxxxyyyyyyzzzzzzzzzzzzzzzzzzzzzz" --request POST --data '{"username":"xyz","password":"xyz"}' https://post-data.xyz.com/post-userdata-v1/user-data
Below are few details:
API type: Post
Lambda function is integrated
Any advice would be greatly appreciated.
Thanks
Biswajit
Since the API is accessible from localhost and servers outside AWS, the setup seems to be fine. The calls from AWS servers would be failing due to the DNS settings in the VPC from which these AWS servers are launched.
Check the following two settings in your VPC and enable them if not done.
You can enable them by following this path: VPCs >> Select Your-VPC >> Actions
Im trying to redirect a call from api gateway to a public elb in AWS. The ELB is open to the world but I cannot make it work going by the API Gateway.
API GateWayConfiguration
I get this response from the postman when I call the events operation
{
"message": "Internal server error"
}
And from AWS test console, Im getting this error:
Wed Jan 17 20:29:12 UTC 2018 : Execution failed due to configuration error: Host name 'public-elb.amazonaws.com' does not match the certificate subject provided by the peer (CN=*.confidential.com)
Wed Jan 17 20:29:12 UTC 2018 : Method completed with status: 500
I assume that the ELB is reachable because then I change to another random URL, the error code is "Invalid endpoint address".
Why am I getting this error? I only have one certificate and is the same in the url and the elb.
Your error is caused by the SSL certificate having the common name "*.confidential.com" and you are redirecting to a different name "public-elb.amazonaws.com"
The solution is to create an ALIAS (preferred) or CNAME record in DNS that maps your domain name to the ELB dns name. Then use that name in your redirect.