Connect AWS ApiGateway to Internal ALB - amazon-web-services

I am trying to make one of the services (say an echo server for this example) that I have on my EKS cluster within my VPC available to external users. My approach to doing this is to first create an internal/private ALB that routes traffic to the service, and then have an ApiGateway HTTP API send external traffic to the ALB. Here are the steps that I have taken:
Create a security group that allows inbound traffic on port 80 from all ipv4 and ipv6.
From the ApiGateway page create a VPCLink. Select the private subnets associated with my ALB as well as the VPC of EKS cluster and ALB.
Create and ApiGateway HTTP API with the $default auto-deployable stage.
Create a route for ANY /{proxy+}. For the record I have also tried using the path ANY / but still get the same result.
For the path created in step 4 create an integration. The integration is set to use the VPCLink created in step 2. I also select my ALB and its listener for port 80.
However, when I follow the above sequence of steps and go to the address created by ApiGateway I get the following response: {"message": "Not found"}.
Any pointers on what I'm doing wrong would be greatly appreciated.

Are you sure the API Gateway is deployed? The error message seems to indicate no routes are available.
If it is deployed, but something is wrong with your backend, the error is typically like so:
{"message":"Internal Server Error"}

Related

Unable to access api hosted in EKS and routed via istio-ingressgateway with NLB

New to AWS here, Trying to expose API and having issues.
I have an API deployed to AWS's EKS cluster, where my API is connected to a clusterIP service.
That clusterIP service is attached to a Virtual Service(VS) exposing API's port and a fixed Hostname with routes as a prefix, this VS is connected to a Gateway (ingressgateway) describing both HTTP(80) and HTTPS(443) for all (* asterisk as hosts) connections.
Post that all our HTTP and HTTPS requests are mapped to 2 node ports under istio-ingressgateway hosted under the istio-system namespace.
Now, these 2 exposed nodePorts are consumed by the target groups registered over the same nodePorts, and these target groups are listened to by our NLB.
The NLB is connected to a DNS entry in Route53 by its NLB's DNS Name of CNAME(also tried with A) type.
Now, I am trying to access my API from the browser using the above setup, but whenever I try to (with A-type) I get 500: internal server error but no errors on my API's pods. And CNAME no results, it just gives timeout.
I followed the same process as how another API is deployed on the same cluster, but the other API is working fine, whereas my API is not accessible.
Edit 1: try capturing the error with CloudWatch, but we got the not-so-informative error:
{
"requestId": "e0etYh9BvHcES6A=",
"IP": "<ip-address>",
"requestTime": "16/Jan/2023:05:19:17 +0000",
"httpMethod": "GET",
"routeKey": "$default",
"status": "500",
"protocol": "HTTP/1.1",
"responseLength": "35"
}
Edit 2: was able to make it work.
Solution: as our NLB is configured with an internal scheme, we needed to connect it with the API gateway other than that the route53 record needed to be configured with an A scheme and once the changes were in place, we are able to access our API from browser.
Questions:
Is this the proper way to expose an API from the EKS cluster with an NLB and istio-ingressgateway service?
Are we only allowed to have one service routed via istio-ingressgateway under istio-system? Do we need to write a new one for another API?
Looks like the issue is related to the NLB being configured with an internal scheme and not being connected to the API Gateway. The Route 53 record need to be configured with an A scheme instead of CNAME.

Amazon EC2 - Listen for HTTPS request and Redirect to SpringBoot

