cloudtasker icon indicating copy to clipboard operation
cloudtasker copied to clipboard

OIDC auth token for Cloud Run services requiring authentication

Open emerson-argueta opened this issue 2 years ago • 3 comments

As mention in issue #28, currently google cloud run services that require authentication cannot currently use cloudtasker. These 3 commits included in the pull request aim to add the oidc auth token feature to cloudtasker. In these commits the following changes are added

  • Add the faraday http client to the cloudtakser.gemspec
  • Add a oidc_enable flag to the config
  • Add logic to fetch the oidc auth token from the google metadata server using the faraday http client in the authenticator if oidc_enabled is set to true

emerson-argueta avatar Apr 20 '22 20:04 emerson-argueta

Alright so I've been digging a bit more into OIDC authentication on GCP and I don't believe this approach will work due to how OIDC tokens expire. TL;DR; Try to schedule a job to run in two hours. If your Cloud Run service is using private invocation then the job should fail because your OIDC token has expired (they are supposed to last an hour)

The concept of OIDC auth with Cloud Run + Cloud Tasks is that:

  1. You configure your Cloud Run service to only be invoked by certain roles (it's not public anymore)
  2. Then you configure tasks to use a certain role with a certain audience. More info here

Then what happens is:

  1. When a task is due to run, Cloud Tasks will generate an idToken/AccessToken and place it as Bearer token on the Authorization header. This token is generated right before the task is sent to the handler so there is no risk of expiration.
  2. Cloud Run will verify that the role attached to the token is allowed to invoke this service and pass the task
  3. Cloudtasker receives the task and skips authentication because authorization has already been validated by Cloud Run.

So in order to support OIDC authentication, we need to do the following:

  1. Allow users to specify an oidc_token configuration via the Cloudtasker initializer. The oidc_token should contain a service_account_email and audience (we could actually infer the audience from the processor_host if no audience is specified)
  2. If an oidc_token config is specified then it should be used in lieu of an Authorization header when creating tasks. See WorkerHandler#task_payload. Also see ruby SDK doc and official doc
  3. When receiving the tasks and if an oidc_token config is specified, Cloudtasker should skip the authorization logic (because we assume that Cloud Run has vetted the request - we should add a BIG BOLD statement about that in the README). See WorkerController#authenticate!.

Let me know if that makes sense. Happy to take your view on it 😃

PS: Back onto our previous discussion regarding the metadata server - and having digged into the docs a bit more - I think you can use Google::Auth.get_application_default instead of fetching credentials manually from the metadata server. This is my source.

alachaum avatar May 10 '22 07:05 alachaum

Greatly appreciate the feedback and the time spend on it. All the points made make sense on why the proposed solution would not work.

Just to make sure I understand, in order to support OIDC authentication we need to:

  1. Explain in the comments on the initializer example how to configure the oidc_token with a users service_account_email and audience. With this information, we would be able to create the Google::Cloud::Tasks::V2::OidcToken object as part of the Google::Cloud::Tasks::V2::HttpRequest oidc_token attribute that cloudtasker uses internally. Going this route, tokens would not expire since the Google::Cloud::Tasks::V2::HttpRequest is now taking care of fetching the OIDC token when a task is scheduled to run.

  2. Check to see if oidc_token config is set and skip adding the authorization header in WorkerHandler#task_payload

  3. Skip authentication in WorkerController#authenticate! if oidc_token config is set

I hope I understood all this correctly. Again thanks for taking the time to investigate this. 🙂

I think I could make the changes using this approach outlined in the previous comment.

emerson-argueta avatar May 14 '22 01:05 emerson-argueta

@emerson-argueta Sorry for the late reply. Your understanding is correct. Don't hesitate to ping me for help or intermediate reviews as you progress! 👍

alachaum avatar May 18 '22 07:05 alachaum

Just as an update, I recently did some investigations with the GCP team on how to best use OIDC tokens manually in Ruby.

I've summarized my approach in this gist: https://gist.github.com/alachaum/e8052d37a5584ad3f5ee37a8cbfe1492

The Google::Auth::IDTokens.verify_oidc(id_token) can be used as an additional layer of verification inside the authenticate! method. This way if the Cloud Run endpoint is left unauthenticated by mistake we still have a layer of authentication in Cloudtasker.

Just to say I haven't forgotten about this PR 😊

alachaum avatar Jan 18 '23 11:01 alachaum

Thanks, for looking further into this. It's been a while since I last worked on this but I will certainly take a look again. 👍

emerson-argueta avatar Jan 18 '23 18:01 emerson-argueta