Home > Software design >  Looking for a better way to authenticate Google Cloud Function with a service account. Right now I&#
Looking for a better way to authenticate Google Cloud Function with a service account. Right now I&#

Time:01-16

I'm looking for a better way to authenticate Google Cloud Function with a service account. Right now I'm storing the credentials json file on the backend. This is the code for my app https://github.com/ChristianOConnor/spheron-react-api-stack. This app could be deployed on any hosting platform, but at the moment the app is built to deploy on a Web3 protocol called Spheron. TLDR, Spheron runs the backend express server on a web3 friendly content serving/hosting platform called Akash. This means that whoever is hosting my backend express server has access to my GCP service account's credentials. You can see all of the code in the link I provided but just for ease of access this is the server.js file which will be on Akash.

server.js

var express = require("express");
var app = express();
require("dotenv").config();
const GoogleAuth = require("google-auth-library").GoogleAuth;
const cors = require("cors");

app.use(
  cors({ origin: process.env.ORIGIN, credentials: process.env.CREDENTIALS })
);

app.get("/hello", async function (req, res) {
  const keyInJsn = JSON.parse(process.env.CREDENTIALS_STR);
  const auth = new GoogleAuth({
    credentials: keyInJsn,
  });
  const url = process.env.RUN_APP_URL;

  //Create your client with an Identity token.
  const client = await auth.getIdTokenClient(url);
  const result = await client.request({ url });
  const resData = result.data;
  res.send(resData);
});

var server = app.listen(8081, function () {
  var host = server.address().address;
  var port = server.address().port;
  console.log("Example app listening at http://localhost:", port);
});

process.env.CREDENTIALS_STR is the service account credentials set up in this format:

CREDENTIALS_STR={"type": "service_account","project_id": "<PROJECT ID>","private_key_id": "<PRIVATE KEY ID>","private_key": "-----BEGIN PRIVATE KEY-----\<PRIVATE KEY>\n-----END PRIVATE KEY-----\n","client_email": "<SERVICE ACCOUNT NAME>@<PROJECT NAME>.iam.gserviceaccount.com","client_id": "<CLIENT ID>","auth_uri": "https://accounts.google.com/o/oauth2/auth","token_uri": "https://oauth2.googleapis.com/token","auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs","client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/<SERVICE ACCOUNT NAME>.iam.gserviceaccount.com"}

The Akash provider can see this string. Is there a better way to do authentication for a GCP service account that doesn't expose the credntials to a hosting/server provider?

Also don't be throw off by the web3 stuff. This app essentially works the same as a traditional web2 app with a backend and a client. If it helps you to think about it different, picture that I'm deploying on Netlify with a static client and a Netlify Function.

CodePudding user response:

The compromise I came to was creating an API Gateway for the function. This allows the function to be called without any credentials and still run from a service account. It creates a separate quasi-vulnerability though, as anyone with the API Gateway link can also call the function unauthenticated.

First, I enabled Service Management APIs, API Gateway API, and Service Control API. Then I made an API Gateway with my service account that runs my referenced cloud function. I uploaded a file like this for the api spec:

swagger: '2.0'
info:
  title: api-gateway-cloud-function
  description: API Gateway Calling Cloud Function
  version: 1.0.0
schemes:
  - https
produces:
  - application/json
paths:
  /whateveryouwanttocallthispath:
    get:
      summary: My Cloud Function
      operationId: whatever
      x-google-backend:
        address: <CLOUD_RUN_URL>
      responses:
        '200':
          description: OK

You can test it by running the function via curl command in a bash terminal curl {gatewayId}-{hash}.{region_code}.gateway.dev/v1/whateveryouwanttocallthispath. It works with no credential json file.

The problem is that you could achieve a similar result by just allowing the function to be called unauthenticated... Idk if this method has many benefits.

  • Related