I have a frontend React application hosted on Amazon Amplify and a backend SpringBoot application hosted on Amazon EC2.
My domain can only send https request but SpringBoot by default is http. My question is how can my EC2 instance listen to HTTPS request and then redirect to http port in SpringBoot.
I checked other posts and seems like you should not add SSL to your SpringBoot application, but rather to the Load Balancer in front of it. At the end of the today, this is what I want:
POST https: xxx.xxx.xxxx:443/user/signin
---> http: xxx.xxx.xxxx:8080/user/signin
---------------------------------------- update ----------------------------------------
Marcin has provided a top level idea on how to solve this, thank you!
I also attached the step-by-step solution for people like me, please see answers below
Below is the complete steps to take to convert your
http api to https using aws ec2
disclaimer: I only researched for couple hours, some concept might be inaccurate or wrong, but following this guide does gets the job done, correct me for misleading information
(1) springboot:
keep your server port on 8080 and don't change it to https (443)
(2) make sure your EC2 instance has the correct *VPC* and *IPv4 CIDR* set up
go to Instances -> Description -> VPC ID and then click on it
you should now see the list of VPCs, find the one that associated with your instance
In detail -> IPv4 CIDR -> check if it has two or more values in below format:
xxx.xx.0.0/16
xxx.xx.0.0/16
(3) skip this step if you have two IPv4 CIDR set up
select your VPC instance -> click Actions -> EDIT CIDRS -> Add new IPv4 CIDR
make sure two IPv4 CIDR are in different zone
more information on IPv4 CIDR:
https://docs.aws.amazon.com/vpc/latest/userguide/working-with-vpcs.html
(4) now we want to create an application load balancer that listens to https:443 request
select HTTP HTTPS Application Balancer and for each step (as shown in aws)
step 1.
Load Balancer Protocol and Port: https: 443
step 1.
Availability Zones: now is the time to select your VPC and two zones
step 2.
Security Setting choose a certificate from ACM (assume you have one on Route 53)
step 3.
Security Group: make sure to select the same group as your EC2 instance
step 4.
Routing http: 8080, target type: instance
step 5.
Register Targets select your EC2 instance, on port 80, please don't forget!
(5) now load balancer set up, double check security group of your EC2 instance
go to instances -> Description -> Security Group and click on it
for inbound rule, keep port 443, 22, 80, 8080 don't remove 8080
443 is for https, 22 for ssh client, 80 for tomcat
(6) now find the ips to use for the https request
this is not the public ip address of your EC2 instance
your application is behind a load balancer, the ip address should be the network interface IP. each network interface IP associates with a subnet ID that your VPC uses.
so go to Network Interfaces in your EC2 console. select any of your network interface IDs with a subnetId under your EC2's VPC.
click Details -> and scroll down to find the public IP you need
(7) before start next step, make sure you have the following:
a domain hosted in Route 53 (I have one for my frontend UI)
SSL certificated got from aws Certificate Manager
you should have put this SSL to your load balancer in step 3
if don't know what to do, check this stackOverFlow post for answers:
Adding SSL to domain hosted on route 53 AWS
(8) before next step, make sure you understand the following:
If you directly test your https request in postman, you will likely succeed.
However you will fail if using in production, like this:
axios.get("https:xxx.xxx.xxx:443/user/signin");
(failed)net:ERR_CERT_COMMON_NAME_INVALID
This is because whatever static IP you are using, does not match the AName for your SSL certificate. For example, if your domain name is helloworld.com, your backend API request should be https://helloworld.com/user/signin
(9) create a subdomain and config it in Route 53
I'm getting lazy, please see the link below:
https://aws.amazon.com/premiumsupport/knowledge-center/create-subdomain-route-53/
(10) final step!!!!!!
Now you have a working subdomain, let's use it for your network interface IP
For test, you can pick any IP from the list of network interface IPs. Go to Route 53 -> Hosted Zones -> select your subdomain, example: api.helloworld.com -> create record
Record Format
name: api.helloworld.com
type: A
Routing: Simple
Value: <your_network_interface_ip_address_multiple>
(11) sorry not yet ready
please wait for couple of days for DNS record to be updated, if DNS can interpret your subdomain, let's say api.helloworld.com into your network interface IP and since you can already test the correctness of your IP in postman, you should be ready to go!
--------------------------- end of useful information ---------------------------
If you want to keep using only the instance (no load balancer or cloudfront), then you need to get your own domain for it. Then you have to register a valid, public SSL certificate for that using, e.g. letsencyrpt. Once you have that, you can setup nginx on the instance to accept the https connections, and forward to your spring boot as http.
The easier route is to use application load balancer. You still need your own domain, but once you have it, you can easly get free SSL cert from AWS ACM and then deploy it on the balancer. No need to change your instance. So it would be:
Client ---(HTTPS)---> ALB ---(HTTP)---> EC2

Allow request from API Gateway to private ALB

