mirror of
https://gitlab.kube.huskypup.net/Scooby/Homelabv4.git
synced 2026-08-21 05:26:49 +00:00
Initial commit
This commit is contained in:
@@ -0,0 +1,96 @@
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: grafana-dashboard-checkov
|
||||
namespace: grafana
|
||||
labels:
|
||||
grafana_dashboard: "1"
|
||||
data:
|
||||
checkov-compliance.json: |
|
||||
{
|
||||
"annotations": { "list": [] },
|
||||
"editable": true,
|
||||
"panels": [
|
||||
{
|
||||
"title": "Checkov Scanner Job Status",
|
||||
"type": "timeseries",
|
||||
"datasource": { "type": "prometheus", "uid": "Prometheus" },
|
||||
"targets": [
|
||||
{
|
||||
"expr": "kube_job_status_succeeded{namespace=\"checkov\"}",
|
||||
"legendFormat": "Succeeded"
|
||||
},
|
||||
{
|
||||
"expr": "kube_job_status_failed{namespace=\"checkov\"}",
|
||||
"legendFormat": "Failed"
|
||||
}
|
||||
],
|
||||
"gridPos": { "h": 8, "w": 12, "x": 0, "y": 0 }
|
||||
},
|
||||
{
|
||||
"title": "Last Successful Scan Age (hours)",
|
||||
"type": "stat",
|
||||
"datasource": { "type": "prometheus", "uid": "Prometheus" },
|
||||
"targets": [
|
||||
{
|
||||
"expr": "(time() - kube_cronjob_status_last_successful_time{namespace=\"checkov\", cronjob=\"checkov-scanner\"}) / 3600",
|
||||
"legendFormat": "Hours since last scan"
|
||||
}
|
||||
],
|
||||
"fieldConfig": {
|
||||
"defaults": {
|
||||
"unit": "h",
|
||||
"thresholds": {
|
||||
"steps": [
|
||||
{ "color": "green", "value": 0 },
|
||||
{ "color": "yellow", "value": 25 },
|
||||
{ "color": "red", "value": 49 }
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"gridPos": { "h": 8, "w": 12, "x": 12, "y": 0 }
|
||||
},
|
||||
{
|
||||
"title": "Kyverno Policy Violations (Audit Mode)",
|
||||
"type": "timeseries",
|
||||
"datasource": { "type": "prometheus", "uid": "Prometheus" },
|
||||
"targets": [
|
||||
{
|
||||
"expr": "sum(kyverno_policy_results_total{rule_result=\"fail\"}) by (policy_name)",
|
||||
"legendFormat": "{{policy_name}}"
|
||||
}
|
||||
],
|
||||
"gridPos": { "h": 8, "w": 24, "x": 0, "y": 8 }
|
||||
},
|
||||
{
|
||||
"title": "Kyverno Pass vs Fail",
|
||||
"type": "piechart",
|
||||
"datasource": { "type": "prometheus", "uid": "Prometheus" },
|
||||
"targets": [
|
||||
{
|
||||
"expr": "sum(kyverno_policy_results_total) by (rule_result)",
|
||||
"legendFormat": "{{rule_result}}"
|
||||
}
|
||||
],
|
||||
"gridPos": { "h": 8, "w": 12, "x": 0, "y": 16 }
|
||||
},
|
||||
{
|
||||
"title": "Tetragon Security Events",
|
||||
"type": "stat",
|
||||
"datasource": { "type": "prometheus", "uid": "Prometheus" },
|
||||
"targets": [
|
||||
{
|
||||
"expr": "sum(rate(tetragon_events_total{type=~\"PROCESS_KPROBE|PROCESS_TRACEPOINT\"}[1h]))",
|
||||
"legendFormat": "Events/hr"
|
||||
}
|
||||
],
|
||||
"gridPos": { "h": 8, "w": 12, "x": 12, "y": 16 }
|
||||
}
|
||||
],
|
||||
"schemaVersion": 39,
|
||||
"tags": ["checkov", "kyverno", "compliance", "zero-trust"],
|
||||
"time": { "from": "now-24h", "to": "now" },
|
||||
"title": "Zero Trust Compliance Overview",
|
||||
"uid": "zero-trust-compliance"
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
apiVersion: monitoring.coreos.com/v1
|
||||
kind: PrometheusRule
|
||||
metadata:
|
||||
name: checkov-alerts
|
||||
namespace: prometheus
|
||||
labels:
|
||||
release: kube-prometheus-stack
|
||||
spec:
|
||||
groups:
|
||||
- name: checkov-security
|
||||
rules:
|
||||
- alert: CheckovScanJobFailed
|
||||
expr: kube_job_status_failed{namespace="checkov", job_name=~"checkov-scanner.*"} > 0
|
||||
for: 5m
|
||||
labels:
|
||||
severity: warning
|
||||
annotations:
|
||||
summary: "Checkov security scan job failed"
|
||||
description: "The Checkov IaC scanner CronJob has failed. Check logs for details."
|
||||
|
||||
- alert: CheckovScanNotRunRecently
|
||||
expr: time() - kube_cronjob_status_last_successful_time{namespace="checkov", cronjob="checkov-scanner"} > 172800
|
||||
for: 1h
|
||||
labels:
|
||||
severity: warning
|
||||
annotations:
|
||||
summary: "Checkov scan has not run in 48+ hours"
|
||||
description: "The Checkov security scanner has not completed successfully in over 48 hours."
|
||||
@@ -0,0 +1,245 @@
|
||||
---
|
||||
# Checkov & Regula IaC Scanner CronJob
|
||||
# Scans Kubernetes manifests daily for security misconfigurations
|
||||
# Results stored in ConfigMap for Grafana dashboard consumption
|
||||
apiVersion: v1
|
||||
kind: Namespace
|
||||
metadata:
|
||||
name: checkov
|
||||
labels:
|
||||
app: checkov-scanner
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: ServiceAccount
|
||||
metadata:
|
||||
name: checkov-scanner
|
||||
namespace: checkov
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRole
|
||||
metadata:
|
||||
name: checkov-scanner
|
||||
rules:
|
||||
# Read all resources for scanning
|
||||
- apiGroups: [""]
|
||||
resources: ["pods", "services", "configmaps", "secrets", "namespaces", "serviceaccounts"]
|
||||
verbs: ["get", "list"]
|
||||
- apiGroups: ["apps"]
|
||||
resources: ["deployments", "statefulsets", "daemonsets", "replicasets"]
|
||||
verbs: ["get", "list"]
|
||||
- apiGroups: ["networking.k8s.io"]
|
||||
resources: ["ingresses", "networkpolicies"]
|
||||
verbs: ["get", "list"]
|
||||
- apiGroups: ["rbac.authorization.k8s.io"]
|
||||
resources: ["roles", "rolebindings", "clusterroles", "clusterrolebindings"]
|
||||
verbs: ["get", "list"]
|
||||
# Write scan results
|
||||
- apiGroups: [""]
|
||||
resources: ["configmaps"]
|
||||
verbs: ["create"]
|
||||
- apiGroups: [""]
|
||||
resources: ["configmaps"]
|
||||
verbs: ["update", "patch"]
|
||||
resourceNames: ["checkov-scan-results"]
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRoleBinding
|
||||
metadata:
|
||||
name: checkov-scanner
|
||||
roleRef:
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
kind: ClusterRole
|
||||
name: checkov-scanner
|
||||
subjects:
|
||||
- kind: ServiceAccount
|
||||
name: checkov-scanner
|
||||
namespace: checkov
|
||||
---
|
||||
apiVersion: batch/v1
|
||||
kind: CronJob
|
||||
metadata:
|
||||
name: checkov-scanner
|
||||
namespace: checkov
|
||||
labels:
|
||||
app: checkov-scanner
|
||||
spec:
|
||||
schedule: "0 3 * * *" # Daily at 3 AM
|
||||
concurrencyPolicy: Forbid
|
||||
successfulJobsHistoryLimit: 3
|
||||
failedJobsHistoryLimit: 3
|
||||
jobTemplate:
|
||||
spec:
|
||||
backoffLimit: 1
|
||||
activeDeadlineSeconds: 1800 # 30 min timeout
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: checkov-scanner
|
||||
spec:
|
||||
serviceAccountName: checkov-scanner
|
||||
restartPolicy: Never
|
||||
containers:
|
||||
- name: scanner
|
||||
image: docker.io/bridgecrew/checkov:3.2.334
|
||||
securityContext:
|
||||
runAsUser: 10000
|
||||
runAsGroup: 10000
|
||||
runAsNonRoot: true
|
||||
allowPrivilegeEscalation: false
|
||||
readOnlyRootFilesystem: true
|
||||
capabilities:
|
||||
drop: ["ALL"]
|
||||
seccompProfile:
|
||||
type: RuntimeDefault
|
||||
volumeMounts:
|
||||
- name: tmp
|
||||
mountPath: /tmp
|
||||
command:
|
||||
- python3
|
||||
- -c
|
||||
- |
|
||||
import subprocess, json, os, ssl, urllib.request, datetime
|
||||
|
||||
print("=== Checkov Kubernetes Security Scan ===")
|
||||
print(f"Scan started at: {datetime.datetime.utcnow().isoformat()}Z")
|
||||
|
||||
# Fetch Kubernetes resources as YAML files for scanning
|
||||
token_path = "/var/run/secrets/kubernetes.io/serviceaccount/token"
|
||||
ca_path = "/var/run/secrets/kubernetes.io/serviceaccount/ca.crt"
|
||||
api = "https://kubernetes.default.svc"
|
||||
|
||||
with open(token_path) as f:
|
||||
token = f.read().strip()
|
||||
|
||||
ctx = ssl.create_default_context(cafile=ca_path)
|
||||
headers = {"Authorization": f"Bearer {token}"}
|
||||
|
||||
os.makedirs("/tmp/k8s-manifests", exist_ok=True)
|
||||
|
||||
# Fetch deployments, statefulsets, daemonsets, pods, services
|
||||
# Focus on workload resources (skip pods/configmaps to reduce memory)
|
||||
resources = [
|
||||
("apps/v1", "deployments"),
|
||||
("apps/v1", "statefulsets"),
|
||||
("apps/v1", "daemonsets"),
|
||||
("v1", "services"),
|
||||
("networking.k8s.io/v1", "ingresses"),
|
||||
]
|
||||
|
||||
# Map API group to kind names
|
||||
kind_map = {
|
||||
"deployments": ("Deployment", "apps/v1"),
|
||||
"statefulsets": ("StatefulSet", "apps/v1"),
|
||||
"daemonsets": ("DaemonSet", "apps/v1"),
|
||||
"pods": ("Pod", "v1"),
|
||||
"services": ("Service", "v1"),
|
||||
"configmaps": ("ConfigMap", "v1"),
|
||||
"ingresses": ("Ingress", "networking.k8s.io/v1"),
|
||||
}
|
||||
for api_ver, kind in resources:
|
||||
url = f"{api}/apis/{api_ver}/{kind}" if "/" in api_ver and api_ver != "v1" else f"{api}/api/{api_ver}/{kind}"
|
||||
req = urllib.request.Request(url, headers=headers)
|
||||
try:
|
||||
with urllib.request.urlopen(req, context=ctx) as resp:
|
||||
data = json.loads(resp.read())
|
||||
kind_name, api_version = kind_map.get(kind, (kind.title(), api_ver))
|
||||
for item in data.get("items", []):
|
||||
# Ensure apiVersion and kind are set (required by checkov)
|
||||
item["apiVersion"] = api_version
|
||||
item["kind"] = kind_name
|
||||
# Remove status (not part of manifest)
|
||||
item.pop("status", None)
|
||||
ns = item.get("metadata", {}).get("namespace", "default")
|
||||
name = item.get("metadata", {}).get("name", "unknown")
|
||||
fname = f"/tmp/k8s-manifests/{kind}-{ns}-{name}.json"
|
||||
with open(fname, "w") as f:
|
||||
json.dump(item, f)
|
||||
except Exception as e:
|
||||
print(f"Warning: failed to fetch {kind}: {e}")
|
||||
|
||||
file_count = len(os.listdir("/tmp/k8s-manifests"))
|
||||
print(f"Fetched {file_count} Kubernetes resources")
|
||||
|
||||
# Run checkov scan on fetched manifests
|
||||
print("Scanning resources with Checkov...")
|
||||
result = subprocess.run(
|
||||
["checkov", "--framework", "kubernetes", "-d", "/tmp/k8s-manifests",
|
||||
"--output", "json", "--output-file", "/tmp/checkov-out",
|
||||
"--soft-fail", "--quiet"],
|
||||
capture_output=True, text=True
|
||||
)
|
||||
|
||||
# Parse results (checkov creates dir with results_json.json inside)
|
||||
passed = failed = skipped = 0
|
||||
results_file = "/tmp/checkov-out/results_json.json"
|
||||
if not os.path.isfile(results_file):
|
||||
results_file = "/tmp/checkov-out"
|
||||
try:
|
||||
with open(results_file) as f:
|
||||
data = json.load(f)
|
||||
if isinstance(data, list):
|
||||
for entry in data:
|
||||
s = entry.get("summary", {})
|
||||
passed += s.get("passed", 0)
|
||||
failed += s.get("failed", 0)
|
||||
skipped += s.get("skipped", 0)
|
||||
else:
|
||||
s = data.get("summary", {})
|
||||
passed = s.get("passed", 0)
|
||||
failed = s.get("failed", 0)
|
||||
skipped = s.get("skipped", 0)
|
||||
except Exception as e:
|
||||
print(f"Warning: failed to parse results: {e}")
|
||||
|
||||
print(f"\n=== Scan Summary ===")
|
||||
print(f"Passed: {passed}")
|
||||
print(f"Failed: {failed}")
|
||||
print(f"Skipped: {skipped}")
|
||||
scan_time = datetime.datetime.utcnow().strftime("%Y-%m-%dT%H:%M:%SZ")
|
||||
print(f"Scan completed at: {scan_time}")
|
||||
|
||||
# Store results in ConfigMap via Kubernetes API
|
||||
cm = json.dumps({
|
||||
"apiVersion": "v1",
|
||||
"kind": "ConfigMap",
|
||||
"metadata": {
|
||||
"name": "checkov-scan-results",
|
||||
"namespace": "checkov",
|
||||
"labels": {"app": "checkov-scanner"}
|
||||
},
|
||||
"data": {
|
||||
"last-scan": scan_time,
|
||||
"passed": str(passed),
|
||||
"failed": str(failed),
|
||||
"skipped": str(skipped),
|
||||
}
|
||||
}).encode()
|
||||
|
||||
# Try patch, then create
|
||||
try:
|
||||
req = urllib.request.Request(
|
||||
f"{api}/api/v1/namespaces/checkov/configmaps/checkov-scan-results",
|
||||
data=cm, headers={**headers, "Content-Type": "application/merge-patch+json"},
|
||||
method="PATCH")
|
||||
urllib.request.urlopen(req, context=ctx)
|
||||
print("Updated ConfigMap checkov-scan-results")
|
||||
except urllib.error.HTTPError as e:
|
||||
if e.code == 404:
|
||||
req = urllib.request.Request(
|
||||
f"{api}/api/v1/namespaces/checkov/configmaps",
|
||||
data=cm, headers={**headers, "Content-Type": "application/json"},
|
||||
method="POST")
|
||||
urllib.request.urlopen(req, context=ctx)
|
||||
print("Created ConfigMap checkov-scan-results")
|
||||
else:
|
||||
print(f"Warning: failed to store results: {e}")
|
||||
resources:
|
||||
limits:
|
||||
memory: 1Gi
|
||||
cpu: 500m
|
||||
requests:
|
||||
memory: 512Mi
|
||||
cpu: 100m
|
||||
volumes:
|
||||
- name: tmp
|
||||
emptyDir: {}
|
||||
Reference in New Issue
Block a user