GCP Extensible Service Proxy encounters error when forwarding request - google-app-engine

I have a the following setup:
1. Application (Java microservice) deployed on app engine.
2. Custom domain mapped to hit this service:.
myfavmicroservice.project-amazing.dev.corporation.com
3. This endpoint is secured to require authentication by enabling IAP.
4. Configured ESP to intercept, authenticate and fulfill request to all
backend microservices (like above) with a common gateway endpoint.
5. Microservice is deployed using app.yaml.
6. ESP endpoint is configured using api.yaml (OpenAPI API Surface document)
This is the tutorial I am following:
https://cloud.google.com/endpoints/docs/openapi/get-started-app-engine-standard
app.yaml to deploy the microservice:
runtime: java11
entrypoint: java -jar tar/worker.jar
instance_class: F2
service: myfavmicroservice
handlers:
- url: /.*
script: this field is required, but ignored
The ESP api.yaml for describing microservice api surface is like this
swagger: "2.0"
info:
title: "My fav micro Service"
description: "Serve my favorite microservice content"
version: "1.0.0"
# This field will be replaced by the deploy_api.sh script.
host: microservice-system-gateway-5c4s43dedq-ue.a.run.app
schemes:
- https
produces:
- application/json
paths:
/myfavmicroservice:
get:
summary: Greet the user
operationId: hello
description: "Get helloworld mainpage"
x-google-backend:
address: https://myfavmicroservice.project amazing.dev.corporation.com
jwt_audience: .....
responses:
'200':
description: "Success."
schema:
type: string
'400':
description: "The IATA code is invalid or missing."
schema:
type: string
But the problem is that whenever I make request to endpoint like this:
GET
https://microservice-system-gateway-5c4s43dedq-ue.a.run.app/myfavmicroservice
I always get gateway 500 error. Upon inspection of ESP logs I am finding primarily
1. SSL Handshake Error with Error no 40
2. upstream server temporarily disabled while SSL handshaking to upstream
3. request: "GET /metadatasvc-hello HTTP/1.1", upstream: "https://[3461:f4f0:5678:a13::63]:443/myfavmicroservice
So the ESP is intercepting my request correctly, perhaps forwarding the request in correct format as well as evidenced from #3. But I am getting SSL error.
Why am I getting this error?

Ok figured out the issue. For the benefit of stackoverflow community I am posting the solution here.
I figured that if you use custom domains that you map to app engine like this in the OpenAPI Configuration (That you deploy to ESP), SSL handshake fails:
x-google-backend:
address: https://my-microservice.my-custom-domain.company.com
However if you use the default URL that is assigned by APP Engine upon startup of the microservice like this, everything is fine:
x-google-backend:
address: https://my-microservice.appspot.com
So I am trying to figure out how to use custom domain mappings in ESP OpenAPI configuration. For now though, if I do that the SSL proxying is not working inside ESP.

Related

Is there correct way to set Ngrok file to skip browser warning page

Here is sample of Ngrok file which I'm using within tunnel:
authtoken: somevalue
version: "2"
tunnels:
sometunellName
proto: http
addr: 5555
schemes:
- http
- https
host_header: rewrite
request_header:
add:
- "ngrok-skip-browser-warning:true"
log_level: debug
log_format: json
log: ngrok.log
Several common headers didn't give any new result.
The "ngrok-skip-browser-warning:true" header has to be added in the browser as the ngrok cloud side of things has to see it to skip the browser warning. With your config, you've added it in the ngrok cloud so only your app is seeing it.
~ an ngrok employee

AWS API Gateway: 403 MissingAuthenticationTokenException for /static/js/bundle.js even though Authorization is set as None

I have set up this flow:
external world --> AWS API Gateway ---> VPC Link ---> Network Load Balancer ---> my single EC2 instance
The API Gateway is working because when I input the custom domain in my browser, it successfully visits the frontend service running on my EC2 instance.
However, for /static/js/bundle.js, I'm getting a 403 MissingAuthenticationTokenException error:
But the request to the main domain is ok:
Also, all other resources failed:
/static/js/vendors~main.chunk.js
static/js/main.chunk.js
/favicon.ico
/manifest.json
My frontend is written in React JS.
Note that it says: x-amzn-errortype: MissingAuthenticationTokenException.
But I have set the Authorization to be None:
What is going on?