I have a public API gateway set up, I want to forward the requests from API Gateway to a private ALB in the VPC. On AWS Console, for API Gateway VPC link setup I could only select an NLB in the VPC.
Is there a reason why we can only route to NLB and not to ALB?
Is there a way I can route to private ALB from the API Gateway?
Currently AWS only supports connecting to NLB for VPC link integrations. They have a feature request in place to enable support for ALB as well. For now, you can do -
Public API --> VPC Link --> NLB --> ALB
In the target groups of the NLB, add the private IPs of the ALB. This way you can reap benefits of the NLB (TCP layer) and ALB (HTTPS).
Using static IP addresses for Application Load Balancers
The selected answer is outdated. It is possible to have API Gateway integrate, thorugh http, with an internal facing ALB by using VPC Link and private resource integration.
For step by step details, see my answer on another question: https://stackoverflow.com/a/67413951/2948212
edit: I see I was confusing this post with another one... I believe my answer still adds value though, so I am leaving it (I thought this specified REST API Gateways and not HTTP API Gateways, but it does not).
Answer
While #diegosasw's answer is valid and useful, it is for AWS HTTP API Gateways, not AWS REST API Gateways.
With that being said, they are correct in saying it is possible! Please see the following AWS documentation regarding how to accomplish this: https://aws.amazon.com/premiumsupport/knowledge-center/api-gateway-application-load-balancers/
Please note one particular downside of AWS's documented approach: it requires a public ALB. Of course this is not ideal, though one can still harden their ALB so that it only accepts traffic originating from the REST API Gateway. If this is not acceptable for the existing use case, then #Suraj Bhatia's answer above must be followed (for REST API integrations, at least). If HTTP Gateways are acceptable, then #diegosasw's answer is the better approach to take due to it being simpler to manage and still allowing for a private ALB 🙂
For prosperity, AWS's documentation states the following:
Note: The following procedure assumes two things:
You have access to a public Application Load Balancer and its DNS
name. You have an API Gateway REST API resource with an HTTP method.
In the API Gateway console, choose the API you want to integrate with the Application Load Balancer.
In the Resources pane, for Methods, choose the HTTP method that your API uses.
Choose Integration Request.
In the Integration Request pane, for Integration Type, choose HTTP.
Note: To pass the entire API request and its parameters to the backend
Application Load Balancer, create one of the following instead: An
HTTP proxy integration
-or- An HTTP custom integration
For more information, see Set up HTTP integrations in API Gateway.
In the Endpoint URL field, enter either the Application Load Balancer's default DNS name or custom DNS name. Then, add the
configured protocol of its listener. For example, an Application Load
Balancer that's configured with an HTTPS listener on port 8080
requires the following endpoint URL format: https://domain-name:8080/
Important: Make sure that you create an HTTP listener or HTTPS
listener for the Application Load Balancer using the port and listener
rules of your choice. For more information, see Listeners for your
Application Load Balancers. For an Application Load Balancer
configured with an HTTPS listener, the associated certificate must be
issued by an API Gateway-supported certificate authority. If you have
to use a certificate that's self-signed or issued by a private
certificate authority, then set insecureSkipVerification to true in
the integration's tlsConfig.
Choose Save.
Deploy the API.

Invoke Private API Gateway Without Host/x-apigw-api-id Headers

I have a private API Gateway stage with an associated VPC endpoint, and I have already followed the instructions here: https://aws.amazon.com/premiumsupport/knowledge-center/api-gateway-vpc-connections/
on how to connect to my API from inside the VPC.
Using the Host/x-apigw-api-id works as expected, but I have some services that are third-party and I cannot add those headers to make them connect.
Is there any way to connect to an endpoint-specific hostname WITHOUT either Host or x-apigw-api-id header?
e.g.
(current) curl "https://vpc-endpoint-specific-hostname/route/" -H 'x-apigw-api-id: '
(desired) curl "https://.vpc-endpoint-specific-hostname/route/" (note no headers)
I know what the documents say, which is to use either of the two headers Host/x-apigw-api-id but I cannot add those headers for some of my services.
if you enable private DNS, you can access the private API directly using this url
https://{restapi-id}.execute-api.{region}.amazonaws.com/{stage}
you can get read more here
Even If you don’t have Private DNS enabled, you can still reach the Private API Gateway by using custom domains (which are technically not supported by Private API Gateways), we can ‘trick’ the VPC endpoint into understanding where to send traffic, without custom Host / x-apigw-api-id headers.
Steps:
Create an internal-only Application Load Balancer
The ALB needs to point to the IP addresses of the ENIs for the Interface VPC Endpoint - API Gateway (Steps to Create Interface VPC endpoint - API Gateway)
Make sure that you have a DNS entry - example.com(in Route 53 or any other DNS provider) that points to the above load balancer we created.
Make sure the ACM certificate is created for the above custom domain - example.com
Navigate to the API Gateway console and click Custom Domain Names in the left menu bar. Select Regional and fill in your custom hostname and ACM certificate we created in steps 3 & 4
Please find the detailed steps here.

Kubernetes service unable to access s3 with Istio sidecar

Just wondering if anyone has any luck/solution when using AWS SDK to access AWS resource such as S3 when service injected with Istio sidecar.
As Istio's document points out:
traffic will go through Istio sidecar, you will need white list the DNS or IPs.
https is not available. Can only do by changing the format to something like "http://www.google.com:443"
However, AWS SDK handles the https connection hence I can't rewrite the URL. Subsequently, I'll get an "http: server gave HTTP response to HTTPS client" error.
Many thanks.