Reputation: 1620
I keep getting a 403 with the testcode below. Is it just me or is it overly complicated to call a function within the same project? I did some research here and here.
I've set the cloud function invoker on the default service account for both functions. And the allow internal traffic
So i have tried both codes below. The token is printed to the logs in the first function, so why do i still get a 403?
Script1:
const axios = require("axios");
/**
* Responds to any HTTP request.
*
* @param {!express:Request} req HTTP request context.
* @param {!express:Response} res HTTP response context.
*/
exports.helloWorld = async (req, res) => {
console.log(JSON.stringify(process.env));
const sample_api_url = `https://someRegionAndSomeProject.cloudfunctions.net/sample-api`;
const metadataServerURL =
"http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/identity?audience=";
const tokenUrl = metadataServerURL + sample_api_url;
// Fetch the token
const tokenResponse = await axios(tokenUrl, {
method: "GET",
headers: {
"Metadata-Flavor": "Google",
},
});
const token = tokenResponse.data;
console.log(token);
const functionResponse = await axios(sample_api_url, {
method: "GET",
headers: {
Authorization: `Bearer ${token}`,
"Content-Type": "application/json",
},
});
const data = functionResponse.data;
console.log(data);
res.status(200).json({ token, data });
};
Script2:
const {GoogleAuth} = require('google-auth-library');
/**
* Responds to any HTTP request.
*
* @param {!express:Request} req HTTP request context.
* @param {!express:Response} res HTTP response context.
*/
exports.helloWorld = async (req, res) => {
const url = 'https://someRegionAndSomeProject.cloudfunctions.net/sample-api';
const targetAudience = url;
const auth = new GoogleAuth();
const client = await auth.getIdTokenClient(targetAudience);
const response = await client.request({url});
res.status(200).json({data: response.data})
};
Upvotes: 1
Views: 1432
Reputation: 75930
There is 2 types of security on Cloud Functions
Authorization: bearer <token>
header, with token is an identity token which has, at least, the cloud functions invoker role. You can also add the allUsers
user with the cloud functions invoker role to make the function publicly reachable (no security header required)You can combine the 2 security solutions if you want. In your code, you correctly add the security header. However, your request is rejected by the network check.
The solution is not so simple, not free, and I totally agree with you that this pattern should be simpler.
To achieve this, you must create a serverless VPC connector and attach it on your functions that perform the call. You also have to set the egress to ALL on that functions. That's all
The consequence are the following: The traffic originated from your function will be routed to your VPC thanks to the serverless VPC connector. The Cloud Functions URL being always a public URL, you have to set the egress to ALL, to route the traffic going to public URL through the serverless VPC connector.
Upvotes: 3
Reputation: 15266
Based on your post, I created a sample that worked for me. I created two GCP functions "func1" and "func2". I then determined how to call func2 from func1 where func2 is only exposed to be invoked by the service account identity which func1 runs as.
The final code for func1 is as follows:
const func2Url = 'https://us-central1-XXX.cloudfunctions.net/func2';
const targetAudience = func2Url;
const {GoogleAuth} = require('google-auth-library');
const auth = new GoogleAuth();
async function request() {
console.info(`request ${func2Url} with target audience ${targetAudience}`);
const client = await auth.getIdTokenClient(targetAudience);
const res = await client.request({url: func2Url});
console.info(res.data);
return res;
}
exports.func1 = async (req, res) => {
let message = `Hello from func1`;
try {
let response = await request();
res.status(200).send(`${message} + ${response.data}`);
}
catch(e) {
console.log(e);
res.status(500).send('failed');
}
};
The primary recipe used here is as described in the Google Docs here. I also found this medium article helpful.
Upvotes: 1