Last active
May 12, 2022 01:08
-
-
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/
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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