mirror of
https://github.com/mukul975/Anthropic-Cybersecurity-Skills.git
synced 2026-08-28 04:09:40 +03:00
Initial commit - 611 cybersecurity skills across all subdomains
This commit is contained in:
@@ -0,0 +1,239 @@
|
||||
---
|
||||
name: implementing-rbac-hardening-for-kubernetes
|
||||
description: Harden Kubernetes Role-Based Access Control by implementing least-privilege policies, auditing role bindings, eliminating cluster-admin sprawl, and integrating external identity providers.
|
||||
domain: cybersecurity
|
||||
subdomain: container-security
|
||||
tags: [kubernetes, rbac, access-control, least-privilege, security-hardening, iam, oidc, service-accounts]
|
||||
version: "1.0"
|
||||
author: mahipal
|
||||
license: MIT
|
||||
---
|
||||
|
||||
# Implementing RBAC Hardening for Kubernetes
|
||||
|
||||
## Overview
|
||||
|
||||
Kubernetes RBAC regulates access to cluster resources based on roles assigned to users, groups, and service accounts. Default configurations often grant excessive permissions, and without active hardening, RBAC becomes a primary attack vector for privilege escalation, lateral movement, and data exfiltration. Hardening requires implementing least-privilege principles, eliminating unnecessary ClusterRole bindings, separating service accounts, integrating external identity providers, and continuous auditing.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- Kubernetes cluster v1.24+ with RBAC enabled (default since v1.6)
|
||||
- kubectl access with cluster-admin for initial audit
|
||||
- External identity provider (OIDC) for user authentication
|
||||
- Audit logging enabled on the API server
|
||||
|
||||
## Core Hardening Principles
|
||||
|
||||
### 1. Eliminate cluster-admin Sprawl
|
||||
|
||||
Audit and remove unnecessary cluster-admin bindings:
|
||||
|
||||
```bash
|
||||
# List all cluster-admin bindings
|
||||
kubectl get clusterrolebindings -o json | jq -r '
|
||||
.items[] |
|
||||
select(.roleRef.name == "cluster-admin") |
|
||||
"\(.metadata.name) -> \(.subjects[]? | "\(.kind)/\(.name) (\(.namespace // "cluster"))")"
|
||||
'
|
||||
```
|
||||
|
||||
### 2. Namespace-Scoped Roles Over ClusterRoles
|
||||
|
||||
Use Role and RoleBinding instead of ClusterRole and ClusterRoleBinding:
|
||||
|
||||
```yaml
|
||||
# Good: Namespace-scoped role
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: Role
|
||||
metadata:
|
||||
namespace: application
|
||||
name: app-developer
|
||||
rules:
|
||||
- apiGroups: ["apps"]
|
||||
resources: ["deployments"]
|
||||
verbs: ["get", "list", "watch", "create", "update", "patch"]
|
||||
- apiGroups: [""]
|
||||
resources: ["pods", "pods/log"]
|
||||
verbs: ["get", "list", "watch"]
|
||||
- apiGroups: [""]
|
||||
resources: ["configmaps"]
|
||||
verbs: ["get", "list"]
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: RoleBinding
|
||||
metadata:
|
||||
namespace: application
|
||||
name: app-developer-binding
|
||||
subjects:
|
||||
- kind: Group
|
||||
name: dev-team
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
roleRef:
|
||||
kind: Role
|
||||
name: app-developer
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
```
|
||||
|
||||
### 3. Dedicated Service Accounts Per Workload
|
||||
|
||||
```yaml
|
||||
apiVersion: v1
|
||||
kind: ServiceAccount
|
||||
metadata:
|
||||
name: payment-processor
|
||||
namespace: payments
|
||||
automountServiceAccountToken: false # Disable auto-mount
|
||||
---
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: payment-processor
|
||||
namespace: payments
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
serviceAccountName: payment-processor
|
||||
automountServiceAccountToken: true # Only mount when explicitly needed
|
||||
containers:
|
||||
- name: processor
|
||||
image: payments/processor:v2.1@sha256:abc...
|
||||
```
|
||||
|
||||
### 4. Restrict Dangerous Permissions
|
||||
|
||||
Block permissions that enable privilege escalation:
|
||||
|
||||
```yaml
|
||||
# Dangerous verbs/resources to restrict:
|
||||
# - secrets: get, list, watch (exposes all secrets in namespace)
|
||||
# - pods/exec: create (enables command execution in pods)
|
||||
# - pods: create with privileged securityContext
|
||||
# - serviceaccounts/token: create (generates new tokens)
|
||||
# - clusterroles/clusterrolebindings: create, update (self-escalation)
|
||||
# - nodes/proxy: create (bypasses API server authorization)
|
||||
|
||||
# Safe read-only role example
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRole
|
||||
metadata:
|
||||
name: security-viewer
|
||||
rules:
|
||||
- apiGroups: [""]
|
||||
resources: ["pods", "services", "namespaces", "nodes"]
|
||||
verbs: ["get", "list", "watch"]
|
||||
- apiGroups: ["apps"]
|
||||
resources: ["deployments", "daemonsets", "statefulsets"]
|
||||
verbs: ["get", "list", "watch"]
|
||||
- apiGroups: ["networking.k8s.io"]
|
||||
resources: ["networkpolicies"]
|
||||
verbs: ["get", "list", "watch"]
|
||||
```
|
||||
|
||||
### 5. OIDC Integration for User Authentication
|
||||
|
||||
```yaml
|
||||
# API server flags for OIDC integration
|
||||
apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
name: kube-apiserver
|
||||
spec:
|
||||
containers:
|
||||
- name: kube-apiserver
|
||||
command:
|
||||
- kube-apiserver
|
||||
- --oidc-issuer-url=https://idp.company.com
|
||||
- --oidc-client-id=kubernetes
|
||||
- --oidc-username-claim=email
|
||||
- --oidc-groups-claim=groups
|
||||
- --oidc-ca-file=/etc/kubernetes/pki/oidc-ca.crt
|
||||
```
|
||||
|
||||
## RBAC Audit Process
|
||||
|
||||
### Step 1: Enumerate All Bindings
|
||||
|
||||
```bash
|
||||
# All ClusterRoleBindings with subjects
|
||||
kubectl get clusterrolebindings -o json | jq -r '
|
||||
.items[] | select(.subjects != null) |
|
||||
.subjects[] as $s |
|
||||
"\(.metadata.name) | \(.roleRef.name) | \($s.kind)/\($s.name)"
|
||||
' | sort | column -t -s '|'
|
||||
|
||||
# All RoleBindings across namespaces
|
||||
kubectl get rolebindings --all-namespaces -o json | jq -r '
|
||||
.items[] | select(.subjects != null) |
|
||||
.subjects[] as $s |
|
||||
"\(.metadata.namespace) | \(.metadata.name) | \(.roleRef.name) | \($s.kind)/\($s.name)"
|
||||
' | sort | column -t -s '|'
|
||||
```
|
||||
|
||||
### Step 2: Identify Overprivileged Service Accounts
|
||||
|
||||
```bash
|
||||
# Find service accounts with cluster-admin or admin roles
|
||||
kubectl get clusterrolebindings -o json | jq -r '
|
||||
.items[] |
|
||||
select(.roleRef.name == "cluster-admin" or .roleRef.name == "admin") |
|
||||
select(.subjects[]?.kind == "ServiceAccount") |
|
||||
"\(.subjects[] | select(.kind == "ServiceAccount") | "\(.namespace)/\(.name)")"
|
||||
'
|
||||
```
|
||||
|
||||
### Step 3: Check Default Service Account Usage
|
||||
|
||||
```bash
|
||||
# Find pods using the default service account
|
||||
kubectl get pods --all-namespaces -o json | jq -r '
|
||||
.items[] |
|
||||
select(.spec.serviceAccountName == "default" or .spec.serviceAccountName == null) |
|
||||
"\(.metadata.namespace)/\(.metadata.name)"
|
||||
'
|
||||
```
|
||||
|
||||
### Step 4: Verify Token Auto-Mount
|
||||
|
||||
```bash
|
||||
# Find pods with auto-mounted service account tokens
|
||||
kubectl get pods --all-namespaces -o json | jq -r '
|
||||
.items[] |
|
||||
select(.spec.automountServiceAccountToken != false) |
|
||||
"\(.metadata.namespace)/\(.metadata.name) sa=\(.spec.serviceAccountName // "default")"
|
||||
'
|
||||
```
|
||||
|
||||
## Tooling
|
||||
|
||||
### rbac-lookup
|
||||
|
||||
```bash
|
||||
# Install rbac-lookup
|
||||
kubectl krew install rbac-lookup
|
||||
|
||||
# View RBAC for a specific user
|
||||
kubectl rbac-lookup developer@company.com
|
||||
|
||||
# View all RBAC bindings wide format
|
||||
kubectl rbac-lookup --kind user -o wide
|
||||
```
|
||||
|
||||
### rakkess (Review Access)
|
||||
|
||||
```bash
|
||||
# Install rakkess
|
||||
kubectl krew install access-matrix
|
||||
|
||||
# Show access matrix for current user
|
||||
kubectl access-matrix
|
||||
|
||||
# Show access for a specific service account
|
||||
kubectl access-matrix --sa payments:payment-processor
|
||||
```
|
||||
|
||||
## References
|
||||
|
||||
- [Kubernetes RBAC Documentation](https://kubernetes.io/docs/reference/access-authn-authz/rbac/)
|
||||
- [CIS Kubernetes Benchmark - RBAC Controls](https://www.cisecurity.org/benchmark/kubernetes)
|
||||
- [Kubernetes Security Hardening Guide 2025](https://sealos.io/blog/a-practical-guide-to-kubernetes-security-hardening-your-cluster-in-2025/)
|
||||
- [OWASP Kubernetes Security Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/Kubernetes_Security_Cheat_Sheet.html)
|
||||
@@ -0,0 +1,30 @@
|
||||
# RBAC Hardening Assessment Template
|
||||
|
||||
## Cluster Information
|
||||
| Field | Value |
|
||||
|-------|-------|
|
||||
| Cluster Name | |
|
||||
| Kubernetes Version | |
|
||||
| Assessment Date | |
|
||||
|
||||
## RBAC Audit Results
|
||||
| Metric | Count |
|
||||
|--------|-------|
|
||||
| ClusterRoleBindings | |
|
||||
| cluster-admin bindings | |
|
||||
| Wildcard permissions | |
|
||||
| Default SA bindings | |
|
||||
|
||||
## Hardening Checklist
|
||||
- [ ] Removed unnecessary cluster-admin bindings
|
||||
- [ ] All workloads use dedicated service accounts
|
||||
- [ ] automountServiceAccountToken disabled on default SAs
|
||||
- [ ] OIDC integration configured
|
||||
- [ ] RBAC monitoring and alerting active
|
||||
- [ ] Quarterly review process established
|
||||
|
||||
## Sign-Off
|
||||
| Role | Name | Date |
|
||||
|------|------|------|
|
||||
| Security Engineer | | |
|
||||
| Platform Lead | | |
|
||||
@@ -0,0 +1,18 @@
|
||||
# Standards - RBAC Hardening for Kubernetes
|
||||
|
||||
## CIS Kubernetes Benchmark v1.9
|
||||
- 5.1.1: Ensure cluster-admin role is only used where required
|
||||
- 5.1.2: Minimize access to secrets
|
||||
- 5.1.3: Minimize wildcard use in Roles and ClusterRoles
|
||||
- 5.1.4: Minimize access to create pods
|
||||
- 5.1.5: Ensure default service accounts are not actively used
|
||||
- 5.1.6: Ensure Service Account Tokens are only mounted where necessary
|
||||
|
||||
## NIST SP 800-190
|
||||
- Section 3.4: Orchestrator security -- access control hardening
|
||||
- Section 4.4: Countermeasures for orchestrator vulnerabilities
|
||||
|
||||
## MITRE ATT&CK
|
||||
- T1078.004: Valid Accounts: Cloud Accounts -- compromised service accounts
|
||||
- T1098: Account Manipulation -- RBAC escalation
|
||||
- T1069: Permission Groups Discovery -- enumerating RBAC bindings
|
||||
@@ -0,0 +1,17 @@
|
||||
# Workflows - RBAC Hardening
|
||||
|
||||
## Hardening Workflow
|
||||
1. Audit all existing ClusterRoleBindings and RoleBindings
|
||||
2. Identify overprivileged accounts (cluster-admin sprawl)
|
||||
3. Create namespace-scoped Roles with minimum required permissions
|
||||
4. Migrate workloads to dedicated service accounts
|
||||
5. Disable automountServiceAccountToken on default service accounts
|
||||
6. Integrate OIDC for user authentication
|
||||
7. Deploy RBAC monitoring and alerting
|
||||
8. Schedule quarterly RBAC reviews
|
||||
|
||||
## Continuous Compliance
|
||||
- Weekly: automated RBAC audit with rbac-lookup
|
||||
- Monthly: review new RoleBindings created in past 30 days
|
||||
- Quarterly: full access review with stakeholder sign-off
|
||||
- Annually: penetration test RBAC boundaries
|
||||
@@ -0,0 +1,193 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Kubernetes RBAC Audit and Hardening Tool
|
||||
|
||||
Audits RBAC configurations to identify overprivileged accounts,
|
||||
cluster-admin sprawl, default service account usage, and
|
||||
generates hardening recommendations.
|
||||
"""
|
||||
|
||||
import json
|
||||
import subprocess
|
||||
import sys
|
||||
import argparse
|
||||
from datetime import datetime
|
||||
from collections import defaultdict
|
||||
|
||||
|
||||
DANGEROUS_VERBS = {"*", "create", "update", "patch", "delete"}
|
||||
DANGEROUS_RESOURCES = {
|
||||
"secrets", "pods/exec", "clusterroles", "clusterrolebindings",
|
||||
"roles", "rolebindings", "serviceaccounts/token", "nodes/proxy"
|
||||
}
|
||||
DANGEROUS_API_GROUPS = {"*"}
|
||||
|
||||
|
||||
def run_kubectl(args: list[str]) -> str:
|
||||
try:
|
||||
result = subprocess.run(
|
||||
["kubectl"] + args, capture_output=True, text=True, timeout=30
|
||||
)
|
||||
return result.stdout.strip()
|
||||
except (subprocess.TimeoutExpired, FileNotFoundError):
|
||||
return ""
|
||||
|
||||
|
||||
def get_cluster_role_bindings() -> list[dict]:
|
||||
output = run_kubectl(["get", "clusterrolebindings", "-o", "json"])
|
||||
if not output:
|
||||
return []
|
||||
try:
|
||||
return json.loads(output).get("items", [])
|
||||
except json.JSONDecodeError:
|
||||
return []
|
||||
|
||||
|
||||
def get_role_bindings() -> list[dict]:
|
||||
output = run_kubectl(["get", "rolebindings", "--all-namespaces", "-o", "json"])
|
||||
if not output:
|
||||
return []
|
||||
try:
|
||||
return json.loads(output).get("items", [])
|
||||
except json.JSONDecodeError:
|
||||
return []
|
||||
|
||||
|
||||
def get_cluster_roles() -> dict:
|
||||
output = run_kubectl(["get", "clusterroles", "-o", "json"])
|
||||
if not output:
|
||||
return {}
|
||||
try:
|
||||
items = json.loads(output).get("items", [])
|
||||
return {item["metadata"]["name"]: item.get("rules", []) for item in items}
|
||||
except json.JSONDecodeError:
|
||||
return {}
|
||||
|
||||
|
||||
def audit_cluster_admin_bindings(crbs: list[dict]) -> list[dict]:
|
||||
findings = []
|
||||
for crb in crbs:
|
||||
if crb.get("roleRef", {}).get("name") == "cluster-admin":
|
||||
for subject in crb.get("subjects", []):
|
||||
findings.append({
|
||||
"severity": "CRITICAL",
|
||||
"type": "cluster_admin_binding",
|
||||
"binding": crb["metadata"]["name"],
|
||||
"subject_kind": subject.get("kind", ""),
|
||||
"subject_name": subject.get("name", ""),
|
||||
"subject_namespace": subject.get("namespace", ""),
|
||||
"description": f"cluster-admin bound to {subject.get('kind', '')}/{subject.get('name', '')}"
|
||||
})
|
||||
return findings
|
||||
|
||||
|
||||
def audit_wildcard_permissions(roles: dict) -> list[dict]:
|
||||
findings = []
|
||||
for role_name, rules in roles.items():
|
||||
for rule in rules:
|
||||
verbs = rule.get("verbs", [])
|
||||
resources = rule.get("resources", [])
|
||||
api_groups = rule.get("apiGroups", [])
|
||||
|
||||
if "*" in verbs and "*" in resources:
|
||||
findings.append({
|
||||
"severity": "HIGH",
|
||||
"type": "wildcard_permissions",
|
||||
"role": role_name,
|
||||
"description": f"ClusterRole {role_name} has wildcard verbs and resources"
|
||||
})
|
||||
elif "*" in verbs:
|
||||
findings.append({
|
||||
"severity": "MEDIUM",
|
||||
"type": "wildcard_verbs",
|
||||
"role": role_name,
|
||||
"resources": resources,
|
||||
"description": f"ClusterRole {role_name} has wildcard verbs on {resources}"
|
||||
})
|
||||
return findings
|
||||
|
||||
|
||||
def audit_dangerous_permissions(roles: dict) -> list[dict]:
|
||||
findings = []
|
||||
for role_name, rules in roles.items():
|
||||
for rule in rules:
|
||||
verbs = set(rule.get("verbs", []))
|
||||
resources = set(rule.get("resources", []))
|
||||
dangerous_matches = resources.intersection(DANGEROUS_RESOURCES)
|
||||
has_dangerous_verbs = verbs.intersection(DANGEROUS_VERBS)
|
||||
|
||||
if dangerous_matches and has_dangerous_verbs:
|
||||
findings.append({
|
||||
"severity": "HIGH",
|
||||
"type": "dangerous_permission",
|
||||
"role": role_name,
|
||||
"resources": list(dangerous_matches),
|
||||
"verbs": list(has_dangerous_verbs),
|
||||
"description": f"ClusterRole {role_name} grants {list(has_dangerous_verbs)} on {list(dangerous_matches)}"
|
||||
})
|
||||
return findings
|
||||
|
||||
|
||||
def audit_default_service_accounts(rbs: list[dict], crbs: list[dict]) -> list[dict]:
|
||||
findings = []
|
||||
for binding in rbs + crbs:
|
||||
for subject in binding.get("subjects", []):
|
||||
if subject.get("kind") == "ServiceAccount" and subject.get("name") == "default":
|
||||
findings.append({
|
||||
"severity": "MEDIUM",
|
||||
"type": "default_sa_binding",
|
||||
"binding": binding["metadata"]["name"],
|
||||
"namespace": subject.get("namespace", "N/A"),
|
||||
"role": binding.get("roleRef", {}).get("name", ""),
|
||||
"description": f"Default service account in {subject.get('namespace', 'N/A')} has role binding"
|
||||
})
|
||||
return findings
|
||||
|
||||
|
||||
def generate_report(all_findings: list[dict], output_format: str = "text") -> str:
|
||||
critical = [f for f in all_findings if f["severity"] == "CRITICAL"]
|
||||
high = [f for f in all_findings if f["severity"] == "HIGH"]
|
||||
medium = [f for f in all_findings if f["severity"] == "MEDIUM"]
|
||||
|
||||
if output_format == "json":
|
||||
return json.dumps({
|
||||
"timestamp": datetime.utcnow().isoformat(),
|
||||
"summary": {"critical": len(critical), "high": len(high), "medium": len(medium)},
|
||||
"findings": all_findings
|
||||
}, indent=2)
|
||||
|
||||
lines = ["=" * 70, "KUBERNETES RBAC HARDENING AUDIT REPORT",
|
||||
f"Generated: {datetime.utcnow().isoformat()}", "=" * 70]
|
||||
lines.append(f"\nFindings: {len(critical)} Critical, {len(high)} High, {len(medium)} Medium")
|
||||
|
||||
for sev, items in [("CRITICAL", critical), ("HIGH", high), ("MEDIUM", medium)]:
|
||||
if items:
|
||||
lines.append(f"\n## {sev}")
|
||||
for f in items:
|
||||
lines.append(f" [{f['type']}] {f['description']}")
|
||||
|
||||
lines.append("\n" + "=" * 70)
|
||||
return "\n".join(lines)
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description="Kubernetes RBAC Audit Tool")
|
||||
parser.add_argument("--format", choices=["text", "json"], default="text")
|
||||
args = parser.parse_args()
|
||||
|
||||
crbs = get_cluster_role_bindings()
|
||||
rbs = get_role_bindings()
|
||||
roles = get_cluster_roles()
|
||||
|
||||
all_findings = []
|
||||
all_findings.extend(audit_cluster_admin_bindings(crbs))
|
||||
all_findings.extend(audit_wildcard_permissions(roles))
|
||||
all_findings.extend(audit_dangerous_permissions(roles))
|
||||
all_findings.extend(audit_default_service_accounts(rbs, crbs))
|
||||
|
||||
print(generate_report(all_findings, args.format))
|
||||
sys.exit(1 if any(f["severity"] == "CRITICAL" for f in all_findings) else 0)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user