Skip to content

Instantly share code, notes, and snippets.

@salrashid123
Last active May 12, 2022 01:08
Show Gist options
  • Save salrashid123/62178224324ccbc80a358920d5281a60 to your computer and use it in GitHub Desktop.
Save salrashid123/62178224324ccbc80a358920d5281a60 to your computer and use it in GitHub Desktop.
Reading live audit log changes using GCP Cloud Logging (https://blog.salrashid.dev/articles/2022/asset_monitor/
package main
import (
"bytes"
"flag"
"fmt"
"io"
"google.golang.org/protobuf/encoding/protojson"
logging "cloud.google.com/go/logging/apiv2"
"github.com/gogo/protobuf/jsonpb"
"golang.org/x/net/context"
"google.golang.org/genproto/googleapis/cloud/audit"
iamadminpb "google.golang.org/genproto/googleapis/iam/admin/v1"
iampb "google.golang.org/genproto/googleapis/iam/v1"
loggingiampb "google.golang.org/genproto/googleapis/iam/v1/logging"
loggingpb "google.golang.org/genproto/googleapis/logging/v2"
pubsubpb "google.golang.org/genproto/googleapis/pubsub/v1"
storagepb "google.golang.org/genproto/googleapis/storage/v1"
"google.golang.org/protobuf/proto"
"google.golang.org/protobuf/types/known/anypb"
)
const ()
var (
projectID = flag.String("projectID", "", "projectID")
)
func main() {
flag.Parse()
if *projectID == "" {
fmt.Println("projectID must be set")
return
}
ctx := context.Background()
c, err := logging.NewClient(ctx)
if err != nil {
fmt.Printf("%v", err)
return
}
defer c.Close()
stream, err := c.TailLogEntries(ctx)
if err != nil {
fmt.Printf("%v", err)
return
}
go func() {
req := loggingpb.TailLogEntriesRequest{
ResourceNames: []string{fmt.Sprintf("projects/%s", *projectID)},
}
if err := stream.Send(&req); err != nil {
fmt.Printf("%v", err)
return
}
//stream.CloseSend()
}()
for {
resp, err := stream.Recv()
if err == io.EOF {
fmt.Printf("EOF\n")
break
}
if err != nil {
fmt.Printf(" %v", err)
return
}
for _, e := range resp.Entries {
fmt.Printf("Entry: %v\n", e.InsertId)
var audit_data audit.AuditLog
err := anypb.UnmarshalTo(e.GetProtoPayload(), &audit_data, proto.UnmarshalOptions{})
if err != nil {
fmt.Printf("Error: could not unmarshall to audit log %v\n", err)
} else {
fmt.Printf(" MethodName: %v\n", audit_data.MethodName)
fmt.Printf(" AuthenticationInfo: %v\n", audit_data.AuthenticationInfo)
fmt.Printf(" Authorization: %v\n", audit_data.AuthorizationInfo)
//fmt.Printf(" Request %v\n", audit_data.Request)
//fmt.Printf(" ServiceData %v\n", audit_data.ServiceData)
//fmt.Printf(" Metadata %v\n", audit_data.Metadata)
reqTyp, ok := audit_data.Request.AsMap()["@type"]
if !ok {
fmt.Printf("Unable to find request.@type")
} else {
fmt.Printf("Request @type %v\n", reqTyp)
// awkward stuff, see https://github.com/golang/protobuf/issues/1448
// you have to read in the structpb.Struct, then find the @type that the struct
// represents. Then load and unmarshal the specific type.
// I've only setup a few of the expressions here: iam, pubsub and storage
// this is only a paltry apis covered in google audit log.
// it is lef to the reader to add on the imports and handle the
// apis you're interested in.
// These are just sample unmarshalers!
iamRequestStruct, err := audit_data.Request.MarshalJSON()
if err != nil {
fmt.Printf("%v", err)
return
}
switch reqTyp {
// === IAM
case "type.googleapis.com/google.iam.v1.GetIamPolicyRequest":
g := &iampb.GetIamPolicyRequest{}
err = protojson.UnmarshalOptions{
DiscardUnknown: true,
}.Unmarshal(iamRequestStruct, g)
if err != nil {
fmt.Println("Could not unmarshal to google.iam.v1.GetIamPolicyRequest")
fmt.Printf("error %v \n", err)
return
} else {
fmt.Printf("google.iam.v1.GetIamPolicyRequest %v\n", g)
}
case "type.googleapis.com/google.iam.v1.SetIamPolicyRequest":
g := &iampb.SetIamPolicyRequest{}
err = protojson.UnmarshalOptions{
DiscardUnknown: true,
}.Unmarshal(iamRequestStruct, g)
if err != nil {
fmt.Println("Could not unmarshal to google.iam.v1.SetIamPolicyRequest")
fmt.Printf("error %v \n", err)
return
} else {
fmt.Printf("google.iam.v1.SetIamPolicyRequest %v\n", g)
}
// === IAM Admin
case "type.googleapis.com/google.iam.admin.v1.GetPolicyDetailsRequest":
// this is an google cloud console api call, ther'es not external proto to use
// type.googleapis.com/google.iam.admin.v1.GetPolicyDetailsRequest
case "type.googleapis.com/google.iam.admin.v1.QueryGrantableRolesRequest":
g := &iamadminpb.QueryGrantableRolesRequest{}
err = protojson.UnmarshalOptions{
DiscardUnknown: true,
}.Unmarshal(iamRequestStruct, g)
if err != nil {
fmt.Println("Could not unmarshal to SetIamPolicyRequest")
fmt.Printf("error %v \n", err)
return
} else {
fmt.Printf("google.pubsub.v1.Publisher.ListTopicSubscriptions %v\n", g)
}
// === PubSub
case "type.googleapis.com/google.pubsub.v1.ListTopicsRequest":
g := &pubsubpb.ListTopicsRequest{}
err = protojson.UnmarshalOptions{
DiscardUnknown: true,
}.Unmarshal(iamRequestStruct, g)
if err != nil {
fmt.Println("Could not unmarshal to /google.iam.v1.GetIamPolicyRequest")
fmt.Printf("error %v \n", err)
return
} else {
fmt.Printf("google.pubsub.v1.ListTopicsRequest %v\n", g)
}
case "type.googleapis.com/google.pubsub.v1.ListTopicSubscriptionsRequest":
g := &pubsubpb.ListTopicSubscriptionsRequest{}
err = protojson.UnmarshalOptions{
DiscardUnknown: true,
}.Unmarshal(iamRequestStruct, g)
if err != nil {
fmt.Println("Could not unmarshal to SetIamPolicyRequest")
fmt.Printf("error %v \n", err)
return
} else {
fmt.Printf("google.pubsub.v1.Publisher.ListTopicSubscriptions %v\n", g)
}
// === GCS
case "type.googleapis.com/google.storage.v1.GetIamPolicyRequest":
g := &storagepb.GetIamPolicyRequest{} // storagepb "google.golang.org/genproto/googleapis/storage/v1"
err = protojson.UnmarshalOptions{
DiscardUnknown: true,
}.Unmarshal(iamRequestStruct, g)
if err != nil {
fmt.Println("Could not unmarshal to google.storage.v1.GetIamPolicyRequest")
fmt.Printf("error %v \n", err)
return
} else {
fmt.Printf("google.storage.v1.GetIamPolicyRequest %v\n", g)
}
case "type.googleapis.com/google.storage.v1.UpdateBucketRequest":
g := &storagepb.UpdateBucketRequest{}
err = protojson.UnmarshalOptions{
DiscardUnknown: true,
}.Unmarshal(iamRequestStruct, g)
if err != nil {
fmt.Println("Could not unmarshal to google.storage.v1.UpdateBucketRequest")
fmt.Printf("error %v \n", err)
return
} else {
fmt.Printf("google.storage.v1.UpdateBucketRequest %v\n", g)
}
case "type.googleapis.com/google.storage.v1.ListObjectsRequest":
g := &storagepb.ListObjectsRequest{}
err = protojson.UnmarshalOptions{
DiscardUnknown: true,
}.Unmarshal(iamRequestStruct, g)
if err != nil {
fmt.Println("Could not unmarshal to google.storage.v1.ListObjectsRequest")
fmt.Printf("error %v \n", err)
return
} else {
fmt.Printf("google.storage.v1.ListObjectsRequest %v\n", g)
}
default:
fmt.Printf("Unknown Request Type %s \n", reqTyp)
}
}
respTyp, ok := audit_data.Response.AsMap()["@type"]
if !ok {
fmt.Printf("Unable to find response.@type")
} else {
fmt.Printf("Response @type %v\n", respTyp)
iamResponsetStruct, err := audit_data.Response.MarshalJSON()
if err != nil {
fmt.Printf("%v", err)
return
}
switch respTyp {
// === IAM
case "type.googleapis.com/google.iam.v1.Policy":
g := &iampb.Policy{}
err = protojson.UnmarshalOptions{
DiscardUnknown: true,
}.Unmarshal(iamResponsetStruct, g)
if err != nil {
fmt.Println("Could not unmarshal to google.iam.v1.Policy")
fmt.Printf("error %v \n", err)
return
} else {
fmt.Printf("google.iam.v1.Policy %v\n", g)
}
// == Pubsub
case "type.googleapis.com/google.pubsub.v1.Topic":
g := &pubsubpb.Topic{}
err = protojson.UnmarshalOptions{
DiscardUnknown: true,
}.Unmarshal(iamResponsetStruct, g)
if err != nil {
fmt.Println("Could not unmarshal to google.pubsub.v1.Topic")
fmt.Printf("error %v \n", err)
return
} else {
fmt.Printf("google.pubsub.v1.Topic %v\n", g)
}
case "type.googleapis.com/google.pubsub.v1.ListTopicssResponse":
g := &pubsubpb.ListTopicsResponse{}
err = protojson.UnmarshalOptions{
DiscardUnknown: true,
}.Unmarshal(iamResponsetStruct, g)
if err != nil {
fmt.Println("Could not unmarshal to google.pubsub.v1.ListTopicsResponse")
fmt.Printf("error %v \n", err)
return
} else {
fmt.Printf("google.pubsub.v1.ListTopicsResponse %v\n", g)
}
// == GCS
case "type.googleapis.com/google.storage.v1.Bucket":
g := &storagepb.Bucket{}
err = protojson.UnmarshalOptions{
DiscardUnknown: true,
}.Unmarshal(iamResponsetStruct, g)
if err != nil {
fmt.Println("Could not unmarshal to google.storage.v1.Bucket")
fmt.Printf("error %v \n", err)
return
} else {
fmt.Printf("google.storage.v1.Bucket %v\n", g)
}
case "type.googleapis.com/google.storage.v1.ListObjectsResponse":
g := &storagepb.ListObjectsResponse{}
err = protojson.UnmarshalOptions{
DiscardUnknown: true,
}.Unmarshal(iamResponsetStruct, g)
if err != nil {
fmt.Println("Could not unmarshal to google.storage.v1.ListObjectsResponse")
fmt.Printf("error %v \n", err)
return
} else {
fmt.Printf("google.storage.v1.ListObjectsResponse %v\n", g)
}
default:
fmt.Printf("Unknown Response Type %s \n", reqTyp)
}
fmt.Printf("Response @type %v\n", respTyp)
}
if audit_data.Metadata != nil {
j, err := audit_data.Metadata.MarshalJSON()
if err != nil {
fmt.Printf("%v", err)
return
}
ct := &loggingiampb.AuditData{}
re := bytes.NewReader(j)
if err := jsonpb.Unmarshal(re, ct); err != nil {
fmt.Printf("%v", err)
return
}
fmt.Printf(" Metadata AuditData %v\n", ct)
}
if (audit_data.ServiceData) != nil {
ct := &loggingiampb.AuditData{}
err = anypb.UnmarshalTo(audit_data.ServiceData, ct, proto.UnmarshalOptions{
AllowPartial: true,
})
if err != nil {
fmt.Printf("Error %v\n", err)
return
}
fmt.Printf(" ServiceData AuditData %v\n", ct)
}
fmt.Println()
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment