0
votes

I have a cloud function - named source - that is invoking another called target.

Following the documentation provided by google on https://cloud.google.com/functions/docs/securing/authenticating, I have done the following :

  • Set up the identity of source with a service account where the role roles/cloudfunctions.invoker is attached
  • Set up the permission on target with the according service account and its role
  • In the invoker source code, I request a jwt token from the metadata server with the appropriate audience to be the target function url (the call for the token is a success) and pass it through the header of the http call for target.

However, calling the target results in a 401 response :

Error: Unauthorized Your client does not have permission to the requested URL /target

What am I missing here ?

1
Did you add [email protected] Cloud Functions Invoker role ? - Vikram Shinde
Can you share how you add the header? do you use upper case for the first letter? Authorization: Bearer <token> Can you print the response content of the call to the metadata server? - guillaume blaquiere

1 Answers

1
votes

I was able to successfully complete the scenario you are describing; here I share my code so that it may shed some light. For debugging purposes I included some print statements in the source function.

main.py

import requests  
REGION = 'us-central1'
PROJECT_ID = 'project123'
RECEIVING_FUNCTION = 'targetFunction123'

function_url = f'https://us-central1-project123.cloudfunctions.net/targetFunction123'
metadata_server_url = 'http://metadata/computeMetadata/v1/instance/service-accounts/default/identity?audience='
token_full_url = metadata_server_url + function_url
token_headers = {'Metadata-Flavor': 'Google'}


def source(request):
    print(REGION, PROJECT_ID, RECEIVING_FUNCTION)
    print('Token full url: ',token_full_url)
    token_response = requests.get(token_full_url, headers=token_headers)
    jwt = token_response.text
    print('JWT: ',jwt)
    function_headers = {'Authorization': f'bearer {jwt}'}
    function_response = requests.get(function_url, headers=function_headers)
    print('Result =',function_response.text)
    return 'ok'

def target(request):
    return 'Target function called from source function'

requirements.txt

requests

Here are the gcloud commands I executed on both the called function and the calling function.

Called Function:

gcloud functions deploy targetFunction123 --region=us-central1 --entry-point=target --runtime=python37 --memory=128MB --trigger-http --verbosity=debug

gcloud functions add-iam-policy-binding targetFunction123 --member='serviceAccount:[email protected]' --role='roles/cloudfunctions.invoker'

Calling Function:

gcloud functions deploy sourceFunction123 --region=us-central1 --allow-unauthenticated --entry-point=source --runtime=python37 --memory=128MB --trigger-http --service-account=serviceaccount123@project123.iam.gserviceaccount.com --verbosity=debug

#Command to add the invoker role to serviceaccount123  
gcloud projects add-iam-policy-binding project123 --member=serviceAccount:[email protected] --role='roles/cloudfunctions.invoker'

#Command to list all the roles associated with my serviceaccount123 (also for debugging)
gcloud projects get-iam-policy project123 --flatten="bindings[].members" --format='table(bindings.role)' --filter="bindings.members:[email protected]"

The result returned by the called function will be printed in the stackdriver logs of the calling function.

I hope you find this useful.