From 339eaf5abadcb3a72b4e5b9d23eeb493413a84d6 Mon Sep 17 00:00:00 2001 From: Carlos Polop Date: Sat, 4 Jul 2026 13:36:42 +0200 Subject: [PATCH] changes --- .../exposing-services-in-kubernetes.md | 17 +- .../kubernetes-security/kubernetes-basics.md | 12 +- .../kubernetes-securitycontext-s.md | 8 +- theme/css/chrome.css | 232 +++++++++++++----- theme/index.hbs | 152 +++++++++--- 5 files changed, 320 insertions(+), 101 deletions(-) diff --git a/src/pentesting-cloud/kubernetes-security/exposing-services-in-kubernetes.md b/src/pentesting-cloud/kubernetes-security/exposing-services-in-kubernetes.md index da2c54fe3..5395873b6 100644 --- a/src/pentesting-cloud/kubernetes-security/exposing-services-in-kubernetes.md +++ b/src/pentesting-cloud/kubernetes-security/exposing-services-in-kubernetes.md @@ -95,6 +95,19 @@ spec: 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**. +When reviewing NodePort or LoadBalancer Services, also inspect traffic-policy fields because they change which nodes and backends are useful from a given source: + +```bash +kubectl get services --all-namespaces \ + -o custom-columns='NAMESPACE:.metadata.namespace,NAME:.metadata.name,TYPE:.spec.type,ETP:.spec.externalTrafficPolicy,ITP:.spec.internalTrafficPolicy,AFFINITY:.spec.sessionAffinity,DIST:.spec.trafficDistribution,NODEPORTS:.spec.ports[*].nodePort' +``` + +- `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. +- `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. +- `internalTrafficPolicy: Local` limits in-cluster Service traffic to endpoints local to the source node. This is locality routing, not an authorization boundary. +- `sessionAffinity: ClientIP` can make repeated tests from one client hit the same backend, hiding other ready endpoints during manual checks. +- `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. + ### LoadBalancer 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. @@ -254,8 +267,10 @@ Check Gateway listeners, allowed route namespaces, Route `parentRefs`, hostnames - [https://medium.com/google-cloud/kubernetes-nodeport-vs-loadbalancer-vs-ingress-when-should-i-use-what-922f010849e0](https://medium.com/google-cloud/kubernetes-nodeport-vs-loadbalancer-vs-ingress-when-should-i-use-what-922f010849e0) - [https://kubernetes.io/docs/concepts/services-networking/service/](https://kubernetes.io/docs/concepts/services-networking/service/) - [https://kubernetes.io/blog/2026/05/14/kubernetes-v1-36-deprecation-and-removal-of-service-externalips/](https://kubernetes.io/blog/2026/05/14/kubernetes-v1-36-deprecation-and-removal-of-service-externalips/) +- [https://kubernetes.io/docs/concepts/services-networking/service-traffic-policy/](https://kubernetes.io/docs/concepts/services-networking/service-traffic-policy/) +- [https://kubernetes.io/docs/tutorials/services/source-ip/](https://kubernetes.io/docs/tutorials/services/source-ip/) +- [https://kubernetes.io/docs/concepts/services-networking/topology-aware-routing/](https://kubernetes.io/docs/concepts/services-networking/topology-aware-routing/) - [https://kubernetes.io/docs/concepts/services-networking/endpoint-slices/](https://kubernetes.io/docs/concepts/services-networking/endpoint-slices/) - [https://gateway-api.sigs.k8s.io/](https://gateway-api.sigs.k8s.io/) {{#include ../../banners/hacktricks-training.md}} - diff --git a/src/pentesting-cloud/kubernetes-security/kubernetes-basics.md b/src/pentesting-cloud/kubernetes-security/kubernetes-basics.md index 50fe24675..e5412f576 100644 --- a/src/pentesting-cloud/kubernetes-security/kubernetes-basics.md +++ b/src/pentesting-cloud/kubernetes-security/kubernetes-basics.md @@ -39,6 +39,8 @@ Note that as the might be several nodes (running several pods), there might also 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. +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. + **Other configurations:** - **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! @@ -564,6 +566,12 @@ https://sickrov.github.io/ https://www.youtube.com/watch?v=X48VuDVv0do {{#endref}} +{{#ref}} +https://kubernetes.io/docs/concepts/storage/volumes/#image +{{#endref}} + +{{#ref}} +https://kubernetes.io/docs/tasks/configure-pod-container/image-volumes/ +{{#endref}} + {{#include ../../banners/hacktricks-training.md}} - - diff --git a/src/pentesting-cloud/kubernetes-security/kubernetes-hardening/kubernetes-securitycontext-s.md b/src/pentesting-cloud/kubernetes-security/kubernetes-hardening/kubernetes-securitycontext-s.md index 33203cf40..30860657f 100644 --- a/src/pentesting-cloud/kubernetes-security/kubernetes-hardening/kubernetes-securitycontext-s.md +++ b/src/pentesting-cloud/kubernetes-security/kubernetes-hardening/kubernetes-securitycontext-s.md @@ -67,6 +67,9 @@ High-risk combinations to prioritize: - `seccompProfile: Unconfined`, `procMount: Unmasked`, or missing runtime profiles on sensitive workloads. - Writable root filesystems or broad writable volume mounts in workloads that process untrusted input. - Missing CPU, memory, or ephemeral-storage requests and limits in multi-tenant namespaces. +- Missing or unrealistic pod-level `spec.resources` budgets, and principals with `patch` or `update` on the Pod `resize` subresource, because supported clusters can change running CPU and memory desired state without recreating the Pod. + +Resource controls are not part of `securityContext`, but review them in the same workload pass because they define the availability boundary. Modern Kubernetes can define CPU, memory, and hugepage budgets at Pod level under `spec.resources` in addition to container-level `resources`. A Pod with sidecars may be bounded by an aggregate Pod envelope even when one container has no individual limits, while local ephemeral storage still needs separate `ephemeral-storage` limits, `emptyDir.sizeLimit`, LimitRanges, and ResourceQuotas. Also compare desired resources in the Pod spec with `status.containerStatuses[].resources` after an in-place resize request; a failed or pending resize can leave the requested value in `spec` while the kubelet keeps the previous runtime allocation and reports a `PodResizePending` condition. For most application workloads, a good baseline is to run as a non-root UID, set `runAsNonRoot: true`, set `allowPrivilegeEscalation: false`, drop all capabilities and add back only the minimum required ones, use `seccompProfile: RuntimeDefault`, prefer a read-only root filesystem, and avoid host namespaces, hostPath mounts, and privileged mode. @@ -77,10 +80,11 @@ At cluster level, use [Pod Security Admission](https://kubernetes.io/docs/concep - [https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.23/#podsecuritycontext-v1-core](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.23/#podsecuritycontext-v1-core) - [https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.23/#securitycontext-v1-core](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.23/#securitycontext-v1-core) - [https://kubernetes.io/docs/tasks/configure-pod-container/security-context/](https://kubernetes.io/docs/tasks/configure-pod-container/security-context/) +- [https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/](https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/) - [https://kubernetes.io/docs/concepts/security/linux-kernel-security-constraints/](https://kubernetes.io/docs/concepts/security/linux-kernel-security-constraints/) - [https://kubernetes.io/docs/concepts/security/pod-security-standards/](https://kubernetes.io/docs/concepts/security/pod-security-standards/) - [https://kubernetes.io/docs/concepts/security/pod-security-admission/](https://kubernetes.io/docs/concepts/security/pod-security-admission/) +- [https://kubernetes.io/docs/tasks/configure-pod-container/assign-pod-level-resources/](https://kubernetes.io/docs/tasks/configure-pod-container/assign-pod-level-resources/) +- [https://kubernetes.io/docs/tasks/configure-pod-container/resize-container-resources/](https://kubernetes.io/docs/tasks/configure-pod-container/resize-container-resources/) {{#include ../../../banners/hacktricks-training.md}} - - diff --git a/theme/css/chrome.css b/theme/css/chrome.css index f546d1b28..30c444b2d 100644 --- a/theme/css/chrome.css +++ b/theme/css/chrome.css @@ -155,18 +155,80 @@ body.sidebar-visible #menu-bar { margin: 0 15px; display: flex; flex-wrap: wrap; - column-gap: 2.6rem; + column-gap: 1.25rem; align-items: center; } .right-buttons a { text-decoration: none; } +.menu-socials { + position: relative; + display: inline-flex; + align-items: center; +} +.menu-socials-toggle { + display: inline-flex; + align-items: center; + gap: 6px; + border: 0; + padding: 0; + font: inherit; + background: transparent; + cursor: pointer; +} +.menu-socials-caret { + font-size: 14px; + line-height: 1; + opacity: 0.9; +} +.menu-socials-popup { + position: absolute; + right: 0; + top: 30px; + z-index: 105; + border-radius: 5px; + font-size: 14px; + color: var(--fg); + background: var(--bg); + border: 1px solid var(--table-border-color); + margin: 0; + padding: 0; + display: none; + flex-direction: column; + min-width: 140px; + overflow: hidden; +} +.menu-socials:hover .menu-socials-popup, +.menu-socials:focus-within .menu-socials-popup { + display: flex; +} +.menu-socials-popup .menu-bar-link { + display: inline-flex; + align-items: center; + gap: 8px; + border: 0; + margin: 0; + padding: 8px 20px; + line-height: 25px; + white-space: nowrap; + text-align: start; + color: inherit; + background: inherit; + font-size: inherit; +} +.menu-socials-popup .menu-bar-link i { + width: 16px; + text-align: center; +} +.menu-socials-popup .menu-bar-link:hover { + background-color: var(--theme-hover); +} @media only screen and (min-width:800px) { #menubar-collapse { display: flex !important; flex-wrap: wrap; - column-gap: 2.6rem; + column-gap: 1.25rem; align-items: center; } @@ -289,6 +351,22 @@ body.sidebar-visible #menu-bar { overflow: auto; background: transparent; } +#menubar-collapse-popup.show-socials #menubar-socials-popup { + display: flex; +} +#menubar-socials-popup { + display: none; + flex-direction: column; + border-top: 1px solid var(--table-border-color); + background: transparent; +} +#menubar-collapse-popup #menubar-socials-popup .menu-bar-link { + padding-left: 34px; +} +#menubar-socials-popup .menu-bar-link i { + width: 16px; + text-align: center; +} #menubar-collapse-popup .menu-bar-link { border: 0; margin: 0; @@ -796,7 +874,7 @@ ul#searchresults li a span.teaser em { } } -@media only screen and (min-width:550px) { +@media only screen and (min-width:800px) { .sidebar-float-toggle { position: fixed; top: calc(var(--menu-bar-height) + 90px); @@ -841,15 +919,7 @@ ul#searchresults li a span.teaser em { margin-right: auto; } -#sidebar-toggle-anchor:not(:checked) ~ .page-wrapper .page #container .sidetoc { - position: fixed; - right: max(var(--page-padding), calc((100vw - var(--container-max-width)) / 2)); - top: 0; - width: 250px; - margin-top: 25px; -} - -@media only screen and (max-width:549px) { +@media only screen and (max-width:799px) { .sidebar-float-toggle { position: fixed; top: calc(var(--menu-bar-height) + 10px); @@ -877,14 +947,14 @@ ul#searchresults li a span.teaser em { } } -@media only screen and (max-width:549px) { +@media only screen and (max-width:799px) { #sidebar-toggle-anchor:checked ~ .page-wrapper .page #container .sidebar-float-toggle { left: calc(100vw - 34px); } } /* Sidebar */ -@media only screen and (min-width:550px) { +@media only screen and (min-width:800px) { .sidebar { position: sticky; top: 4rem; @@ -903,7 +973,7 @@ ul#searchresults li a span.teaser em { display: none !important; } } -@media only screen and (max-width:549px) { +@media only screen and (max-width:799px) { .sidebar { position: fixed; bottom: 0; @@ -919,7 +989,7 @@ ul#searchresults li a span.teaser em { z-index: 105; } } -@media only screen and (max-width:549px) { +@media only screen and (max-width:799px) { #sidebar { display: none; } @@ -935,11 +1005,26 @@ ul#searchresults li a span.teaser em { #sidebar-toggle-anchor:checked ~ .page-wrapper .page #container #sidebar { display: block !important; } + #sidebar-toggle-anchor:checked ~ .page-wrapper .page #container #sidebar .sidebar-scrollbox { + bottom: 58px; + } + #sidebar-toggle-anchor:checked ~ .page-wrapper .page #container #sidebar .sidebar-socials { + display: flex; + } #sidebar-toggle-anchor:checked ~ .page-wrapper .page #container #content { display: none !important; } + #sidebar-toggle-anchor:checked ~ .page-wrapper .page > .footer { + display: none !important; + } + #sidebar-toggle-anchor:not(:checked) ~ .page-wrapper .page #container #content { + display: block !important; + } + #sidebar-toggle-anchor:not(:checked) ~ .page-wrapper .page > .footer { + display: flex !important; + } } -@media only screen and (max-width:549px) { +@media only screen and (max-width:799px) { #sidebar { display: none !important; } @@ -990,6 +1075,11 @@ html:not(.sidebar-resizing) .sidebar { padding: 10px 10px; background-color: transparent; } +@media only screen and (min-width:800px) { + .sidebar .sidebar-scrollbox { + bottom: 58px; + } +} .sidebar .sidebar-resize-handle { position: absolute; cursor: col-resize; @@ -1187,9 +1277,75 @@ html:not(.sidebar-resizing) .sidebar { height: var(--footer-height); border-top: 1px solid var(--table-border-color); margin-top: 1rem; - align-content: center; + display: flex; + align-items: center; + justify-content: center; z-index: 106; } +.footer-socials { + display: flex; + align-items: center; + justify-content: center; + gap: 14px; +} +.footer-socials a, +.footer-socials a:visited { + display: inline-flex; + align-items: center; + justify-content: center; + width: 34px; + height: 34px; + border-radius: 9999px; + color: var(--icons); + text-decoration: none; + border: 1px solid var(--table-border-color); + background: color-mix(in srgb, var(--bg) 88%, transparent); +} +.footer-socials a:hover { + color: var(--icons-hover); + background: var(--theme-hover); +} +.footer-socials i { + font-size: 17px; +} +.sidebar-socials { + display: none; +} +@media only screen and (max-width:799px) { + .sidebar-socials { + position: absolute; + left: 10px; + right: 10px; + bottom: 12px; + padding-top: 10px; + border-top: 1px solid var(--table-border-color); + background: var(--bg); + z-index: 2; + } +} +@media only screen and (min-width:800px) { + .page > .footer { + display: none; + position: fixed; + left: max(0px, calc((100vw - var(--container-max-width)) / 2)); + bottom: 0; + width: var(--sidebar-width); + height: auto; + margin: 0; + padding: 10px; + box-sizing: border-box; + border-top: 1px solid var(--table-border-color); + background: var(--bg); + z-index: 107; + } + #sidebar-toggle-anchor:checked ~ .page-wrapper .page > .footer { + display: flex; + } + [dir=rtl] #sidebar-toggle-anchor:checked ~ .page-wrapper .page > .footer { + left: auto; + right: max(0px, calc((100vw - var(--container-max-width)) / 2)); + } +} .theme-wrapper--menubar { position: relative; display: flex; @@ -1230,46 +1386,6 @@ html:not(.sidebar-resizing) .sidebar { background-color: var(--theme-hover); } -.theme-wrapper--menubar .theme-orb { - position: absolute; - right: 6px; - top: 50%; - width: 8px; - height: 8px; - border-radius: 9999px; - background: radial-gradient(circle at 30% 30%, #ffffff, color-mix(in srgb, var(--links) 70%, transparent)); - opacity: 0.7; - transform: translateY(10px); - filter: blur(0.2px); - pointer-events: none; - animation: theme-orb-rise 4.2s ease-in-out infinite; -} - -.theme-wrapper--menubar .theme-side-icon { - font-size: 16px; - color: var(--icons); - opacity: 0.9; - line-height: 1; -} - -@keyframes theme-orb-rise { - 0% { - transform: translateY(10px); - opacity: 0; - } - 35% { - opacity: 0.6; - } - 70% { - transform: translateY(-22px); - opacity: 0.4; - } - 100% { - transform: translateY(-30px); - opacity: 0; - } -} - /* Details/Summary */ summary { diff --git a/theme/index.hbs b/theme/index.hbs index 2c1687ab1..cf44ba12f 100644 --- a/theme/index.hbs +++ b/theme/index.hbs @@ -100,7 +100,7 @@ var sidebar = null; var sidebar_toggle = document.getElementById("sidebar-toggle-anchor"); var body = document.querySelector("body"); - if (document.body.clientWidth >= 550) { + if (document.body.clientWidth >= 800) { try { sidebar = localStorage.getItem('mdbook-sidebar'); } catch(e) { } sidebar = sidebar || 'visible'; } else { @@ -160,18 +160,24 @@ HT Tools + + HT Friends + - Request Page update - - - Sponsor - - - Linkedin - - - X + Request update +
@@ -242,7 +256,6 @@
-
- -
{{#if print_enable}} @@ -287,6 +298,23 @@ +