MessageLogging (CloudLogging) Google Authentication Not Working

MessageLogging policy configured with CloudLogging is not producing any logs in Google Cloud Logging. The policy deploys successfully but no log entries appear in Cloud Logging.

I have followed all the documentaiton and i can see my proxy is deployed with the service account and that service account has the log creator permission

  1. MessageLogging policy with CloudLogging:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<MessageLogging name="ML-CloudLogging-API-Activity" continueOnError="true" enabled="true">
<DisplayName>ML-CloudLogging-API-Activity</DisplayName>
<CloudLogging>
<LogName>projects/{organization.name}/logs/api-activity</LogName>
<Message contentType="application/json">{
"timestamp": "{system.time}",
"api": {
"operation": "{proxy.pathsuffix}",
"verb": "{request.verb}"
}
}</Message>
<ResourceType>api</ResourceType>
</CloudLogging>
<logLevel>INFO</logLevel>
</MessageLogging>
  1. Policy attached to PostClientFlow in proxy configuration:
<ProxyEndpoint name="default">
<!-- ... other configuration ... -->
<PostClientFlow>
<Response>
<Step>
<Name>ML-CloudLogging-API-Activity</Name>
</Step>
</Response>
</PostClientFlow>
</ProxyEndpoint>
  1. Proxy deployment using service account:
apigeecli apis deploy \
--org "apigeeProjectId" \
--env "environment" \
--name "my-proxy" \
--sa "sa-apigee-development@projectId.iam.gserviceaccount.com"

I see there are discussion of i need to add Google Authentication (https://cloud.google.com/apigee/docs/api-platform/security/google-auth/overview) but it does not say the MessageLogging is supported only it supports AssignMessage, ServiceCallout, ExternalCallout, and TargetEndpoint

Where should i put this <Authentication> element?

PS: all the tutorials i found talk about downloading a json from the SA but they have this footer note that it is not secure and should not be used. So i’m confused what is the best secure way to use cloud logging on a deployed proxy

For example Dino has this repo https://github.com/DinoChiesa/Apigee-GCP-Logging-Example that does it with the json but in the middle of the Readme it says `

Note: For security reasons, Google recommends against creating service account keys, if it can be avoided.

Thanks

1 Like

You don’t need to use any tag for CloudLogging in MessageLogging. It should automatically use your service account.. Does your service account have logging.logEntries.create permission?

I thought so too, i though SA is enough,

yes my SA has the correct permission

Logs Writer is includes creator too

When MessageLogging policy fails, it’s hard to figure out why it is failing. It’s async w.r.t. your proxy, so you cannot see the results in Trace or etc.

I think what’s happening is the message is being sent to the logging endpoint, and nothing/no-one is watching for, or collecting any failure message if that message is malformed, not appropriately authorized, etc. This is the topic of an improvement request in the backlog internal reference b/245233810 . But nothing’s been done on that.

In the past when I’ve had this problem (and I have had the same problem - MessageLogging just silently not doing what I want it to do), I’ve tried to replicate the message etc and send it “manually” from the command line with curl, using an impersonated token from the service account. I had to grant myself iam.serviceAccountTokenCreator role on the service account , and then get a token via

SELF_TOKEN=$(gcloud auth print-access-token)
LIFETIME=900s
curl -X POST https://iamcredentials.googleapis.com/v1/projects/-/serviceAccounts/${SA_EMAIL}:generateAccessToken \
 -H "Authorization: Bearer $SELF_TOKEN" \
 -d '{
  "scope": [ "https://www.googleapis.com/auth/cloud-platform" ],
  "lifetime": "'${LIFETIME}'"
}'

OR,

gcloud auth print-access-token --impersonate-service-account ${SA_EMAIL} \
    --scopes=${SCOPES_REQUIRED}

…where SCOPES_REQUIRED is a comma separated list of things like

Or whatever is appropriate. In this case I think using just the first scope in that list is fine. And then use the resulting token in another curl command sent directly to the logging endpoint, something like

POST https://logging.googleapis.com/v2/entries:write
content-type: application/json
Authorization: Bearer :token