S3 hosted React app gets 405 Method Not Allowed

I have a react app hosted in S3. It is behind a Cloudfront CDN, which is reachable via a custom domain:
console.example.com -> CDN -> S3 hosted app
I also have a serverless application acting as my backend to handle API calls from the React app. This is also behind a Cloudfront CDN, which is reachable via a custom domain:
api.example.com -> CDN -> API Gateway
The front end is correctly configured to point to api.example.com. For the API Gateway, CORS is enabled.
When I navigate to the home page and attempt to login, console.example.com/login, I get the 405 Method Not Allowed error. Login, obviously using POST
I verified that the following curl is successful, when hitting the backend via API Gateway endpoint, Cloudfront Domain and the custom domain, effectively ruling out any API Gateway issue.
curl:
curl --header "Content-Type: application/json" \
--request POST \
--data '{"email":"name#test123.com","password":"xyz"}' \
https://api.example.com/login
In my research, I have found that S3 does not support POST. I have also found similar questions such as this and this, which were not helpful in my case unfortunately.
It is also worth noting that running my frontend and backend locally, works just fine, leaving me to think the S3 issue is my blocker here. But I'm not sure why. My POST endpoints are not trying to POST an object to the S3 bucket, they should be using the bundle.js file to hit the api endpoint.
So what am I missing? While I am not a frontend specialist, I assume others host react apps on S3 and can hit their api's just fine no?
I have intentionally not included the code as there would be a lot to digest there but am happy to include any that would be helpful, such as serverless.yml files or cloudformation templates etc. Any help would be awesome.
**** UPDATE - Added Cloudformation template for frontend Cloudfront CDN ****
Distribution:
Type: AWS::CloudFront::Distribution
Properties:
DistributionConfig:
Origins:
-
# Use the Website as the origin
DomainName: !GetAtt 'Website.DomainName'
Id: !Ref Website
CustomOriginConfig:
HTTPPort: 80
HTTPSPort: 443
OriginProtocolPolicy: https-only
Enabled: true
HttpVersion: http2
DefaultRootObject: index.html
CustomErrorResponses:
- ErrorCode: 404
ResponseCode: 200
ResponsePagePath: /index.html
- ErrorCode: 403
ResponseCode: 200
ResponsePagePath: /index.html
DefaultCacheBehavior:
AllowedMethods:
- DELETE
- GET
- HEAD
- OPTIONS
- PATCH
- POST
- PUT
DefaultTTL: 60
ForwardedValues:
QueryString: true
Cookies:
Forward: none
# The origin id defined above
TargetOriginId: !Ref Website
ViewerProtocolPolicy: "redirect-to-https" # we want to force https
# The certificate to use when using https
Aliases:
- console.example.com
ViewerCertificate:
AcmCertificateArn: arn:aws:acm:us-east-1:11111111:certificate/11111111-fa9b-4705-b9d2-11111111
MinimumProtocolVersion: TLSv1
SslSupportMethod: sni-only
I updated CustomErrorResponses to include 405 and I no longer get "405 Method Not Allowed" error:
CustomErrorResponses:
- ErrorCode: 405
ResponseCode: 200
ResponsePagePath: /index.html
- ErrorCode: 404
ResponseCode: 200
ResponsePagePath: /index.html
- ErrorCode: 403
ResponseCode: 200
ResponsePagePath: /index.html

How to enable api-key auth for all version when deploying multiple versions to same configuration in Google Clould Endpoint

