div><p id="d8fe"><b>Cloud Run</b></p><p id="5377">This POST endpoint add/update employee in Firestore. It is recommend to use the APPEND_PATH_TO_ADDRESS for host_rewriting.</p><div id="504a"><pre><span class="hljs-attribute">/employee</span><span class="hljs-punctuation">:</span>
<span class="hljs-attribute">post</span><span class="hljs-punctuation">:</span>
<span class="hljs-attribute">summary</span><span class="hljs-punctuation">:</span> <span class="hljs-string">Add Employee using Cloud Run Service</span>
<span class="hljs-attribute">operationId</span><span class="hljs-punctuation">:</span> <span class="hljs-string">add_update_employee</span>
<span class="hljs-attribute">x-google-backend</span><span class="hljs-punctuation">:</span>
<span class="hljs-attribute">address</span><span class="hljs-punctuation">:</span> <span class="hljs-string">https://employee-<hash>-uc.a.run.app</span>
<span class="hljs-attribute">path_translation</span><span class="hljs-punctuation">:</span> <span class="hljs-string">APPEND_PATH_TO_ADDRESS</span>
<span class="hljs-attribute">parameters</span><span class="hljs-punctuation">:</span>
<span class="hljs-bullet">-</span> <span class="hljs-string">name: employee</span>
<span class="hljs-attribute">in</span><span class="hljs-punctuation">:</span> <span class="hljs-string">body</span>
<span class="hljs-attribute">description</span><span class="hljs-punctuation">:</span> <span class="hljs-string">Employee to be Added</span>
<span class="hljs-attribute">schema</span><span class="hljs-punctuation">:</span>
<span class="hljs-attribute">ref</span><span class="hljs-punctuation">:</span> <span class="hljs-string">'#/definitions/Employee'</span></pre></div><p id="1556">The Schema definition of the employee</p><div id="022d"><pre><span class="hljs-attribute">definitions</span><span class="hljs-punctuation">:</span>
<span class="hljs-attribute">Employee</span><span class="hljs-punctuation">:</span>
<span class="hljs-attribute">type</span><span class="hljs-punctuation">:</span> <span class="hljs-string">object</span>
<span class="hljs-attribute">required</span><span class="hljs-punctuation">:</span>
<span class="hljs-bullet">-</span> <span class="hljs-string">id</span>
<span class="hljs-bullet">-</span> <span class="hljs-string">firstname</span>
<span class="hljs-bullet">-</span> <span class="hljs-string">lastname</span>
<span class="hljs-attribute">properties</span><span class="hljs-punctuation">:</span>
<span class="hljs-attribute">id</span><span class="hljs-punctuation">:</span>
<span class="hljs-attribute">type</span><span class="hljs-punctuation">:</span> <span class="hljs-string">string</span>
<span class="hljs-attribute">firstname</span><span class="hljs-punctuation">:</span>
<span class="hljs-attribute">type</span><span class="hljs-punctuation">:</span> <span class="hljs-string">string</span>
<span class="hljs-attribute">lastname</span><span class="hljs-punctuation">:</span>
<span class="hljs-attribute">type</span><span class="hljs-punctuation">:</span> <span class="hljs-string">string</span></pre></div><p id="5ca9"><b>App Engine</b></p><p id="b4b7">The DELETE endpoint to delete an employee from the firestore.</p><div id="1420"><pre><span class="hljs-attribute">delete</span><span class="hljs-punctuation">:</span>
<span class="hljs-attribute">summary</span><span class="hljs-punctuation">:</span> <span class="hljs-string">Delete Employee using Google App Engine service.</span>
<span class="hljs-attribute">operationId</span><span class="hljs-punctuation">:</span> <span class="hljs-string">deleteEmployee</span>
<span class="hljs-attribute">x-google-backend</span><span class="hljs-punctuation">:</span>
<span class="hljs-attribute">address</span><span class="hljs-punctuation">:</span> <span class="hljs-string">https://<PROJECT_ID>.appspot.com </span>
<span class="hljs-attribute">path_translation</span><span class="hljs-punctuation">:</span> <span class="hljs-string">APPEND_PATH_TO_ADDRESS</span>
<span class="hljs-attribute">jwt_audience</span><span class="hljs-punctuation">:</span> <span class="hljs-string"><PROJECT_NUMBER>-<HASH>.apps.googleusercontent.com</span>
<span class="hljs-attribute">protocol</span><span class="hljs-punctuation">:</span> <span class="hljs-string">h2</span>
<span class="hljs-attribute">parameters</span><span class="hljs-punctuation">:</span>
<span class="hljs-bullet">-</span> <span class="hljs-string">name: employee_id</span>
<span class="hljs-attribute">in</span><span class="hljs-punctuation">:</span> <span class="hljs-string">path</span></pre></div><p id="7c18">In addition to x-google-backend address, IAP also check the correct audience of the JWT token.
The <code>jwt_audience</code> is the <b>IAP client ID</b>. Get the Client ID from
APIs & Services → Credentials → OAuth 2.0 client IDs →IAP-App-Engine-app</p><p id="59ff"><b>Add Authentication (API Keys)</b></p><p id="eb2f">There are multiple ways to add security, let’s start with API Key.</p><p id="805f">Add API key security in each paths</p><div id="37a4"><pre><span class="hljs-attribute">security</span><span class="hljs-punctuation">:</span>
<span class="hljs-bullet">-</span> <span class="hljs-string">api_key: []</span></pre></div><p id="7f43">And add the security definition at the end of the file</p><div id="80d6"><pre><span class="hljs-symbol">securityDefinitions:</span>
<span class="hljs-symbol"> api_key:</span>
<span class="hljs-symbol"> type:</span> <span class="hljs-string">"apiKey"</span>
<span class="hljs-symbol"> name:</span> <span class="hljs-string">"key"</span>
<span class="hljs-symbol"> in:</span> <span class="hljs-string">"query"</span></pre></div><h2 id="cc1b">Deploy the OpenAPI definition</h2><p id="52fe">Now we have created the OpenAPI definition for the route in our service we need to deploy it to Cloud Endpoints.</p><div id="1388"><pre>gcloud endpoints services deploy openapi-<span class="hljs-keyword">definition</span>.yaml </pre></div><p id="1e8d">When the deployment is successful, please note the CONFIG_ID in the response. It’ll follow the naming convention [YYYY-MM-DDrx] = [2020–01–01r0].</p><h2 id="f7ff">Build ESPv2 Docker image</h2><p id="ebfe">We have our OpenAPI definition deployed. We have our API services deployed in Cloud Run, Cloud Functions and App Engine. Now we need to build an ESPv2 Docker image that holds the OpenAPI definition. The OpenAPI definition is what allows the ESPv2 proxy to understand what to do with requests; how to authenticate them and where to send them.</p><p id="bd50">Fortunately, Google has created a script to do this for us. We only need to input the CONFIG_ID of our OpenAPI definition, the hostname of our proxy and our project id.</p><p id="a33d">Download the <a href="https://github.com/GoogleCloudPlatform/esp-v2/blob/master/docker/serverless/gcloud_build_image">script</a>, make it executable and run the follow command.</p><div id="b5d2"><pre><span class="hljs-meta prompt_"> </span><span class="language-bash"><span class="hljs-built_in">chmod</span> +x gcloud_build_image</span>
<span class="hljs-meta prompt_"> </span><span class="language-bash">./gcloud_build_image -s CLOUD_RUN_HOSTNAME \
-c CONFIG_ID -p PROJECT_ID</span></pre></div><p id="3fcb"><code>CLOUD_RUN_HOSTNAME</code> is name of gateway e.g. gateway-<hash>-uc.a.run.app
After a couple of minutes, the ESPv2 Docker image will complete its build and will be available in Container Registry. The name of the image will follow this naming convention.</hash></p><p id="744a"><code>gcr.io/<PROJECT_ID>/endpoints-runtime-serverless:ESP_VERSION-CLOUD_RUN_HOSTNAME-CONFIG_ID</code></p><h2 id="bdd2">Deploy ESPv2 Docker image</h2><p id="f0b4">Now your ESPv2 proxy has been built you need to deploy it to Cloud Run.</p><div id="2adc"><pre> gcloud run <span class="hljs-keyword">deploy</span> CLOUD_RUN_SERVICE_NAME
<span class="hljs-params">--image=</span><span class="hljs-string">"gcr.io/PROJECT_ID/endpoints-runtime-serverless:ESP_VERSION-CLOUD_RUN_HOSTNAME-CONFIG_ID"</span> \
<span class="hljs-params">--allow-unauthenticated</span> \
<span class="hljs-params">--region</span> us-central1 \
<span class="hljs-params">--platform</span> managed \
<span class="hljs-params">--project=</span>PROJECT_ID</pre></div><h2 id="e0ed">Activate API</h2><p id="6cdd">By deploying the config on Cloud Endpoint, a new API service is created. Now, you have to activate it on your project. You can do this by command line:</p><div id="ff65"><pre><span class="hljs-meta prompt_"> </span><span class="language-bash">gcloud services <span class="hljs-built_in">enable</span> gateway-<<span class="hljs-built_in">hash</span>>-uc.a.run.app</span></pre></div><p id="742f">The API service name is the name of your Cloud Run Endpoint gateway <code>ENDPOINTS_SERVICE_NAME</code></p><p id="4e9f">This API can be enabled and disabled the same way Google’s API’s are. You can call the private API from other GCP projects, providing your projects are part of an organisation.</p><p id="804d">Go to APIs & Services > Dashboard > Enable APIs and Services > enable your private API.</p><h2 id="d740">Creating API Key</h2><p id="e4b4">For reaching the service, <b>you have to use an API Key. For this you have to create one in Google Cloud console</b>.</p><p id="0df2">Go to <code>API & Services</code> and select <code>Credentials</code>. Click on <code>Create credentials</code><i> </i>and select <code>API Key</code><i>.</i></p><p id="c911">Because of the low level of security of an API Key, <b>the best practice is to restrict the key</b>.</p><p id="c043">Edit the key (click on the pencil), under <code>API restrictions</code><i>, </i>click on <code>Restrict key</code> and, in the drop down list, only check your API Name.</p><h2 id="80e8">Cloud Endpoint Authorization</h2><p id="b2f5">We have deployed three services and an Endpoint gateway.
Now we need to give authorization to Endpoint so that that it will be able to access resources in services.
Gateway endpoint service is deployed using <project_number>[email protected] service account.</project_number></p><p id="00cc"><b>Cloud Run</b></p><p id="020c">As describe previously, a private Cloud Run service can be reached by authenticated user with <code>roles/run.invoker</code>. So, let’s grant this role to the Cloud Run Endpoint gateway service-account.</p><div id="83cd"><pre> gcloud run services<span class="hljs-built_in"> add-iam-policy-binding </span>employee
--member <span class="hljs-string">"serviceAccount:[email protected]"</span> \
--role <span class="hljs-string">"roles/run.invoker"</span> \
--platform managed \
--region us-central1 \
--project PROJECT_ID</pre></div><p id="bf8d"><b>Cloud Functions</b></p><p id="6156">Like for Cloud Run, and as described previously, a private Cloud Functions service can be reached by authenticated user with <code>roles/cloudfunctions.invoker</code>.</p><div id="13ed"><pre> gcloud functions<span class="hljs-built_in"> add-iam-policy-binding </span>employee \
--region us-central1 \
--member <span class="hljs-string">"serviceAccount:[email protected]"</span>
--role <span class="hljs-string">"roles/cloudfunctions.invoker"</span>
--project $PROJECT_ID</pre></div><p id="1ae9"><b>App Engine
</b>App Engine is secured by IAP and we have already given access <code>IAP-secured Web App User</code> to Endpoint gateway service-account.</p><p id="9124">All the steps have been completed now let’s access the endpoints using proxy.</p><h1 id="287f">Validate Cloud Endpoints routes</h1><p id="bd71">All the steps have been completed now let’s access the endpoints using proxy.</p><p id="4202">POST the employee
<code>curl POST https://gateway-<hash>-uc.a.run.app/employee?key=API_KEY --header 'Content-Type: application/json' --data-raw '{"id": 11223344, "firstname": "Vikram", "lastname": "Shinde"}'</code></p><p id="b9ee">GET the employee
<code>curl https://gateway-<hash>-uc.a.run.app/employee/11223344?key=API_KEY</code></p><p id="f719">DELETE the employee
<code>curl DELETE https://gateway-<hash>-uc.a.run.app/employee/11223344?key=API_KEY</code></p><h1 id="14da">Best practices with API Keys</h1><p id="c0c0">API keys are not secure way and it can be easily stolen as any <a href="https://en.wikipedia.org/wiki/Man-in-the-middle_attack">man-in-the-middle attack</a> or packet sniffer tool can read them as plain text.</p><p id="f61e">Hence there are some best practices for using API Keys</p><ol><li>Do not embed API keys directly in code.</li><li>Delete unneeded API keys to minimize exposure to attacks.</li><li>Regenerate your API keys periodically.</li><li>Review your code before publicly releasing it. Ensure that your code does not contain API keys.</li><li><b>Apply API key restrictions.</b> API keys are unrestricted by default.</li><li>Add application restrictions. Specify web sites, IP address or apps.</li></ol><h1 id="3ec1">Authenticate with Bearer Auth Token</h1><p id="5ae4">After successfully creating an OpenAPI
Options
definition, and building an ESPv2 proxy for API key authentication, moving to bearer authentication will be easy.</p><h2 id="338c">Resources</h2><ul><li><a href="https://cloud.google.com/endpoints/docs/openapi/service-account-authentication">Server to server bearer auth</a></li></ul><h2 id="e7d0">How bearer auth works in Cloud Endpoints</h2><p id="63d7">There are different types of bearer authentication you can use with Cloud Endpoints. You can use server-to-server or <a href="https://cloud.google.com/endpoints/docs/openapi/authenticating-users-google-id">OAuth2.0 (Google ID token) auth</a>. We’ll be using server-to-server auth. However, OAuth works very similarly to server-to-server, so do some reading if you require users to interact with your API.</p><p id="d115">The following notes are the steps the proxy will follow to authenticate the request. Use these notes when referencing the Cloud Endpoints architecture.</p><ol><li>request made to the proxy</li><li>Service Control authenticates the bearer token
the bearer token is decoded and checks the following:
the token is not expired
issuer is correct
verifies the signature : checks the private certificate in the token against the public certificate</li><li>the proxy forwards the request to the destination</li></ol><h2 id="8f29">Configure the OpenAPI definition for Bearer Auth</h2><p id="c0c6">Let’s update our openapi-definition.yaml to include bearer auth for <code>POST</code> and <code>DELETE</code> methods</p><ol><li>Create service Account and download service account key.</li><li>Update OpenAPI definition</li></ol><div id="6602"><pre><span class="hljs-symbol">securityDefinitions:</span>
<span class="hljs-symbol"> bearer:</span>
<span class="hljs-symbol"> authorizationUrl:</span> <span class="hljs-string">""</span>
<span class="hljs-symbol"> flow:</span> <span class="hljs-string">"implicit"</span>
<span class="hljs-symbol"> type:</span> <span class="hljs-string">"oauth2"</span>
x-google-issuer: <span class="hljs-string">"email_of_sa"</span>
x-google-jwks_uri: <span class="hljs-string">"https://www.googleapis.com/robot/v1/metadata/x509/email_of_sa"</span>
x-google-jwt-locations:
- header: <span class="hljs-string">"Authorization"</span>
<span class="hljs-symbol"> value_prefix:</span> <span class="hljs-string">"Bearer "</span></pre></div><p id="a1fe">Update the email_of_sa with service account email.
Change security flag from <code>api-key</code> to <code>bearer</code></p><div id="565e"><pre><span class="hljs-attribute">security</span><span class="hljs-punctuation">:</span>
<span class="hljs-bullet">-</span> <span class="hljs-string">bearer: []</span></pre></div><p id="c842"><b>Redeploy</b></p><p id="8efc">Since definition has been change, we need to deploy OpenAPI definition, rebuild and redeploy gateway service.</p><div id="5edc"><pre> gcloud endpoints services deploy openapi-<span class="hljs-keyword">definition</span>.yaml</pre></div><p id="0dce">It will generate new CONFIG_ID. e.g. [2020–01–01r<b>1</b>]</p><div id="6a51"><pre><span class="hljs-meta prompt_"> </span><span class="language-bash">./gcloud_build_image -s CLOUD_RUN_HOSTNAME
-c CONFIG_ID -p ESP_PROJECT_ID</span></pre></div><p id="5b24">This will generate new version of image</p><div id="ee63"><pre>$ gcloud run <span class="hljs-keyword">deploy</span> CLOUD_RUN_SERVICE_NAME
<span class="hljs-params">--image=</span><span class="hljs-string">"gcr.io/ESP_PROJECT_ID/endpoints-runtime-serverless:ESP_VERSION-CLOUD_RUN_HOSTNAME-CONFIG_ID"</span>
<span class="hljs-params">--allow-unauthenticated</span>
<span class="hljs-params">--platform</span> managed
<span class="hljs-params">--project=ESP_PROJECT_ID</span></pre></div><p id="45e5">You can use following Python code to create a bearer token.</p>
<figure id="78f4">
<div>
<div>
<iframe class="gist-iframe" src="/gist/vikramshinde12/b3091a350451a44c711cccc2d11252fa.js" allowfullscreen="" frameborder="0" height="undefined" width="undefined">
</div>
</div>
</figure></iframe></div></div></figure><p id="222a">Once bearer token is generated, make a request to the proxy with the token.</p><div id="ab1f"><pre>curl <span class="hljs-params">--request</span> POST \
<span class="hljs-params">--header</span> <span class="hljs-string">"Authorization: Bearer ${token}"</span>
<span class="hljs-string">"https://gateway-{hash}-uc.a.run.app/employee"</span>
<span class="hljs-params">--header</span> 'Content-Type: application/json'
<span class="hljs-params">--data-raw</span> '{
<span class="hljs-string">"id"</span>: 411,
<span class="hljs-string">"last"</span>: <span class="hljs-string">"vikram"</span>,
<span class="hljs-string">"name"</span>: <span class="hljs-string">"Shinde"</span>
}'</pre></div><h1 id="cb17">Quotas for API</h1><p id="edd5">Cloud Endpoints provides quotas which let you control the rate at which applications can call your API.
Setting a quota lets you specify usage limits to protect your API from an excessive number of requests from calling applications.</p><p id="f5b8">Add following 3 sections in <code>openapi-definition.yaml</code> file.</p><ul><li>metrics: A named metric that counts requests to your API.</li><li>limit: Represents a enforced limit on the named metrc.</li><li>metricCosts: The metricCosts maps methods to metrics.</li></ul><div id="f754"><pre>x-google-management:
<span class="hljs-symbol"> metrics:</span>
name: <span class="hljs-string">"YOUR_METRIC_NAME"</span>
<span class="hljs-symbol"> displayName:</span> <span class="hljs-string">"YOUR_METRIC-DISPLAY_NAME"</span>
<span class="hljs-symbol"> valueType:</span> INT64
<span class="hljs-symbol"> metricKind:</span> DELTA</pre></div><p id="ff9e"><code>metricKind</code> and <code>valueType</code> must be <code>DELTA</code> and <code>INT64</code></p><div id="48c1"><pre><span class="hljs-symbol">quota:</span>
<span class="hljs-symbol"> limits:</span>
name: <span class="hljs-string">"YOUR_LIMIT_NAME"</span>
<span class="hljs-symbol"> metric:</span> <span class="hljs-string">"YOUR_METRIC_NAME"</span>
<span class="hljs-symbol"> unit:</span> <span class="hljs-string">"1/min/{project}"</span>
<span class="hljs-symbol"> values:</span>
<span class="hljs-symbol"> STANDARD:</span> VALUE_FOR_THE_LIMIT</pre></div><p id="2b1f">In the paths sections add x-google-quota extension under the method.</p><div id="5850"><pre><span class="hljs-attribute">x-google-quota</span><span class="hljs-punctuation">:</span>
<span class="hljs-attribute">metricCosts</span><span class="hljs-punctuation">:</span>
<span class="hljs-attribute">YOUR_METRIC_NAME</span><span class="hljs-punctuation">:</span> <span class="hljs-string">YOUR_METRIC_COST</span></pre></div><p id="c714">In our example, I have added quota in GET API to limit 5 requests per minute.</p><div id="9d7c"><pre><span class="hljs-attribute">paths</span><span class="hljs-punctuation">:</span>
<span class="hljs-attribute">/employee/{employee_id}</span><span class="hljs-punctuation">:</span>
<span class="hljs-attribute">get</span><span class="hljs-punctuation">:</span>
<span class="hljs-attribute">summary</span><span class="hljs-punctuation">:</span> <span class="hljs-string">Get an Employee</span>
<span class="hljs-attribute">operationId</span><span class="hljs-punctuation">:</span> <span class="hljs-string">getEmployee</span>
<span class="hljs-attribute">x-google-backend</span><span class="hljs-punctuation">:</span>
<span class="hljs-attribute">address</span><span class="hljs-punctuation">:</span> <span class="hljs-string">https://us-central1-<PROJECT_ID>.cloudfunctions.net/employee</span>
<span class="hljs-attribute">protocol</span><span class="hljs-punctuation">:</span> <span class="hljs-string">h2</span>
<span class="hljs-attribute">parameters</span><span class="hljs-punctuation">:</span>
<span class="hljs-bullet">-</span> <span class="hljs-string">name: employee_id</span>
<span class="hljs-attribute">in</span><span class="hljs-punctuation">:</span> <span class="hljs-string">path</span>
<span class="hljs-attribute">responses</span><span class="hljs-punctuation">: </span>
<span class="hljs-attribute">....
security</span><span class="hljs-punctuation">:</span>
<span class="hljs-bullet">-</span> <span class="hljs-string">api_key: []</span>
<span class="hljs-attribute">x-google-quota</span><span class="hljs-punctuation">:</span>
<span class="hljs-attribute">metricCosts</span><span class="hljs-punctuation">:</span>
<span class="hljs-attribute">"api_requests"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">1</span>
....</pre></div><div id="ccfa"><pre><span class="hljs-attribute">x-google-management</span><span class="hljs-punctuation">:</span>
<span class="hljs-attribute">metrics</span><span class="hljs-punctuation">:</span>
<span class="hljs-bullet">-</span> <span class="hljs-string">name: api_requests</span>
<span class="hljs-attribute">valueType</span><span class="hljs-punctuation">:</span> <span class="hljs-string">INT64</span>
<span class="hljs-attribute">metricKind</span><span class="hljs-punctuation">:</span> <span class="hljs-string">DELTA</span>
<span class="hljs-attribute">quota</span><span class="hljs-punctuation">:</span>
<span class="hljs-attribute">limits</span><span class="hljs-punctuation">:</span>
<span class="hljs-bullet">-</span> <span class="hljs-string">name: limit-on-api-requests</span>
<span class="hljs-attribute">metric</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"YOUR_METRIC_NAME"</span>
<span class="hljs-attribute">unit</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"1/min/{project}"</span>
<span class="hljs-attribute">values</span><span class="hljs-punctuation">:</span>
<span class="hljs-attribute">STANDARD</span><span class="hljs-punctuation">:</span> <span class="hljs-string">5</span></pre></div><p id="0f6f">After redeploying the gateway service, I called the <code>GET</code> endpoints multiple times within a minutes and I received following error.</p><div id="dfef"><pre><span class="hljs-meta prompt_"></span><span class="language-bash">curl https://gateway-6bpnwgtf6q-uc.a.run.app/employee/111?key=API_KEY</span></pre></div><div id="43c8"><pre>{<span class="hljs-string">"code"</span>:<span class="hljs-number">429</span>,<span class="hljs-string">"message"</span>:<span class="hljs-string">"RESOURCE_EXHAUSTED:Quota exceeded for quota metric '</span>api_requests' <span class="hljs-built_in">and</span> limit <span class="hljs-string">'limit-on-api-requests'</span> of service <span class="hljs-string">'gateway-6bpnwgtf6q-uc.a.run.app'</span> <span class="hljs-keyword">for</span> consumer <span class="hljs-string">'project_number:499537266481'</span>.<span class="hljs-string">"}</span></pre></div><h1 id="d630">Tracking the API activity</h1><p id="d4fc">View the activity graphs for your API on Endpoints → Service page.</p><figure id="184a"><img src="https://cdn-images-1.readmedium.com/v2/resize:fit:800/1*3FNIqei43WAgJ5nE6xHT7A.png"><figcaption></figcaption></figure><p id="855a">You can also track the activities from APIs & Services → API → Metrics.</p><figure id="02e3"><img src="https://cdn-images-1.readmedium.com/v2/resize:fit:800/1*50Ib6EQ3nfH71akkkJ5b-Q.png"><figcaption></figcaption></figure><h1 id="8c9e">Conclusion</h1><p id="252c">In this tutorial, we have secured APIs on Cloud Run, Cloud Functions, App Engine with Cloud Endpoints ESPV2 using API Key and Bearer token. Also we have seen how to add quotas and rate limits and track the API activities.</p><p id="6b58">Cloud Endpoints is almost free. First 2 Millions API calls per month is free and3.00 per million thereafter.</p><p id="fe66">If you look for advance features on API management, <a href="https://cloud.google.com/apigee/">Apigee</a> is the option.</p><p id="5dbc">Please feedback how find this tutorial and connect me on <a href="https://www.linkedin.com/in/vikrampshinde/">LinkedIn</a>.</p><h1 id="7e89">Reference</h1><p id="2531"><a href="https://cloud.google.com/endpoints/docs/openapi/get-started-cloud-run#creating_a_developer_portal_for_the_api">Getting started with Endpoints for Cloud Run</a></p><p id="3fb1"><a href="https://cloud.google.com/endpoints/docs/openapi/get-started-cloud-functions">Getting started with Endpoints for Cloud Functions</a></p><p id="2c4e"><a href="https://cloud.google.com/endpoints/docs/openapi/get-started-app-engine-standard">Getting started with Endpoints for App Engine</a></p><p id="a294"><a href="https://cloud.google.com/endpoints/docs/openapi/openapi-extensions">OpenAPI extensions for Cloud Endpoints</a></p><p id="6e0f"><a href="https://readmedium.com/secure-cloud-run-cloud-functions-and-app-engine-with-api-key-73c57bededd1">Secure Cloud Run, Cloud Functions, App Engine</a> by <a href="undefined">guillaume blaquiere</a></p><p id="c9b0"><a href="https://cloud.google.com/endpoints/docs/openapi/architecture-overview">Cloud Endpoints architecture</a></p></article></body>
Secure APIs in Cloud Run, Cloud Functions and App Engine Using Cloud Endpoints ESPv2 (Beta)
Just as organisations need services management for microservices, they need API management for their APIs.
With serverless services like Cloud Run, Cloud Functions and App Engine, it is very easy and quick to create APIs. However, Security, Monitoring, Publishing APIs are some of the major challenges.
In this blog, we will see how to secure API’s in Cloud Run, Cloud Functions and App Engine Standard environment using API Keys and Bearer Token.
What is Cloud Endpoints?
Cloud Endpoints is a distributed API management system. It provides an API console, hosting, logging, monitoring and other features to help you to create, share, maintain and secure APIs.
ESPv2 Beta
There are two versions of Extensible Service Proxy (ESP).
ESPv1 is an Nginx-based proxy that is modified to be highly-scalable and to work as a Google product.
ESPv2 is an Envoy-based, highly scalable product that promises to have a small footprint on your infrastructure.
Deploy the App Engine service in Standard environment.
$cd app-engine
$ gcloud app deploy
2. Configure IAP.
The App Engine is public by default, activate IAP on App Engine.
Go to App Engine →Settings → Identity-Aware Proxy → Configure Now.
Give permission “IAP-secured Web App User” to the service account [email protected]
This is important so that App Engine will be accessible by the Cloud Endpoints service that we will be deploying in next step.
Cloud Endpoints (ESPv2)
Now, we have deployed backend services in Cloud Run, Cloud Functions and App Engine in private mode, we can start to frontend them using proxy service in Cloud Endpoints ESPv2 Beta.
Following steps are involved
Deploy an ESPv2 default Docker image
Create an OpenAPI definition
Deploy the OpenAPI definition
Build an ESPv2 Docker image
Deploy the ESPv2 Docker image
Activate API
Create an API key
Authorize Cloud Endpoints
Deploy an ESPv2 default Docker image
As part of the OpenAPI definition, we need to obtain a valid hostname of the gateway service that will front all requests to our API services. We can only obtain a valid hostname for our proxy once we’ve deployed something to Cloud Run. To achieve this, we’ll deploy the default ESPv2 Docker image.
If you notice from the gcloud command, we won’t be blocking any traffic by default. The proxy service itself will do that for us.
Once the proxy service has deployed, take note of the hostname, we’ll use this in the OpenAPI definition.
Create an OpenAPI definition
Now, we have to declare to Cloud Endpoint service where and how to reach the backend services.
You must have an OpenAPI document based on OpenAPI specification.
Let’s create one for our Employee API.
Create a YAML file called openapi-defination.yaml.
let’s start with header
Header
swagger:'2.0'info:
title: Employee API
description: Employee API on Cloud Endpoints with a Cloud Run, Cloud Functionand App Engine with IAP backend
version: 1.0.0host: gateway-<hash>-uc.a.run.app
schemes:
- https
The host is the hostname of the proxy or gateway when we deployed ESPv2 default Docker image.
The title is the name of the API in the API & Services section.
Paths
Add following paths in the openapi-defination.yaml file.
x-google-backendaddress: This is the full URL of the python services deployed earlier. (change <PROJECT_ID> with your project ID)
Cloud Functions
GET employee endpoint: This is endpoint receives employee_id in path parameter.
/employee/{employee_id}:get:summary:Get an EmployeeoperationId:getEmployeex-google-backend:address:https://us-central1-<PROJECT_ID>.cloudfunctions.net/employeeprotocol:h2parameters:-name: employee_idin:path
Cloud Run
This POST endpoint add/update employee in Firestore. It is recommend to use the APPEND_PATH_TO_ADDRESS for host_rewriting.
/employee:post:summary:Add Employee using Cloud Run ServiceoperationId:add_update_employeex-google-backend:address:https://employee-<hash>-uc.a.run.apppath_translation:APPEND_PATH_TO_ADDRESSparameters:-name: employeein:bodydescription:Employee to be Addedschema:$ref:'#/definitions/Employee'
The DELETE endpoint to delete an employee from the firestore.
delete:summary:Delete Employee using Google App Engine service.operationId:deleteEmployeex-google-backend:address:https://<PROJECT_ID>.appspot.com path_translation:APPEND_PATH_TO_ADDRESSjwt_audience:<PROJECT_NUMBER>-<HASH>.apps.googleusercontent.comprotocol:h2parameters:-name: employee_idin:path
In addition to x-google-backend address, IAP also check the correct audience of the JWT token.
The jwt_audience is the IAP client ID. Get the Client ID from
APIs & Services → Credentials → OAuth 2.0 client IDs →IAP-App-Engine-app
Add Authentication (API Keys)
There are multiple ways to add security, let’s start with API Key.
Add API key security in each paths
security:-api_key: []
And add the security definition at the end of the file
When the deployment is successful, please note the CONFIG_ID in the response. It’ll follow the naming convention [YYYY-MM-DDrx] = [2020–01–01r0].
Build ESPv2 Docker image
We have our OpenAPI definition deployed. We have our API services deployed in Cloud Run, Cloud Functions and App Engine. Now we need to build an ESPv2 Docker image that holds the OpenAPI definition. The OpenAPI definition is what allows the ESPv2 proxy to understand what to do with requests; how to authenticate them and where to send them.
Fortunately, Google has created a script to do this for us. We only need to input the CONFIG_ID of our OpenAPI definition, the hostname of our proxy and our project id.
Download the script, make it executable and run the follow command.
CLOUD_RUN_HOSTNAME is name of gateway e.g. gateway--uc.a.run.app
After a couple of minutes, the ESPv2 Docker image will complete its build and will be available in Container Registry. The name of the image will follow this naming convention.
By deploying the config on Cloud Endpoint, a new API service is created. Now, you have to activate it on your project. You can do this by command line:
The API service name is the name of your Cloud Run Endpoint gateway ENDPOINTS_SERVICE_NAME
This API can be enabled and disabled the same way Google’s API’s are. You can call the private API from other GCP projects, providing your projects are part of an organisation.
Go to APIs & Services > Dashboard > Enable APIs and Services > enable your private API.
Creating API Key
For reaching the service, you have to use an API Key. For this you have to create one in Google Cloud console.
Go to API & Services and select Credentials. Click on Create credentialsand select API Key.
Because of the low level of security of an API Key, the best practice is to restrict the key.
Edit the key (click on the pencil), under API restrictions, click on Restrict key and, in the drop down list, only check your API Name.
Cloud Endpoint Authorization
We have deployed three services and an Endpoint gateway.
Now we need to give authorization to Endpoint so that that it will be able to access resources in services.
Gateway endpoint service is deployed using [email protected] service account.
Cloud Run
As describe previously, a private Cloud Run service can be reached by authenticated user with roles/run.invoker. So, let’s grant this role to the Cloud Run Endpoint gateway service-account.
Like for Cloud Run, and as described previously, a private Cloud Functions service can be reached by authenticated user with roles/cloudfunctions.invoker.
App Engine
App Engine is secured by IAP and we have already given access IAP-secured Web App User to Endpoint gateway service-account.
All the steps have been completed now let’s access the endpoints using proxy.
Validate Cloud Endpoints routes
All the steps have been completed now let’s access the endpoints using proxy.
POST the employee
curl POST https://gateway-<hash>-uc.a.run.app/employee?key=API_KEY --header 'Content-Type: application/json' --data-raw '{"id": 11223344, "firstname": "Vikram", "lastname": "Shinde"}'
GET the employee
curl https://gateway-<hash>-uc.a.run.app/employee/11223344?key=API_KEY
DELETE the employee
curl DELETE https://gateway-<hash>-uc.a.run.app/employee/11223344?key=API_KEY
Best practices with API Keys
API keys are not secure way and it can be easily stolen as any man-in-the-middle attack or packet sniffer tool can read them as plain text.
Hence there are some best practices for using API Keys
Do not embed API keys directly in code.
Delete unneeded API keys to minimize exposure to attacks.
Regenerate your API keys periodically.
Review your code before publicly releasing it. Ensure that your code does not contain API keys.
Apply API key restrictions. API keys are unrestricted by default.
Add application restrictions. Specify web sites, IP address or apps.
Authenticate with Bearer Auth Token
After successfully creating an OpenAPI definition, and building an ESPv2 proxy for API key authentication, moving to bearer authentication will be easy.
There are different types of bearer authentication you can use with Cloud Endpoints. You can use server-to-server or OAuth2.0 (Google ID token) auth. We’ll be using server-to-server auth. However, OAuth works very similarly to server-to-server, so do some reading if you require users to interact with your API.
The following notes are the steps the proxy will follow to authenticate the request. Use these notes when referencing the Cloud Endpoints architecture.
request made to the proxy
Service Control authenticates the bearer token
the bearer token is decoded and checks the following:
- the token is not expired
- issuer is correct
- verifies the signature : checks the private certificate in the token against the public certificate
the proxy forwards the request to the destination
Configure the OpenAPI definition for Bearer Auth
Let’s update our openapi-definition.yaml to include bearer auth for POST and DELETE methods
Create service Account and download service account key.
Cloud Endpoints provides quotas which let you control the rate at which applications can call your API.
Setting a quota lets you specify usage limits to protect your API from an excessive number of requests from calling applications.
Add following 3 sections in openapi-definition.yaml file.
metrics: A named metric that counts requests to your API.
limit: Represents a enforced limit on the named metrc.
metricCosts: The metricCosts maps methods to metrics.
{"code":429,"message":"RESOURCE_EXHAUSTED:Quota exceeded for quota metric 'api_requests' and limit 'limit-on-api-requests' of service 'gateway-6bpnwgtf6q-uc.a.run.app'for consumer 'project_number:499537266481'."}
Tracking the API activity
View the activity graphs for your API on Endpoints → Service page.
You can also track the activities from APIs & Services → API → Metrics.
Conclusion
In this tutorial, we have secured APIs on Cloud Run, Cloud Functions, App Engine with Cloud Endpoints ESPV2 using API Key and Bearer token. Also we have seen how to add quotas and rate limits and track the API activities.
Cloud Endpoints is almost free. First 2 Millions API calls per month is free and $3.00 per million thereafter.
If you look for advance features on API management, Apigee is the option.
Please feedback how find this tutorial and connect me on LinkedIn.