DEV Community

Avihai Levi
Avihai Levi

Posted on

How to HTTPS Proxy integration from REST API Gateway to Network Load Balancer

If you have rest api gateway in your application to exposing EKS/ECS services and you still working with http proxy integration you need to change it right now.
here is a tutorial on how to change your http proxy integration to https proxy integration to your VPC services.
all you need is a public domain.

So What Is The Problem?
Now a days when you want to do a proxy integration from your rest api gateway to your VPC ECS\EKS services you have to create network load balancer and work with this Amazon's official tutorial
in this tutorial Amazon recommend to proxy our request to the network load balancer with the load balancer's http DNS, but what if we want to keep our secured tls connection?

So here is a lot of solutions that just don't work:

Solution that didn't work #1
Create self signed / private ca - we can create route 53 private domain and self signed certificate or private ca certificate, but Amazon's rest api gateway proxy integration don't support any private ca or self signed certificate, api gateway expect public certified ca certificate as described in Amazon's official documentation

Solution that didn't work #2
Create private domain and route it to network load balancer DNS - as i mentioned above api gateway expect public certificate but also it cannot get any unknown domain name like: "application.local" etc..

Solution that didn't work #3
Route to your network load balancer https url - this cannot be done because your network load balancer DNS is an Amazon's DNS and network load balancer don't have this domain certificate and therefore api gateway cannot invoke this request to an not secured connection.

Solution that didn't work #4
we have a domain and certificate for our api gateway, why not to use this? - lets assume that we have a domain and certificate, api.example.com, we cannot setup a new private hosted zone with the same name api.example.com because it is already in use for the record to point to the api gateway custom domain name, and we must have a private hosted zone so that the VPC link knows where to route to.

This is how we can solve it?

Let's assume you have rest api gateway and in your VPC you have network load balancer for your application and you're using VPC link to connect to your application.
the api gateway have a domain name for example: api.example.com and certificate for this domain, this is how you solve it:

We are creating new public certificate because api gateway demands only public certificate as i mentioned before, for this we are creating also a new public hosted zone that we can validate the certificate.
We are creating a private hosted zone with the same name as the public hosted zone and now we have a public certificate for our private hosted zone, now we can attach this certificate to our network load balancer and create a record that our api gateway can connect in https to our network load balancer.

Tutorial:

  1. Create public route 53 with this name for example: app.example.com
  2. Create NS record in example.com to your new subdomain app.example.com - with NS from app.example.com
  3. Create certificate in certificate manager for this FQDN app.example.com and *.app.example.com and pass the validation process using route 53 records
  4. Create private route 53 hosted zone with the same domain app.example.com
  5. Create a record for you application network load balancer in the private hosted zone for example - application.app.example.com
  6. Add the public certificate we created in section 3. to your network load balancer.
  7. Change your api gateway proxy integration url to your newly created a record - https://application.app.example.com/{proxy}, and don't forget to deploy this api.

And now you can remove the http port 80 listener from your application load balancer, and your working with https from your api gateway to your application load balancer.

And here it is in CloudFormation Template:


AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: >
  all resources for creating your proxy integration using https

Parameters:

  SubnetName:
    Type: String
    Description: subnet name

  TargetGroupServiceArn:
    Type: String
    Description: target group service arn

  ServiceName:
    Type: String
    Description: eks/ecs service name

  DomainName:
    Type: String
    Default: example.com
    Description: youre domain name

  PrivateSubdomainName:
    Type: String
    Default: app
    Description: subdomain for your vpc resources

  PublicSubdomainName:
    Type: String
    Default: api
    Description: subdomain for your api gateway

  VPC:
    Type: String
    Description: vpc id

  StageName:
    Type: String
    Default: v1
    Description: api gateway stage name

