Skip to main content

kubectl-kubesec

kubectl-kubesec is a kubectl plugin that integrates kubesec.io security scanning directly into your Kubernetes workflow. It analyzes Kubernetes resources (Pods, Deployments, StatefulSets, DaemonSets) for security risks and provides actionable recommendations for hardening.

kubesec assigns a security score based on detected issues, making it easy to track security improvements over time.


How It Works

kubectl-kubesec sends Kubernetes resource definitions to the kubesec.io API (or a local kubesec instance) for analysis. The scanner evaluates:

  • Security context configurations
  • Container capabilities
  • Volume mount security
  • Resource limits and requests
  • Network configuration
  • Service account settings

Each finding affects the overall security score, with critical issues resulting in score deductions.


Installation

Using krew

kubectl krew install kubesec-scan

Manual Installation

# Download the plugin
curl -LO https://github.com/controlplaneio/kubectl-kubesec/releases/download/v2.0.0/kubectl-kubesec_linux_amd64.tar.gz

# Extract and install
tar xzf kubectl-kubesec_linux_amd64.tar.gz
chmod +x kubectl-kubesec
sudo mv kubectl-kubesec /usr/local/bin/

Usage Examples

Scan a Running Pod

kubectl kubesec-scan pod my-pod -n production

Scan a Deployment

kubectl kubesec-scan deployment my-deployment

Scan a DaemonSet

kubectl kubesec-scan daemonset my-daemonset -n kube-system

Scan a StatefulSet

kubectl kubesec-scan statefulset my-statefulset

Scan from YAML File

kubectl kubesec-scan -f deployment.yaml

Scan Multiple Resources

kubectl kubesec-scan -f ./manifests/

Example Output

[
{
"object": "Pod/my-app.production",
"valid": true,
"fileName": "STDIN",
"message": "",
"score": 3,
"scoring": {
"critical": [
{
"id": "Privileged",
"selector": "containers[] .securityContext .privileged == true",
"reason": "Privileged containers share namespaces with the host system",
"points": -30
}
],
"passed": [
{
"id": "ReadOnlyRootFilesystem",
"selector": "containers[] .securityContext .readOnlyRootFilesystem == true",
"reason": "Immutable container filesystems prevent runtime modifications",
"points": 1
},
{
"id": "RunAsNonRoot",
"selector": "containers[] .securityContext .runAsNonRoot == true",
"reason": "Force the container to run as a non-root user",
"points": 1
}
],
"advise": [
{
"id": "ApparmorAny",
"selector": ".metadata .annotations .\"container.apparmor.security.beta.kubernetes.io/nginx\"",
"reason": "AppArmor prevents execution of arbitrary code",
"points": 3
}
]
}
}
]

Understanding Scores

Score Components

CategoryDescriptionPoints
CriticalSevere security issues-30 to -10
PassedSecurity best practices followed+1 to +5
AdviseRecommended improvements+1 to +3 (if implemented)

Score Interpretation

ScoreAssessment
< 0Critical issues present, requires immediate attention
0-3Poor security posture, significant hardening needed
4-6Moderate security, some improvements recommended
7+Good security posture

Common Findings and Remediations

Critical: Privileged Container

Issue: Container runs in privileged mode

Remediation:

spec:
containers:
- name: my-container
securityContext:
privileged: false

Critical: No Security Context

Issue: Container lacks security context

Remediation:

spec:
containers:
- name: my-container
securityContext:
runAsNonRoot: true
runAsUser: 1000
readOnlyRootFilesystem: true
allowPrivilegeEscalation: false
capabilities:
drop:
- ALL

Advise: AppArmor Profile

Issue: No AppArmor profile specified

Remediation:

metadata:
annotations:
container.apparmor.security.beta.kubernetes.io/my-container: runtime/default

Advise: Resource Limits

Issue: No resource limits specified

Remediation:

spec:
containers:
- name: my-container
resources:
limits:
cpu: "1"
memory: "512Mi"
requests:
cpu: "100m"
memory: "128Mi"

CI/CD Integration

GitHub Actions

name: Security Scan
on: [push, pull_request]

jobs:
kubesec:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- name: Run kubesec scanner
uses: controlplaneio/kubesec-action@v2
with:
input: deployment.yaml

- name: Check score
run: |
score=$(cat kubesec-results.json | jq '.[0].score')
if [ "$score" -lt 0 ]; then
echo "Security score too low: $score"
exit 1
fi

Shell Script for CI

#!/bin/bash
set -e

# Scan all YAML files
for file in $(find ./kubernetes -name "*.yaml"); do
echo "Scanning $file..."
result=$(kubectl kubesec-scan -f "$file" 2>/dev/null || true)
score=$(echo "$result" | jq '.[0].score // 0')

if [ "$score" -lt 0 ]; then
echo "FAILED: $file has critical security issues (score: $score)"
echo "$result" | jq '.[0].scoring.critical'
exit 1
fi
done

echo "All resources passed security scan"

Local kubesec Server

For air-gapped environments or to avoid sending manifests externally:

# Run kubesec locally
docker run -d -p 8080:8080 kubesec/kubesec http 8080

# Use local server
kubectl kubesec-scan pod my-pod --server http://localhost:8080

Best Practices

  • Set minimum scores: Require a minimum security score for production deployments.
  • Integrate into CI/CD: Block merges or deployments that fail security scans.
  • Address critical issues first: Focus on negative-scoring findings before optimizing.
  • Use local scanning: Run a local kubesec server for sensitive environments.
  • Combine with other tools: Use alongside kube-linter and kube-score for comprehensive validation.

References

This article is based on information from the following official sources:

  1. kubectl-kubesec GitHub Repository - Control Plane
  2. kubesec.io - kubesec.io
  3. Pod Security Standards - Kubernetes Documentation