{
  "logName": "projects/:project/logs/:logid",
  "resource": {
    "type": "api",
    "labels": {
      "location": "etc",
      "method": "this-content-doesnt-matter-its-just-a-test",
      "service": "apiproxy-name-here",
      "version": "apiproxy-revision-here"
    }
  },
  "entries": [
    {
      "severity": "INFO",
      "jsonPayload": { "your" : "custom", "fields": "here" }
    }
  ],
  "partialSuccess": true
}

And then see if you can narrow down what’s happening from those kinds of tests.

You will see the “silent failure” if your MessageLogging payload is not valid JSON. If you send some slightly broken JSON, the logging endpoint will just reject the call to entries:write . If you forget a close-curly or a comma, this can happen. Your payload looks very simple and not susceptible to this problem. Also, if you use a message template , if one of the variables you substitute is a string containing double quotes, then the result will be broken json. You need to use escapeJSON around it. For example, this will break:

<Message contentType="application/json">{
  "timestamp": "{system.time}",
  "api": {
    "operation": "{proxy.pathsuffix}",
    "verb": "{request.verb}"
  },
  "payload" : "{request.content}"
}</Message>

and you need to use this instead:

<Message contentType="application/json">{
  "timestamp": "{system.time}",
  "api": {
    "operation": "{proxy.pathsuffix}",
    "verb": "{request.verb}"
  },
  "payload" : "{escapeJSON(request.content)}"
}</Message>

Maybe the sample you provided is simplified and this actually IS the problem you’re facing?

1 Like

PS: all the tutorials i found talk about downloading a json from the SA but they have this footer note that it is not secure and should not be used. So i’m confused what is the best secure way to use cloud logging on a deployed proxy> > For example Dino has this repo https://github.com/DinoChiesa/Apigee-GCP-Logging-Example that does it with the json but in the middle of the Readme it says `

Yes, it’s definitely not recommended to download a JSON key file.

The best way to do logging is: use the MessageLogging policy. It handles the authentic/AuthZ automatically.

To generalize, there may be occasions where you want to access other GCP services, like SecretManager, IAM, storage, BigQuery, etc… And If you are using ServiceCallout, then the best approach there is to use the Authentication element inside the ServiceCallout. like this:

<ServiceCallout continueOnError='true' name='SC-Call-to-some-Google-Cloud-service'>
  <Request variable='simplePostRequest'>
     ...
    </Set>
  </Request>
  <Response>myResponse</Response>
  <HTTPTargetConnection>
    <Authentication>
      <GoogleAccessToken>
        <Scopes>
          <Scope>https://www.googleapis.com/auth/cloud-platform</Scope>
        </Scopes>
      </GoogleAccessToken>
    </Authentication>
    <SSLInfo>
      <Enabled>true</Enabled>
      <Enforce>true</Enforce>
      <IgnoreValidationErrors>false</IgnoreValidationErrors>
    </SSLInfo>
    <Properties>
      <Property name='success.codes'>2xx</Property>
    </Properties>
    <URL>https://something.googleapis.com/v1/projects/....</URL>
  </HTTPTargetConnection>
</ServiceCallout>

But there are other ways to proceed. This approach ^^ always use the identity of the Service Account you attached to the proxy when deploying it. But you might want to use a different service account, something that is more dynamically determined. For that your proxy could use IAMcredentials.googleapis.com , and send a generateAccessToken request, specifying a particular service account. Effectively your proxy would be able to impersonate a different service account, for a particular outbound call. The proxy service account needs IAM.serviceAccountTokenCreator role on the “target” service account to do this.

The last and final way is to use the service account key file. And as noted, that is discouraged, as it’s got some security problems.

Thanks Dino, I tried couple of things, sent a simpler json and also changed resource type to global and now seems to be fixed! (not sure how much resource type is relevant)

<ResourceType>global</ResourceType>

hmmm, thank you for that update @alirz !

It could very well be the ResourceType is the problem. I know we have an outstanding ask to have a closer look at Resourcetype.

I’m glad you sorted your problem.