Deploy a proxy to Apigee X to operate as a service account

Another earlier post asks how to use a service account as the Google identity for deploying the proxy.  I think there is some confusion here - if I deploy a backend API as a CloudRun service, and that service requires clients to be authenticated, then the API Proxy needs to *operate* as the service account.

To deploy to Apigee X using a service account, for CICD, is another service account.  It seems to me from looking at the code for the Maven plugin and also trying things, that a command-line such as

mvn clean install \
-Dbearer=$(gcloud auth print-access-token) \
-DgoogleTokenEmail=my-service-account@${GOOGLE_CLOUD_PROJECT}.iam.gserviceaccount.com 

is going to fail, because the maven plugin will attempt to use my personal GCP token to authenticate as the service account.

Is there some way to use the Maven plugin to deploy a proxy that will operate as a service account, potentially using a different service account for deployment (e.g. CICD)?

Solved Solved
0 6 1,606
1 ACCEPTED SOLUTION

The recommended method to use a GCP service account to execute the Apigee Maven deploy module is via Cloud Build. Cloud Build can be configured to run as a service account of your choice (preferred) or you can use the default service account. In either case, you need to make sure the service account has the correct Apigee roles to deploy the artifacts. In this scenario, 'gcloud auth print-access-token' would be ran as the service account Cloud Build uses, not your account.  

Alternatively, you can request a token on behalf of a service account as described here. Then you could simply pass the token using the flag '-Dbearer=${mytoken} ' where 'mytoken' is the token you generated with the service account.

Of course you could always be grant your own personal account the permission 'iam.serviceAccounts.actAs' on the service account. This would allow you to deploy the proxy and grant permissions to impersonate the service account. I would take caution on this approach as it may not be appropriate with your organizations security posture. In any case, if you plan to use a service account that is not the same as the one used for your Cloud Run service, it will also need the same 'iam.serviceAccounts.actAs' for the Cloud Run service account. More about how this works here.

Lastly, you can use the flag '-Dfile={path-to-service_account_file}' and point the command to a service account JSON file. I do not recommend this approach as it does not follow best practices around managing service account credentials. There plenty of reasons why you shouldn't do this, so I encourage you to read more about the best practices here.

There are certainly many more ways to go about this, but hopefully this should get you on the right track.

View solution in original post

6 REPLIES 6

The recommended method to use a GCP service account to execute the Apigee Maven deploy module is via Cloud Build. Cloud Build can be configured to run as a service account of your choice (preferred) or you can use the default service account. In either case, you need to make sure the service account has the correct Apigee roles to deploy the artifacts. In this scenario, 'gcloud auth print-access-token' would be ran as the service account Cloud Build uses, not your account.  

Alternatively, you can request a token on behalf of a service account as described here. Then you could simply pass the token using the flag '-Dbearer=${mytoken} ' where 'mytoken' is the token you generated with the service account.

Of course you could always be grant your own personal account the permission 'iam.serviceAccounts.actAs' on the service account. This would allow you to deploy the proxy and grant permissions to impersonate the service account. I would take caution on this approach as it may not be appropriate with your organizations security posture. In any case, if you plan to use a service account that is not the same as the one used for your Cloud Run service, it will also need the same 'iam.serviceAccounts.actAs' for the Cloud Run service account. More about how this works here.

Lastly, you can use the flag '-Dfile={path-to-service_account_file}' and point the command to a service account JSON file. I do not recommend this approach as it does not follow best practices around managing service account credentials. There plenty of reasons why you shouldn't do this, so I encourage you to read more about the best practices here.

There are certainly many more ways to go about this, but hopefully this should get you on the right track.

I will try this.

Even if it works, it is a bad solution.  You mentioned security - I do not want to deploy a service to run as a service account that has authority to deploy to Apigee.  The service account that Apigee is running as needs to follow least privilege, which means it should not have the ability to deploy to Apigee, but maybe only logs.logWriter and run.Invoker.

Even if it works, it is a bad solution

 I mentioned 4 different approaches in the previous reply. Which one(s) are you referring to?

You should have (at least) two separate and distinct service accounts - (1) SA that has permissions to invoke the Cloud Run app, and another (2) SA that has permissions, at minimum, to deploy the Apigee proxy and actAs permissions for the (1) Cloud Run service account. 

If you want to use a service account for other services, like Cloud Operations (e.g. Cloud Logging), you can create a Shared Flow that uses it's own (3) service account, separate from the (1) Cloud Run service account. In this scenario, similar to pattern for deploying the proxy, (2) service account would need permissions to deploy the shared flow and actAs for (3) Cloud Operations service account.

In my opinion, this design follows least privilege as each service account has a distinct set of permissions, applied specifically for its purpose, and only for that purpose.

 

I see the two solutions of granting your own account the rights to act as the service account and creating a service account for deployment as essentially similar.  The first is a stepping stone towards the second, and I prefer to try one thing at a time.

What is not clear is concretely how to accomplish this.  Since I am using my personal organization for these experiments, I am acting as the gcp admin for the organization, I therefore already have permission to "actAs" the service account.  This is also evident from the fact that I can deploy as that service account from the Apigee X UI.

However, if I use my personal access token via gcloud auth print-access-token, and pass the token to maven via -Dbearer=$OAUTH_TOKEN and the service account to be assumed to maven via -DgoogleTokenEmail=$SA_EMAIL, then I get an authorization error.

Since we are talking about the API described by https://cloud.google.com/apigee/docs/reference/apis/apigee/rest/v1/organizations.environments.apis.r... the serviceAccount should appear as a query parameter called "serviceAccount".  

However, if I clone the maven plugin and then search the source code for "serviceAccount", I do not find it.  Tracing the code into the DeployMojo and then the RestClient method "activateBundleRevision", the "serviceAccount" is not passed as part of the URL encoded query parameters, and so the Maven plugin lacks this capability.

Due to an NDA, I cannot yet submit a pull request, and I'd honestly prefer not to have to do so.  Please update the tooling to support this.

I am going to attempt to fix it in a clone of the code, and if that works, I will open a Github issue and submit a pull request as soon as I am able.  That shouldn't be too long.  It does look like the apigeecli tool has this capability.

I see that the correct branch to look at of the maven plugin is "hybrid", and that the googleTokenEmail is sent as the service account.  So, I must need the "actAs" permission for my personal account (which is also the GCP admin account), and that will likely do the trick.

I will try this.