New issue
Advanced search Search tips
Note: Color blocks (like or ) mean that a user may not be available. Tooltip shows the reason.

Issue 819743 link

Starred by 1 user

Issue metadata

Status: WontFix
Owner:
Closed: Mar 2018
Cc:
Components:
EstimatedDays: ----
NextAction: ----
OS: ----
Pri: 3
Type: Bug



Sign in to add a comment

Implement ServiceAccount.get_email() in service_account recipe module

Project Member Reported by vadimsh@chromium.org, Mar 7 2018

Issue description

This one: https://chromium.googlesource.com/infra/luci/recipes-py/+/0584da0c3d052d75bba9cd4108b307f43852024a/recipe_modules/service_account/api.py#42

Fuchsia recipes want to use it.

Will likely require modifying luci-auth binary to output the email in a structured form (as part of JSON struct), so it can be picked up from recipes without string parsing.
 
To further explain Fuchsia's use case:

I'm creating a recipe that multiple builders (each with it's own service account) will use to upload data to a dashboard.  Each request to the dashboard must be authenticated using a service account that is white-listed with the dashboard.  My plan is to use ServiceAccount.get_email() to read the account email instead of hard-coding it in the recipe, since it will change depending on the builder.  If there's an easier or better way to do this please let me know!

Why the email is needed exactly? Do you want to skip uploading if the email is not whitelisted in the recipe? Also, how does the dashboard authenticates calls? OAuth?
Maybe I'm misunderstanding, but can't you just have each builder decide which service account to use, within the same recipe? Something like..

SERVICE_ACCOUNT_DICT = {
  'Windows': '/creds/service_accounts/windows-builder.json',
  'Linux': '/creds/service_accounts/linux-builder.json',
}

def RunSteps(api):
  ...
  service_account = (
    api.service_account.from_credentials_json(SERVICE_ACCOUNT_JSON[api.properties['buildername']]))


except don't use api.properties['buildername'], there's a better way I think but I forget it.
I'm whitelisting all of the service accounts with the dashboard, before running the builders, so it should never be the case that an account is not whitelisted.

Yes, the dashboard autheticates using OAuth tokens.  I'm using the email in the following way:

Fuchsia has a ServiceAccountApi recipe module at: https://fuchsia.googlesource.com/infra/recipes/+/master/recipe_modules/service_account/api.py

The method `get_json_path` requires the service account name and returns a path to a JSON credentials file for the service account.  I'm hoping to derive the name of the service account from `get_email()` and pass it to `get_json_path()`.

I'm going to pass the path to the credentials filepath returned by `get_json_path()` to a Go binary that will generate an OAuth token from the credentials file using a LUCI authenticator, attach it to a request header, and then execute the request. 

That go binary's source is here: https://fuchsia.googlesource.com/infra/infra/+/master/cmd/catapult/upload.go

The OAuth token is generated here:
https://fuchsia.googlesource.com/infra/infra/+/master/cmd/catapult/upload.go#130
martiniss@ yes that's an option I considered.  It seemed better to use get_email since that would allow me to avoid having to hard-code the service account names and add new ones as I whitelist new accounts, but TBH that's a fine short term solution.
I'm fine with get_email being implemented, just wanted to understand what you're doing. Up to you and vadim.
Oh... I sense a confusion here.

There are two flavors of service accounts used on LUCI builds:

1. Service accounts identified by JSON files with private keys. get_json_path() is about them. They are generally DEPRECATED and should eventually be removed completely. They depend on some external mechanism (Salt in Fuchsia case, Puppet in Chrome Infra case) to deploy the private key on the bot. This reduces agility of bots and not terribly secure (long lived private keys exposed to tasks).

2. Task-associated service accounts exposed through Swarming APIs. It is the one you configure in cr-buildbucket.cfg with 'service_account' field. Their private keys are not directly accessible, the task sees only short lived OAuth tokens. All LUCI libraries, and  most recipes know how to grab such tokens.

In your specific case I see upload.go is already using go.chromium.org/luci/auth, it means it understands how to use tokens from task-associated accounts right out of the box (this is documented in https://godoc.org/go.chromium.org/luci/auth#Method for default AutoSelectMethod, though it is probably not terribly clear...).

All you need to do is just NOT pass -service-account-json to the upload binary, and it will automatically pick up the service account associated with the task if it finds itself running inside the task.

(Also, upload.go does redundant work when generating OAuth tokens. Using authenticator.Client() is sufficient to authenticate calls, the http.Client there injects OAuth tokens in all requests, no need to do it explicitly with SetAuthHeader).
Ah! Thanks so much for the detailed explanation. I'll fix this on our end, so no need to implement get_email and this can be closed. I'll re-open if I have more questions.
Status: WontFix (was: Assigned)

Sign in to add a comment