Resources:

  #your public domain

  PublicDomainHostedZone:
    Type: AWS::Route53::HostedZone
    Properties:
      HostedZoneConfig:
        Comment: 'public hosted zone'
      Name: !Ref DomainName

  PublicDomainCertificate:
    Type: AWS::CertificateManager::Certificate
    Properties:
      DomainName: !Ref DomainName
      ValidationMethod: DNS
      SubjectAlternativeNames:
        - !Join ['.',['*', !Ref DomainName]]
      DomainValidationOptions:
        - DomainName: !Ref DomainName
          HostedZoneId: !Ref PublicDomainHostedZone

  #the public domain for creating certificate

  PublicApplicationDomainHostedZone:
    Type: AWS::Route53::HostedZone
    Properties:
      HostedZoneConfig:
        Comment: 'public application hosted zone'
      Name: !Join ['.',[ !Ref PrivateSubdomainName, !Ref DomainName]]

  PublicApplicationNSHostedZoneRecordSet:
    Type: 'AWS::Route53::RecordSet'
    Properties:
        HostedZoneId: !Ref PublicDomainHostedZone
        Name: !Join ['.',[ !Ref PrivateSubdomainName, !Ref DomainName]]
        Type: NS
        TTL: '900'
        ResourceRecords: !GetAtt PublicApplicationDomainHostedZone.NameServers

  PublicApplicationCertificate:
    Type: AWS::CertificateManager::Certificate
    DependsOn: PublicApplicationNSHostedZoneRecordSet
    Properties:
      DomainName: !Join ['.',[ !Ref PrivateSubdomainName, !Ref DomainName]]
      ValidationMethod: DNS
      SubjectAlternativeNames:
        - !Join ['.',['*', !Ref PrivateSubdomainName, !Ref DomainName]]
      DomainValidationOptions:
        - DomainName: !Join ['.',[ !Ref PrivateSubdomainName, !Ref DomainName]]
          HostedZoneId: !Ref PublicApplicationDomainHostedZone

  # private hosted zone for api gateway --> network load balancer

  PrivateApplicationDomainHostedZone:
    Type: AWS::Route53::HostedZone
    Properties:
      HostedZoneConfig:
        Comment: 'private application hosted zone'
      Name: !Join ['.',[ !Ref PrivateSubdomainName, !Ref DomainName]]
      VPCs:
        - VPCId: !Ref VPC
          VPCRegion : !Ref 'AWS::Region'

  # network load balancer for your application

  PrivateLoadBalancer:
    Type: 'AWS::ElasticLoadBalancingV2::LoadBalancer'
    Properties:
      Scheme: internal
      Subnets:
        - !Ref SubnetName
      Type: network

  SecuredPrivateLoadBalancerListener:
    Type: AWS::ElasticLoadBalancingV2::Listener
    Properties:
      DefaultActions:
      - Type: forward
        TargetGroupArn: !Ref TargetGroupServiceArn
      LoadBalancerArn: !Ref PrivateLoadBalancer
      Port: 443
      Protocol: TLS
      SslPolicy: 'ELBSecurityPolicy-TLS13-1-2-2021-06'
      Certificates:
        - CertificateArn: !Ref PublicApplicationCertificate

  LoadBalancerDNSRecordSet:
    Type: AWS::Route53::RecordSetGroup
    Properties:
      HostedZoneId: !Ref PrivateApplicationDomainHostedZone
      Comment: !Sub DNS record for the ${ServiceName} service
      RecordSets:
        - Name: !Join ['.',[!Ref ServiceName, !Ref PrivateSubdomainName, !Ref DomainName]]
          Type: A
          AliasTarget:
            EvaluateTargetHealth: false
            HostedZoneId: !GetAtt PrivateLoadBalancer.CanonicalHostedZoneID
            DNSName: !GetAtt PrivateLoadBalancer.DNSName

  #api gateway public domain name and certificate

  ApiGatewayDomainName:
    Type: AWS::ApiGateway::DomainName
    Properties:
      RegionalCertificateArn: !Ref PublicDomainCertificate
      DomainName: !Join ['.',[!Ref PublicSubdomainName, !Ref DomainName]]
      SecurityPolicy: TLS_1_2
      EndpointConfiguration:
        Types:
         - 'REGIONAL'

  ApiGatewayDomainRoute53RecordSet:
    Type: AWS::Route53::RecordSet
    Properties:
      HostedZoneId: !Ref PublicDomainHostedZone
      Comment: record for api gateway public domain
      Name: !Join ['.',[!Ref PublicSubdomainName, !Ref DomainName]]
      Type: A
      AliasTarget:
        DNSName: !GetAtt ApiGatewayDomainName.RegionalDomainName
        HostedZoneId: !GetAtt ApiGatewayDomainName.RegionalHostedZoneId

  # api gateway for your application

  RestApiGateway:
    Type: AWS::ApiGateway::RestApi
    Properties:
      Name: !Sub ${ServiceName}-apigw
      Description: !Sub Api Gateway for ${ServiceName}
      EndpointConfiguration:
        Types: [EDGE]

  ApiGatewayVpcLink:
    Type: AWS::ApiGateway::VpcLink
    Properties:
      Description: !Sub vpc link for ${ServiceName}
      Name: !Sub ${ServiceName}-private-link
      TargetArns: [!Ref PrivateLoadBalancer]

  ApiGatewayProxyResource:
    Type: AWS::ApiGateway::Resource
    Properties:
      RestApiId: !Ref RestApiGateway
      ParentId: !GetAtt RestApiGateway.RootResourceId
      PathPart: '{proxy+}'

  ApiGatewayProxyMethod:
    Type: AWS::ApiGateway::Method
    DependsOn: LoadBalancerDNSRecordSet
    Properties:
      RestApiId: !Ref RestApiGateway
      ResourceId: !Ref ApiGatewayProxyResource
      HttpMethod: ANY
      RequestParameters:
        method.request.path.proxy: true
      Integration:
        ConnectionType: VPC_LINK
        Type: HTTP_PROXY
        IntegrationHttpMethod: ANY
        ConnectionId: !Ref ApiGatewayVpcLink
        Uri: !Join ['',['https://', !Ref ServiceName, '.', !Ref PrivateSubdomainName, '.', !Ref DomainName , '/{proxy}']]
        RequestParameters:
          integration.request.path.proxy: 'method.request.path.proxy'

  ApiGatewayDeployment:
    Type: AWS::ApiGateway::Deployment
    DependsOn: ApiGatewayProxyMethod
    Properties:
      RestApiId: !Ref RestApiGateway
      Description: !Sub deployment for {ServiceName} api gateway
      StageName: !Ref StageName
      StageDescription:
        TracingEnabled: true
        LoggingLevel: INFO
        MetricsEnabled: true
        DataTraceEnabled: true

  ApiGatewayPublicDomainMapping:
    Type: AWS::ApiGateway::BasePathMapping
    DependsOn: ApiGatewayDeployment
    Properties: 
      BasePath: !Ref ServiceName
      DomainName: !Ref ApiGatewayDomainName
      RestApiId: !Ref RestApiGateway
      Stage: !Ref StageName

Enter fullscreen mode Exit fullscreen mode

Top comments (2)

Collapse
 
igoldny profile image
Igor Goldny

I stumbled upon your blog post and I must say it's been incredibly helpful! Your step-by-step tutorial on transitioning from http proxy integration to https proxy integration for VPC services is a game-changer.

I've been working with a REST API Gateway in my application, exposing EKS/ECS services, and your insights have made the transition seamless. The clarification on the necessity to switch from http to https proxy integration and the detailed guide on implementing it with a public domain have been a lifesaver.

Thanks a ton for sharing your expertise! Your blog has become my go-to resource for AWS and DevOps insights.

Collapse
 
ladydascalie profile image
Benjamin Cable

Great post, welcome!