Documentation
¶
Index ¶
- Constants
- func NewLogsSeen(count uint64) *uint64
- type Attributes
- type AzureActivityLogBatch
- type AzureActivityLogEvent
- type AzureAuthorization
- type AzureIdentity
- type AzureProperties
- type CdrAlert
- type CdrAlertBatch
- type CdrRule
- type CdrRuleBundle
- type CdrRuleOrigin
- type CloudMetadata
- type CloudProvider
- type CloudService
- type CloudTrailEvent
- type ConnectionLevel
- type CustomerDetails
- type EventData
- type GcpAuditLogEvent
- type GcpAuditLogPayload
- type GcpAuthenticationInfo
- type GcpAuthorizationInfo
- type GcpFirstPartyPrincipal
- type GcpLogEntryOperation
- type GcpMonitoredResource
- type GcpOAuthInfo
- type GcpRequestMetadata
- type GcpResourceAttributes
- type GcpResourceLocation
- type GcpServiceAccountDelegationInfo
- type GcpStatus
- type OnBehalfOf
- type Resource
- type SessionContext
- type SessionIssuer
- type TLSDetails
- type UserIdentity
Constants ¶
const ( CdrEventAccountIDJsonPath = "cdrevent.eventData.awsCloudTrail.userIdentity.accountId" CdrEventOrgIDJsonPath = "cdrevent.eventData.awsCloudTrail.userIdentity.orgId" )
const ( // CdrEventAzureSubscriptionIDJsonPath is the JSON path to the subscription ID // (the Azure equivalent of the AWS account ID) on an Azure Activity Log event. CdrEventAzureSubscriptionIDJsonPath = "cdrevent.eventData.azureActivityLog.subscriptionId" // CdrEventAzureTenantIDJsonPath is the JSON path to the tenant ID // (the Azure equivalent of the AWS org ID) on an Azure Activity Log event. CdrEventAzureTenantIDJsonPath = "cdrevent.eventData.azureActivityLog.tenantId" )
const ( // CdrEventGcpProjectIDJsonPath is the JSON path to the GCP project ID on a GCP // Cloud Audit Log event — the account key the CDR alert is matched to. It is // the GCP equivalent of the AWS accountId / Azure subscriptionId // account-matching path. // // GCP exposes NO org-key equivalent of the AWS orgId / Azure tenantId path: // the audit record carries only project_id, with no org/folder ancestry, so // there is no field to point such a constant at. Org-level connections are // instead attributed by resolving this project_id to the org-connected // account via CADRGcpConfig (an armosec-infra concern), not from the event. CdrEventGcpProjectIDJsonPath = "cdrevent.eventData.gcpAuditLog.resource.labels.project_id" )
Variables ¶
This section is empty.
Functions ¶
func NewLogsSeen ¶ added in v0.0.754
NewLogsSeen returns a pointer suitable for CdrAlertBatch.LogsSeen. Producers use it so that reporting a genuine zero is a one-liner and does not accidentally become "not reported".
Types ¶
type Attributes ¶
type AzureActivityLogBatch ¶ added in v0.0.737
type AzureActivityLogBatch struct {
Records []AzureActivityLogEvent `json:"records"`
}
AzureActivityLogBatch is the envelope Azure Monitor delivers to Event Hub: one Event Hub message carries many Activity Log records. The in-account collector unwraps this and embeds each AzureActivityLogEvent into a CdrAlert's EventData.
type AzureActivityLogEvent ¶ added in v0.0.737
type AzureActivityLogEvent struct {
Time time.Time `json:"time"`
ResourceID string `json:"resourceId,omitempty"`
OperationName string `json:"operationName,omitempty"`
Category string `json:"category,omitempty"`
ResultType string `json:"resultType,omitempty"`
ResultSignature string `json:"resultSignature,omitempty"`
CorrelationID string `json:"correlationId,omitempty"`
CallerIPAddress string `json:"callerIpAddress,omitempty"`
// Caller is the identity that performed the operation (UPN or object ID).
Caller string `json:"caller,omitempty"`
Level string `json:"level,omitempty"`
// Location is the Azure region the operation acted in — the analog of AWS
// awsRegion. Often "global" for control-plane operations (subscription /
// resource-group scope), a region (e.g. "eastus") for regional resources.
Location string `json:"location,omitempty"`
// Channels is the Activity Log channel (e.g. "Operation").
Channels string `json:"channels,omitempty"`
// SubscriptionID / TenantID / ResourceGroupName are the account identifiers
// (Azure's equivalents of the AWS accountId/orgId). Present on the
// management/REST shape; on the Event Hub shape SubscriptionID may need to be
// derived from ResourceID.
SubscriptionID string `json:"subscriptionId,omitempty"`
TenantID string `json:"tenantId,omitempty"`
ResourceGroupName string `json:"resourceGroupName,omitempty"`
// Authorization is the RBAC context of the operation, on the REST/management
// shape. Empty on the Event Hub shape — use EffectiveAuthorization.
Authorization *AzureAuthorization `json:"authorization,omitempty"`
// Claims is the caller's token claims (objectId, appid, ipaddr, tenantid, ...),
// on the REST/management shape. Empty on the Event Hub shape — use
// EffectiveClaims.
Claims map[string]string `json:"claims,omitempty"`
// Identity is where the Event Hub / diagnostic-settings export nests the
// authorization and claims. Nil on the REST/management shape.
Identity *AzureIdentity `json:"identity,omitempty"`
// Properties is the operation-specific bag (eventCategory, entity, message,
// hierarchy, statusCode, responseBody, ...). Kept raw on purpose: Activity Log
// delivers it as an object for most operations and as a STRING containing JSON
// for some, so a map[string]any field rejects the second form and — because the
// failure is not local — fails the WHOLE record decode, discarding a detection
// over a field no rule references. No Go code reads it; rules are evaluated
// against the raw record JSON, not this struct. See AzureProperties for why the
// raw form needs its own type rather than a bare json.RawMessage.
Properties AzureProperties `json:"properties,omitempty" bson:"properties,omitempty"`
}
AzureActivityLogEvent is a single Azure Activity Log record — the control-plane audit event that is the Azure equivalent of an AWS CloudTrail management event (see CloudTrailEvent in aws.go).
The same record reaches us in two shapes that disagree on where the caller identity lives, and this struct models the identity of BOTH (verified against captured samples of each):
- Event Hub / diagnostic-settings export — what the in-account collector consumes, and the only shape decoded through this struct today. Nests the RBAC context and token claims under an "identity" object, carries no top-level "caller", and adds wrapper fields (RoleLocation, Stamp, ReleaseVersion, VmSku) that are intentionally not modelled.
- Azure Monitor REST / management API — puts "authorization" and "claims" at the top level alongside a "caller".
The REST shape is modelled for its IDENTITY LAYOUT only. A verbatim REST response does NOT decode through this struct: REST sends operationName and category as {value, localizedValue} objects, which the string fields below reject. That is deliberate — no code decodes a REST response here, and the collector's records always carry the string form. Add normalization when a REST reader actually exists, not before.
Read the identity through EffectiveClaims / EffectiveAuthorization / EffectiveCaller rather than the fields directly, so callers work on both shapes. Reading Claims or Authorization directly silently yields nothing on the Event Hub shape, which is where the collector's alerts come from.
func (AzureActivityLogEvent) EffectiveAuthorization ¶ added in v0.0.751
func (e AzureActivityLogEvent) EffectiveAuthorization() *AzureAuthorization
EffectiveAuthorization returns the operation's RBAC context from whichever shape the event arrived in, preferring the Event Hub's nested identity.
func (AzureActivityLogEvent) EffectiveCaller ¶ added in v0.0.751
func (e AzureActivityLogEvent) EffectiveCaller() string
EffectiveCaller returns the human identity that performed the operation. The REST shape carries it in "caller"; the Event Hub shape has no such field, so it falls back to the claims that hold the user's name or UPN.
func (AzureActivityLogEvent) EffectiveClaims ¶ added in v0.0.751
func (e AzureActivityLogEvent) EffectiveClaims() map[string]string
EffectiveClaims returns the caller's token claims from whichever shape the event arrived in, preferring the Event Hub's nested identity. It always returns a non-nil map, so ranging over the result is safe even when the event carries no claims at all.
type AzureAuthorization ¶ added in v0.0.737
type AzureAuthorization struct {
Scope string `json:"scope,omitempty"`
Action string `json:"action,omitempty"`
Evidence map[string]interface{} `json:"evidence,omitempty"`
}
AzureAuthorization is the RBAC authorization context of an Activity Log operation.
type AzureIdentity ¶ added in v0.0.751
type AzureIdentity struct {
Authorization *AzureAuthorization `json:"authorization,omitempty"`
Claims map[string]string `json:"claims,omitempty"`
}
AzureIdentity is the "identity" object of an Event Hub Activity Log record, holding the RBAC context and the caller's token claims.
type AzureProperties ¶ added in v0.0.757
type AzureProperties json.RawMessage
AzureProperties holds an Activity Log record's "properties" bag verbatim as JSON, and survives a MongoDB round trip in both directions.
Raw, because Activity Log delivers the bag as an object for most operations and as a STRING containing JSON for some. A typed field rejects the second form and — the failure not being local — fails the WHOLE record decode, discarding a detection over a field no rule references.
Its own type, because this struct is not only a wire shape: config-service persists runtime incidents with it embedded, so the bag also crosses BSON. A bare json.RawMessage is a []byte there, and the driver's byte-slice codec accepts only BSON binary or string — never an embedded document, which is what every incident stored before this type existed holds. Reading one back failed the whole find with
error decoding key cdrevent.eventdata.azureactivitylog.properties: cannot decode document into json.RawMessage
and every Azure incident 500ed. Writing was as quietly wrong in the other direction: []byte marshals to BSON binary, so incidents stored during that window hold an opaque blob that no Mongo filter or index can reach.
The methods below therefore accept every shape the collection now holds — document (before), binary (during), string, null — and store the bag in its natural BSON form, so a structured bag stays queryable as a document or array rather than becoming an opaque blob. Reading and writing are symmetric: every shape the writer can produce, the reader reads back. Decoding never fails: an unreadable bag yields nil rather than sinking the incident it belongs to, which is the same bargain the JSON side makes.
func (AzureProperties) MarshalBSONValue ¶ added in v0.0.757
func (p AzureProperties) MarshalBSONValue() (bsontype.Type, []byte, error)
MarshalBSONValue stores the bag in its natural BSON form: an object becomes an embedded document and an array a BSON array, so a structured bag stays queryable and indexable rather than becoming an opaque blob; a top-level JSON scalar becomes the matching BSON scalar. A bag that is not valid JSON is stored as a string rather than dropped, and an empty or null one as BSON null.
Every type this can emit is read back by UnmarshalBSONValue. Keep the two in step: a shape the writer emits and the reader does not recognise is silent data loss on the round trip.
func (AzureProperties) MarshalJSON ¶ added in v0.0.757
func (p AzureProperties) MarshalJSON() ([]byte, error)
MarshalJSON emits the bag verbatim. A named type does not inherit json.RawMessage's methods, and without this encoding/json would base64 it.
func (*AzureProperties) UnmarshalBSONValue ¶ added in v0.0.757
func (p *AzureProperties) UnmarshalBSONValue(t bsontype.Type, data []byte) error
UnmarshalBSONValue reads back every shape the collection holds, and never fails: a bag it cannot read becomes nil instead of failing the surrounding incident decode.
Documents reach JSON through jsonifyBSON, so BSON types with no JSON scalar form (dates, binary) come back as their driver representation rather than their original text. An Activity Log bag is plain JSON, so this is exact in practice; it is not a byte-exact round trip for arbitrary BSON.
func (*AzureProperties) UnmarshalJSON ¶ added in v0.0.757
func (p *AzureProperties) UnmarshalJSON(data []byte) error
UnmarshalJSON keeps the bag verbatim, whatever JSON type it arrived as.
type CdrAlert ¶
type CdrAlert struct {
// CloudMetadata is the metadata of the cloud
CloudMetadata `json:"cloudMetadata,omitempty"`
// EventData is the event data
EventData `json:"eventData,omitempty"`
// RuleName is the name of the rule
RuleName string `json:"ruleName,omitempty"`
// RuleID is the unique identifier of the rule
RuleID string `json:"ruleID,omitempty"`
// Description is the description of the rule
Description string `json:"description,omitempty"`
// Priority is the severity of the rule
Priority string `json:"priority,omitempty"`
// Tags is the tags of the rule
Tags []string `json:"tags,omitempty"`
// Message is the failure message
Message string `json:"message,omitempty"`
// MitreTactic is the MITRE ATT&CK tactic
MitreTactic string `json:"mitreTactic,omitempty"`
// MitreTechnique is the MITRE ATT&CK technique
MitreTechnique string `json:"mitreTechnique,omitempty"`
// UniqueID is the unique identifier of the alert
UniqueID string `json:"uniqueID,omitempty"`
}
type CdrAlertBatch ¶
type CdrAlertBatch struct {
// CustomerGUID is the unique identifier of the customer
CustomerGUID string `json:"customerGUID,omitempty"`
// CloudAccountID is the unique identifier of the cloud account
CloudAccountID string `json:"cloudAccountID,omitempty"`
// OrgID is the unique identifier of the organization
OrgID string `json:"orgID,omitempty"`
// Provider is the cloud provider
Provider CloudProvider `json:"provider,omitempty"`
// RuleFailures is the list of rule failures
RuleFailures []CdrAlert `json:"ruleFailures,omitempty"`
// IsHeartbeat marks a periodic liveness message (no RuleFailures); absent/false = a normal alert batch. See docs/features/cdr-heartbeat-contract.md.
IsHeartbeat bool `json:"isHeartbeat,omitempty"`
// ConnectionLevel states account- vs organization-level so the ingester routes keep-alive by intent, not by OrgID presence; empty = legacy inference. See docs/features/cdr-heartbeat-contract.md.
ConnectionLevel ConnectionLevel `json:"connectionLevel,omitempty"`
// LogsSeen counts audit records the collector received through its log pipe since start, so the backend can gate Pending -> Connected on evidence rather than liveness; nil = producer does not report it, 0 = reports it and has seen none. Read it with LogsSeenValue. See docs/features/cdr-heartbeat-contract.md.
LogsSeen *uint64 `json:"logsSeen,omitempty"`
}
func (*CdrAlertBatch) LogsSeenValue ¶ added in v0.0.754
func (b *CdrAlertBatch) LogsSeenValue() (count uint64, reported bool)
LogsSeenValue returns the batch's logsSeen count and whether the producer reported it at all.
Branch on reported rather than treating a missing count as zero. The two say different things, and conflating them breaks the gate in both directions: a producer that does not report the signal (AWS, Azure) has to fall back to liveness, while a producer reporting zero is explicitly saying "no log has traversed the pipe yet" and must not be treated as connected. This is why the field is a pointer — omitempty tests nil, not the pointee, so an explicit 0 still reaches the wire.
The count gates the Pending -> Connected transition ONLY, and that transition latches. The count is cumulative per collector instance and resets when the instance is replaced, so an already-Connected account will legitimately send logsSeen: 0 again after a restart; regressing it to Pending on that would flap the connection on every recycle.
type CdrRule ¶ added in v0.0.741
type CdrRule struct {
// RuleID is the unique identifier of the rule.
RuleID string `json:"ruleID"`
// Name is the human-readable rule name.
Name string `json:"name"`
// Description is the rule description.
Description string `json:"description,omitempty"`
// Service is the target log stream the rule evaluates (e.g. activitylogs, cloudtrail).
Service CloudService `json:"service"`
// Expression is the CEL rule text the collector compiles and evaluates.
Expression string `json:"expression"`
// Priority is the severity of the rule (matches CdrAlert.Priority for pass-through).
Priority string `json:"priority,omitempty"`
// MitreTactic is the MITRE ATT&CK tactic.
MitreTactic string `json:"mitreTactic,omitempty"`
// MitreTechnique is the MITRE ATT&CK technique.
MitreTechnique string `json:"mitreTechnique,omitempty"`
// Tags is the rule tags.
Tags []string `json:"tags,omitempty"`
// Message is the alert-message template emitted on a match.
Message string `json:"message,omitempty"`
// UniqueID is the templated dedup key for a match.
UniqueID string `json:"uniqueID,omitempty"`
// Origin is managed (default) or custom.
Origin CdrRuleOrigin `json:"origin,omitempty"`
}
CdrRule is the sensor-facing projection of a CDR detection rule served to the in-account collector over the managed rule-delivery endpoint. It is deliberately slim and decoupled from the internal runtime-rule storage model so the wire contract stays stable as that storage evolves. Its fields align with the alert a match produces, so rule metadata passes straight through to the emitted CdrAlert.
type CdrRuleBundle ¶ added in v0.0.741
type CdrRuleBundle struct {
// Version is an opaque content hash of the served rule set; it changes if and only if the set
// changes. Clients must treat it as opaque.
Version string `json:"version"`
// Provider is the cloud provider the bundle is scoped to.
Provider CloudProvider `json:"provider"`
// GeneratedAt is when the bundle was assembled (informational; not part of Version).
GeneratedAt time.Time `json:"generatedAt"`
// Rules is the enabled, provider-scoped CDR rule set for the tenant.
Rules []CdrRule `json:"rules"`
}
CdrRuleBundle is the response body of the managed rule-delivery endpoint: the provider-scoped CEL rule set for a tenant plus an opaque content version for change detection. The collector downloads it, authenticated with its per-tenant access key, instead of shipping rules baked into the deployed image. Version is emitted as the response ETag; the collector echoes it via If-None-Match to get a cheap 304 when the set is unchanged.
type CdrRuleOrigin ¶ added in v0.0.741
type CdrRuleOrigin string
CdrRuleOrigin distinguishes centrally-managed rules from customer-authored ones. Customer authoring is not yet supported; the field exists so the delivery contract can carry custom rules in future without a wire change. The server always sets it (managed or custom); a consumer should treat an absent value as managed.
const ( // CdrRuleOriginManaged is a rule maintained centrally by ARMO. CdrRuleOriginManaged CdrRuleOrigin = "managed" // CdrRuleOriginCustom is a customer-authored rule. CdrRuleOriginCustom CdrRuleOrigin = "custom" )
type CloudMetadata ¶
type CloudMetadata struct {
// Provider is the cloud provider
Provider CloudProvider `json:"provider,omitempty"`
// SourceService is the source service (e.g cloudtrail, cloudwatch, etc)
SourceService CloudService `json:"sourceService,omitempty"`
}
type CloudProvider ¶
type CloudProvider string
Cloud providers
const ( // AWS is the AWS cloud provider AWS CloudProvider = "aws" // Azure is the Microsoft Azure cloud provider Azure CloudProvider = "azure" // GCP is the Google Cloud Platform cloud provider GCP CloudProvider = "gcp" )
type CloudService ¶
type CloudService string
Cloud services
const ( // CloudTrail is the cloudtrail service CloudTrail CloudService = "cloudtrail" // ActivityLogs is the Azure Activity Log service (control-plane audit log) ActivityLogs CloudService = "activitylogs" // CloudAuditLogs is the GCP Cloud Audit Logs service; the CDR pipe consumes // the Admin Activity control-plane audit log (the GCP equivalent of AWS // CloudTrail management events / the Azure Activity Log). CloudAuditLogs CloudService = "cloudauditlogs" )
type CloudTrailEvent ¶
type CloudTrailEvent struct {
EventVersion string `json:"eventVersion"`
UserIdentity UserIdentity `json:"userIdentity"`
EventTime time.Time `json:"eventTime"`
EventSource string `json:"eventSource"`
EventName string `json:"eventName"`
AWSRegion string `json:"awsRegion"`
SourceIPAddress string `json:"sourceIPAddress"`
UserAgent string `json:"userAgent"`
ErrorCode string `json:"errorCode,omitempty"`
ErrorMessage string `json:"errorMessage,omitempty"`
RequestParameters map[string]interface{} `json:"requestParameters,omitempty"`
ResponseElements map[string]interface{} `json:"responseElements,omitempty"`
AdditionalEventData map[string]interface{} `json:"additionalEventData,omitempty"`
RequestID string `json:"requestId"`
EventID string `json:"eventId"`
EventType string `json:"eventType"`
APIVersion string `json:"apiVersion,omitempty"`
ReadOnly bool `json:"readOnly"`
ManagementEvent bool `json:"managementEvent"`
Resources []Resource `json:"resources,omitempty"`
RecipientAccountId string `json:"recipientAccountId,omitempty"`
VpcEndpointId string `json:"vpcEndpointId,omitempty"`
TLSDetails *TLSDetails `json:"tlsDetails,omitempty"`
ServiceEventDetails map[string]interface{} `json:"serviceEventDetails,omitempty"`
}
type ConnectionLevel ¶ added in v0.0.747
type ConnectionLevel string
ConnectionLevel states whether a CDR batch belongs to a single-account connection or an organization/tenant-wide one. The ingester routes keep-alive on this stated intent rather than inferring it from OrgID presence; an empty value means "infer" — the legacy behavior existing AWS producers rely on. See docs/features/cdr-heartbeat-contract.md.
const ( // ConnectionLevelAccount is a single-account connection (AWS account / Azure // subscription / GCP project); keep-alive is keyed on CloudAccountID. ConnectionLevelAccount ConnectionLevel = "account" // ConnectionLevelOrganization is an organization/tenant-wide connection (AWS org / // Azure tenant / GCP org); keep-alive is keyed on OrgID. ConnectionLevelOrganization ConnectionLevel = "organization" )
type CustomerDetails ¶
type EventData ¶
type EventData struct {
// AWSCloudTrail cloudtrail event
AWSCloudTrail *CloudTrailEvent `json:"awsCloudTrail,omitempty"`
// AzureActivityLog azure activity log event
AzureActivityLog *AzureActivityLogEvent `json:"azureActivityLog,omitempty"`
// GcpAuditLog gcp cloud audit log (Admin Activity) event
GcpAuditLog *GcpAuditLogEvent `json:"gcpAuditLog,omitempty"`
// Target resource
TargetResource string `json:"targetResource,omitempty"`
// Identifiers of the alert
Identifiers *common.Identifiers `json:"identifiers,omitempty"`
}
The types corresponds to the SourceService type
func (EventData) EventTime ¶ added in v0.0.745
EventTime returns the timestamp of whichever provider event is set.
type GcpAuditLogEvent ¶ added in v0.0.738
type GcpAuditLogEvent struct {
// InsertID uniquely identifies the log entry; it is stable across Pub/Sub
// redeliveries and forms half of the dedup key hash(insertId + rule_id).
InsertID string `json:"insertId,omitempty"`
// LogName is the fully-qualified log name, e.g.
// projects/<project>/logs/cloudaudit.googleapis.com%2Factivity.
LogName string `json:"logName,omitempty"`
// Timestamp is when the logged action occurred.
Timestamp time.Time `json:"timestamp"`
// ReceiveTimestamp is when Cloud Logging received the entry.
ReceiveTimestamp time.Time `json:"receiveTimestamp"`
// Severity is the log severity (e.g. "NOTICE").
Severity string `json:"severity,omitempty"`
// Resource is the monitored resource the entry is about; resource.labels
// carries the project_id used to match the alert to a GCP account.
Resource *GcpMonitoredResource `json:"resource,omitempty"`
// Operation ties together the entries of a single long-running operation.
// ABSENT on synchronous events — the detection gate is
// `!has(operation) || operation.last`.
Operation *GcpLogEntryOperation `json:"operation,omitempty"`
// ProtoPayload is the google.cloud.audit.AuditLog record.
ProtoPayload *GcpAuditLogPayload `json:"protoPayload,omitempty"`
}
GcpAuditLogEvent is a single GCP Cloud Logging LogEntry carrying an Admin Activity audit record — the control-plane audit event that is the GCP equivalent of an AWS CloudTrail management event (see CloudTrailEvent in aws.go) and the Azure Activity Log record (see AzureActivityLogEvent in azure.go).
The Pub/Sub push pipe delivers one LogEntry per message; unlike the Azure Event Hub path there is no batch envelope. The audit payload lives under ProtoPayload (google.cloud.audit.AuditLog), while Operation lives at the LogEntry level — deliberately, because the synchronous-vs-long-running distinction the detection gate depends on is a LogEntry property, not a payload one.
The field set and its nil-safety were measured against real events in the gcp-cdr-poc (see its FINDINGS.md §1); the round-trip test in gcp_test.go is the guard. POC corrections baked into the shape below:
- Operation is a pointer: synchronous admin calls (create SA, set IAM policy) carry NO operation block at all, so the detection gate is `!has(operation) || operation.last`. Long-running calls emit two entries sharing operation.id, flagged first / last.
- Operation booleans are OMITTED when false (not serialized as false), so CEL rules must use has() guards; the Go zero-value (false) is safe.
- ResourceName is the PARENT for create-style methods (CreateServiceAccount -> projects/X); the created resource is in response.name — model both.
- Status is `{}` on success, so status.code is absent rather than 0.
- AuthorizationInfo carries permission / permissionType / granted, enabling denied-attempt (granted:false) detection rules.
All optional object/slice/string fields are pointers or omitempty so CEL has()-guards behave and GCP's per-service omissions round-trip cleanly. The two time.Time fields follow the aws.go/azure.go convention (bare, non-pointer); like EventTime/Time there, an absent timestamp re-serializes as the zero time rather than staying omitted — harmless in practice, as Cloud Logging stamps both timestamp and receiveTimestamp on every delivered entry.
type GcpAuditLogPayload ¶ added in v0.0.738
type GcpAuditLogPayload struct {
// Type is the payload @type discriminator
// ("type.googleapis.com/google.cloud.audit.AuditLog").
Type string `json:"@type,omitempty"`
// ServiceName is the API that produced the event (e.g. "iam.googleapis.com").
ServiceName string `json:"serviceName,omitempty"`
// MethodName is the API method invoked (e.g.
// "google.iam.admin.v1.CreateServiceAccount").
MethodName string `json:"methodName,omitempty"`
// ResourceName is the resource the request targets. For create-style methods
// it is the PARENT (e.g. "projects/X"), not the created resource — the real
// target is in Response["name"]. Do NOT assume ResourceName == affected
// resource (POC FINDINGS §1c②).
ResourceName string `json:"resourceName,omitempty"`
// AuthenticationInfo is the caller identity (principalEmail / principalSubject).
AuthenticationInfo *GcpAuthenticationInfo `json:"authenticationInfo,omitempty"`
// AuthorizationInfo is the per-permission authorization decisions for the
// call; granted:false entries enable denied/attempted-action detection.
AuthorizationInfo []GcpAuthorizationInfo `json:"authorizationInfo,omitempty"`
// RequestMetadata carries the caller IP and user agent.
RequestMetadata *GcpRequestMetadata `json:"requestMetadata,omitempty"`
// Status is the operation status. Present but EMPTY ({}) on success, so
// status.code is absent rather than 0 — guard with has() in CEL.
Status *GcpStatus `json:"status,omitempty"`
// PolicyViolationInfo carries org-policy / VPC Service Controls violation
// details when a request is denied by policy — a detection class of its own
// (org-policy and perimeter denials). Shape varies; kept generic.
PolicyViolationInfo map[string]interface{} `json:"policyViolationInfo,omitempty"`
// ResourceLocation is where the resource is/was located (current vs original),
// e.g. for data-residency-aware rules.
ResourceLocation *GcpResourceLocation `json:"resourceLocation,omitempty"`
// NumResponseItems is the number of items returned by a list/query method.
// protojson encodes int64 as a quoted JSON string ("3"), but the protobuf-JSON
// spec also accepts a bare number (3) on parse. json.Number accepts BOTH forms;
// a plain string would fail the whole decode on a bare number — a dropped event
// for a field nothing depends on.
//
// The bson tag is REQUIRED, not decoration. Most audit events omit this field
// (only list/query methods return items), leaving the zero json.Number — the
// empty string. The driver encodes json.Number by parsing it, and "" parses as
// neither int nor float, so it errors; because BSON encoding is not per-field,
// that error fails the WHOLE document write. The json tag's omitempty does not
// help: the driver ignores json tags unless useJSONStructTags is set, which it
// is not by default. Without bson omitempty, no GCP CDR incident can persist.
//
// The tag deliberately names NO field. A bson tag sets the stored key, and
// these types carry none, so every other field here is stored under the
// driver's default — the lowercased Go name, "numresponseitems". Spelling the
// name here would rename the field to "numresponseItems" casing, orphaning any
// value already stored and making this the one camelCase key among its
// siblings. Renaming a stored field is a migration, not a side effect of a fix.
NumResponseItems json.Number `json:"numResponseItems,omitempty" bson:",omitempty"`
// Request is the operation-specific request bag; shape varies by method.
Request map[string]interface{} `json:"request,omitempty"`
// Response is the operation-specific response bag; for create-style methods
// Response["name"] is the created resource (the real target).
Response map[string]interface{} `json:"response,omitempty"`
// Metadata is where several services (GKE, BigQuery, Cloud SQL, ...) put their
// audit detail instead of Request / Response; shape varies by service.
Metadata map[string]interface{} `json:"metadata,omitempty"`
}
GcpAuditLogPayload models the google.cloud.audit.AuditLog protoPayload of an Admin Activity entry — the fields the CDR detection and identifier normalization lean on. Optional bags (Request / Response) are kept generic; Response in particular carries the real target of create-style methods (response.name) that ResourceName does not.
type GcpAuthenticationInfo ¶ added in v0.0.738
type GcpAuthenticationInfo struct {
// PrincipalEmail is the caller's email (user or service account).
PrincipalEmail string `json:"principalEmail,omitempty"`
// PrincipalSubject is the prefixed principal ("user:..." / "serviceAccount:..."),
// which cleanly distinguishes user from service-account callers for
// common.Identifiers normalization (POC FINDINGS §1c⑤).
PrincipalSubject string `json:"principalSubject,omitempty"`
// ServiceAccountKeyName is the resource name of the service-account key used
// to authenticate, when a long-lived key was used rather than short-lived
// credentials — a standing detection / compliance signal.
ServiceAccountKeyName string `json:"serviceAccountKeyName,omitempty"`
// ServiceAccountDelegationInfo is the identity-delegation (impersonation)
// chain. Service-account impersonation (iam.serviceAccounts.getAccessToken /
// actAs) is the canonical GCP privilege-escalation path; this is what lets an
// alert say who impersonated whom. Absent unless impersonation occurred (so
// not seen in the human-auth POC run — its absence there is not evidence of
// rarity in the field).
ServiceAccountDelegationInfo []GcpServiceAccountDelegationInfo `json:"serviceAccountDelegationInfo,omitempty"`
// OAuthInfo carries the OAuth client that made the call, when present.
OAuthInfo *GcpOAuthInfo `json:"oauthInfo,omitempty"`
}
GcpAuthenticationInfo is the AuditLog.authenticationInfo — the caller identity.
type GcpAuthorizationInfo ¶ added in v0.0.738
type GcpAuthorizationInfo struct {
Permission string `json:"permission,omitempty"`
PermissionType string `json:"permissionType,omitempty"`
Granted bool `json:"granted,omitempty"`
Resource string `json:"resource,omitempty"`
// ResourceAttributes describes the resource the permission was checked
// against. Its Type (e.g. "iam.googleapis.com/ServiceAccount") is the kind of
// thing touched — the field to fall back on when ResourceName is the parent
// rather than the target (POC FINDINGS §1c②); mirrors AWS Resource.ResourceType.
ResourceAttributes *GcpResourceAttributes `json:"resourceAttributes,omitempty"`
}
GcpAuthorizationInfo is one AuditLog.authorizationInfo[] entry — the authorization decision for a single permission. PermissionType (e.g. "ADMIN_WRITE") is a clean control-plane-write discriminator, and Granted:false surfaces denied/attempted privileged actions (recon, privilege probing).
NOTE: like the operation booleans, GCP omits Granted from the wire when false, so it unmarshals to the false zero-value; CEL rules must has()-guard it.
type GcpFirstPartyPrincipal ¶ added in v0.0.738
type GcpFirstPartyPrincipal struct {
PrincipalEmail string `json:"principalEmail,omitempty"`
}
GcpFirstPartyPrincipal is the first-party identity in a delegation link.
type GcpLogEntryOperation ¶ added in v0.0.738
type GcpLogEntryOperation struct {
ID string `json:"id,omitempty"`
Producer string `json:"producer,omitempty"`
First bool `json:"first,omitempty"`
Last bool `json:"last,omitempty"`
}
GcpLogEntryOperation is the LogEntry.operation block. It is present only on long-running operations, whose request and completion entries share the same ID and are flagged First / Last respectively.
NOTE: First / Last are omitted from the wire when false (they are not serialized as false). Go unmarshals the missing key to the false zero-value (safe), but CEL rule authors must use has() guards rather than comparing to false. The chosen gate reads only operation.last, so it is has()-safe.
type GcpMonitoredResource ¶ added in v0.0.738
type GcpMonitoredResource struct {
Type string `json:"type,omitempty"`
Labels map[string]string `json:"labels,omitempty"`
}
GcpMonitoredResource is the Cloud Logging monitored-resource descriptor. For an Admin Activity entry, Type is e.g. "service_account" / "gce_network" and Labels carries "project_id" (and often "location", etc.).
type GcpOAuthInfo ¶ added in v0.0.738
type GcpOAuthInfo struct {
OAuthClientID string `json:"oauthClientId,omitempty"`
}
GcpOAuthInfo is the AuditLog.authenticationInfo.oauthInfo block.
type GcpRequestMetadata ¶ added in v0.0.738
type GcpRequestMetadata struct {
CallerIP string `json:"callerIp,omitempty"`
CallerSuppliedUserAgent string `json:"callerSuppliedUserAgent,omitempty"`
// CallerNetwork is the VPC network the call originated from, when applicable.
CallerNetwork string `json:"callerNetwork,omitempty"`
// RequestAttributes is the request context (time, auth, method, ...); shape
// varies, kept generic.
RequestAttributes map[string]interface{} `json:"requestAttributes,omitempty"`
// DestinationAttributes is the request destination context; shape varies,
// kept generic.
DestinationAttributes map[string]interface{} `json:"destinationAttributes,omitempty"`
}
GcpRequestMetadata is the AuditLog.requestMetadata — the caller's network context, useful for SourceInformation normalization.
type GcpResourceAttributes ¶ added in v0.0.738
type GcpResourceAttributes struct {
Service string `json:"service,omitempty"`
Name string `json:"name,omitempty"`
Type string `json:"type,omitempty"`
}
GcpResourceAttributes is the AuthorizationInfo.resourceAttributes — the typed resource the authorization decision was made against.
type GcpResourceLocation ¶ added in v0.0.738
type GcpResourceLocation struct {
CurrentLocations []string `json:"currentLocations,omitempty"`
OriginalLocations []string `json:"originalLocations,omitempty"`
}
GcpResourceLocation is the AuditLog.resourceLocation — the resource's current and (for moves) original locations.
type GcpServiceAccountDelegationInfo ¶ added in v0.0.738
type GcpServiceAccountDelegationInfo struct {
// PrincipalSubject is the delegated principal at this link.
PrincipalSubject string `json:"principalSubject,omitempty"`
// FirstPartyPrincipal is set when a first-party (Google) identity delegated.
FirstPartyPrincipal *GcpFirstPartyPrincipal `json:"firstPartyPrincipal,omitempty"`
// ThirdPartyPrincipal is set for third-party (external) delegation; shape
// varies, kept generic.
ThirdPartyPrincipal map[string]interface{} `json:"thirdPartyPrincipal,omitempty"`
}
GcpServiceAccountDelegationInfo is one link in the impersonation chain (AuditLog.authenticationInfo.serviceAccountDelegationInfo[]).
type GcpStatus ¶ added in v0.0.738
type GcpStatus struct {
Code int `json:"code,omitempty"`
Message string `json:"message,omitempty"`
}
GcpStatus is the AuditLog.status (google.rpc.Status). Empty ({}) on success, so Code is absent rather than 0.
type OnBehalfOf ¶
type SessionContext ¶
type SessionContext struct {
SessionIssuer *SessionIssuer `json:"sessionIssuer,omitempty"`
Attributes *Attributes `json:"attributes,omitempty"`
}
type SessionIssuer ¶
type TLSDetails ¶
type UserIdentity ¶
type UserIdentity struct {
Type string `json:"type"`
PrincipalID string `json:"principalId"`
ARN string `json:"arn,omitempty"`
AccountID string `json:"accountId"`
OrgID string `json:"orgId,omitempty"`
AccessKeyID string `json:"accessKeyId,omitempty"`
UserName string `json:"userName,omitempty"`
InvokedBy string `json:"invokedBy,omitempty"`
SessionContext *SessionContext `json:"sessionContext,omitempty"`
OnBehalfOf *OnBehalfOf `json:"onBehalfOf,omitempty"`
CredentialId string `json:"credentialId,omitempty"`
}