8 minutes
Kubernetes-Native Container Security with Shell Scripting
As organizations accelerate their adoption of cloud-native infrastructure, Kubernetes has become the de facto standard for container orchestration. But with that ubiquity comes a critical responsibility: securing workloads running at scale. While Kubernetes ships with a strong set of built-in security primitives, the real-world security posture of a cluster often depends on how well those primitives are applied — and automated.
Shell scripting is one of the most practical tools in a platform engineer’s security toolkit. In this post, we’ll walk through how to use Bash (and compare it with PowerShell) to enforce container security policies, automate compliance checks, and harden your Kubernetes environment.

Why Shell Scripting for Kubernetes Security?
Kubernetes provides several native security mechanisms out of the box:
- Network Policies – Restrict pod-to-pod and pod-to-external traffic
- Pod Security Admission (PSA) – Enforce security standards at the namespace level
- RBAC – Control who can do what within the cluster
- Secrets Management – Store and inject sensitive data securely
However, these tools are building blocks — not a complete security solution. Organizations often need custom enforcement logic that reflects their specific threat models, compliance requirements (PCI-DSS, SOC 2, HIPAA), or internal platform standards.
Shell scripting bridges that gap by allowing you to:
- Automate repetitive security audits across namespaces and clusters
- Enforce policies declaratively without deploying additional tooling
- Reduce human error through consistent, testable scripts
- Integrate with CI/CD pipelines to catch misconfigurations before they reach production
Prerequisites
Before diving in, make sure you have the following:
- A working Kubernetes cluster (local via
minikube/kindor remote) kubectlinstalled and configured with appropriate permissions- Basic familiarity with Bash scripting
jqinstalled for JSON parsing (apt install jqorbrew install jq)- (Optional) PowerShell 7+ for cross-platform PowerShell scripts
Bash Scripts for Kubernetes Security
1. Enforce the Latest Container Image Version
Running outdated container images is one of the most common sources of known vulnerabilities. The script below checks all pods in a given namespace and updates any container that isn’t using the latest tagged version from Docker Hub.
#!/bin/bash
# enforce-latest-image.sh
# Ensures all pods in a namespace use the latest tag of a specified image.
set -euo pipefail
NAMESPACE="${1:-default}"
IMAGE="${2:-nginx}"
echo "[*] Fetching latest tag for image: $IMAGE"
LATEST_TAG=$(curl -s "https://registry.hub.docker.com/v1/repositories/${IMAGE}/tags" \
| jq -r 'first(.[].name)')
if [[ -z "$LATEST_TAG" ]]; then
echo "[!] Could not retrieve latest tag for $IMAGE. Exiting."
exit 1
fi
echo "[*] Latest tag: $LATEST_TAG"
for POD in $(kubectl get pods -n "$NAMESPACE" \
-o jsonpath='{range .items[*]}{.metadata.name}{"\n"}{end}'); do
CONTAINER_IMAGE=$(kubectl get pod "$POD" -n "$NAMESPACE" \
-o jsonpath="{.spec.containers[?(@.name=='${IMAGE}')].image}")
if [[ "$CONTAINER_IMAGE" != "${IMAGE}:${LATEST_TAG}" ]]; then
echo "[~] Updating $POD: $CONTAINER_IMAGE -> ${IMAGE}:${LATEST_TAG}"
kubectl set image "pod/$POD" "${IMAGE}=${IMAGE}:${LATEST_TAG}" -n "$NAMESPACE"
else
echo "[✓] $POD is already up to date"
fi
done
Note: In production, prefer using an image digest (e.g.,
nginx@sha256:abc123...) instead of a mutable tag likelatest. Tags can be overwritten; digests are immutable.
2. Detect Containers Running as Root
Containers running as the root user (UID 0) represent a significant security risk. If an attacker escapes the container, they may inherit root privileges on the host node. The following script audits all running pods and flags any container that lacks a runAsNonRoot: true policy.
#!/bin/bash
# detect-root-containers.sh
# Flags containers that may be running as root.
set -euo pipefail
NAMESPACE="${1:-default}"
FOUND=0
echo "[*] Scanning namespace: $NAMESPACE"
for POD in $(kubectl get pods -n "$NAMESPACE" \
-o jsonpath='{range .items[*]}{.metadata.name}{"\n"}{end}'); do
CONTAINERS=$(kubectl get pod "$POD" -n "$NAMESPACE" \
-o jsonpath='{range .spec.containers[*]}{.name}{"\n"}{end}')
for CONTAINER in $CONTAINERS; do
RUN_AS_NON_ROOT=$(kubectl get pod "$POD" -n "$NAMESPACE" \
-o jsonpath="{.spec.containers[?(@.name=='${CONTAINER}')].securityContext.runAsNonRoot}")
RUN_AS_USER=$(kubectl get pod "$POD" -n "$NAMESPACE" \
-o jsonpath="{.spec.containers[?(@.name=='${CONTAINER}')].securityContext.runAsUser}")
if [[ "$RUN_AS_NON_ROOT" != "true" ]] || [[ "$RUN_AS_USER" == "0" ]]; then
echo "[!] ALERT: Container '$CONTAINER' in pod '$POD' may be running as root"
FOUND=$((FOUND + 1))
fi
done
done
echo ""
echo "[*] Scan complete. $FOUND potential root container(s) found."
To remediate flagged containers, add a securityContext block to your pod spec:
securityContext:
runAsNonRoot: true
runAsUser: 1000
allowPrivilegeEscalation: false
3. Enforce Pod Security Annotations
While Pod Security Admission (PSA) is the modern approach, many clusters still rely on annotations for compatibility with older tooling. This script ensures all pods in a namespace carry the required seccomp profile annotation.
#!/bin/bash
# enforce-pod-security-policy.sh
# Annotates pods missing a required seccomp security policy.
set -euo pipefail
NAMESPACE="${1:-default}"
POLICY="${2:-runtime/default}"
ANNOTATION_KEY="seccomp.security.alpha.kubernetes.io/pod"
echo "[*] Enforcing seccomp policy '$POLICY' in namespace '$NAMESPACE'"
for POD in $(kubectl get pods -n "$NAMESPACE" \
-o jsonpath='{range .items[*]}{.metadata.name}{"\n"}{end}'); do
CURRENT_POLICY=$(kubectl get pod "$POD" -n "$NAMESPACE" \
-o jsonpath="{.metadata.annotations['${ANNOTATION_KEY}']}" 2>/dev/null || echo "")
if [[ "$CURRENT_POLICY" != "$POLICY" ]]; then
echo "[~] Annotating pod '$POD': $ANNOTATION_KEY=$POLICY"
kubectl annotate pod "$POD" -n "$NAMESPACE" \
"${ANNOTATION_KEY}=${POLICY}" --overwrite
else
echo "[✓] Pod '$POD' already has correct policy"
fi
done
Heads up: The
seccomp.security.alpha.kubernetes.ioannotation is deprecated in Kubernetes v1.27+. For modern clusters, use thesecurityContext.seccompProfilefield in your pod spec instead.
4. Audit Pods with Privileged Containers
Privileged containers can access all devices on the host and bypass most security mechanisms. This script lists any pod running a privileged container.
#!/bin/bash
# audit-privileged-containers.sh
# Lists any containers running in privileged mode.
set -euo pipefail
NAMESPACE="${1:---all-namespaces}"
NS_FLAG="-n $NAMESPACE"
[[ "$NAMESPACE" == "--all-namespaces" ]] && NS_FLAG="--all-namespaces"
echo "[*] Auditing privileged containers ($NAMESPACE)..."
kubectl get pods $NS_FLAG -o json | jq -r '
.items[] |
.metadata.namespace as $ns |
.metadata.name as $pod |
.spec.containers[] |
select(.securityContext.privileged == true) |
"[!] PRIVILEGED: \($ns)/\($pod) -> container: \(.name)"
'
Comparing Bash and PowerShell for Kubernetes Security
Bash is the default choice in Linux and macOS environments — and since most Kubernetes nodes run Linux, it’s usually the natural fit. But PowerShell has become a compelling option, especially for teams with a Windows-heavy background or mixed OS environments.
Key Differences at a Glance
| Feature | Bash | PowerShell |
|---|---|---|
| Default OS | Linux / macOS | Windows (also available on Linux/macOS) |
| Output model | Text streams | Structured objects (.NET) |
| JSON handling | Requires jq |
Native via ConvertFrom-Json |
| Kubernetes modules | kubectl only |
kubectl + PSKubernetes module |
| Learning curve | Unix-familiar | Windows/OOP-familiar |
| CI/CD support | Universal | Good, growing |
PowerShell Example: List All Pods in a Namespace
# list-pods.ps1
$Namespace = "default"
$AllPods = kubectl get pods -n $Namespace -o json | ConvertFrom-Json
foreach ($Pod in $AllPods.items) {
Write-Host "$($Pod.metadata.namespace)/$($Pod.metadata.name)"
}
Because PowerShell works natively with objects, you skip the jq dependency entirely and can chain property access naturally.
PowerShell Example: Enforce Pod Security Policy via PSKubernetes
The PSKubernetes module wraps kubectl with idiomatic PowerShell cmdlets:
# enforce-security-policy.ps1
Import-Module PSKubernetes
$Namespace = "default"
$Policy = "runtime/default"
$AnnotationKey = "seccomp.security.alpha.kubernetes.io/pod"
$PodList = Get-KubernetesPod -Namespace $Namespace
foreach ($Pod in $PodList) {
$CurrentPolicy = $Pod.metadata.annotations.$AnnotationKey
if ($CurrentPolicy -ne $Policy) {
Write-Host "[~] Updating $($Pod.metadata.name)"
Set-KubernetesPod -Namespace $Namespace -Name $Pod.metadata.name `
-Annotation @{ $AnnotationKey = $Policy }
} else {
Write-Host "[✓] $($Pod.metadata.name) already compliant"
}
}
When to Choose Which
Choose Bash when:
- Your infrastructure runs on Linux-based nodes (most clusters)
- You’re integrating with Unix-native CI tools (GitHub Actions, GitLab CI, Jenkins)
- Your team has strong Unix/Linux fluency
- You want minimal external dependencies
Choose PowerShell when:
- Your team comes from a Windows/Azure background
- You need to integrate Kubernetes security with Windows-based tooling
- You’re managing hybrid Windows/Linux node pools
- You want native object-oriented scripting without
jq
Both languages are capable. The best choice is typically the one your team will actually maintain.
Security Best Practices When Writing These Scripts
Regardless of which shell you choose, keep the following practices in mind:
- Use
set -euo pipefailin Bash — This ensures your script exits on any error and treats unset variables as failures. - Avoid hardcoded credentials — Use Kubernetes ServiceAccounts with minimal RBAC permissions instead of embedding kubeconfig credentials in scripts.
- Parameterize your scripts — Accept namespace and policy values as arguments rather than hardcoding them. This makes scripts reusable and testable.
- Log all changes — Append script output to an audit log so you have a record of what was modified and when.
- Test in a non-production namespace first — Even simple
kubectl set imageorkubectl annotatecommands can have unintended side effects on running workloads. - Pin image versions by digest, not tag — Tags are mutable. Digests (
image@sha256:...) guarantee you’re running exactly the image you audited. - Integrate with your CI/CD pipeline — Run security audit scripts as a post-deploy gate to catch policy drift before it accumulates.
Going Further: Beyond Shell Scripts
Shell scripts are an excellent starting point, but as your security requirements grow, you may want to explore purpose-built Kubernetes security tools:
- Kyverno — A Kubernetes-native policy engine that enforces policies as Kubernetes resources (no scripting required)
- OPA/Gatekeeper — Open Policy Agent integration for fine-grained admission control
- Falco — Runtime threat detection for containers and Kubernetes
- Trivy — Vulnerability scanning for container images, which you can invoke directly from shell scripts
- kube-bench — Checks your cluster configuration against CIS Kubernetes Benchmarks
Shell scripting and dedicated tools aren’t mutually exclusive — you can script the invocation of trivy or kube-bench as part of a broader security automation pipeline.
Conclusion
Shell scripting is a powerful and accessible way to extend Kubernetes security beyond its defaults. With a few well-crafted Bash or PowerShell scripts, you can detect misconfigurations, enforce organizational policies, and automate compliance checks across your entire cluster fleet.
The key is to treat your security scripts like any other code: version-control them, test them, review them, and iterate on them as your security posture matures. Start with the examples above, adapt them to your environment, and progressively move toward policy-as-code tools as your needs scale.
Photo by Brandon Jaramillo on Unsplash.