Detection rules › Panther
Kubernetes System Principal Accessed from Non-Cloud Public IP
This detection identifies when Kubernetes system principals (service accounts with usernames starting with "system:", "eks:", or "aks:") are accessed from non-cloud provider public IP addresses. System principals should only operate from within the cluster (private IPs) or from legitimate cloud infrastructure. Access from external public IPs indicates potential service account token theft or compromise, often following initial access to a cluster.
MITRE ATT&CK coverage
| Tactic | Techniques |
|---|---|
| Initial Access | T1190 Exploit Public-Facing Application |
| Credential Access | T1528 Steal Application Access Token |
| Lateral Movement | T1021.007 Remote Services: Cloud Services |
Rule body yaml
AnalysisType: rule
RuleID: "Kubernetes.System.Principal.PublicIP"
DisplayName: "Kubernetes System Principal Accessed from Non-Cloud Public IP"
Enabled: true
Status: Experimental
Filename: k8s_system_principal_public_ip.py
LogTypes:
- Amazon.EKS.Audit
- Azure.MonitorActivity
- GCP.AuditLog
Tags:
- Kubernetes
- Initial Access
- Lateral Movement
- Credential Access
- Unified Detection
Severity: High
Description: >
This detection identifies when Kubernetes system principals (service accounts with usernames
starting with "system:", "eks:", or "aks:") are accessed from non-cloud provider public IP
addresses. System principals should only operate from within the cluster (private IPs) or
from legitimate cloud infrastructure. Access from external public IPs indicates potential
service account token theft or compromise, often following initial access to a cluster.
Runbook: |
1. Find all Kubernetes API requests from the sourceIPs address in the 24 hours before and after the alert to identify targeted resources and operations
2. Query for authentication and service account token events by this username in the 48 hours before the alert to determine if the token was recently compromised
3. Search for other system principal alerts from the same sourceIPs address across all clusters in the past 7 days to assess campaign scope
Reference: https://kubernetes.io/docs/concepts/security/rbac-good-practices/
Reports:
MITRE ATT&CK:
- TA0001:T1190 # Initial Access: Exploit Public-Facing Application
- TA0006:T1528 # Credential Access: Steal Application Access Token
- TA0008:T1021.007 # Lateral Movement: Remote Services: Cloud Services
DedupPeriodMinutes: 60
SummaryAttributes:
- username
- p_any_ip_addresses
- p_source_label
Tests:
- Name: EKS System ServiceAccount from Non-AWS Public IP
ExpectedResult: true
Log:
{
"kind": "Event",
"apiVersion": "audit.k8s.io/v1",
"verb": "get",
"user": {
"username": "system:serviceaccount:kube-system:coredns",
"groups": ["system:serviceaccounts", "system:authenticated"]
},
"sourceIPs": ["1.2.3.4"],
"objectRef": {
"resource": "endpointslices",
"apiVersion": "v1"
},
"responseStatus": {"code": 200},
"stage": "ResponseComplete",
"p_log_type": "Amazon.EKS.Audit",
"p_source_label": "eks-cluster",
"p_enrichment": {
"ipinfo_asn": {
"sourceIPs": [{
"asn": "AS12345",
"domain": "example-isp.com",
"name": "Example ISP",
"p_match": "1.2.3.4",
"type": "isp"
}]
}
}
}
- Name: AKS System ServiceAccount from Non-Azure Public IP
ExpectedResult: true
Log:
{
"p_log_type": "Azure.MonitorActivity",
"category": "kube-audit",
"operationName": "Microsoft.ContainerService/managedClusters/diagnosticLogs/Read",
"properties": {
"log": "{\"kind\":\"Event\",\"apiVersion\":\"audit.k8s.io/v1\",\"verb\":\"list\",\"user\":{\"username\":\"system:serviceaccount:kube-system:metrics-server\",\"groups\":[\"system:serviceaccounts\",\"system:authenticated\"]},\"sourceIPs\":[\"8.8.8.8\"],\"objectRef\":{\"resource\":\"nodes\"},\"responseStatus\":{\"code\":200},\"stage\":\"ResponseComplete\"}"
},
"p_source_label": "aks-cluster",
"p_enrichment": {
"ipinfo_asn": {
"sourceIPs": [{
"asn": "AS15169",
"domain": "google.com",
"name": "Google LLC",
"p_match": "8.8.8.8",
"type": "hosting"
}]
}
}
}
- Name: GKE System ServiceAccount from Non-GCP Public IP
ExpectedResult: true
Log:
{
"protoPayload": {
"authenticationInfo": {"principalEmail": "system:serviceaccount:kube-system:default"},
"methodName": "io.k8s.core.v1.pods.list",
"requestMetadata": {
"callerIP": "1.2.3.4",
"callerSuppliedUserAgent": "kubectl/v1.27.0"
},
"resourceName": "core/v1/namespaces/default/pods",
"serviceName": "k8s.io",
"status": {}
},
"resource": {
"type": "k8s_cluster",
"labels": {"project_id": "test-project"}
},
"p_log_type": "GCP.AuditLog",
"p_source_label": "gke-cluster",
"p_enrichment": {
"ipinfo_asn": {
"callerIP": [{
"asn": "AS12345",
"domain": "example-isp.com",
"name": "Example ISP",
"p_match": "1.2.3.4",
"type": "isp"
}]
}
}
}
- Name: EKS Legitimate Node from AWS IP
ExpectedResult: false
Log:
{
"kind": "Event",
"apiVersion": "audit.k8s.io/v1",
"verb": "create",
"user": {
"username": "system:node:ip-192-168-3-178.us-west-2.compute.internal",
"groups": ["system:nodes", "system:authenticated"]
},
"sourceIPs": ["54.212.83.236"],
"objectRef": {
"resource": "serviceaccounts",
"subresource": "token"
},
"responseStatus": {"code": 201},
"stage": "ResponseComplete",
"p_log_type": "Amazon.EKS.Audit",
"p_source_label": "eks-cluster",
"p_enrichment": {
"ipinfo_asn": {
"sourceIPs": [{
"asn": "AS16509",
"domain": "amazon.com",
"name": "Amazon.com, Inc.",
"p_match": "54.212.83.236",
"route": "54.212.0.0/16",
"type": "hosting"
}]
}
}
}
- Name: EKS addon-manager from AWS Lambda
ExpectedResult: false
Log:
{
"kind": "Event",
"verb": "get",
"user": {
"username": "eks:addon-manager",
"extra": {
"arn": ["arn:aws:sts::123412341234:assumed-role/AWSWesleyClusterManagerLambda-Add-AddonManagerRole/session"]
},
"groups": ["system:authenticated"]
},
"sourceIPs": ["35.163.244.48"],
"objectRef": {"resource": "deployments", "namespace": "kube-system"},
"responseStatus": {"code": 200},
"stage": "ResponseComplete",
"p_log_type": "Amazon.EKS.Audit"
}
- Name: Non-System User from Public IP
ExpectedResult: false
Log:
{
"kind": "Event",
"verb": "get",
"user": {"username": "admin@example.com"},
"sourceIPs": ["1.2.3.4"],
"objectRef": {"resource": "pods"},
"responseStatus": {"code": 200},
"stage": "ResponseComplete",
"p_log_type": "Amazon.EKS.Audit"
}
- Name: System User from Private IP
ExpectedResult: false
Log:
{
"kind": "Event",
"verb": "watch",
"user": {"username": "system:serviceaccount:kube-system:coredns"},
"sourceIPs": ["10.0.27.115"],
"objectRef": {"resource": "endpointslices"},
"responseStatus": {"code": 200},
"stage": "ResponseComplete",
"p_log_type": "Amazon.EKS.Audit"
}
- Name: 403 Response (Excluded)
ExpectedResult: false
Log:
{
"kind": "Event",
"verb": "get",
"user": {"username": "system:serviceaccount:default:test"},
"sourceIPs": ["1.2.3.4"],
"objectRef": {"resource": "secrets"},
"responseStatus": {"code": 403},
"stage": "ResponseComplete",
"p_log_type": "Amazon.EKS.Audit"
}
- Name: AKS CSI Disk Driver from Azure Node
ExpectedResult: false
Log:
{
"p_log_type": "Azure.MonitorActivity",
"category": "kube-audit",
"operationName": "Microsoft.ContainerService/managedClusters/diagnosticLogs/Read",
"properties": {
"log": "{\"kind\":\"Event\",\"apiVersion\":\"audit.k8s.io/v1\",\"verb\":\"get\",\"user\":{\"username\":\"system:serviceaccount:kube-system:csi-azuredisk-node-sa\",\"groups\":[\"system:serviceaccounts\",\"system:authenticated\"]},\"sourceIPs\":[\"20.127.0.45\"],\"objectRef\":{\"resource\":\"nodes\"},\"responseStatus\":{\"code\":200},\"stage\":\"ResponseComplete\"}"
},
"p_source_label": "aks-cluster",
"p_enrichment": {
"ipinfo_asn": {
"sourceIPs": [{
"asn": "AS8075",
"domain": "microsoft.com",
"name": "Microsoft Corporation",
"p_match": "20.127.0.45",
"type": "hosting"
}]
}
}
}
- Name: AKS CSI File Driver from Azure Node
ExpectedResult: false
Log:
{
"p_log_type": "Azure.MonitorActivity",
"category": "kube-audit",
"operationName": "Microsoft.ContainerService/managedClusters/diagnosticLogs/Read",
"properties": {
"log": "{\"kind\":\"Event\",\"apiVersion\":\"audit.k8s.io/v1\",\"verb\":\"list\",\"user\":{\"username\":\"system:serviceaccount:kube-system:csi-azurefile-node-sa\",\"groups\":[\"system:serviceaccounts\",\"system:authenticated\"]},\"sourceIPs\":[\"52.152.245.238\"],\"objectRef\":{\"resource\":\"persistentvolumes\"},\"responseStatus\":{\"code\":200},\"stage\":\"ResponseComplete\"}"
},
"p_source_label": "aks-cluster"
}
- Name: GKE Anonymous Health Check with User Agent
ExpectedResult: false
Log:
{
"protoPayload": {
"authenticationInfo": {"principalEmail": "system:anonymous"},
"methodName": "io.k8s.core.v1.healthz.get",
"requestMetadata": {
"callerIP": "35.186.224.25",
"callerSuppliedUserAgent": "GoogleHC/1.0"
},
"resourceName": "core/v1/healthz",
"serviceName": "k8s.io",
"status": {}
},
"resource": {
"type": "k8s_cluster",
"labels": {"project_id": "test-project"}
},
"p_log_type": "GCP.AuditLog",
"p_source_label": "gke-cluster",
"p_enrichment": {
"ipinfo_asn": {
"callerIP": [{
"asn": "AS15169",
"domain": "google.com",
"name": "Google LLC",
"p_match": "35.186.224.25",
"type": "hosting"
}]
}
}
}
- Name: GKE Anonymous Readyz Check via RequestURI
ExpectedResult: false
Log:
{
"protoPayload": {
"authenticationInfo": {"principalEmail": "system:anonymous"},
"methodName": "io.k8s.readyz",
"requestMetadata": {
"callerIP": "108.177.75.65",
"callerSuppliedUserAgent": "GoogleKubernetesEngineFrontend"
},
"serviceName": "k8s.io",
"status": {}
},
"p_log_type": "GCP.AuditLog",
"p_source_label": "gke-cluster",
"p_enrichment": {
"ipinfo_asn": {
"callerIP": [{
"asn": "AS15169",
"domain": "google.com"
}]
}
}
}
- Name: Pod with Mixed Public and Private IPs
ExpectedResult: false
Log:
{
"kind": "Event",
"verb": "watch",
"user": {"username": "system:serviceaccount:default:app"},
"sourceIPs": ["10.0.1.5", "54.212.83.236"],
"objectRef": {"resource": "endpoints"},
"responseStatus": {"code": 200},
"stage": "ResponseComplete",
"p_log_type": "Amazon.EKS.Audit"
}
- Name: AKS System Node from Azure IP
ExpectedResult: false
Log:
{
"p_log_type": "Azure.MonitorActivity",
"category": "kube-audit",
"operationName": "Microsoft.ContainerService/managedClusters/diagnosticLogs/Read",
"properties": {
"log": "{\"kind\":\"Event\",\"apiVersion\":\"audit.k8s.io/v1\",\"verb\":\"create\",\"user\":{\"username\":\"system:node:aks-nodepool1-12345-vmss000001\",\"groups\":[\"system:nodes\",\"system:authenticated\"]},\"sourceIPs\":[\"20.127.0.100\"],\"objectRef\":{\"resource\":\"leases\"},\"responseStatus\":{\"code\":201},\"stage\":\"ResponseComplete\"}"
},
"p_source_label": "aks-cluster",
"p_enrichment": {
"ipinfo_asn": {
"sourceIPs": [{
"asn": "AS8075",
"domain": "microsoft.com",
"name": "Microsoft Corporation",
"p_match": "20.127.0.100",
"type": "hosting"
}]
}
}
}
- Name: Invalid IP in sourceIPs (Skip Invalid)
ExpectedResult: false
Log:
{
"kind": "Event",
"verb": "get",
"user": {"username": "system:serviceaccount:kube-system:coredns"},
"sourceIPs": ["not-an-ip", "10.0.1.5"],
"objectRef": {"resource": "services"},
"responseStatus": {"code": 200},
"stage": "ResponseComplete",
"p_log_type": "Amazon.EKS.Audit"
}
- Name: system:anonymous from Public IP (Excluded - Not a Real Principal)
ExpectedResult: false
Log:
{
"kind": "Event",
"verb": "propfind",
"user": {"username": "system:anonymous"},
"sourceIPs": ["176.65.134.20"],
"objectRef": {"resource": ""},
"responseStatus": {"code": 403},
"stage": "ResponseComplete",
"p_log_type": "Amazon.EKS.Audit"
}
- Name: system:unauthenticated from Public IP (Excluded - Not a Real Principal)
ExpectedResult: false
Log:
{
"kind": "Event",
"verb": "get",
"user": {"username": "system:unauthenticated"},
"sourceIPs": ["1.2.3.4"],
"objectRef": {"resource": "apis"},
"responseStatus": {"code": 403},
"stage": "ResponseComplete",
"p_log_type": "Amazon.EKS.Audit"
}
- Name: GKE system:anonymous Health Check (Excluded - Anonymous)
ExpectedResult: false
Log:
{
"protoPayload": {
"authenticationInfo": {"principalEmail": "system:anonymous"},
"methodName": "io.k8s.readyz",
"requestMetadata": {
"callerIP": "108.177.75.65",
"callerSuppliedUserAgent": "GoogleHC/1.0"
},
"resourceName": "/readyz",
"serviceName": "k8s.io",
"status": {}
},
"p_log_type": "GCP.AuditLog",
"p_source_label": "gke-cluster"
}
Detection logic
Condition
not (stage is_not_null and stage ne "ResponseComplete")
responseStatus.code ne "403"
username starts_with "system:serviceaccount:" or username starts_with "system:node:" or username starts_with "eks:" or username starts_with "aks:"
sourceIPs is_not_null
not ((p_log_type contains "Amazon.EKS" and (username in ["eks:addon-manager", "eks:node-manager"] or (username starts_with "system:node:" and user.groups contains "system:nodes" and user.groups contains "system:authenticated"))) or (p_log_type contains "Azure.MonitorActivity" and (username in ["system:serviceaccount:kube-system:csi-azuredisk-node-sa", "system:serviceaccount:kube-system:csi-azurefile-node-sa", "system:serviceaccount:kube-system:csi-secrets-store-provider-azure", "system:serviceaccount:kube-system:cloud-node-manager"] or username starts_with "system:node:")) or (p_log_type contains "GCP.AuditLog" and (username starts_with "system:node:" or (username eq "system:anonymous" and protoPayload.requestMetadata.callerSuppliedUserAgent in ["GoogleKubernetesEngineFrontend", "GoogleHC/1.0"] and (protoPayload.resourceName ends_with "readyz" or requestURI ends_with "readyz" or protoPayload.resourceName ends_with "livez" or requestURI ends_with "livez" or protoPayload.resourceName ends_with "healthz" or requestURI ends_with "healthz")))))
This rule also runs imperative logic the parser cannot express as a filter; the conditions above are the structured part it could extract.
Exclusions
Top-level NOT(...) conjuncts: predicates this rule actively suppresses.
| Field | Kind | Excluded values |
|---|---|---|
protoPayload.resourceName | ends_with | healthz |
protoPayload.resourceName | ends_with | livez |
protoPayload.resourceName | ends_with | readyz |
requestURI | ends_with | healthz |
requestURI | ends_with | livez |
requestURI | ends_with | readyz |
protoPayload.requestMetadata.callerSuppliedUserAgent | in | GoogleHC/1.0, GoogleKubernetesEngineFrontend |
username | eq | system:anonymous |
username | starts_with | system:node: |
p_log_type | contains | GCP.AuditLog |
user.groups | contains | system:authenticated |
user.groups | contains | system:nodes |
username | starts_with | system:node: |
username | in | eks:addon-manager, eks:node-manager |
p_log_type | contains | Amazon.EKS |
username | starts_with | system:node: |
username | in | system:serviceaccount:kube-system:cloud-node-manager, system:serviceaccount:kube-system:csi-azuredisk-node-sa, system:serviceaccount:kube-system:csi-azurefile-node-sa, system:serviceaccount:kube-system:csi-secrets-store-provider-azure |
p_log_type | contains | Azure.MonitorActivity |
stage | is_not_null | |
stage | ne | ResponseComplete |
Indicators
Each row is a field, operator, and value that the rule matches. The corpus column counts how many other rules in the catalog look for the same combination: high numbers point to widely-used, community-vetted indicators. Blank or 1 shows that the indicator is specific to this rule.
| Field | Kind | Values |
|---|---|---|
responseStatus.code | ne |
|
sourceIPs | is_not_null | |
username | starts_with |
|
Output fields
Fields the rule emits when it matches. Chronicle authors list these in the outcome block; they appear on the detection and $risk_score drives alerting. Sentinel / Defender XDR rules build them up through project / summarize / extend stages. Sentinel maps these into alert fields via entityMappings and customDetails; Defender XDR custom detections surface them as alert fields directly.
| Field | Source |
|---|---|
username | |
sourceIPs | |
userAgent | |
namespace | |
verb | |
resource | |
requestURI | |
responseStatus | |
cluster | p_source_label |