You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: src/pentesting-cloud/gcp-security/gcp-services/gcp-containers-gke-and-composer-enum.md
+18-1Lines changed: 18 additions & 1 deletion
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -74,6 +74,24 @@ You can read more about `gcloud` for containers [here](https://cloud.google.com/
74
74
75
75
This is a simple script to enumerate kubernetes in GCP: [https://gitlab.com/gitlab-com/gl-security/security-operations/gl-redteam/gcp_k8s_enum](https://gitlab.com/gitlab-com/gl-security/security-operations/gl-redteam/gcp_k8s_enum)
76
76
77
+
### Current GKE identity and metadata checks
78
+
79
+
When reviewing modern GKE clusters, separate Google Cloud IAM permissions, Kubernetes RBAC, pod workload identity, and node credentials. A Google principal can often retrieve cluster endpoint data with `container.clusters.get`, but the resulting Kubernetes requests still need to pass GKE/Kubernetes authorization and any network restrictions such as private endpoints or authorized networks.
80
+
81
+
Workload Identity Federation for GKE is the preferred way for pods to access Google Cloud APIs. Check whether the cluster has a workload pool and whether Kubernetes service accounts are mapped directly as IAM principals or are allowed to impersonate IAM service accounts:
kubectl get serviceaccounts -A -o yaml | grep -n 'iam.gke.io' -B 5 -A 8
88
+
kubectl get pods -A -o custom-columns='NS:.metadata.namespace,NAME:.metadata.name,SA:.spec.serviceAccountName,NODE:.spec.nodeName'
89
+
```
90
+
91
+
If a service account has the annotation `iam.gke.io/gcp-service-account`, review the IAM service account policy for `roles/iam.workloadIdentityUser` grants to Kubernetes service account principals. Also check IAM allow policies for direct workload identity principals or broad principal sets.
92
+
93
+
Metadata access depends on cluster mode, node pool configuration, and workload settings. Do not assume every pod can steal the node service account. In Workload Identity-enabled environments, ordinary pods should use the GKE metadata server to obtain the workload identity intended for their Kubernetes service account. Node compromise, `hostNetwork` pods in some Standard configurations, and legacy node metadata exposure can still change the blast radius, so verify the actual node pool metadata mode, node service account, OAuth scopes, and pod placement.
94
+
77
95
### TLS Boostrap Privilege Escalation
78
96
79
97
Initially this privilege escalation technique allowed to **privesc inside the GKE cluster** effectively allowing an attacker to **fully compromise it**.
@@ -104,4 +122,3 @@ Even if the API **doesn't allow to modify resources**, it could be possible to f
There is a special kind of a Kubernetes secret of type **kubernetes.io/service-account-token** which stores serviceaccount tokens.
360
+
There is a special kind of Kubernetes Secret of type **kubernetes.io/service-account-token** which stores service account tokens. Modern Kubernetes versions do **not** automatically create one long-lived Secret for every ServiceAccount; projected, bound TokenRequest tokens are the normal workload path. However, manually created service account token Secrets are still supported, and upgraded or legacy clusters may still contain long-lived token Secrets. Current clusters can also mark unused auto-generated legacy token Secrets invalid and eventually clean them up, leaving labels such as `kubernetes.io/legacy-token-invalid-since` and `kubernetes.io/legacy-token-last-used`.
361
+
361
362
If you have permissions to create and read secrets, and you also know the serviceaccount's name, you can create a secret as follows and then steal the victim serviceaccount's token from it:
Copy file name to clipboardExpand all lines: src/pentesting-cloud/kubernetes-security/exposing-services-in-kubernetes.md
+71-13Lines changed: 71 additions & 13 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -95,6 +95,19 @@ spec:
95
95
96
96
If you **don't specify** the **nodePort** in the yaml (it's the port that will be opened) a port in the **range 30000–32767 will be used**.
97
97
98
+
When reviewing NodePort or LoadBalancer Services, also inspect traffic-policy fields because they change which nodes and backends are useful from a given source:
- `externalTrafficPolicy: Local` preserves the original client source IP for NodePort/LoadBalancer traffic and avoids forwarding to endpoints on other nodes. A node without a local ready endpoint may drop the traffic even if the Service has endpoints elsewhere.
106
+
- `externalTrafficPolicy: Cluster` is the default and can forward through any node, but backend logs may see node IPs instead of the real external client IP.
107
+
- `internalTrafficPolicy: Local` limits in-cluster Service traffic to endpoints local to the source node. This is locality routing, not an authorization boundary.
108
+
- `sessionAffinity: ClientIP` can make repeated tests from one client hit the same backend, hiding other ready endpoints during manual checks.
109
+
- `trafficDistribution`and EndpointSlice topology hints can prefer same-zone or same-node endpoints on newer clusters; treat them as routing preferences rather than hard security policy.
110
+
98
111
### LoadBalancer
99
112
100
113
Exposes the Service externally **using a cloud provider's load balancer**. On GKE, this will spin up a [Network Load Balancer](https://cloud.google.com/compute/docs/load-balancing/network/) that will give you a single IP address that will forward all traffic to your service. In AWS it will launch a Load Balancer.
@@ -116,6 +129,8 @@ kubectl get services --all-namespaces -o=custom-columns='NAMESPACE:.metadata.nam
116
129
117
130
Traffic that ingresses into the cluster with the **external IP** (as **destination IP**), on the Service port, will be **routed to one of the Service endpoints**. `externalIPs` are not managed by Kubernetes and are the responsibility of the cluster administrator.
118
131
132
+
`externalIPs`is a sensitive route-control field because a user who can set it might claim traffic for an IP address the Service owner should not control if the surrounding network routes that IP to the cluster. Kubernetes announced the deprecation and planned removal of Service `externalIPs` in v1.36, so prefer controller-owned exposure mechanisms such as LoadBalancer integrations or Gateway API where possible, and restrict/admit this field carefully while it still exists.
133
+
119
134
In the Service spec, `externalIPs` can be specified along with any of the `ServiceTypes`. In the example below, "`my-service`" can be accessed by clients on "`80.11.12.10:80`" (`externalIP:port`)
120
135
121
136
```yaml
@@ -160,6 +175,21 @@ List all ExternalNames:
160
175
kubectl get services --all-namespaces | grep ExternalName
161
176
```
162
177
178
+
### EndpointSlices
179
+
180
+
EndpointSlices show the concrete backend addresses and ports that a Service currently routes to. They are especially useful when a Service has no selector, when labels do not explain the traffic path, or when only some backends are ready.
181
+
182
+
List EndpointSlices associated with Services:
183
+
184
+
```bash
185
+
kubectl get endpointslices --all-namespaces
186
+
kubectl get endpointslice -n <namespace> -l kubernetes.io/service-name=<service-name> -o yaml
187
+
kubectl get endpointslice -n <namespace> -l kubernetes.io/service-name=<service-name> \
When reviewing exposure, compare the Service selector with the EndpointSlice `targetRef`, endpoint addresses, readiness conditions, and ports. A selectorless Service can be paired with manually managed EndpointSlices and route traffic to non-Pod or unexpected destinations.
192
+
163
193
### Ingress
164
194
165
195
Unlike all the above examples, **Ingress is NOT a type of service**. Instead, it sits **in front of multiple services and act as a “smart router”** or entrypoint into your cluster.
@@ -171,28 +201,37 @@ The default GKE ingress controller will spin up a [HTTP(S) Load Balancer](https:
171
201
The YAML for a Ingress object on GKE with a [L7 HTTP Load Balancer](https://cloud.google.com/compute/docs/load-balancing/http/) might look like this:
172
202
173
203
```yaml
174
-
apiVersion: extensions/v1beta1
204
+
apiVersion: networking.k8s.io/v1
175
205
kind: Ingress
176
206
metadata:
177
207
name: my-ingress
178
208
spec:
179
-
backend:
180
-
serviceName: other
181
-
servicePort: 8080
209
+
defaultBackend:
210
+
service:
211
+
name: other
212
+
port:
213
+
number: 8080
182
214
rules:
183
215
- host: foo.mydomain.com
184
216
http:
185
217
paths:
186
-
- backend:
187
-
serviceName: foo
188
-
servicePort: 8080
218
+
- path: /
219
+
pathType: Prefix
220
+
backend:
221
+
service:
222
+
name: foo
223
+
port:
224
+
number: 8080
189
225
- host: mydomain.com
190
226
http:
191
227
paths:
192
-
- path: /bar/*
228
+
- path: /bar
229
+
pathType: Prefix
193
230
backend:
194
-
serviceName: bar
195
-
servicePort: 8080
231
+
service:
232
+
name: bar
233
+
port:
234
+
number: 8080
196
235
```
197
236
198
237
List all the ingresses:
@@ -207,12 +246,31 @@ Although in this case it's better to get the info of each one by one to read it
207
246
kubectl get ingresses --all-namespaces -o=yaml
208
247
```
209
248
249
+
### Gateway API
250
+
251
+
Gateway API is the newer Kubernetes API for exposing Services. It separates infrastructure-owned Gateway objects from application-owned Route objects such as HTTPRoute. This is useful for delegation, but it also means exposure can be split across namespaces.
252
+
253
+
List Gateway API exposure objects:
254
+
255
+
```bash
256
+
kubectl get gatewayclasses
257
+
kubectl get gateways --all-namespaces
258
+
kubectl get httproutes --all-namespaces
259
+
kubectl get gateway -n <namespace> <gateway-name> -o yaml
260
+
kubectl get httproute -n <namespace> <route-name> -o yaml
261
+
```
262
+
263
+
Check Gateway listeners, allowed route namespaces, Route `parentRefs`, hostnames, filters, backend references, and status conditions such as whether the route was accepted. A Route that is accepted by a shared Gateway can expose a backend even when no legacy Ingress object exists.
Copy file name to clipboardExpand all lines: src/pentesting-cloud/kubernetes-security/kubernetes-basics.md
+4-2Lines changed: 4 additions & 2 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -39,6 +39,8 @@ Note that as the might be several nodes (running several pods), there might also
39
39
40
40
When a pod creates data that shouldn't be lost when the pod disappear it should be stored in a physical volume. **Kubernetes allow to attach a volume to a pod to persist the data**. The volume can be in the local machine or in a **remote storage**. If you are running pods in different physical nodes you should use a remote storage so all the pods can access it.
41
41
42
+
Kubernetes also supports **image volumes** in recent versions. An `image` volume mounts an OCI image or artifact as a **read-only** filesystem source inside the Pod, using fields such as `volumes[].image.reference` and `volumes[].image.pullPolicy`. The kubelet pulls the artifact with the same credential sources used for container images, including node credentials, Pod `imagePullSecrets`, and ServiceAccount `imagePullSecrets`. During a security review, treat image volumes as runtime inputs and supply-chain dependencies: check whether the reference is pinned by digest, which registry credentials can fetch it, where it is mounted, and whether `subPath` limits the visible directory.
43
+
42
44
**Other configurations:**
43
45
44
46
-**ConfigMap**: You can configure **URLs** to access services. The pod will obtain data from here to know how to communicate with the rest of the services (pods). Note that this is not the recommended place to save credentials!
- [Argo CD projects (`AppProject` restrictions)](https://argo-cd.readthedocs.io/en/latest/user-guide/projects/)
640
642
- [Argo CD multiple sources / external Helm value files](https://argo-cd.readthedocs.io/en/latest/user-guide/multiple_sources/#helm-value-files-from-external-git-repository)
Copy file name to clipboardExpand all lines: src/pentesting-cloud/kubernetes-security/kubernetes-enumeration.md
+72-11Lines changed: 72 additions & 11 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -192,6 +192,23 @@ k api-resources --namespaced=false #Resources NOT specific to a namespace
192
192
{{#endtab }}
193
193
{{#endtabs }}
194
194
195
+
### Object metadata worth checking
196
+
197
+
When you can read an object, export the full YAML or JSON instead of relying only on table output or `describe`. The most useful security context is often in generic object fields that exist across many resource types:
kubectl get pods -A -o custom-columns='NS:.metadata.namespace,NAME:.metadata.name,SA:.spec.serviceAccountName,NODE:.spec.nodeName,PHASE:.status.phase'
203
+
```
204
+
205
+
-`metadata.uid`, `name`, `namespace`, `apiVersion` and `kind` identify the exact object and avoid confusion between objects with the same name in different namespaces or API groups.
206
+
-`metadata.labels` and selectors connect Services, Deployments, ReplicaSets, Pods, NetworkPolicies and automation. Following selectors is often the fastest way to identify the real backend pods for a Service.
207
+
-`metadata.annotations` can leak operational context such as ingress behavior, cloud load balancer settings, GitOps or Helm metadata, policy exemptions, and service mesh configuration. They should not contain secrets, but real clusters often expose useful clues there.
208
+
-`metadata.ownerReferences` shows controller lineage. If a Pod is owned by a ReplicaSet owned by a Deployment, changing or deleting only the Pod usually does not fix the source.
209
+
-`metadata.finalizers` and `metadata.deletionTimestamp` explain resources stuck in deletion and can reveal cleanup controllers or persistence/disruption tricks.
210
+
-`status`, Events, and conditions can reveal node placement, pod IPs, image IDs, failure messages, scheduling issues, admission denials, and controller progress. They are useful clues, but audit logs are still required to prove who performed an action.
**DaeamonSets**allows to ensure that a **specific pod is running in all the nodes** of the cluster (or in the ones selected). If you delete the DaemonSet the pods managed by it will be also removed.
464
+
**DaemonSets** ensure that a **specific Pod is running on all selected nodes** of the cluster. If you delete the DaemonSet, the Pods managed by it will also be removed.
0 commit comments