I deployed 2 versions of openapi.yaml file to Google Cloud Endpoint using the Cloud Endpoint's versioning feature(i.e gcloud service-management deploy openapi_v1.yaml openapi_v2.yaml). Each version of the yaml file contains a version number and basepath different from the other, one endpoint that use api-key authentication, and definition for api-key authentication tag. After deployed to Endpoint, the configuration shows both yaml file, however deploying an api to GAE using this configuration will only have api-key authentication turned on for the newer version.
Does anyone know if this is a known bug, or there is something else I need to do to enable authentication for all versions?
The .yaml file looks like the following. The two versions I used to test on are identical except version and bathpath:
swagger: "2.0"
info:
description: "This API is used to connect 3rd-party ids to a common user identity"
version: "0.0.1"
title: "****"
host: "uie-dot-user-id-exchange.appspot.com"
basePath: "/v0"
...
- "https"
x-google-allow: all
paths:
...
/ids/search:
get:
operationId: "id_search"
produces:
- "application/json"
security:
- api_key: []
tags:
- "Ids"
summary: "Privileged endpoint. Provide any id (3rd party or otherwise) and get a hash of all ids associated with it."
parameters:
- in: "query"
name: "id_type"
description: "Type of id to search"
required: true
type: string
- in: "query"
name: "id_value"
description: "Value of id to search"
required: true
type: string
responses:
200:
description: "AssociatedIdsHash"
schema:
$ref: '#/definitions/AssociatedIdsHash'
400:
description: "Bad request. Requires both id_type and id_value query parameters."
401:
description: "Unauthorized. Please provide a valid api-key in the \"api-key\" header."
404:
description: "Not found - no entry found for key provided"
...
################ SECURITY DEFINITIONS ################
securityDefinitions:
# This section configures basic authentication with an API key.
api_key:
type: "apiKey"
name: "key"
in: "query"
I can replicate this issue and it appears to be a bug.
What does work is adding the API key restriction on the global level for both versions rather than at the per-path level. Perhaps this workaround will suffice for your use case.
...
security:
- api_key: []
path:
...

How to access local Cloud Endpoints API from mobile device

I want to access my Cloud Endpoints API hosted on my local dev machine from an Android app running on a mobile device I use for testing.
My device can access my dev machine by IP address. I passed --host=192.1.168.101 to the App Engine launcher so that my local App Engine instance binds to the IP address. Although I can access the App Engine instance from 192.168.1.101, I get a 404 when my app makes an API call.
I noticed that going to http://192.168.1.101:9080/_ah/api/explorer/ does not show my API; it redirects to https://developers.google.com/apis-explorer/#p/. If I use http://localhost:9080/_ah/api/explorer/ I'm able to see my API as intended. It seems that using an IP address as the host is not working with Cloud Endpoints.
I'd rather not root my device to change its /etc/hosts file. Changing that might not be a solution anyway, since I'm unable to bind my App Engine instance to a hostname other than localhost.
This is my app.yaml config:
application: my-server
version: 1
runtime: python27
threadsafe: true
api_version: 1
handlers:
# Endpoints handler
- url: /_ah/spi/.*
script: services.application
- url: /favicon\.ico
static_files: favicon.ico
upload: favicon\.ico
- url: .*
script: main.app
You bound to your specific IP, but as a reminder, you can also bind to 0.0.0.0 (all available IPs). This is handy if you're using the maven appengine plugin and don't want to update the pom.xml file whenever your IP changes.
Next, make sure you're on the same network and can connect between the machines. I typically use ConnectBot to test by opening a telnet session to the IP address and port you defined for running locally. This will ensure your firewall isn't causing an issue.
Finally, update your code by adjusting the root url for your API. That would look something like this if your IP address were 192.168.1.100 and port were 8080:
Helloworld.Builder helloWorld = new Helloworld.Builder(AppConstants.HTTP_TRANSPORT,
AppConstants.JSON_FACTORY, credential);
helloWorld.setRootUrl("http://192.168.1.100:8080/_ah/api/");
In your generated source code (usually the file named after your API name, such as Tictactoe.java), DEFAULT_ROOT_URL should be set to http://192.168.1.101:9080/_ah/api/. This URL isn't expected to provide anything useful if you load it in a browser. Rather, it's the base of the path to your API requests, e.g. http://192.168.1.101:9080/_ah/api/tictactoe/v1/board.
If you want to confirm your device is properly connecting to your local server (via your local network), load
http://192.168.1.101:9080/_ah/api/explorer/ from the device browser.
The problem had nothing to do with the IP address. I needed to include a path in my API method decorator:
#endpoints.method(HelloRequest, HelloResponse, name='helloworld', path='test', http_method='GET')
def helloworld(self, request):

Resources